第三章AOP
5.1AOP概念及优点
AOP为Aspect Oriented Programming的缩写,被称为面向切面编程。
AOP主要用于处理通用逻辑,例如日志记录,性能统计,安全控制,事务处理,异常处理等等。AOP可以将这些共通逻辑从普通业务逻辑代码中分离出来,这样在日后修改这些逻辑的时就不会影响普通业务逻辑的代码。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。
OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元。
AOP则是针对业务处理过程中切面进行提取,他所面对的是处理过程中某个步骤或阶段,以获得逻辑过程中各个部分之间低耦合性的隔离效果。
AOP需要以OOP为前提和基础。
什么是方面
方面是指封装统一处理的组件,该组件被作用到其他目标组件方法上。
什么是目标
目标是指被一个或多个方面作用的对象。
什么是切入点
切入点是用于指定那些组件和方法使用方面功能,在Spring中利用一个表达式指定切入目标。
Spring提供了以下常用的切入点表达式
方法限定表达式
executtion(修饰符?返回类型 方法名(参数)throws 异常类型?)
类型限定表达式
Within(包名.类名)
Bean名称限定表达式
Bean(“Bean的id或name属性”)
什么是通知
通知是用于指定方面组件和目标组件作用时机。例如方面功能在目标方法之前或之后执行等时机。
Spring框架提供以下集中类型的通知
前置通知:先执行方面功能再执行目标功能
后置通知:先执行目标功能再执行方面功能(目标无异常才执行方面)
最终通知:先执行目标功能再执行方面功能(目标有无异常都执行方面)
异常通知:先执行目标,抛出异常后执行方面
环绕通知:先执行方面前置部分,然后执行目标,最后再执行方面后置部分
Spring框架提供5中通知,可以按照下面try-catch-finally结构理解。
try{
//前置通知—执行方面
//环绕通知前置部分
//执行目标组件方法
//环绕通知后置部分
//后置通知—执行方面
}catch(){
//异常通知—执行方面
}finally{
//最终通知—执行方面
}
AOP实现原理
Spring AOP实现主要是基于动态代理技术。当Spring采用AOP配置后,Spring容器返回目标对象,实质上是Spring利用动态代理技术生成一个代理类。代理类重写了原目标组件方法的功能,再代理类中调用方面对象和目标对象功能。
Spring框架采用了两种动态代理实现:
利用cglib工具包
目标接口采用此方法,代理类是利用继承方法生成一个目标子类。
利用JDK Proxy API
目标有接口采用此方法,代理类是采用实现目标接口方法生成一个类。
5.1.1动态代理(JDK代理)
被代理对象必须要实现接口,才能产生代理对象.如果没有接口将不能使用动态代理技术
1.创建接口
package com.xyd.test;
public interface UserService {
void select();
void del();
void update();
void add();
}
2.创建实现类
package com.xyd.test;
public class UserServiceImpl implements UserService {
public void select() {
System.out.println("select:查询用户");
}
public void del() {
System.out.println("del:删除用户");
}
public void update() {
System.out.println("update:修改用户");
}
public void add() {
System.out.println("add:添加用户");
}
}
3.创建动态代理工厂
package com.xyd.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserServiceProxyFactory implements InvocationHandler{
private UserService userService;
public UserServiceProxyFactory(UserService userService) {
super();
this.userService = userService;
}
//生成代理
public UserService getUserService() {
UserService uService = (UserService) Proxy.newProxyInstance(this.getClass().getClassLoader(),
//代理目标对象
UserServiceImpl.class.getInterfaces(), (InvocationHandler) this);
return uService;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
Object invoke= method.invoke(userService, args);
System.out.println("关闭事务");
return null;
}
}
4.测试
package com.xyd.test;
import org.junit.Test;
public class TestCase {
@Test
public void test() {
//1.获取接口对象
UserService uService = new UserServiceImpl();
//2.获取动态代理工厂
UserServiceProxyFactory proxyFactory = new UserServiceProxyFactory(uService);
//3.获取代理对象
UserService service = proxyFactory.getUserService();
//4.执行方法
service.select();
service.add();
service.del();
service.update();
}
}
5.1.2cglib代理(没有接口)
1.定义一个Student接口
package com.xyd.test2;
public interface StudentDao {
void select();
void del();
void add();
void update();
}
2.学生的实现类,并没有实现接口,所以不能使用jdk动态代理,只能使用cglib动态代理
package com.xyd.test2;
/*
* 学生的实现类,并没有实现接口,所以不能使用jdk动态代理,只能使用cglib动态代理
*/
public class StudentDaoImpl {
public void select() {
System.out.println("select:查询学生");
}
public void add() {
System.out.println("add:增加学生");
}
public void del() {
System.out.println("del:删除学生");
}
public void update() {
System.out.println("update:修改学生");
}
}
3.动态代理类实现
package com.xyd.test2;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class StudentProxy implements MethodInterceptor {
private Object obj;
//创建代理对象
public Object getInstance(Object obj) {
this.obj = obj;
//帮我生成代理对象
Enhancer enhancer = new Enhancer();
//设置对谁进行代理
enhancer.setSuperclass(this.obj.getClass());
//代理要做什么
enhancer.setCallback(this);
//返回代理对象
return enhancer.create();
}
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
//业务方法之前
System.out.println("=========开启事务");
arg3.invokeSuper(arg0, arg2);
System.out.println("=========关闭事务");
return null;
}
}
4.测试
package com.xyd.test2;
import org.junit.Test;
public class TestCase {
@Test
public void test() {
//获取接口对象
StudentDaoImpl studentDaoImpl= new StudentDaoImpl();
//获取动态代理工厂
StudentProxy proxy = new StudentProxy();
//获取代理对象
StudentDaoImpl stu = (StudentDaoImpl) proxy.getInstance(studentDaoImpl);
//执行方法
stu.add();
stu.del();
stu.update();
stu.select();
}
}
5.2 Spring AOP相关术语
一、joinpoint(连接点):
目标对象中,所有都<可以增强>的方法,叫做连接点
二、Pointcut(切入点):
目标对象,<已经增强>的方法,叫做切入点
三、Advice(通知/增强):
1、<增强的>代码
2、所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.
|-通知分为
|-前置通知
-目标方法运行前调用
|-后置通知
-目标方法运行后调用
|-异常通知
-如出现异常就会调用
|-最终通知 (无论是否出现异常都会调用)
-目标方法运行后调用
|-环绕通知(切面要完成的功能)
-目标方法运行前后调用
四、Target(目标对象):
代理的目标对象(被代理的对象)
五、Weaving(织入):
将通知应用到切入点的过程
六、Proxy(代理):
将通知织入到目标对象之后,而形成代理对象
七、Aspect(切面):
切入点和通知结合
5.2XML配置实现AOP开发步骤
-
创建测试接口
package com.xyd.test; public interface UserService { void select(); void del(); void update(); void add(); }
-
准备目标对象
package com.xyd.test; public class UserServiceImpl implements UserService { public void select() { System.out.println("select:查询用户"); } public void del() { System.out.println("del:删除用户"); } public void update() { System.out.println("update:修改用户"); } public void add() { System.out.println("add:添加用户"); } }
-
准备通知
1、前置通知 |-目标方法运行前调用
2、后置通知 |-目标方法运行后调用
3、环绕通知 |-目标方法运行前后调用
4、异常通知 |-如出现异常就会调用
5、最终通知 |-(无论是否出现异常都会 调用) 目标方法运行后调用
package com.xyd.test; import org.aspectj.lang.ProceedingJoinPoint; public class UserAdvice { //指定前置通知方法 public void getBefore() { System.out.println("--前置通知--"); } //最终后置通知 public void getAfter() { System.out.println("--最终后置通知-无论是否出现异常都会调用--"); } //指定环绕通知 public Object getAround(ProceedingJoinPoint pj)throws Throwable { System.out.println("--环绕通知-前--"); Object proceed = pj.proceed();//调用目标方法 System.out.println("--环绕通知-后--"); return proceed; } //异常拦截 通知 public void getException() { System.out.println("--异常通知--"); } //后置通知 public void getAfterException() { System.out.println("--后置通知-如出现异常不会调用--"); } }
-
配置将通知织入目标对象
<!-- 1.配置目标对象 --> <bean name="userServiceImpl" class="com.xyd.test.UserServiceImpl"></bean> <!-- 2.配置通知对象 --> <bean name="userAdvice" class="com.xyd.test.UserAdvice"></bean> <!-- 3.将通知织入目标对象 --> <aop:config> <!-- 3.1配置切入点 --> <!-- 当前只对select方法增强 --> <aop:pointcut expression="execution(* com.xyd.test.UserServiceImpl.select())" id="pt"/> <!-- 3.2指定通知 --> <aop:aspect ref="userAdvice"> <!-- 3.2.1指定前置通知,并指定切入点 --> <aop:before method="getBefore" pointcut-ref="pt"/> <!-- 3.2.2指定后置通知 ,并指定切入点--> <aop:after method="getAfterException" pointcut-ref="pt"/> <!-- 3.2.3指定环绕通知,并指定切入点- --> <aop:around method="getAround" pointcut-ref="pt"/> <!-- 3.2.4指定异常拦截通知,并指定切入点- --> <aop:after-throwing method="getException" pointcut-ref="pt"/> <!-- 3.2.5指定异常拦截通知,并指定切入点--> <aop:after method="getAfter" pointcut-ref="pt"/> </aop:aspect> </aop:config>
5.测试(使用Spring提供的方式测试)
package com.xyd.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springmvc-aop.xml")
public class SpringTest {
@Resource(name="userServiceImpl")
private UserService userServiceImpl;
@Test
public void test1() {
userServiceImpl.select();
}
}
6.使用通配符给所有的方法增强:
通配符:
-
():无参数
-
(…):匹配任意个参数
-
(String**,*)匹配仅有2个参数的方法,且第一个参数为String
<aop:config> <!-- 3.1配置切入点 --> <!-- 当前只对select方法增强 --> <aop:pointcut expression="execution(* com.xyd.test.*ServiceImpl.*(..))" id="pt"/> <!-- 3.2指定通知 --> <aop:aspect ref="userAdvice"> <!-- 3.2.1指定前置通知,并指定切入点 --> <aop:before method="getBefore" pointcut-ref="pt"/> <!-- 3.2.2指定后置通知 ,并指定切入点--> <aop:after method="getAfterException" pointcut-ref="pt"/> <!-- 3.2.3指定环绕通知,并指定切入点- --> <aop:around method="getAround" pointcut-ref="pt"/> <!-- 3.2.4指定异常拦截通知,并指定切入点- --> <aop:after-throwing method="getException" pointcut-ref="pt"/> <!-- 3.2.5指定异常拦截通知,并指定切入点--> <aop:after method="getAfter" pointcut-ref="pt"/> </aop:aspect> </aop:config>
5.3 注解实现AOP
开发步骤:
1.创建测试接口
package com.xyd.test;
public interface UserService {
void select();
void del();
void update();
void add();
}
2.准备目标对象
package com.xyd.test;
public class UserServiceImpl implements UserService {
public void select() {
System.out.println("select:查询用户");
}
public void del() {
System.out.println("del:删除用户");
}
public void update() {
System.out.println("update:修改用户");
}
public void add() {
System.out.println("add:添加用户");
}
}
3.将通知织入目标对象
package com.xyd.test;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class UserAdvice {
//指定前置通知方法
@Before("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getBefore() {
System.out.println("--前置通知--");
}
//最终后置通知
@After("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getAfter() {
System.out.println("--最终后置通知-无论是否出现异常都会调用--");
}
//指定环绕通知
@Around("execution(* com.xyd.test.*ServiceImpl.*(..))")
public Object getAround(ProceedingJoinPoint pj)throws Throwable {
System.out.println("--环绕通知-前--");
Object proceed = pj.proceed();//调用目标方法
System.out.println("--环绕通知-后--");
return proceed;
}
//异常拦截 通知
@AfterThrowing("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getException() {
System.out.println("--异常通知--");
}
//后置通知
@AfterReturning("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getAfterException() {
System.out.println("--后置通知-如出现异常不会调用--");
}
}
- 配置Spring配置文件
<!-- 1.配置目标对象 -->
<bean name="userServiceImpl" class="com.xyd.test.UserServiceImpl"></bean>
<!-- 2.配置通知对象 -->
<bean name="userAdvice" class="com.xyd.test.UserAdvice"></bean>
<!-- 3.开启注解完成织入 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
5.测试
package com.xyd.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springmvc-aop2.xml")
public class SpringTest {
@Resource
private UserService userServiceImpl;
@Test
public void test1() {
userServiceImpl.del();
userServiceImpl.add();
userServiceImpl.select();
userServiceImpl.update();
}
}
public class UserServiceImpl implements UserService {
public void select() {
System.out.println("select:查询用户");
}
public void del() {
System.out.println("del:删除用户");
}
public void update() {
System.out.println("update:修改用户");
}
public void add() {
System.out.println("add:添加用户");
}
}
3.将通知织入目标对象
```java
package com.xyd.test;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class UserAdvice {
//指定前置通知方法
@Before("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getBefore() {
System.out.println("--前置通知--");
}
//最终后置通知
@After("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getAfter() {
System.out.println("--最终后置通知-无论是否出现异常都会调用--");
}
//指定环绕通知
@Around("execution(* com.xyd.test.*ServiceImpl.*(..))")
public Object getAround(ProceedingJoinPoint pj)throws Throwable {
System.out.println("--环绕通知-前--");
Object proceed = pj.proceed();//调用目标方法
System.out.println("--环绕通知-后--");
return proceed;
}
//异常拦截 通知
@AfterThrowing("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getException() {
System.out.println("--异常通知--");
}
//后置通知
@AfterReturning("execution(* com.xyd.test.*ServiceImpl.*(..))")
public void getAfterException() {
System.out.println("--后置通知-如出现异常不会调用--");
}
}
- 配置Spring配置文件
<!-- 1.配置目标对象 -->
<bean name="userServiceImpl" class="com.xyd.test.UserServiceImpl"></bean>
<!-- 2.配置通知对象 -->
<bean name="userAdvice" class="com.xyd.test.UserAdvice"></bean>
<!-- 3.开启注解完成织入 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
5.测试
package com.xyd.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springmvc-aop2.xml")
public class SpringTest {
@Resource
private UserService userServiceImpl;
@Test
public void test1() {
userServiceImpl.del();
userServiceImpl.add();
userServiceImpl.select();
userServiceImpl.update();
}
}