这里我们只对基于注解的AOP进行讲解。
首先定义一个类用于扫描包:
@Service
@ComponentScan(basePackages = {"com.wml"})
public class EnableComponentScan {
}
然后创建一个类开启AOP的注解扫描:
这也是Springboot开启AOP的方式。
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class EnableAOP {
}
一、@EnableAspectJAutoProxy注解做了什么?
这里@Service
会被@ComponentScan
注解扫描到,具体如何被扫描的,是从AnnotationConfigApplicationContext
入口,在该方法中,会通过ComponentScan注解,然后像解析自定义标签一样,扫描basePackages下的带注解的类,封装为BeanDefinition,然后会遍历这些BeanDefinition,看看其类上有没有@Import
、@Compontent
等注解,如果有,会继续将涉及的类注册到IOC容器中,这里不是本文的重点,可以自己去了解下。
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
因此一定会扫描到EnableAspectJAutoProxy
注解,因为该注解上面有@Import
注解,就会将EnableAspectJAutoProxy
类和引入的AspectJAutoProxyRegistrar
封装成BeanDefinition
对象。
我们具体看@EnableAspectJAutoProxy
注解,到底做了什么:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*
* true
* 1、目标对象实现了接口 – 使用CGLIB代理机制
* 2、目标对象没有接口(只有实现类) – 使用CGLIB代理机制
*
* false
* 1、目标对象实现了接口 – 使用JDK动态代理机制(代理所有实现了的接口)
* 2、目标对象没有接口(只有实现类) – 使用CGLIB代理机制
*
*
*/
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
继续跟踪@Import
引入的AspectJAutoProxyRegistrar
类:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//先看这里,注册注解AOP入口类
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
...
}
}
最终会调用下面的方法:
将我们定义的AOP的入口类封装成BeanDefinition
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
......
//把AOP入口类封装成beanDefinition对象,要实例化
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//入口类的beanName名称,将BeanDefinition注册到IOC
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
二、AOP的Demo
我们先来看下AOP的简单使用【仅仅用于讲解】,定义一个切面:
@Component
@Aspect
public class Aspectj {
//拦截service包下所有类
@Pointcut("execution(public * com.wml.service.*.*(..))")
public void pointCut1(){
}
//拦截service包下的saveAccount方法
@Pointcut("execution(public * com.wml.service.*.saveAccount*(..))")
public void pointCut2(){}
//使用pointCut2
@Before("pointCut2()")
public void beforeAdvice(){
System.out.println("before前置通知");
}
//使用pointCut1
@Around("pointCut1()")
public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {
System.out.println("around的前置通知");
Object proceed = point.proceed();
System.out.println("around的后置通知");
return proceed;
}
}
@Service
public class AccountServiceImpl implements AccountService {
public void saveAccount() {
System.out.println("saceAccount");
}
}
启动:
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanBean.class);
AccountService bean = applicationContext.getBean(AccountService.class);
bean.saveAccount();
}
结果:
around的前置通知
before前置通知
saceAccount//被代理方法
around的后置通知
当我们拿到AccountService
实例时,通过断点可以看到其是一个代理类型的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5HAs42J1-1586597515084)(C:\Users\MI\AppData\Roaming\Typora\typora-user-images\1586515840827.png)]
那么它原本的实例在哪呢?回顾前面文章讲bean实例化和依赖注入的时候,有个位置是AOP的入口,AOP想要用原类,那么一定在该类实例化和依赖注入完成后才可以用,因此AOP入口是在doCreateBean
的populateBean
方法(实例化后的依赖注入)后进行,即initializeBean
方法,如下:
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
.....
}
else {
//调用Aware方法
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//调用@PostConstruct,Aware等接口方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//调用InitializingBean接口的afterPropertiesSet方法和init-method
invokeInitMethods(beanName, wrappedBean, mbd);
}
.................
}
if (mbd == null || !mbd.isSynthetic()) {
//这里就是AOP的入口了
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
三、AOP入口
applyBeanPostProcessorsAfterInitialization
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
这里遍历所有的BeanPostProcessor
接口,调用postProcessAfterInitialization
方法,该方法是bean声明周期中,初始化后的后置操作,这里主要交给其子类AbstractAutoProxyCreator
处理:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
//看这个方法
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//代码1:如果当前bean在切点pointcut表达式范围内,则获得当前bean的切面
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//代码2:如果有切面,则生成该bean的代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//把被代理对象bean实例封装到SingletonTargetSource对象中
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;
}
这个方法定义了AOP的核心流程:
- 如果当前类在切点范围内需要增强,则获取该类的切面
- 如果有切面,则创建该bean的代理对象返回。
我们首先看第一个重点,获取bean的切面:
四、获取bean的切面Aspect
进入该方法,涉及的类为:AbstractAdvisorAutoProxyCreator
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//找到符合条件的切面
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
继续跟踪:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//找到工程所有有@Aspectj注解的类封装成Advisor返回
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//模糊匹配当前类是否作用的pointcut表达式中
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//对有@Order@Priority进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
该方法主要做了两件事:
- 寻找所有被
@Aspect
注解标注的类,并封装成Advisor
返回 - 判断当前类是在pointcut表达式范围内
我们先来看是如何寻找@Aspect
注解标注的类:
最终会调用如下方法,构造Advisor集合:
4.1 buildAspectJAdvisors 构造切面集合
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
//首先获取容器中的所有bean的名称BeanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
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.
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//判断类上是否有@Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
//创建获取有@Aspect注解类的实例工厂进行获取
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//创建切面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;
}
}
}
................缓存的切面aspectNames不为null但是空的,就返回空集合;
如果有缓存,就调用getAdvisors获取并加入缓存.....
return advisors;
}
这个方法我们只要关注第一个if,会遍历当前spring容器中所有的beanName,然后通过MetadataAwareAspectInstanceFactory
实例工厂获取切面对象Advisor
。
4.1.1 getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
//1.借助工厂获取有@Aspect注解的类Class
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
//2.获取所有有@Aspect注解的类的beanName
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
//创建工厂的装饰类,获取实例只会获取一次
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
//3.循环没有@Pointcut注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
//3.1 具体获取
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
//判断属性上是否有引介注解
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
//判断属性上是否有DeclareParents注解,如果有返回切面
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
该方法核心就是:
从刚刚的工厂中获取有@Aspect
注解的class对象和beanName,然后遍历该类中没有@PointCut
注解的类,也就是我们定义的@Around
、@Before
、@After
等注解通知的方法,通过getAdvisor
创建具体的切面类:
4.1.2getAdvisor 解析通知方法构造切面类
该方法就是对我们有通知的具体的方法进行解析。解析what?无非就是注解信息,啥类型的注解?注解里的参数是啥?
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
//代码1:获取pointCut对象【解析注解信息,创建PointCut对象,将解析出来的pointcut表达式设置到对应属性中】
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
//创建Aspect切面类,这才是真正的切面类,一个切面类里面肯定要有1、pointCut 2、advice
//这里pointCut是expressionPointcut, advice 增强方法是 candidateAdviceMethod
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
4.1.2.1 getPointcut 创建pointcut对象
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
//代码1:并把注解信息封装成AspectJAnnotation对象
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
//代码2:创建一个PointCut类
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
///2.1把前面从注解里面解析的表达式设置进去
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
//将pointCut返回
return ajexp;
}
4.1.2.1.1 封装注解信息
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
//找到Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
//注解的方法,并且把注解里面的信息封装成AspectJAnnotation对象
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
ASPECTJ_ANNOTATION_CLASSES
存储了切点类以及几个用于增强的注解类,判断当前方法用的是哪个注解,然后进行解析封装成AspectJAnnotation
对象返回,如果没有这些注解就返回null
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
内部会调用return new AspectJAnnotation<>(result);
,result就是一个注解类对象,将其封装为AspectJAnnotation:
public AspectJAnnotation(A annotation) {
this.annotation = annotation;
//这里就是判断当前注解是啥类型,取值是枚举类:
//AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing
this.annotationType = determineAnnotationType(annotation);
try {
//1. 这里就是解析注解上的value值,即pointcut值,如@Before("pointCut2()")中的pointCut2()或是具体的execution表达式
this.pointcutExpression = resolveExpression(annotation);
//2.获取注解上的参数
Object argNames = AnnotationUtils.getValue(annotation, "argNames");
this.argumentNames = (argNames instanceof String ? (String) argNames : "");
}
catch (Exception ex) {
throw new IllegalArgumentException(annotation + " is not a valid AspectJ annotation", ex);
}
}
这里做的无非就是将注解的类型、pointcut表达式和参数[不重要]封装成AspectJAnnotation
对象返回。
这样就拿到了当前增强方法的注解信息(主要是pointcut表达式),回到4.1.2.1,后面就很简单了。
4.1.2.1.2 创建PointCut对象
看上述代码,就是创建了PointCut类AspectJExpressionPointcut
,然后将刚刚得到的注解信息中的表达式赋值上去,然后将该类返回就完成了。
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
回到4.1.2,拿到pointCut对象后,就开始创建具体的切面类。
4.1.2.2 创建切面类
切面由什么构成?切点pointcut和增强Advice呀,所以封装切面,主要就是封装这两个属性,pointcut前面我们有了,主要的就是创建Advice
对象
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
//pointcut类及其他属性赋值
this.declaredPointcut = declaredPointcut;
this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
this.methodName = aspectJAdviceMethod.getName();
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName;
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
//创建了Advice对象
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
创建Advice对象
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
//创建Advice对象
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
getAdvice
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
//获取有@Aspect注解的类
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
//就是前面封装注解信息的方法,这里又进行了一遍
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
AbstractAspectJAdvice springAdvice;
//这里就会根据注解类型【前面说的枚举】创建不同的advice类
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
//实现了MethodInterceptor接口
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
//实现了MethodBeforeAdvice接口,没有实现MethodInterceptor接口
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
//实现了MethodInterceptor接口
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
//实现了AfterReturningAdvice接口,没有实现MethodInterceptor接口
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
//实现了MethodInterceptor接口
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
//计算argNames和类型的对应关系
springAdvice.calculateArgumentBindings();
return springAdvice;
}
这里我们只要知道是根据注解的类型创建了不同的Advice类,然后返回,而几个Advice类的细节,以及有的实现的MethodInterceptor
接口,有的实现的MethodBeforeAdvice
接口后面会具体讲解。
到这里就对一个方法完成了全部的解析,并且构造了一个切面对象Advisor
,接着会遍历4.1.1中所有的方法构建Advisor
对象。
回到4.1.1,拿到Advisor
对象。集合,然后就该处理引介注解。
4.1.3 处理引介注解!!!
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
//判断属性上是否有DeclareParents注解,如果有返回切面
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
4.2 匹配当前bean是否在表达式范围中
最后就回到第四部分,拿到所有Advisor
对象后,就要遍历Advisor
集合,判断当前类是否在pointcut表达式的范围中,这里进行模糊匹配,了解就好。
如果使用了@Order
或@Priority
注解,就排序后再返回。
回到第三部分,下一步就是如果有切面,则创建该bean的代理对象。
五、创建代理对象
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//1.创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
//设置proxyTargetClass属性
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//2. 把advice类型的增强包装成advisor切面
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//将所有切面放入代理工厂
proxyFactory.addAdvisors(advisors);
//设置目标类
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
////用来控制代理工厂被配置后,是否还允许修改代理的配置,默认为false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//3.获取代理实例
return proxyFactory.getProxy(getProxyClassLoader());
}
5.1 buildAdvisors
buildAdvisors方法 将所有拦截器、增强器、增强方法统一封装advisors
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
// Handle prototypes correctly...
//自定义MethodInterceptor.拿到AnnotationAwareAspectJAutoProxyCreator对象调用setInterceptorNames方法
Advisor[] commonInterceptors = resolveInterceptorNames();
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
//对自定义的advice要进行包装,把advice包装成advisor对象,切面对象
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
5.2.获取代理实例
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
该方法会根据目标对象是否有接口来判断是采用CgLib代理还是jdk动态代理:
首先看createAopProxy()
:
5.2.1 createAopProxy创建代理类[cglib或jdk动态代理]
会获取AOP工厂,调用工厂的createAopProxy
方法创建实例,this是ProxyCreatorSupport
,前面的AOP工厂就是继承了该类,并且该类有AopProxyFactory
的引用。
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
这里就会根据注解中的ProxyTargetClass
属性和目标类是否实现了接口来判断是创建Cglib代理还是JDK动态代理,注意,这里会将config,即我们的代理工厂传入具体的代理类,保存到advised
中,而工厂中又保存了所有的切面对象,后面就会用到。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
5.2.2 拿到代理后的实例
拿到代理对象,就要开始执行对应的代理,以JDK动态代理为例:
JdkDynamicAopProxy#getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
//通过代理工厂对象advised【上面提到的】得到被代理的对象
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//这里就是通过JDK的Proxy创建动态代理实例
//参数分为:classLoader、被代理的对象、InvocationHandler实例增强
//因为当前代理类实现了InvocationHandler接口,因此可以直接传this
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
到这里,就完成了AOP代理的创建,得到了需要增强的类的代理对象。
接着,就是调用invoke
方法,进行代理实例的调用,依然以JDK动态代理为例,JdkDynamicAopProxy
为Spring中的JDK动态代理类,因此invoke
方法一定在该类中,找到它:
六、代理实例调用invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
//代码1:从代理工厂中拿到TargetSource,内部保存了被代理的实例bean
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//被代理对象的equals方法和hashCode方法不能被代理,直接走对应的方法,不走下面的切面。
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
........
Object retVal;
//设置exposeProxy属性
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//从targetSource拿到被代理实例
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//代码2:从代理工厂中拿过执行链, Object是一个被包装成MethodInterceptor类型的Advice对象
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 代码3:有调用链,就进行调用
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())) {
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);
}
}
}
这里主要做了两个工作:
- 拿到被代理类后,就通过代理工厂获取该类的调用链
- 如果如果有执行链,说明需要拦截,就进行增强调用。
先看1,获取执行链。
6.1 获取执行链
从该方法获取到后,会存在缓存中。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//从代理工厂对象config拿到被代理类的所有切面advisors
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
//如本例的around和before两个切面
for (Advisor advisor : advisors) {
//大部分走这里
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//通过切面的pointcut判断当前类是否需要拦截,即匹配通配符
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
//再通过pointcut判断当前方法是否需要拦截,因为我们可能只拦截部分方法
match = mm.matches(method, actualClass);
}
if (match) {//到这里类和方法都匹配,即该方法要进行拦截
//获取到切面advisor中的advice,并且包装成MethodInterceptor类型的对象
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
//处理引介切面
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
该方法主要做了如下的事:
- 匹配当前类是否和切面的pointcut匹配
- 如果1,则判断当前方法是否和切面pointcut匹配
- 如果都匹配,则将切面中的各种Advice包装成
MethodInterceptor
后添加到执行链集合返回。这里主要是为了后面调用的时候可以统一调用。
因此,我们主要来看看如何包装Advice的:
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
/*
如果是MethodInterceptor类型的,即前面创建Advice时,几个类实现了MethodInterceptor接口:
AspectJAroundAdvice
AspectJAfterAdvice
AspectJAfterThrowingAdvice
*/
if (advice instanceof MethodInterceptor) {
//包装成MethodInterceptor
interceptors.add((MethodInterceptor) advice);
}
//这里就是将没有实现MethodInterceptor接口的两个Advice: AspectJMethodBeforeAdvice 和AspectJAfterReturningAdvice包装成MethodInterceptor
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
到这里就拿到了当前方法的执行链,如果当类不需要增强,则就不会被匹配拦截,其执行链也就是空,就会直接反射执行其本身的方法,如果不为空,就需要执行代理增强的方法:
6.2 链式调用
回到六中的代码3,会将proxy, target, method, args, targetClass, chain
几个参数封装为ReflectiveMethodInvocation
然后调用proceed
方法进行链式调用:
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//代码1:如果执行链中的advice全部调用完毕,则直接调用joinPoint方法,即被代理的方法本身
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//interceptorsAndDynamicMethodMatchers就是刚刚获取的执行链,有当前方法需要调用的所有Advice
//代码2:这里就会按顺序调用里面的advice,然后索引加1
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
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 {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
//代码3:调用MethodInterceptor中具体的invoke方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这里流程也很简单,就是按顺序取刚刚得到的调用链中的Advice,然后转成MethodInterceptor
调用对应子类的invoke
方法,当调用的索引和调用链集合一样大时,说明所有的Advice都调用完成了,这时就可以执行被代理的方法。
那么有个问题,这里没有for循环,是如何调用多次的呢?其实很简单,已经说了是链式调用,因此调用完上一个Advice后,会在上一个invoke中的最后再调用proceed
方法,这时就开始了下一次Advice的调用。
Advice有多个子类,分别对应Before、Around、AfterRturning、AfterThrowing、After等注解增强。
其首先会调用Around注解方法:
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
//反射调用Around注解中的方法
return invokeAdviceMethod(pjp, jpm, null, null);
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
}
这就执行了文章一开始写的Demo中的System.out.println("around的前置通知");
,然后走到Object proceed = point.proceed();
又会回到proceed
方法,继续调用,接着就会调用@Before注解的方法,执行System.out.println("before前置通知");
,之后又会调用proceed()继续上面的流程片【如下】。这时全部Advice已经调用完毕,就会执行被代理的方法,执行System.out.println("saveAccount............");
,然后从@Around的proceed()返回执行System.out.println("around的后置通知");
结束调用。
//MethodBeforeAdviceInterceptor #invoke
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
但是@AfterReturning
这种需要在被代理方法执行后再执行的Adivice怎么办呢?看一下吧:
//AfterReturningAdviceInterceptor#invoke
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
可以看到,如果调用到该方法,则执行再次进入proceed
方法,其实就相当于@Around中的proceed方法的作用,等全部执行完后,再执行后置通知。对应@AfterThrowing
是一样的道理,只是将proceed放入了try中,在finally中会判断是否发生异常,如果发生异常,就执行@AfterThrowing
注解的方法。
七、总结
AOP入口前,会扫描basepackages下的所有带注解的类,@Component
、@Configuration
、@Bean
、@Import
等,此时就会将开启AOP的注解,以及使用@Import引入的配置类封装成BeanDefinition注册到spring容器中。
- 首先在依赖注入后进入AOP的入口
- 遍历容器中的beanName,如果bean有@Aspect注解,则通过一个实例工厂进行解析,获取切面集合。
- 接着会遍历带@Aspect注解的类中的所有没有
@PointCut
注解的方法,对方法上的注解解析(主要是Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
)几个注解,将注解的类型、pointcut表达式等封装成AspectJAnnotation
,再将pointcut值封装到AspectJExpressionPointcut
【是一个PointCut子类】中返回。 - 拿到PointCut对象后,再根据注解的类型创建对应的Advice类
- 将PointCut和Advice封装为切面Advisor,遍历完方法后会得到一个切面集合
- 将当前bean与得到的切面匹配,判断是否在范围内,得到新的切面集合,然后再进行排序
- 切面不为空就创建对应的代理
- 创建代理时 将所有拦截器、增强器、增强方法统一封装为advisors,放入代理工厂,通过代理工厂获取代理类
- 代理工厂通过
ProxyTargetClass
的取值和是否实现了接口,创建cglib代理或JDK动态代理类,再根据代理类创建代理对象返回 - 进行链式调用
- 分别匹配类和类中的方法是否需要拦截,如果需要,则将切面中的Advice封装为
MethodInterceptor
类型的执行链集合 - 执行链不为空,说明需要拦截,按索引获取Advice,调用对应的invoke方法,每个子类的invoke方法中会继续调用proceed实现链式调用,直到索引值和advice集合相同,说明所有Advice执行完毕,此时就可以调用被代理对象。
@AfterReturning
和@AfterThrowing
会在被代理方法执行完毕或前面调用出现异常时分别调用。
- 分别匹配类和类中的方法是否需要拦截,如果需要,则将切面中的Advice封装为
OK。到这AOP的调用流程就讲解完毕了。