AOP是一种面向切面编程思想,也是面向对象设计(OOP)的一种延伸。
在Spring实现AOP有两种实现方式,一种是采用JDK动态代理实现,另外一种就是采用CGLIB代理实现,Spring是如何实现的在上篇已经讲到了Spring Bean的生命周期以及IOC源码解析
AOP可以做日志记录,或者事务回滚,在Spring的事务使用就是通过AOP进行事务的回滚的
JDK动态代理
这个是属于JDK提供的一种代理方式,需提供接口才能使用,主要用的类有两个:1、Proxy:这个主要是生成接口代理对象;2、InvocationHandler:反射射包下的一个接口,Proxy生成的代理接口对象,调用接口方法就会走InvocationHandler实现类的invoke() 方法
使用示例:
//创建一个接口
public class UserDao {
String getUserInfo();
}
//实现一个InvocationHandler接口的实现类
public class MyInvocationHandler implements InvocationHandler {
//需实现的方法
//proxy 代表当前对象自己,建议不要使用,如果使用的话会反复的调用自己,而调用自己会反复走当前invoke方法,容易出现栈溢出;
//method 指调用的方法Method;
//args 调用方法的参数;
@Override
public Object invoke(Object proxy, Method method, Object[] args){
//....
return null;
}
//获取接口代理对象
public <T> T getProxy(CLass<T> cls){
//第一个参数为类加载器,这个可以默认使用当前的AppClassLoader,即使用getClassLoader()方法接口
//需创建代理对象的接口class数组,
//invocationHandler的实现类对象,这里调用的是当前对象,调用的时候就会走当前对象的invoke方法
return (E) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[] {cls},this);
}
}
public static void main(Stirng[] args){
UserDao dao = new MyInvocationHandler().getProxy(UserDao.class);
//这样就能获取到了UserDao接口的代理对象了
}
在Spring当中是采用 JdkDynamicAopProxy 这个类实现的,可以去源码里看一下这个类继承了InvocationHandler
CGLIB代理
这个代理就比较厉害了,是通过ASM修改 .class字节码进行实现的,并且不需要接口,具体的组成结构可以去看一下大佬写的这篇CGLIB(Code Generation Library)
使用示例:
/**
* CGLIB 代理
*/
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SpringTest06Service.class);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println(Thread.currentThread().getName()+"run start...");
Object result = arg3.invokeSuper(arg0, arg2);
System.out.println(Thread.currentThread().getName()+"result:"+result);
System.out.println(Thread.currentThread().getName()+"run end...");
return result;
}
});
service = (SpringTest06Service) enhancer.create();
System.out.println(service);
String testAop = service.testAop("aaaaaaaa", "bbbbbbbbbbb");
System.out.println(testAop);
在Spring里面实现的类是 ObjenesisCglibAopProxy
上面就是两种代理的实现方式,但是在Spring里面是怎么使用的呢
Spring使用AOP
需引入Spring相关的AOP的包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
在配置类上需开启AOP进行方法拦截,@EnableAspectJAutoProxy 启动AOP代理,这个注解有两个参数
1、proxyTargetClass:这个是指定使用JDK动态代理拦截(false)还是使用CGLIB进行拦截(true),默认为false,
2、exposeProxy:表示是否能够让AopContext访问,默认为false就是不能访问。
源码是怎么解析可以自己去看,这个注解通过使用@Import注入了一个SmartInstantiationAwareBeanPostProcessor的实现类,可以自己调试查看,这里就不具体讲了,有空就在聊聊吧
最后配置相对应的切面拦截类
@Aspect
@Component
public class AopConfig {
private static final Log log = LogFactory.getLog(AopConfig.class);
//切点,需指定到具体修饰符,具体方法名以及具体参数类型,这里表示所有修饰符,com.test.SpringCoreTest.test06.service这个包下的所有类的所有方法并且参数为两个的类型为String的方法进行拦截
@Pointcut("execution(* com.test.SpringCoreTest.test06.service.*.*(String,String))")
public void pointCut() {}
//执行前,位于Around环绕方法前的后面执行
@Before(value = "pointCut()")
public void before() {
System.out.println("方法执行前 @Before ....");
}
//执行后,位于Around环绕方法后的后面执行
@After(value = "pointCut()")
public void after() {
System.out.println("方法执行后 @After ....");
}
//即将返回执行
@AfterReturning(value = "pointCut()")
public void afterReturning() {
System.out.println("方法执行后返回 @AfterReturning ....");
}
//抛出异常执行
@AfterThrowing(value="pointCut()",throwing="e")
public void afterThrowing(Exception e) {
System.out.println("方法执行异常后返回 @AfterThrowing ....");
System.out.println(e);
}
//环绕方法,能够拦截执行参数,并且能够修改
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around方法执行前@Around ....");
Object obj = joinPoint.proceed();
System.out.println("around方法执行后@Around ....");
return obj;
}
}
配置好后,调用拦截的方法就可以触发AOP拦截了。