- 目录
- 前言
- AOP的理解
- AOP基本概念
- AOP实现原理
- AOP案例
- AOP解析器
- AOP解析器:AspectJAutoProxyBeanDefinitionParser
- 注册AnnotationAwareAspectJAutoProxyCreator
- 处理proxy-target-class和ExposeProxy
- 增强器
- 获取增强器
- 解析切面中的增强方法
- 创建增强器
- 增强器的调用
- 选择适用的增强器
- 创建代理
- 代理创建器:AbstractAutoProxyCreator
- JDK动态代理:JDKDynamicAopProxy
- 方法增强拦截器:advice的织入
- 使用反射调用方法
- 通过 ReflectiveMethodInvocation 调用方法
IOC/DI , AOP 是Spring最重要的两个特性 ,也是面试高频被问到的部分,前面我们已经分析了Spring的IOC相关源码以及DI依赖注入相关源码,从本篇文章开始我们着手分析Spring的AOP源码 。
开始之前,你需要对AOP 原理,JDK动态代理,CGLIB动态代理有一定的理解。这里先上一个图,后面源码分析的时候可以看着图来
AOP是为面向切面编程,为什么要面向切面,何为切面?我们知道对于OOP面向对象而言在某些开发场景中是有一定不足,由于面向对象的思想是纵向的,它面对的是一个一个的对象,当我们需要在多个类中引入同一个公共的业务时(比如:事务,操作日志等),那么在每个类中都要引入公共的业务代码,从而造成代码大量重复,代码结构不优雅,不方便维护 ,这个时候就需要使用到面向切面的编程来解决这个问题。使用AOP可以把分散在多个类中的公共的代码剥离出来,和业务本身的代码解耦, 然后通过动态代理将公共业务代码作用到多个对象,代码结构也更加优雅。
所以可以认为 面向切面 是对 面向对象 的补充,它的思想是横向的,它面向的是一个切面,如果把一个对象看做一个点,那么多个对象就是一个面,是为切面,AOP多用于:事务,日志,监控,流控等等业务场景。
AOP的实现原理是基于动态代理,动态代理就是在运行时期动态的为某个类(原生类)生成代理类以达到代码增强的目的,且代理类是持有原生类的,可以在代理类中调用原生类以及做一些增强业务。
动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包。两者的区别是JDK动态代理要求原始类(被代理类)需要实现至少一个接口。而CGLIB则是基于继承进行代理,原生类可以不实现任何接口。
对于Spring而言默认采用JDK动态代理,如果原生类没有实现任何接口,Spring会选择CGLIB代理,或者你可以通过配置文件强制指定使用CGLIB代理。
AOP案例下面我们使用AOP来做一个事务管理案例:在每个Service方法执行前后添加事务的代码
1.创建一个普通类,这个类的方法需要有事务
@Service
public class UserServiceImpl implements IUserService {
public void insert() {
System.out.println("UserServiceImpl.insert:保存User...");
}
public void delete() {
System.out.println("UserServiceImpl.delete:删除User");
}
}
2.创建一个切面类,这个类里面提供公共的业务代码,即:事务代码
@Component
@Aspect
public class TranscationManager {
//定义切点,表达式作用于所有到service的所有的方法
@Pointcut("execution(* cn.xxx.*.service.*.*(..))")
public void pointcut(){}
//前置通知 , 方法执行前执行,用来开启事务
@Before("pointcut()")
public void begin(JoinPoint joinPoint){
System.out.println("TranscationManager.begin:开启事务...:");
}
//后置返回通知 ,方法正常返回后执行, 用来提交事务
@AfterReturning("pointcut()")
public void commit(){
System.out.println("TranscationManager.commit:提交事物...");
}
//异常通知,方法出现异常调用,用来回滚事务
@AfterThrowing(value = "pointcut()",throwing="e")
public void rollback(JoinPoint joinPoint,Throwable e){
System.out.println("TranscationManager.rollback:回滚事物咯...:"+e.getMessage());
}
//最终通知,不管方法会不会出现异常,都会执行,用来关闭资源
@After("pointcut()")
public void close(){
System.out.println("TranscationManager.close:关闭连接...");
}
//环绕通知,拦截原始类的类的方法,可以通过 joinPoint调用原始的类
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
return null;
}
}
3.配置Spring支持AOP注解
4.测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application_aop.xml")
public class AopTest {
@Autowired
private IUserService userService ;
@Test
public void testAop(){
userService.insert();
System.out.println("======================================");
userService.delete();
}
}
控制台效果
TranscationManager.begin:开启事务…: UserServiceImpl.insert:保存User… TranscationManager.commit:提交事物… TranscationManager.close:关闭连接… ====================================== TranscationManager.begin:开启事务…: UserServiceImpl.delete:删除User… TranscationManager.commit:提交事物… TranscationManager.close:关闭连接…
这里我们看到了,使用AOP可以把公共的代码剥离出去和业务变身代码解耦,同时也方便扩展。
怎么理解这个AOP案例,首先要明白需求:在多个service方法执行前,否 或 出现异常做出相应的事务处理。我们把UserService看着原生类, 把TranscationManager 切面看着是增强代码,那么Spring是如何做到把 TranscationManager 的增强逻辑 加入到 UserService的方法前后的呢?
-
找到所有原生类(被代理类):我们在 TranscationManager中定义了一个切点:
@Pointcut("execution(* cn.xxx.*.service.*.*(..))")
:这切点定义了一个表达式,这个表达式的含义就是找到cn.xxx包下所有的service的所有方法,Spring就知道了需要拦截这些方法的执行。 -
找到了service的方法,如何在方法前,后做事情?我们在TranscationManager中定义了@Before(“pointcut()”) 前置通知,@AfterReturning(“pointcut()”)后置通知等,当我们调用userService的方法之前就会触发 Before 前置通知中的逻辑 ,方法执行完成就会触发 AfterReturning 后置通知的逻辑,异常通知和最终通知也是一个道理。
-
实现原理就是动态代理,其实Spring为所有切点切到的类都生成了代理类,也就是说在test类中注入的
@Autowired private IUserService userService ;
其实并不是我们写的那个UserService,而是基于UserService代理出来的一个代理类。代理类持有原生类,且代理类和原生类有相同的方法,所以你感觉你在调用UserService,其实在调用代理类。在代理类中的方法对原生类方法做了增强 ,而增强的逻辑就是 TranscationManager 中的逻辑,比如调用userService.insert 实则进入了代理类的insert方法,方法中先调用TranscationManager@Before 前置通知业务,然后调用原生类的insert,方法结尾调用TranscationManager@AfterReturning 后置通知,出现异常调用TranscationManager@AfterThrowing异常通知等等。这样是不是就达到了上面的增强效果了。
如果没有去研究过JDK动态代理和CGLIB代理可能你看起来比较晕,对于Spring切面的东西这里不做太多解释了,不是本篇主要内容,下面我就对于这个AOP案例做源码分析。
AOP解析器在上面的案例中我们通过 配置来开启AOP支持,稍微动动脑袋能想到Spring一定会有一个类来处理该配置。 在Spring中有一个接口叫 NamespaceHandlerSupport ,它提供了Spring配置文件的namespace的解析支持。 其中有一个实现叫 AopNamespaceHandler , 它是针对于
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?