1. 什么是 aop
?
aop 的作用是在书写上将辅助业务逻辑从主业务的逻辑中拆出来,实现主业务和辅助业务的解耦
,但在执行主业务逻辑的时候,辅助业逻辑业也会执行。从而保证拆前拆后功能不变。
那辅助业务拆出来放在哪里?
辅助业务封装在切面中,所以 面向切面编程
。
什么是切面?
切面就是一个类,并且被 @Aspect
注释。
辅助业务在切面的哪里?
辅助业务封装在切面的方法里,不同的方法封装不同的辅助业务逻辑。
切面中的方法是怎么触发的呢?
给封装辅助业务逻辑所在的方法标注 @Before 、@After、@AfterRunning 、@AfterThrowing、@Around
其中一个或几个注解。这样当主业务方法执行时,辅助业务的逻辑所在的方法就知道何时触发了。
切面中的辅助业务逻辑所在的方法由两个作用:1. 辅助业务内容是什么?2. 辅助业务何时触发。切面中具有这两个作用的方法叫做:
通知 。
主业主逻辑执行时,通知也会在恰当时机执行,那到底是哪个主业执行时会触发通知?
总不能随便执行一个类的方法,通知就跟着触发一遍吧,这不乱套了咩。
在切面里还有一样东西,它规定了哪几个类的哪些方法执行时,触发当前切面中的通知。
这样东西就叫切点,一个被@Pointcut
注释切面中的方法。
所以:
aop 是啥?面向切面编程,将辅助业务和主业务解耦,但要保证功能不变。
切面是啥?通知和切点同时所在的那个类,就叫切面,脑门上还被@Aspect
注释了。
通知和切点有都是啥?自己去上面重新看一遍,放心,不长。
切面长什么样子?
// 一个简单的切面。
@Aspect
public class LoggerAspect {
// 切点,“切” com.forum.controller 包下的所有类的所有方法。
@Pointcut("execution(public * com.forum.controller.*.*(..))")
public void log() {
}
// 通知1,在目标方法开始执行时,执行该通知。
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
// .......
// 辅助业务1
// .......
}
// 通知2,在目标方法开始执行结束后,执行该通知。
@After("log()")
public void doAfter() {
// .......
// 辅助业务2
// .......
}
}
2. aop的实现过程
主业务在 A 类中,辅助业务在切面 B 类中,在 Spring 项目的启动过程中,会将 A 和 B 类“组合”成一个新的类 C,C 在执行 A 的主业务时,在恰当的时机,会调用 B 中的方法。C 就是就是常说的代理类。
重点:当且仅当 代理对象执行目标方法时,aop 功能才会触发!
先规定几个术语:
- 主业务类 A 叫做目标类,A 中的方法叫做目标方法。
- 辅助业务类 B 叫做切面,B中的方法要么是通知、要么是切点。
- C 类叫做代理类。
所以,AOP 的实现过程有两个阶段:
- 目标类是怎么变成代理类的?
- 代理类是怎么执行目标方法的?
2.1 目标类变成代理类的过程
这个准确的说是目标对象变成代理对象的过程,因为类不实例化不能使用。所以首先是目标类实例化成目标对象,这是 IOC 的构成,这里就不详细描述了。
// AbstractAutoWireCapableBeanFactory.java 的 doCreateBean() 方法中。
// A 的 bean 被创建出来,仅仅是一个内存空间,属性值都是默认的初始值。
Object bean = instanceWrapper.getWrappedInstance();
// 一直往下走....
// 如果 bean 被 aop ”切“了,在 initializeBean 方法中 bean 会被变成代理对象返回。
exposedObject = initializeBean(beanName, exposedObject, mbd);
先有的 bean ,再为其创建代理对象。
// AbstractAutoWireCapableBeanFactory.java。
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
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()) {
// 这个方法将 bean 变成了代理对象。
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
那怎么变的呢?
bean 被创建后,接下来会调用所有的BeanPostProcessor
作用在 bean 上,其中有一个名叫 AnnotationAwareAspectJAutoProxyCreator
的 BeanPostProcessor,是它将 bean 变成了对应的代理类。
注意类中的Aspect
,意思是处理切面的处理器
(至于 AnnotationAwareAspectJAutoProxyCreator
是怎么来的,在后面会写到。)
// AbstractAutoWireCapableBeanFactory.java。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
// 遍历所有的 beanPostProcessor。
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 在 bean 上执行每一个 beanPostProcessor 的 postProcessAfterInitialization() 方法。
Object current = processor.postProcessAfterInitialization(result, beanName);
// 下面的代码就是将结果返回。
if (current == null) {
return result;
}
result = current;
}
return result;
}
先看下 AnnotationAwareAspectJAutoProxyCreator
继承的类图,不然后面走方法会有点懵。
接下来看下代理类是怎么生成的,通过ctrl+F12,找到方法postProcessAfterInitialization(@Nullable Object bean, String beanName)
// AbstractAutoProxyCreator.java
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 缓存 cacheKey 类似 bean 的 id。
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 如果 bean 已经生成了代理,那就跳过,不再重复生成。
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 为 bean 创建代理对象并返回。
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
为什么要判断 bean 是否已经生成了代理?因为在工程启动过程中有两个地方可以将目标对象变成 aop 代理,一个在这里,另一个是目标对象从三级缓存转到二级缓存的时候,所以在这里需要判断下。
// 如果 bean 需要被代理,为他创建一个代理对象。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果 beanName 是有效的,且 beanName 已经被代理了。
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
// 此时不需再创建代理,直接返回 bean。
return bean;
}
// 不需要处理
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
// 不需要创建代理,直接返回 bean。
return bean;
}
// 基础设施类(Advice、Pointcut、Advisor、AopInfrastructureBean 这些类为接口的子类)不应该为其创建代理;
// 或者 beanName 以 ".ORIGINAL" 结尾,也不应该为其创建代理。
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 如果 bean 有方法被"切"了,为其创建代理。
// 获取针对 bean 的所有增强器,(每一个通知都会被构建成一个增强器。)
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 判断 specificInterceptors 不为空。如果是空,说明当前 bean 没有被”切“。
if (specificInterceptors != DO_NOT_PROXY) {
// 设置需要为 cacheKey 创建代理。
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理。
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 保存 cacheKey 的代理。
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回创建好的代理对象。
return proxy;
}
// 如果拿不到 advices,标识 bean 不需要增强。(都没被切,增强个毛线)
this.advisedBeans.put(cacheKey, Boolean.FALSE);
// 返回 bean 或者 bean的代理对象。
return bean;
}
这块的逻辑简单,判断有没有针对 bean 的 切面,如果没有,直接将 bean 返回。如果有,为 bean 创建代理对象。(至于代理对象是怎么创建的,这个后面再写。)
这里就有个问题了:
- 切面信息是怎么拿到的?(更确切的说切面是怎么被解析的)
- 怎么判断有没有针对当前 bean 的切面?其实找的是有没有作用在当前 bean 上的通知。
2.1.1 获取切面信息
继续往下看代码,简单的逻辑就不解释了,直接看注释吧。
通过ctrl+F12,找到方法protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource )
// AbstractAutoProxyCreator.java,
// 但是记住,因为被继承了,所以此时应该是是在 AnnotationAwareAspectJAutoProxyCreator.java 中
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass,
String beanName,
@Nullable TargetSource targetSource
) {
// 为 beanClass 寻找符合条件的增强器(advisors),增强器就是通知,这个后面有解释。
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
// 如果找不到
if (advisors.isEmpty()) {
// 返回 null
return DO_NOT_PROXY;
}
// 找到了增强器,以数组格式将这些增强器返回。
return advisors.toArray();
}
继续往下走…,找到findEligibleAdvisors(Class<?> beanClass, String beanName)
方法
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 找到所有的 advisors,包括事务的 advisor 和 普通 aop 的 advisor。。
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 在所有的 advisors 中筛选符合 beanClass 使用的 advisor。(通过切点那块的表达式判定有没有切到 beanClass)
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 向 eligibleAdvisors 中额外再添加一个 ExposeInvocationInterceptor 类型的 advisor。
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 如果有多个 advisor,谁先谁后,在这里排序。
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
// 返回符合条件的 advisor
return eligibleAdvisors;
}
findCandidateAdvisors()
这个方法中把上面两个问题都回答了,去方法里面看看。
// 这里一定要进到 AnnotationAwareAspectJAutoProxyCreator.java 中,走错地方就找不到这个方法了。
protected List<Advisor> findCandidateAdvisors() {
// 这是是找事务相关的 advisor。因为事务相关的 advisor 少,而且是现成的。
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
// 接下来才找 aspectJ 的 advisor。
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
// 返回找到的所有通知。
return advisors;
}
事务相关的,这里不解释了。主要看自定义的切面。
2.1.2 切面是怎么被解析的。
切面刚开始也只是个 class 文件,只有被注入、解析后才能使用。先大概简述下切面被注入和解析的思路:
-
切面类上有
@Component
注解,所以在工程启动时调用ConfigurationClassPostProcessor
会加载切面的 java 文件,创建对应的 BeanDefinition,最后再遍历所有的 BeanDefinition 并为每一个创建对应的 bean 保存到applicationContext
中的beanFactory
字段的beanDefinitionMaps
字段中。(切面就这样注入了)。 -
接下来就是解析切面了,最终解析到切面的通知上去,因为辅助逻辑都在通知里面写着。
2.1 去
beanFactory
中拿所有的 object 的 beanName,即获取所有的 bean 的名字。2.2 遍历 beanNames,为每一个 beanName 创建 Class 对象。
2.3 判断该 Class 对象上有没有
@Aspect
注解,如果有,那这个类就是切面类了。2.4 切面类的 Class 对象都拿到了,获取类中的全部方法肯定也是能办到的。遍历类中的每一个方法,判断方法上有没有
@Before、@After、@Around、等注解
,如果有,拿这个方法就是通知
,为每一个通知创建一个 Advisor 对象。最后将所有的 Advisor 对象 保存到 advisors 列表中
。最后返回。
解析切面的流程大概就是这样,详细的源码如下,先不管缓存的事情,后面会写切面缓存,以及解析切面的执行时机。
public List<Advisor> buildAspectJAdvisors() {
// aspectBeansNames 中保存的是已经解析的切面的名称。
// 因为不止这一个地方有解析切面的代码,在doCreateBean() 方法之前也解析过切面。
// 并且将解析到的通知都缓存起来。并标记该切面已被解析,下次再要解析时直接从缓存中拿。
List<String> aspectNames = this.aspectBeanNames;
// aspectNames == null 说明这是第一次解析,那就要执行正真的解析流程了。
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
// 用于保存所有解析出来的 Advisors 集合对象。
List<Advisor> advisors = new ArrayList<>();
// 用于保存所切面名称的集合。
aspectNames = new ArrayList<>();
/**
* aop 功能在这里传入的是 Object 对象,表示从容器中获取所有组件的名称,然后遍历所有组件,
* 这个遍历过程是非常消耗性能的,所以 Spring 在这个过程中加入了保存切面信息的缓存。但是事务
* 功能不一样,事务模块的功能是直接去容器中获取 Advisor 类型的,选择范围小,且不消耗性能。
* 所以 Spring 在事务模块中没有加入缓存来保存事务相关的 advisor。
*/
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 遍历从 IOC 容器中拿到的所有 beanName。
for (String beanName : beanNames) {
// 判断 beanName 是合法的。
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 通过 beanName 去容器中获取到 class 对象。(能拿到 class对象,bean的全部信息也就都拿到了)
Class<?> beanType = this.beanFactory.getType(beanName, false);
if (beanType == null) {
continue;
}
// 根据 class 对象判断是不是切面。(有没有被 @Aspect 注解)
if (this.advisorFactory.isAspect(beanType)) {
// 是切面,
// beanName 加到缓存中。
aspectNames.add(beanName);
// 用 class 对象和 beanName 构建一个 AspectMetadata 对象。
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 构建切面注解的实例工厂
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 获取切面中的所有通知。
/**
* 思想:都已经拿到 class 对象了,从中可以获取到类中的所有方法,遍历每一个方法,
* 判断方法上有没有 @Before、@After、@Around、等注解。如果有,就为这个方法 new 对应类型的 Advisor()。
*/
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 加入到缓存中。
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
// 能走到这里,切面已经被解析了,通知已经被转换成 advisor 缓存起来了。
List<Advisor> advisors = new ArrayList<>();
// 遍历所有已解析的切面
for (String aspectName : aspectNames) {
// 获取每一个切面里的全部通知。
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
// 将缓存的通知添加到最终结果中,准备返回。
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
// 返回最终结果。
return advisors;
}
2.1.3 解析切面的执行时机
在上面的代码中可以看到,只有当 this.aspectBeanNames
为空的时候才执行解析,否则去 this.advisorsCache
中直接拿advisor,最后返回。那就说明在前面某个地方已经解析过切面了,并且将解析的结果缓存了起来。
问题1:在哪里解析的?
问题2:为什么要缓存?又将解析结果缓存到了哪里?
答案1:在 createBean()
方法中 doCreateBean()
方法之前解析的。
// AbstractAutowireCapableBeanFactory.java
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// -----
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
// beanName 的 Class 对象是能拿到的。
if (targetType != null) {
// 在这里解析的切面。
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// AnnotationAwareAspectJAutoProxyCreator 调用 postProcessBeforeInstantiation 方法。
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// shouldSkip(beanClass, beanName) 方法里解析了切面。
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// 这个方法是不是很熟悉。
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
@Override
protected List<Advisor> findCandidateAdvisors() {
// 这是是找事务相关的 advisor。因为事务相关的 advisor 少,而且是现成的。
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
// 接下来才找 aspectJ 的 advisor。
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
// 返回找到的所有通知。
return advisors;
}
答案2:自定义切面的解析过程是,从 beanFactory 中拿出所有的 bean 执行遍历,看脑门上有没有 @Aspect
注解(确认是不是切面),如果有再去遍历这个类的方法,也是根据脑门上的注解判断方法是不是通知,最后为每个通知构建一个 Advisor 对象。
这就会出现两个开销和大的地方:
- 如果 beanFactory 中的 bean 非常多,全部遍历一遍解析本身就很花时间。(这个过程是解析了项目中全部切面)
- beanFactory 中的 beanDefinition 非常多时,每个 beanDefinition 变成 bean 时都要解析一遍切面,那么1过程将会被重复执行好多遍,这就更费性能了。
所以缓存是有必要的。只需要将全部切面解析一遍缓存起来,下次直接从缓存中获取,就能降低开销。
那缓存到哪里了呢?
前面说到过,AnnotationAwareAspectJAutoProxyCreator
的 bean 是已经被注入到 beanFactory 中了。 AnnotationAwareAspectJAutoProxyCreator
里面有个字段叫 BeanFactoryAspectJAdvisorsBuilder
,BeanFactoryAspectJAdvisorsBuilder
中有两个字段
// 缓存所有被解析的切面的 beanName。
List<String> aspectBeanNames
// 缓存解析的所有切面的通知。 key: 切面 beanName, value: 切面下的所有通知构成的list。
Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>()
beanFactory 中的 bean 是全局的,而且只要项目处于启动状态,bean就一直存在。所以下次如果再要解析切面,直接从缓存中拿。
2.1.4 筛选作用于当前 bean 的增强器
在上面拿到了所有项目中所有的增强器,但不是每个增强器都作用在当前bean 上的,所以还需要筛选出那些作用在当前bean上的增强器。
先补一个概念,上面提到的 为每一个通知构建一个 Advisor() 对象,增强器里不只有通知,还有切点。
// 这是项目中增强器的类型
final class InstantiationModelAwarePointcutAdvisorImpl{
private final Pointcut pointcut; // 切点
private Advice instantiatedAdvice; // 通知
// 其他字段就不贴了。
}
代码中是这么筛选增强器的,candidateAdvisors 是所有的增强器,beanClass, beanName 表示当前bean,通过切点筛选有木有作用在 bean 上的增强器。
// 在所有的 advisors 中筛选符合 beanClass 使用的 advisor。(通过切点那块的表达式判定有没有切到 beanClass)
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
细节代码不贴了,套的太深…
2.1.5 创建代理对象
bean有了,作用在 bean上的增强器也被找到了,接下来就是拿着两个东西创建代理对象。
这块的思路很简单:
-
创建代理工厂。
ProxyFactory proxyFactory = new ProxyFactory();
-
将目标对象和它的增强器都 set 到 代理工厂中。
proxyFactory.setTargetSource(targetSource); proxyFactory.addAdvisors(advisors);
-
代理工厂创建代理对象,并返回。
proxyFactory.getProxy(getProxyClassLoader())
代理工厂返回的是
AopProxy
接口,它有两个实现CglibAopProxy
和JdkDynamicAopProxy
。至于创建的到底是哪一个,要根据条件判断的,这篇文章不对这一部分进行描述。
(到这里,目标对象变成代理对象的流程就算是描述完了。)
再加一个小点,AnnotationAwareAspectJAutoProxyCreator 干了那么多事情,有没有好奇它是怎么来的?
2.1.7 AnnotationAwareAspectJAutoProxyCreator 怎么被加载的?
前面写到 AnnotationAwareAspectJAutoProxyCreator
解析了切面,创建了增强器,最后将目标对象变成了代理对象,那这个类是怎么被加载到的呢?
Spring 项目启动一般都会有一个配置类。
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.aop")
public class AppConfig {
}
@EnableAspectJAutoProxy
表面上被翻译成 “启动自动代理”,看看它里面到底干了啥。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}
@Import(AspectJAutoProxyRegistrar.class)
这个很关键。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
// 注册一个 BeanDefinition。
// 注册了 beanDefinition, 那后面肯定会有对应的 bean 产生,
// 关键是 注册了哪个类的 beanDefinition。
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 答案在这里。
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 剩下的代码就不贴了。
}
}
// -----
// 顺着上面的方法一直走下去,会走到这个方法
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
// 看下它的方法体。
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
// 多余的代码这里不贴了。
// cls = AnnotationAwareAspectJAutoProxyCreator.class
// 为 cls 创建 beanDefinition。
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册 beanDefinition。
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
到这里就清楚了:
@EnableAspectJAutoProxy
注解将AnnotationAwareAspectJAutoProxyCreator
的 beanDefinition 注入了 applicationContext 中。- 接下来走 beanDefinition 创建对应 bean 的过程,
AnnotationAwareAspectJAutoProxyCreator
的 bean 也就注入了 applicationConext 中。AnnotationAwareAspectJAutoProxyCreator
实现了BeanPostProcessor
接口,所以在对目标类的 bean 执行初始化时,会调用所有的 BeanPostProcessor,当然少不了annotationAwareAspectJAutoProxyCreator
,它执行postProcessAfterInitialization(Object bean, String beanName)
方法,将目标类变成了代理类。(它解析切面的时机,不在赘述了)- 至于
@Import
注解是什么时候执行的,不用猜都知道它在ConfigurationClassPostProcessor
类的processConfigBeanDefinitions()
方法中执行的。
2.2. 代理类执行目标方法的过程
代理对象创建好了,最关键的时刻来了:代理对对象执行目标方法并在恰当时机将通知织入。
2.2.1 一个 aop 的例子
// 目标对象。
@Component
public interface TestBean {
void test(int numberA,int numberB);
}
@Component
public class TestBeanImpl implements TestBean {
public void test(int numberA, int numberB) {
System.out.println("目标方法执行: "+ numberA/numberB);
}
}
// 切面
@Aspect
@Component
public class AspectJTest {
@Pointcut("execution(* com.aop.domain..test(..))")
public void test() {
}
@Before("test()")
public void beforeTest() {
System.out.println("before ...");
}
@After("test()")
public void afterTest() {
System.out.println("after ...");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint p) {
System.out.println("around before ...");
Object object = null;
try {
object = p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around after ....");
return object;
}
@AfterReturning("test()")
public void afterReturn() {
System.out.println("after return ...");
}
@AfterThrowing("test()")
public void afterThrow() {
System.out.println("after throw ...");
}
}
// 主程序
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
AppConfig.class);
// 此时拿出来的 bean 已经是代理对象了。
TestBean bean = (TestBean) context.getBean("testBeanImpl");
// 代理对象执行目标方法。
bean.test(6,3);
}
}
结果:
around before ...
before ...
目标方法执行: 2
after return ...
after ...
around after ....
除了异常通知没有执行以外,其他通知都执行了。
2.2.2 底层流程
在上面的例子中代理代理对象 bean 中已经包含了所有的增强器。
当执行目标方法 bean.test(6,3);
时,代理将调用:
// JdkDynamicAopProxy.java
// proxy 是代理对象。 method 是目标方法。 agrs 是[6,3]
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)) {
// “采用反射执行连接点” 意思就是采用反射的方式直接执行方法。
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// 暴露代理。把我们的代理对象暴露到线程中。这个跟 ThreadLocal 有关。
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
// 获取目标对象。
target = targetSource.getTarget();
// 获取目标对象的 class 对象。
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
// 从目标类的所有通知中找出作用与当前方法的那些通知(增强器),保存到 chain 中。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 如果拦截器链为空,说明没有作用于当前方法的通知,则通过反射直接执行目标方法。
// 先获取方法的入口参数。
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 再通过反执行方法。(JoinPoint,连接点说的就是方法)
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
// 如果拦截器不为空,就要织入了。所谓织入就是在执行目标方法的时候,将它的通知在恰当的地方一并执行。
// 先创建 MethodInvocation 对象,将所有需要的信息都封装在里面。
// (代理、目标对象、目标方法、方法参数、目标对象的Class对象 、拦截链)
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 开始执行链式调用。
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
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()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
看看 chain
里面到底有多少个通知,因为自定义切面中写了 5 个,解析切面时还会额外添加 1 个。
chain 中增强器的排列顺序就是执行目标方法时通知的织入顺序。
那具体是怎么执行的呢?
public Object proceed() throws Throwable {
// currentInterceptorIndex 的初始值是 -1.
// interceptorsAndDynamicMethodMatchers 是个列表里面存放着那 6 个增强器。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 当遍历完所有拦截器后,执行目标方法。
return invokeJoinpoint();
}
// 根据索引拿出拦截器。
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
// 拦截器执行 invoke 方法。
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这里肯定看懵了吧。
3. 几小问题
问题1:aop 的底层实现是什么?
底层实现是 java 动态代理 和 cglib。
问题2:为什么jdk动态代理必须是接口?
jdk 动态代理会生成一个字节码文件,也就是 xxx.class
文件。将这个文件反编译后就会看到
public final class $Proxy extends Proxy implements CustomDefinedInterface{
// .......
}
因为生成的代理对象默认继承了 Proxy 类,又因为 java 是单继承的,所以我们的被代理对象必须是接口。
问题3:aop实现到默认是使用 jdk动态代理还是 cglib?
aop没有默认使用哪个代理,都是根据条件判断的。
注解是 @EnableAspectJAutoProxy
时,如果目标类有父接口,采用 jdk 动态代理;如果目标类没有父接口,采用 cglib 代理。
注解是 @EnableAspectJAutoProxy(proxyTargetClass = true)
时,采用 cglib 代理。
问题4:切面解析的顺序怎么确定?
使用 @order(number)
注解,number 越小,优先级越高。