文章目录
1. Spring AOP 与 AspectJ 的关系
Spring AOP 要实现的是在我们原来写的代码的基础上,进行一定的包装,如在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者增强处理。Aop依赖于IOC,Aop可以看做是调用IOC的后置处理器来实现的。 默认地,如果使用接口的,用 JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现。Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这也是为什么我们不需要显式的引入这两个依赖。
作为 Java 开发者,我们都很熟悉 AspectJ 这个词,甚至于我们提到 AOP 的时候,想到的往往就是 AspectJ,但真实情况是Spring虽然提供了AspectJ的支持,但只用到的AspectJ的切点解析和匹配。比如 @Aspect、@Pointcut、@Before、@After 、@Around 等注解都是来自于 AspectJ,利用AspectJ的解析execution、@Annotation等表达式的能力去解析,因为AspectJ也是一个优秀的框架,Spring为了不重复造轮子嘛,就利用到了这些。但是动态代理功能的实现是纯 Spring AOP 自己实现的。AspectJ 能干很多 Spring AOP 干不了的事情,它是 AOP 编程的完全解决方案。Spring AOP 致力于解决的是企业级开发中最普遍的 AOP 需求(方法织入),而不是力求成为一个像 AspectJ 一样的 AOP 编程完全解决方案。
在性能方面,由于Spring AOP 是基于代理实现的,在容器启动的时候需要生成代理实例,在方法调用上也会增加栈的深度,使得 Spring AOP 的性能不如 AspectJ 那么好。
Spring AOP术语解释
- 切面(Aspect):横切业务代码,带有@Aspect注解的类,被称为切面类,用于存放不同的切点、通知方式(@Around)和切点逻辑等。
- 连接点(Join point):在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。
- 通知(Advice): 前置通知@Before、后置@After、环绕@Around等等多种通知类型,不同的通知类型决定在不同的地方执行增强代码。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
- 顾问(advisor):是Advice的一种包装,是Pointcut和Advice的一种结合!
- 切点(Pointcut):类似于连接点,表示从哪个位置切入,一般与通知关联使用。
- 织入(Weaving): 把通知逻辑切入连接点的过程
- 引入(Introduction): 把其他接口和实现 动态的引入到目标类的过程
问题:为什么spring 不使用AspectJ全套的东西呢?而是只使用了部分呢?
猜测原因如下:
- ①:AspectJ大部分内容是动态植入,因为AspectJ编译后的文件是.aj 结尾的,JVM编译后的是.class,如果spring使用AspectJ的动态植入,那么就要使用AspectJ的编译器,JVM是肯定编译不了的,无疑增加了开发成本!所以spring自己实现了一套代码实现植入增强!
- ②:Spring在引入别的框架时,理念是取其精华、弃其糟粕。取Aspect对自己有用的理念和切点解析部分,舍弃掉了会增加开发成本的部分!
2. JDK和Cglib动态代理的区别
相同点 :
- JDK动态代理和Cglib动态代理在 jdk1.7版本后都使用修改字节码的方式来进行代理。
不同点:
- ①:如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
- ②:JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。
- ③:JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()
- ④:如果目标对象没有实现接口,必须采用CGLIB,主要方式是对指定的类生成一个子类,覆盖其中的方法。Spring会自动在JDK动态代理和CGLIB之间转换。
- ⑤:Cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类。
- ⑥:每一次jdk版本升级,jdk代理效率都得到提升,1.8版本已经略高于CGLIB代理
- ⑦:Cglib会重复调用动态代理类,而JDK不会!!
代理类型 | JDK | Cglib |
---|---|---|
使用场景 | 目标类实现了接口,且未指定ProxyTargetClass = true | 目标类未实现接口 |
代理类的字节码文件数量 | 根据接口生成1个$proxy.class文件 | 根据具体类生成多个cglib.class文件 |
调用 原始方法 的方式 | 反射 | 直接调用(正因为直接调用速度快,所以cglib在调用时比jdk快) |
在被增强的方法中调用其他方法时 | 其他方法不会被增强,动态代理类只调用一次 | 其他方法会被增强,因为每一个方法都会调用动态代理类! |
代码形式 | InvocationHandler.invoke | MethodInterceptor.intercept |
2.1 JDK动态代理
JDK动态代理实现: 实现InvocationHandler
和 实现目标类的接口
,通过(Proxy.newProxyInstance)
创建目标类的代理类
// 接口
public interface UserService {
void save();
}
// 目标类
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("保存用户");
}
}
// InvocationHandler实现
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
Object result = method.invoke(target, args);
System.out.println("后置增强");
return result;
}
}
// 生成代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new MyInvocationHandler(new UserServiceImpl())
);
proxy.save();
为什么JDK动态代理必须基于接口?
根本原因
:JDK动态代理的实现依赖于Java原生的Proxy
类,其设计初衷是基于接口的代理,这是由Java语言规范和反射机制决定的。生成的代理类文件格式为$Proxy0
实现方式如下:
-
动态生成的代理类需要实现指定接口:
Proxy.newProxyInstance()
方法要求传入目标接口列表,生成的代理类会直接实现这些接口,并通过InvocationHandler
将方法调用转发给目标对象。 -
Java单继承的限制:生成的代理类一定继承自
Proxy
类(已固定),但Java不支持多继承,因此无法再继承目标类,只能通过实现接口的方式复用目标类的方法
2.2 Cglib动态代理
CGLIB动态代理实现:实现MethodInterceptor
和 继承目标类
,通过enhancer.create()
创建目标类的代理类
// 目标类(无需接口)
public class UserService {
public void save() {
System.out.println("保存用户");
}
}
// MethodInterceptor实现
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置增强");
Object result = proxy.invokeSuper(obj, args); // 调用父类方法
System.out.println("后置增强");
return result;
}
}
// 生成代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MyMethodInterceptor());
UserService proxy = (UserService) enhancer.create();
proxy.save();
为什么CGLib需要基于继承?
根本原因
:CGLib通过继承目标类并重写方法来实现动态代理,这是为了绕过JDK动态代理必须依赖接口的限制
。注意:并不是说一定要设计出来一个Cglib代理,而是为了解决有些目标类没有实现接口,但又需要AOP时(此时无法使用JDK动态代理)提供的一种解决方案。生成的代理类文件格式为UserEntity$$EnhancerByCGLIB
实现方式如下:
- 通过生成目标类的子类:代理类继承目标类,覆盖非final方法,并在方法中插入拦截逻辑(通过
MethodInterceptor
)。 - 利用字节码操作技术(
ASM
):直接操作字节码生成子类,无需目标类实现接口。
3. Spring AOP应用案例
@Aspect //切面
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect {
protected Logger logger = LoggerFactory.getLogger(getClass());
//切入点,寻找带有@DataSource注解的方法
@Pointcut("@annotation(com.chinalife.policy_manage.common.datasource.annotation.DataSource) " +
"|| @within(com.chinalife.policy_manage.common.datasource.annotation.DataSource)")
public void dataSourcePointCut() {
}
//环绕通知 ProceedingJoinPoint 连接点/切入点
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
//获取目标类
Class targetClass = point.getTarget().getClass();
//获取目标方法
Method method = signature.getMethod();
//获取目标类上的@DataSource注解
DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
//获取目标方法上的@DataSource注解
DataSource methodDataSource = method.getAnnotation(DataSource.class);
//如果@DataSource注解不为空,执行增强逻辑
if(targetDataSource != null || methodDataSource != null){
String value;
if(methodDataSource != null){
value = methodDataSource.value();
}else {
value = targetDataSource.value();
}
DynamicContextHolder.push(value);
logger.debug("set datasource is {}", value);
}
try {
return point.proceed();
} finally {
DynamicContextHolder.poll();
logger.debug("clean datasource");
}
}
}
4. Spring AOP有几种配置方式?
- ①: Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的(如:MethodInterceptor、MethodBeforeAdvice)。
- 实现MethodBeforeAdvice:≈ 前置通知
public class LogAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { String methodName = method.getName(); System.out.println("执行目标方法【" + methodName + "】的<前置通知>,入参" + Arrays.asList(args)); } }
- 实现MethodInterceptor ≈ 环绕通知
然后把他们注册进容器中!即可实现增强逻辑,运行结果如下:public class LogInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(getClass()+"调用方法前"); Object ret=invocation.proceed(); System.out.println(getClass()+"调用方法后"); return ret; } }
此中方法有个致命的问题,如果我们只能指定单一的Bean的AOP, 如果多个Bean需要创建多个ProxyFactoryBean 。而且,我们看到,我们的拦截器的粒度只控制到了类级别,类中所有的方法都进行了拦截。
后来有了升级版,通过配置 Advisor(内部封装了Advice通知),精确定位到需要被拦截的方法,然后使用内部的 Advice 执行逻辑处理。@Bean public NameMatchMethodPointcutAdvisor tulingLogAspect() { NameMatchMethodPointcutAdvisor advisor=new NameMatchMethodPointcutAdvisor(); // 通知(Advice) :是我们的通知类 // 通知者(Advisor):是经过包装后的细粒度控制方式。 advisor.setAdvice(tulingLogAdvice()); advisor.setMappedNames("div"); return advisor; }
- 实现MethodBeforeAdvice:≈ 前置通知
- ②: Spring 2.0 XML 配置:Spring 2.0 以后使用 XML 的方式来配置,使用命名空间 ,主要是针对xml形式来配置!
- ③:Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫做 @AspectJ,但是这个和 AspectJ 其实没啥关系。
5. Spring AOP源码解析
Aop源码大概分为以下几步:
- spring boot 自动配置
AopAutoConfiguration
类中带有@EnableAspectJAutoProxy
,项目启动即开启对spring AOP的支持,该注解注册了AnnotationAwareAspectJAutoProxyCreator
类,该类实现了bean的后置处理器,可以在类创建过程中做一些其他操作 - 在bean后置处理器的
postProcessBeforeInstantiation
方法中,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache中! - 在创建每一个bean时,在bean的后置处理器中的
postProcessAfterInitialization
方法中,拿到缓存中所有的Advisor,根据切入点PointCut与当前bean做匹配,匹配成功与否决定是否需要创建动态代理!如果匹配到了,则根据实际情况创建动态代理 - 调用目标方法时,会调用经过动态代理增强的方法逻辑 !
5.1 自动配置类 AopAutoConfiguration 开启对Aop的支持
在spring boot项目中,项目启动时会自动加载许多自动配置类,以完成项目结构!其中就有AopAutoConfiguration
,该类的作用就是为项目提供Aop的支持,一种是jdk
动态代理,一种是cglib
动态代理
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
//自动配置类
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
//开启自动代理:@EnableAspectJAutoProxy
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
//jdk动态代理
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
//Cglib动态代理
static class CglibAutoProxyConfiguration {
}
}
可以看到自动配置类AopAutoConfiguration
除了帮我们配置了jdk动态代理和cglib动态代理,还有一个注解@EnableAspectJAutoProxy
,这个注解内部通过@Import
导入了一个bean定义的注册器AspectJAutoProxyRegistrar
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class) //通过`@Import`导入了一个bean定义的注册器
public @interface EnableAspectJAutoProxy {
这个注册器帮我们注册了一个Aop中非常重要的类AnnotationAwareAspectJAutoProxyCreator
!该类实现了bean的后置处理器BeanPostProcessor
,可以在类创建前后做一些操作,具体如下:
- 在
postProcessBeforeInstantiation
方法中,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache中! - 在
postProcessAfterInitialization
方法中,匹配切入点,创建动态代理
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 注册了一个Aop中非常重要的bean的后置处理器`AnnotationAwareAspectJAutoProxyCreator`!
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
看一下AnnotationAwareAspectJAutoProxyCreator
的继承关系:
5.2 解析切面类,封装成Advisor
①: 通过bean的后置处理器解析切面类,把通知封装成Advisor,并放入advisorsCache缓存中!
与spring事务一样,Aop也是通过bean的后置处理器解析带有@AspectJ的bean,这个bean的后置处理器在容器创建的时候就被注册,在解析时可以直接调用!
注意:下图中的AspectJAwareAdvisorAutoProxyCreator
正是AnnotationAwareAspectJAutoProxyCreator
的父类
Spring AOP发生在创建bean的时候,也就是finishBeanFactoryInitialization()
内部的creatBean()
方法中
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
//创建bean的方法
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
在creatBean()
方法内部,resolveBeforeInstantiation
方法会扫描@Aspect
注解,解析切面类,把通知封装成Advisor,并放入缓存advisorsCache
中!
try {
/**
* 第一次调用bean的后置处理器,事务在这里不会被调用,aop的才会被调用
* 为啥aop在这里调用了?因为在此处需要解析出对应的切面保存到缓存中
*/
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
进入resolveBeforeInstantiation
方法:
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// 如果有bean后置处理器: InstantiationAwareBeanPostProcessors
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
//调用 postProcessBeforeInstantiation 方法
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
//调用 postProcessAfterInitialization 方法
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
在实例化bean之前,会第一次调用bean的后置处理器,解析到所有的@AspectJ
的类,保存到缓存中。那怎么解析的呢?进入上文resolveBeforeInstantiation
方法中的applyBeanPostProcessorsBeforeInstantiation
方法中!
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
/**
* 获取容器中的所有后置处理器
*/
for (BeanPostProcessor bp : getBeanPostProcessors()) {
//判断后置处理器是不是InstantiationAwareBeanPostProcessor
if (bp instanceof InstantiationAwareBeanPostProcessor) {
//把我们的BeanPostProcessor强制转为InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
/**
* 【很重要】
* 我们AOP @EnableAspectJAutoProxy 为我们容器中导入了 AnnotationAwareAspectJAutoProxyCreator
* 我们事务注解@EnableTransactionManagement 为我们的容器导入了 InfrastructureAdvisorAutoProxyCreator
* 都是实现了我们的 BeanPostProcessor接口,InstantiationAwareBeanPostProcessor,
* 进行后置处理解析切面
*/
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
//构建我们的缓存key
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//已经被解析过 直接返回
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
/**
* 判断是不是基础的bean
* 判断是不是应该跳过 (aop解析直接解析出我们的切面信息(并且把我们的切面信息进行保存),而事务在这里是不会解析的)
*/
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
由于AOP使用的是AnnotationAwareAspectJAutoProxyCreator
类,所以选择这个类中的shouldSkip方法,在所有的bean定义中选择是否跳过,如果带有@AspectJ
注解,就不跳过,把这个类中的带有@Before、@After、@Around
等注解的方法封装成一个个Advisor(顾问),它是Pointcut和Advice的一种结合! 并添加进集合中
@Override
protected List<Advisor> findCandidateAdvisors() {
//找出事务相关的advisor
List<Advisor> advisors = super.findCandidateAdvisors();
//找出Aspect相关的信息之后封装为一个advisor
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
//返回我们所有的通知
return advisors;
}
buildAspectJAdvisors
方法内部会把带有下面的注解的挨个解析成Advisor
//获取到切面类中的所有方法,但是该方法不会解析标注了@PointCut注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
//挨个去解析我们切面中的方法
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
//看是否含有这些注解!
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
把解析到的Advisor放入advisorsCache缓存中
//加入到缓存中
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
原理图:
注意:以上解析切面的操作,是在bean的第一个后置处理器postProcessBeforeInstantiation中完成的!
5.3 匹配并创建动态代理
② 根据 Advisor 中的 PointCut 决定当前bean是否创建动态代理
我们都知道创建动态代理的时机是在初始化之后(如果存在循环依赖则在实例化之后!),所以在源码中创建动态代理在doCreateBean
方法中的initializeBean
方法中,这个方法内部调用了bean的后置处理器postProcessAfterInitialization
,在后置处理器中完成了判断和动态代理的创建
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
//1.回调各种 Aware 接口
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//2.调用 BeanPostProcessorsBeforeInitialization 扩展
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//3.调用实现InitializingBean的afterPropertiesSet方法
// 调用xml方式的 bean标签里配置init-mothod属性
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//4.调用 BeanPostProcessorsAfterInitialization 扩展,动态代理在这里!
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
***********************************************************************************************
==================== applyBeanPostProcessorsAfterInitialization 内部创建动代理 ==================
***********************************************************************************************
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
/** 【很重要】
* 我们AOP @EnableAspectJAutoProxy 为我们容器中导入了 AnnotationAwareAspectJAutoProxyCreator
* 我们事务注解@EnableTransactionManagement 为我们的容器导入了 InfrastructureAdvisorAutoProxyCreator
* 都是实现了我们的 BeanPostProcessor接口,InstantiationAwareBeanPostProcessor,
* 在这里实现的是BeanPostProcessor接口的postProcessAfterInitialization来生成我们的代理对象 */
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
AbstractAutoProxyCreator
抽象类扩展了这个后置处理器
/**
* 在该后置方法中 我们的事务和aop的代理对象都是在这生成的
* @param bean bean实例
* @param beanName bean的名称
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
//获取缓存key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//如果循环依赖时已经创建了代理,在这里把他移除掉!!
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//找到合适的就会被代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary
方法如下:主要内容就是拿到所有通知与当前类匹配,如果匹配成功则创建动态代理
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//已经被处理过
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//排除掉不需要增强的,比如Aop一些基础类
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//是不是基础的bean 是不是需要跳过的
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//***如果Advisor中的切点表达式命中了这个类,就返回适合本类的通知器Advisor!
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//我们的合适的通知器不为空
if (specificInterceptors != DO_NOT_PROXY) {
//表示当前的对象已经代理模式处理过了
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建我们的真正的代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
//加入到缓存
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
其中getAdvicesAndAdvisorsForBean
方法中拿到了所有的Advisor与当前bean进行了匹配,返回合适本类的通知器advisor,如果合适的advisor
为空,则返回DO_NOT_PROXY
,不需要代理,表示不需要代理。
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
/**
* 找到合适的增强器对象advisor
*/
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
//若合适的通知器为空,则返回DO_NOT_PROXY,不需要代理
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//找到ioc容器中候选的通知 (找到Aop扫描到所有通知的Advisor,注意是所有的!!!)
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//判断我们的通知Advisor能不能作用到当前的类上,返回合适本类的通知器advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//加了一个内置的advisor
extendAdvisors(eligibleAdvisors);
//对我们的advisor进行排序,如果有多个切面类,则根据order排序
//排序方式:异常--返回通知--后置通知--前置通知
//这样排序的原因是,后边调用目标方法会讲!!
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findAdvisorsThatCanApply
的调用链很深,这里就不再跟进了。
以上代码其实都是匹配阶段的代码,如果匹配成功,则进入上文wrapIfNecessary方法中的createProxy方法中,开始真正创建动态代理对象
创建动态代理是在createProxy方法中由ProxyFactory代理工厂来创建的
//创建一个代理对象工厂
ProxyFactory proxyFactory = new ProxyFactory();
。。。
//真正的创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
createAopProxy()
: 该方法用来创建我们的代理对象
代理形式由ProxyTargetClass和是否实现接口来决定!!
- ①:我们代理的类没有实现接口,那么会直接走cglib代理
- ②:我们代理的类实现了接口,且ProxyTargetClass 指定为false才会走jdk动态代理,如果ProxyTargetClass指定的有值,则还是使用cglib代理
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//判断我们是否前置指定使用cglib代理ProxyTargetClass =true fasle
//判断是否实现了接口
//判断是否是Optimize() 可手动设置,一般为false
//三个判断只要有一个是true,就会使用cglib动态代理!
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
//所targetClass是接口 使用的就是jdk代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
//cglib代理
return new ObjenesisCglibAopProxy(config);
}
else {
//动态代理
return new JdkDynamicAopProxy(config);
}
}
至此,目标类的动态代理创建完成!
5.4 调用代理类增强逻辑
③ 调用目标方法
如果使用的时jdk动态代理,在调用目标方法时会进入JdkDynamicAopProxy
中的 invoke
方法,把通知加入责任链,把Advisor转换成Inteceptor,通过责任链的方式递归调用proceed()方法完成对方法的增强调用处理
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
//获取到我们的目标对象
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//若执行代理对象的equals方法不需要代理
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
//若执行的是hashCode方法 不需要代理
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
//若执行的class对象是DecoratingProxy 也不要拦截器执行
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
/**
* 这个配置很重要很实用【暴露我们的代理对象到线程变量中】需要搭配@EnableAspectJAutoProxy(exposeProxy = true)
* 一起使用.
* 比如我们的aop中 multi和 mode方法都是被切入的方法,但是在切入的方法中通过
* this来调用另外一个方法的时候,那么该方法就不会被代理执行,而是通过方法内部执行
*还有的就是事务方法调用事务方法的时候 也需要这样
*/
if (this.advised.exposeProxy) {
//把我们的代理对象暴露到线程变量中
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//获取我们的目标对象
target = targetSource.getTarget();
//获取我们目标对象的class
Class<?> targetClass = (target != null ? target.getClass() : null);
//把我们的aop的advisor 转化为拦截器,相当于责任链的Handler顶级接口
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//加入我们的拦截器链为空
if (chain.isEmpty()) {
//通过反射直接调用执行
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
//创建一个方法调用对象
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//调用执行,注意proceed方法是递归调用,
//会把需要的通知 通过责任链模式全部调用
retVal = invocation.proceed();
}
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
注意:如果是jdk动态代理,在invoke方法中,会把代理对象暴露到本地线程变量ThreadLocal中,这样做的目的是:在A方法中调用另一个B方法时,保证两个方法都享受到动态代理的增强! 如果没有暴露出来,那么在调用B方法时,B方法是不会有增强逻辑的!而cglib就不存在这样的问题,因为他每次调用都会重新获取代理对象!
把advisor
转化为Inteceptor
拦截器,用于责任链调用,这个拦截器就相当于责任链中的顶级接口Handler。然后递归调用proceed方法
@Override
@Nullable
public Object proceed() throws Throwable {
//执行到了最后一个拦截器的时候(从-1开始,结束条件执行目标方法是下标=拦截器的长度-1)
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//如果责任链运行到了最后一个,表示增强代码结束,开始运行我们自己的代码逻辑
return invokeJoinpoint();
}
/**
* 获取第一个方法拦截器,按照之前排好序的advisor获取
* 顺序为:(新增的内置拦截器)-- 异常--返回通知--后置通知--前置通知
*/
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
//在这个地方需要注意,抵用第一个拦截器的invoke方法,传入的是this 当前的方法拦截器对象
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
问题:那么为什么之前要按照 异常–返回通知–后置通知–前置通知 的方式来排序呢?
答:是因为在递归调用时,递的过程最底层的通知(前置通知)首先被执行,然后才会有归的过程。 所以为了使通知顺序保持 前置通知 – 目标方法 – 异常通知(如果有异常) – 返回通知 – 后置通知 的顺序,就必须按照这样排序!!
6. 五种通知执行顺序
6.1 目标方法无异常时
- ①:前置通知
- ②:环绕通知的调用目标方法之前的代码
- ③:目标方法
- ④:环绕通知的调用目标方法之后的代码
- ⑤:返回通知
- ⑥:后置通知
6.2 在目标方法抛出异常的情况下
- ①:前置通知
- ②:环绕通知的调用目标方法之前的代码
- ③:目标方法 抛出异常 异常通知
- ④:后置通知