理论知识
AOP是面向切面编程(Aspect Oriented Programming)的意思。定义一些切点(pointcut),然后可以在切点织入一些通知(advice),对切点方法进行代理增强,与核心业务逻辑分离开来,以提高系统的可维护性、可扩展性和重用性。
AOP的核心思想是将系统中的功能模块按照不同的关注点进行横切划分,然后通过一种称为“切面”的手段,将这些关注点与主要业务逻辑进行解耦。在Spring AOP中,我们可以通过定义切面、切点和通知三个元素来实现AOP的功能:
- 切面(Aspect):一组横切关注点的集合,它描述了在何时、何地、何种情况下执行横切关注点。
- 切点(PointCut):使用表达式语言,匹配目标对象中的连接点(Join Point),从而识别出需要执行横切关注点的地方。
- 通知(Advisor):它定义了在连接点处执行的代码逻辑,通常包括前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)、异常通知(After Throwing Advice)和最终通知(After Returning Advice)等,这些通知组成了切面的核心实现逻辑
在aspectjweaver-1.9.7.jar包里有常用的切面注解。
开启切面代理
需要添加@EnableAspectJAutoProxy注解,会引入AspectJAutoProxyRegistrar类。该类会往BeanDefinitionRegistry里注册一个(org.springframework.aop.config.internalAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator)类型的beanDef。前面bean的实例化过程知道beanDef会被初始化成一个bean。
具体位置AopConfigUtils.registerOrEscalateApcAsRequired方法
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {//如果这个beanName已注册
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {//和当前指定的class不一致
/**
查找两个类的优先级
这里有三个ProxyCreator:
InfrastructureAdvisorAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
我们通过@EnableAspectJAutoProxy是引入的AnnotationAwareAspectJAutoProxyCreator,这里就是判断如果容器已经指定了creator,如果是其它的两个,这里会被替换掉,也是方法名Escalate升级的意思。Annotation这个优先级最高
**/
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
//优先级最高
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
来看下AnnotationAwareAspectJAutoProxyCreator类的结构
我们发现他实现了BeanPostProcessor接口,是一个bean后置处理器。当一个bean初始化时,会调用beanpostPorocessor的before和after模板方法。其实代理也是在post的这两个方法实现的。具体在AbstractAutoProxyCreator类。
Aspect的解析
后置处理器的AbstractAutoProxyCreator#before方法
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方法会判断该bean是否需要aop切面
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
return null;
}
shouldSkip会调用多次,这里面主要时判断当前bean有没有匹配的切面织入点,这里shouldSkip要看AspectJAwareAdvisorAutoProxyCreator子类的实现
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
//shouldSkip-1 findCandidateAdvisors方法在类AnnotationAwareAspectJAutoProxyCreator
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//这里找到的advisor都是InstantiationModelAwarePointcutAdvisorImpl类型,for循环不成立
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
shouldSkip-1调用的AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors方法,先获取配置的所有advisor。也就是@Aspect注解标记的类
protected List<Advisor> findCandidateAdvisors() {
//先从缓存查找
List<Advisor> advisors = super.findCandidateAdvisors();
// 不存在构建解析
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
第一次从缓存中无法找到,会走下面的BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors方法去解析advisors
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来
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
//是否有@Aspect注解,调用AnnotationUtils.findAnnotation(clazz, Aspect.class)判断
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//buildAspect-2 查找所有的advisor,获取所有的bean方法,过滤掉所有带@Pointcut注解的,然后依次在方法上查找切面的注解
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
//放入缓存 key是beanName,value是所有的切面advisor方法
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {//非单例}
}
}
//解析完一次后,就存起来,下次不用拿出所有的bean进行判断是不是AspectBean了
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
//第二次aspectNames不是空,直接到这里拿出所有的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;
}
从上面的advisor解析看到,查找@Aspect是从beanFacotry里查找bean。所以我们的@Aspect也要是一个bean,否则只有@Aspect注解是无效的。
buildAspect-2处查找Aspect切面的方法AbstractAspectJAdvisorFactory#findAspectJAnnotationOnMethod
Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
//在方法上查找所有切面注解
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
切面方法匹配
上面before方法看完,只是对Aspect进行了解析。没有具体使用,那么继续看after方法。还是从beanpost的after方法
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//是否需要包装代理
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;
}
//又调用一次shouldSkip
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//after-1 寻找适配这个类的所有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;
}
after-1处寻找适配的Advisor方法最后会调到findEligibleAdvisors方法
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();//拿出所有的advisor
//findEligible-1 找出这个类可以使用的advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//对通知进行排序,使用的PartialOrder
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findEligible-1 找匹配的advisor方法
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {//这一步是干什么不知道,好像都不匹配
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
//会走这里
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
canApply方法
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
//...
//拿出所有的类方法与切点规则进行匹配,只要有一个匹配则返回true
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
//matches怎么匹配的,看不懂不看了。反正就是拿出来joinPoint切面表达式和方法Signature进行匹配
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
代理创建
如果找到切面能匹配当前bean,则该bean需要被代理
创建代理主要在createProxy方法,这里会将所有相关的信息构造成一个ProxyFactory对象,最后调用proxyFactory.getProxy方法获取代理对象。proxyFactory内有一个DefaultAopProxyFactory,其createAopProxy方法用来创建代理对象。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
//判断目标是否是接口
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {//使用jdk创建动态代理
return new JdkDynamicAopProxy(config);
}//使用cglib代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
这里可以看到接口类会使用jdk动态代理,其它使用cglib进行创建代理。cglib不太懂,看JdkDynamicAopProxy。这个类实现了InvocationHandler接口。
方法的执行
以jdk代理为例。执行的开始就是handler的invoker方法了。
来看JdkDynamicAopProxy的invoker方法
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 {
//...
Object retVal;
//这个是根据@EnableAspectJAutoProxy注解的exposeProxy属性判断是否暴露原被代理对象,放到ThreadLocal里,在advisor方法逻辑里就可以获取到被代理对象了。
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//invoke-1 从可用advisors中获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//拦截器链为空,直接使用反射调用
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
//invoke-2 根据拦截器链构造MethodInvocation
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//invoke-3 执行责任链上的方法
retVal = invocation.proceed();
}
return retVal;
}
finally {
//...
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
invoke-1创建拦截器链最后实际处理逻辑在DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice中.前面切点发现的时候说了,某个bean只要有一个方法符合切面匹配。就会被代理。这里实际调用某个方法的时候,还需要再判断一次切面规则是否匹配当前代理类被调用方法。有可能一个类中有的方法符合规则有的不符合规则。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
/**intercept-1
这里获取registry,默认会创建一个DefaultAdvisorAdapterRegistry类型register。
里面初始化三个adapter:
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
对应三个切面类型
*/
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {//拿出所有的advisor进行方法匹配
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
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 {
match = mm.matches(method, actualClass);
}
//匹配
if (match) {//intercept-2这里根据不同的advisor获取不同类型的拦截器
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
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;
}
intercept-2从advisorAdapter获取拦截器
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//这里的adapter对应上面intercept-1初始化的三个切面类型adapter
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {//类型匹配 根据advice获取对应的intecept
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
invoke-3 proceed方法是执行的关键。这里会递归的调用proceed方法进行所有拦截器invoke
public Object proceed() throws Throwable {
// 所有拦截器都执行,最后执行切点方法,也就是被代理原方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
//...
//proceed 执行拦截器invoke方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这里不同的拦截器内部invoke方法不一样,来看一下:
1-ExposeInvocationInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
这个没有什么逻辑,就是继续调用下一个。也没注意什么时候生成的,就是threadlocal里执行前后换了下值。
2-MethodBeforeAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
@Before注解对应拦截器。会先执行before advice方法,然后继续执行下一个拦截器。也就是我们要植入的方法。
before方法的实现会到AspectJMethodBeforeAdvice类里
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
3-AspectJAfterAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
@After注解对应的拦截器。最后在执行advice方法。我们看到advice放到的执行放到了finally里,所以@after标记的切面方法即使被代理原方法抛异常也会被执行。
4-AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
@AfterThrowing对应拦截器,advisor方法在catch块里,异常时候会执行advisor方法。
使用例子
配置开启aop
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
@ComponentScan(basePackages = {"com.cpx.service.aop"})
public class AopConfig {}
切面处理类:
@Component
@Aspect
@Slf4j
public class LogTestAspect {
@Pointcut("execution(* com.cpx.service.*.*(..))")
public void point(){};
@After(value = "point()")
public void methodAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
log.info("after method execute");
}
@AfterThrowing(value = "point()")
public void afterThrow(JoinPoint joinPoint){
log.info(" exception happen.............");
}
public void test(){
log.info("hhh");
}
@Before(value = "point()")
public void methodBefore(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
log.info("before method execute");
}
}
启动测试
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
UserService userService = (UserService) ctx.getBean("userService");
userService.sayHello();
ctx.close();
总结
Spring AOP实现是通过bean后置处理器去实现。在后置处理器方法找到所有的Advisor,即@Aspect注解标注的bean。然后每个被创建的bean都会拿出所有的类方法与解析到的Advisors切点规则进行匹配,只要有一个规则匹配,则该bean会被通过JDK动态代理或cglib方式代理包装返回代理对象。通过代理执行原方法时,会计算出当前方法匹配的所有Advisor规则组成Intecepters依次调用。