springboot整合AOP

发布时间:2024-04-02 16:01

springboot:1.5.17.RELEASE
使用aop需要我们配置什么呢?答案是不需要,springboot已经全部封装好直接使用即可,那么springboot是如何帮我们完成aop配置的呢?直接看autoconfig jar呀,里面可以看到aop目录有一个AopAutoConfiguration配置类,那么看下都做了哪些动作吧

AopAutoConfiguration

配置类代码很简单,自动启用AOP的条件限制

  1. 当前jvm中存在类:EnableAspectJAutoProxy.class, Aspect.class, Advice.class
  2. 环境中存在spring.aop.auto=true,如果没有配置则默认为true

其实上面可以认为就一个条件1,几乎不需要认为干预。配置文件中有两类代理配置

  1. jdk动态代理代理,spring.aop.proxy-target-class=false,如果没有该配置项则默认为true,即默认为jdk代理
  2. cglib代理
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
	public static class CglibAutoProxyConfiguration {

	}

}

EnableAspectJAutoProxy

import导入AspectJAutoProxyRegistrar注册器

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * 表示是否使用基于子类(CGLIB)创建代理,相反则使用标准java的基于接口的代理,默认为false
	 */
	boolean proxyTargetClass() default false;
    
	/**
	 * 表示代理将由AOP框架作为一个ThreadLocal暴露,为了可以通过
	 * {@link org.springframework.aop.framework.AopContext} 类取回代理。
	 * 默认关闭。即不保证 {@code AopContext} 访问能工作。
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

AspectJAutoProxyRegistrar

  1. 注册AspectJAnnotationAutoProxyCreator至工厂如果需要的话
  2. 如果proxyTargetClass参数为true则强制自动代理创建者使用子类方式作为代理
  3. 如果exposeProxy参数为true则强制自动代理创建者启用暴露代理类
/**
 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
 * {@code @Configuration} class.
 */
@Override
public void registerBeanDefinitions(
		AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

	AnnotationAttributes enableAspectJAutoProxy =
			AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
	if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
		AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
	}
	if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
		AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
	}
}

registerAspectJAnnotationAutoProxyCreatorIfNecessary

注册或升级APC(AutoProxyCreator)定义。如果注册中心已经存在APC类型定义则判断当前要注册的APC(AnnotationAwareAspectJAutoProxyCreator)的优先级,使用优先级高的定义,优先级定义如下

// 注册AnnotationAwareAspectJAutoProxyCreator bean定义
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
// org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST
static {
	// Set up the escalation list...
	APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
	APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
	APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}

如果注册中心中不存在APC类型定义则创建,默认lazyInit为false,完成上下文刷新时会初始化该bean

// org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
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

springboot整合AOP_第1张图片

AnnotationAwareAspectJAutoProxyCreator bean父类AbstractAutoProxyCreator实现SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware接口

setBeanFactory

初始化bean时回调setBeanFactory方法,初始化advisor通知检索工具类,切面通知者构建者:aspectJAdvisorsBuilder,反射切面通知者工厂ReflectiveAspectJAdvisorFactory(工厂可以从支持AspectJ5语法的类中创建创建通知者,使用反射调用相应的advice方法)

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#initBeanFactory
this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
// org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#initBeanFactory
if (this.aspectJAdvisorFactory == null) {
	this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
}
this.aspectJAdvisorsBuilder =
		new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);

predictBeanType

postProcessBeforeInstantiation回调时返回当前bean之前的最终类型

getEarlyBeanReference

如果需要则包装bean

wrapIfNecessary

包装bean如果需要

  1. 如果targetSourcedBeans已包含bean直接返回bean
  2. 如果advisedBeans对应的value为false则直接返回bean
  3. 如果bean为切面基础类型(Advice、Pointcut、Advisor、AopInfrastructureBean),或者bean为Aspect切面bean则设置advisedBeans值为false,并返回bean
  4. 获取bean的通知或者通知者getAdvicesAndAdvisorsForBean,如果为null则为bean创建代理,设置advisedBeans为true,返回代理bean
  5. 否则设置advisedBeans为false,返回bean

postProcessAfterInitialization

逻辑同getEarlyBeanReference,但是不需要向earlyProxyReferences缓存中添加该bean对应的cacheKey

postProcessBeforeInstantiation

  1. 如果满足wrapIfNecessary的前两条则返回null(即使用bean默认实例,不适用代理替换目标bean)
  2. 如果我们有一个自定义的TargetSource则创建代理:customTargetSourceCreators不为空,并且beanFactory不为空,并且beanFactory包含当前bean定义。如果符合条件则基于getAdvicesAndAdvisorsForBean返回的特定拦截器创建代理(拦截器可以为空)
  3. 否则使用默认bean实例,不适用代理

getAdvicesAndAdvisorsForBean

返回给定的bean是否被代理,如果是返回代理bean应用的附加通知(例如:AOP Alliance interceptors)和advisors通知者。由父类实现该方法:org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

findEligibleAdvisors

findCandidateAdvisors

  1. 为该自动代理的类查找所有合格的通知者Advisor,根据类型获取所有Advisor类型包含工厂父类中的Advisor类型的beanName,根据beanName与Advisor类型获取bean,返回advisors
  2. 向advisors中追加由切面通知者构建者aspectJAdvisorsBuilder构建的通知者列表,获取工厂中所有Object类型bean包含工厂父类(即所有bean),如果bena存在Aspect注解,并且类中不存在"ajc$"开头的字段,则使用工厂advisorFactory与MetadataAwareAspectInstanceFactory构建通知者

findAdvisorsThatCanApply

  1. 是否可以应用canApply,如果是IntroductionAdvisor类型则获取过滤器getClassFilter判断与目标类是否匹配。如果是PointcutAdvisor类型,则通过Pointcut获取过滤器getClassFilter判断是否匹配,并且如果存在方法匹配器getMethodMatcher判断与目标类是否匹配

extendAdvisors
如果使用的是AspectJ切面,则添加默认通知:org.springframework.aop.support.DefaultPointcutAdvisor
sortAdvisors
使用AnnotationAwareOrderComparator比较器排序通知

createProxy

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
基于getAdvicesAndAdvisorsForBean返回的指定拦截器创建代理:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy

  1. 为工厂中bean定义设置原始类型:org.springframework.aop.framework.autoproxy.AutoProxyUtils#ORIGINAL_TARGET_CLASS_ATTRIBUTE
  2. 创建代理工厂:ProxyFactory,并copy当前实例配置(AbstractAutoProxyCreator为ProxyConfig子类)
  3. 如果proxyTargetClass=false,判断如果bean定义的org.springframework.aop.framework.autoproxy.AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE属性为true则将重写proxyTargetClass配置为true
  4. 否则遍历目标类所有接口添加至代理工厂,如果不存在接口则重写proxyTargetClass配置为true,即改为cglib代理

基于beanName与特定的拦截器构建统一类型的通知(org.springframework.aop.Advisor):org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#buildAdvisors。org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#wrap将其他联盟(包含aspectJ)实现spring-aop接口org.aopalliance.aop.Advice类型(包含org.aopalliance.intercept.MethodInterceptor)的实例封装为默认的:DefaultPointcutAdvisor
回调AdvisedSupportListener.adviceChanged方法通知监听通知发生变更
回调自定义配置重写代理工厂配置:customizeProxyFactory
从代理工厂中获取代理并返回

第一次创建代理时回调org.springframework.aop.framework.AdvisedSupportListener#activated方法通知监听器当前创建代理配置(可再次重写配置)
创建aop代理:org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy

  1. AdvisedSupport.optimize优化参数为true,或者proxyTargetClass=true,或者没有可被代理的接口
    1. 如果目标类本身是接口,或者目标类为java.lang.reflect.Proxy子类型,则使用jdk代理:JdkDynamicAopProxy
    2. 否则使用cglib代理:ObjenesisCglibAopProxy
  2. 否则使用jdk代理:JdkDynamicAopProxy
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);
	}
}

基于创建的代理获取代理对象:org.springframework.aop.framework.AopProxy#getProxy(java.lang.ClassLoader)
JdkDynamicAopProxy、ObjenesisCglibAopProxy而且区别也就不再多说,相比普通的两种代理而言,aop代理在原代理基础上增加了切面需要注入的钩子方法来实现before,after,around三种类型的通知,或者MethodInterceptor.invoke的包装

案例

来点直观的感受吧看下aop所生成的动态代理类是个啥样子
目标bean:MyService

/**
 * @author 会灰翔的灰机
 * @date 2020/9/6
 */
@Component
public class MyService {

    public void sayHello(){
        System.out.println("Hello !!!");
    }

}

MyService切面:MyServiceAspect

/**
 * @author 会灰翔的灰机
 * @date 2020/9/6
 */
@Aspect
@Component
public class MyServiceAspect {

    @Pointcut("execution(public * com.dianwoda.billing.settle.provider.MyService.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = new Object();
        System.out.println("MyServiceAspect start");
        result = proceedingJoinPoint.proceed();
        System.out.println("MyServiceAspect end");
        return result;
    }

}

代理类

MyService$$EnhancerBySpringCGLIB$$399fe137.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Dispatcher;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;

public class MyService$$EnhancerBySpringCGLIB$$399fe137 extends MyService implements SpringProxy, Advised, Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private MethodInterceptor CGLIB$CALLBACK_1;
    private NoOp CGLIB$CALLBACK_2;
    private Dispatcher CGLIB$CALLBACK_3;
    private Dispatcher CGLIB$CALLBACK_4;
    private MethodInterceptor CGLIB$CALLBACK_5;
    private MethodInterceptor CGLIB$CALLBACK_6;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$sayHello$0$Method;
    private static final MethodProxy CGLIB$sayHello$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK257() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.dianwoda.billing.settle.provider.MyService$$EnhancerBySpringCGLIB$$399fe137");
        Class var1;
        CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello", "()V"}, (var1 = Class.forName("com.dianwoda.billing.settle.provider.MyService")).getDeclaredMethods())[0];
        CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHello", "CGLIB$sayHello$0");
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    }

    final void CGLIB$sayHello$0() {
        super.sayHello();
    }

    public final void sayHello() {
        try {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }

            if (var10000 != null) {
                var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
            } else {
                super.sayHello();
            }
        } catch (Error | RuntimeException var1) {
            throw var1;
        } catch (Throwable var2) {
            throw new UndeclaredThrowableException(var2);
        }
    }
    ...
}

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号