一、spring中AOP解析(注解式)
AOP介绍与使用
AOP直译为面向切面编程,解释为在不改变代码顺序的前提下,实现在一个顺序执行的逻辑代码之间插入扩展逻辑的的目的;举个例子:一个逻辑A执行顺序是X->Y,现在有另外一个逻辑C,想要在不改变逻辑A代码的前提下将逻辑C插入到X和Y之间,将逻辑A执行顺序改为X->C->Y,这就是切面编程的应用(方法增强)。
spring中已经默认实现了AOP功能,我们可以通过简单的配置就能使用AOP实现对指定方法的业务扩展。下面我们来写一个简单的AOP实例:
net.aop.AopTest.java(切面类)
@EnableAspectJAutoProxy
@Aspect
@Component
public class AopTest {
//切点方法(代指被代替的方法)
//这里使用了execution表达式(任意返回值 net.aop包及子包的所有类的所有方法 参数数量不限)
@Pointcut("execution(* net.aop..*(..))")
public void aspect() { }
//切点方法前执行
@Before("aspect()")
public void before(JoinPoint joinPoint){
System.out.println("===========Before切点前======="+joinPoint);
}
//环绕切点方法执行
@Around("aspect()")
public void Around(JoinPoint joinPoint) throws Throwable {
System.out.println("===========Around环绕前======="+joinPoint);
((ProceedingJoinPoint)joinPoint).proceed();
System.out.println("===========Around环绕后======="+joinPoint);
}
//切点方法后执行
@After("aspect()")
public void after(JoinPoint joinPoint){
System.out.println("===========After切点后======="+joinPoint);
}
//切点方法返回后执行
@AfterReturning("aspect()")
public void afterReturning(JoinPoint joinPoint){
System.out.println("===========afterReturning返回后======="+joinPoint);
}
//切点方法抛出异常后执行
@AfterThrowing("aspect()")
public void afterThrowing(JoinPoint joinPoint){
System.out.println("===========AfterThrowing异常抛出后======="+joinPoint);
}
}
net.aop.ExecutorBean.java(被扩展的bean)
@Component
public class ExecutorBean {
private String msg = "msg";
public void test(){
System.out.println("========="+msg);
}
}
通过spring调用:
@Test
public void test() {
ApplicationContext configApplicationContext = new AnnotationConfigApplicationContext("net.aop");
ExecutorBean bean = configApplicationContext.getBean(ExecutorBean.class);
bean.test();
}
//执行结果:
===========Around环绕前=======execution(void net.aop.ExecutorBean.test())
===========Before切点前=======execution(void net.aop.ExecutorBean.test())
=========msg
===========Around环绕后=======execution(void net.aop.ExecutorBean.test())
===========After切点后=======execution(void net.aop.ExecutorBean.test())
===========afterReturning返回后=======execution(void net.aop.ExecutorBean.test())
上述实例在没有改变ExecutorBean的前提下,实现了对于test()方法的扩展。主要逻辑是创建了一个切面bean,将切面bean的切点指向了net.aop包下的所有类的所有方法,也就是执行net.aop包下的所有方法都会触发执行切面定义的扩展方法。
注意:spring中使用AOP,需要开启AOP解析器,上述实例全部使用注解,注解开启AOP的是@EnableAspectJAutoProxy,而XML式的配置是<aop:aspectj-autoproxy/>,都只需要配置一次就可以启用AOP功能。
spring中AOP的解析
(注意:下文内容需要知道到springIOC创建流程、spring对beanProsessor接口类的应用和JDK、Cglib动态代理相关知识,请提前了解相关知识要点)
spring创建IOC容器时,会先根据获取到bean信息创建一个BeanDefinition对象列表,之后根据BeanDefinition列表对bean进行一一实例化。而AOP的逻辑就是使用beanProsessor接口类拦截了AOP被代理类的创建,动态创建了一个织入切面方法的被代理类bean,之后将这个bean加入IOC容器提供使用。下面会通过spring源码了解这个过程:
- 上文我们通过一个切面类AopTest和**@EnableAspectJAutoProxy就完成了AOP的配置,这里我们从AOP开关@EnableAspectJAutoProxy**开始看起:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//注册AnnotationAwareAspectJAutoProxyCreator.class到spring中
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
//代码省略...
}
}
其中@EnableAspectJAutoProxy中的关键是@Import(AspectJAutoProxyRegistrar.class) @Import注解的作用是将参数class封装成BeanDefinition注册到spring中;但是AspectJAutoProxyRegistrar.class是一个注册器对象(ImportBeanDefinitionRegistrar接口对象),spring不会直接将注册器对象直接注册到spring中,而是在之后会执行ImportBeanDefinitionRegistrar接口对象的registerBeanDefinitions方法,将注册器中指定的对象注册到spring中。AspectJAutoProxyRegistrar.class的接口方法注册的是AnnotationAwareAspectJAutoProxyCreator.class对象,这个对象是一个BeanPostProcessor接口对象,也是AOP的功能实现核心对象。继承链如下图所示:
AnnotationAwareAspectJAutoProxyCreator对象会和其他BeanPostProcessor接口对象一样,会提前实例化并加入spring的BeanPostProcessors列表中,在之后的spring bean预实例化中会循环这个BeanPostProcessors列表执行拦截方法作为创建其他bean时的扩展拦截器;主要拦截方法有两个,都在父类AbstractAutoProxyCreator中,一个是postProcessBeforeInstantiation()在bean实例化前执行,另一个是postProcessAfterInitialization在bean初始化完成后执行,代码逻辑如下:
//org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
@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;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
//省略...
return null;
}
//AspectJAwareAdvisorAutoProxyCreator#shouldSkip
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors(); //查询切面bean列表
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
//AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//构建有@Aspect注解的切面bean信息及对应的通知方法列表
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
我们以上文的ExecutorBean为例,在spring预实例化时,要通过ExecutorBean的BeanDefinition对象去实例化ExecutorBean,在实例化前会执行postProcessBeforeInstantiation()方法,此方法的主要逻辑是循环spring中bean列表,找到有@Aspect注解的切面bean即AopTest,将有AOP中通知注解(@Before、@After等)的方法封装成一个个Advisor接口对象(实际是InstantiationModelAwarePointcutAdvisorImpl对象),这个对象中创建了和通知注解对应的回调对象(MethodInterceptor接口对象,如:AspectJMethodBeforeAdvice),在循环完AopTest的方法后将封装的Advisor接口对象组织成list,放入advisorsCache<beanName, advisorList> Map缓存中。以上逻辑存在于postProcessBeforeInstantiation()中的shouldSkip方法中(shouldSkip主要判断是否要跳过当前bean),只有在实例化用户自定义的第一个bean时才会执行完成,在之后实例化其他bean时会直接返回advisorsCache缓存数据。
postProcessBeforeInstantiation()执行完后开始bean的创建和初始化,初始化完后执行postProcessAfterInitialization()方法,代码逻辑如下:
//org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
@Override
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;
}
//AbstractAutoProxyCreator#wrapIfNecessary
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;
}
// Create proxy if we have advice.(判断当前bean是否匹配到某个切面切点表达式)
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;
}
//org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
@Override
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.");
}
//JDK代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
//Cglib代理
return new ObjenesisCglibAopProxy(config);
}
else {
//JDK代理
return new JdkDynamicAopProxy(config);
}
}
postProcessAfterInitialization()的主要逻辑主要在方法wrapIfNecessary()中,该方法这主要逻辑是获取之前的切面Map缓存advisorsCache,通过切面的切点表达式来匹配当前bean路径,若匹配成功则返回对应切面的Advisor接口对象列表,进而执行createProxy方法创建bean的代理对象,创建代理对象会选择是JDK方式创建还是Cglib方式创建,创建时会将Advisor接口对象列表设置到代理对象中。代理对象创建完成后会替换创建的原实例化对象放入IOC容器中,当调用代理对象方法时会将Advisor接口对象列表设置到代理对象的回调方法中,并按顺序一一执行切面的通知方法逻辑,以此完成AOP的代理调用。
总结逻辑
- 通过**@EnableAspectJAutoProxy获取AspectJAutoProxyRegistrar注册器,AspectJAutoProxyRegistrar会执行注册器方法registerBeanDefinitions注册AnnotationAwareAspectJAutoProxyCreator**(继承BeanPostProcessor接口)到spring的 beanDefinition列表中
- AnnotationAwareAspectJAutoProxyCreator在实例化后加入BeanPostProcessors列表中
- ExecutorBean预实例化时,在createBean()方法中循环BeanPostProcessors列表执行AnnotationAwareAspectJAutoProxyCreator的postProcessBeforeInstantiation获取切面方法列表(MethodInterceptor接口对象)
- 之后在doCreateBean()#initializeBean()方法中循环BeanPostProcessors列表执行postProcessAfterInitialization方法—>通过wrapIfNecessary方法判断是否需求创建代理对象(若点面方法的execution表达式匹配到了当前bean才会创建代理对象)选择用JDK或Cglib创建bean代理对象(插入切面bean的方法),将之前的切面方法列表赋值得到代理对象的回调列表中,之后返回代理对象完成实例化
- 调用ExecutorBean的test方法会调用Cglib代理对象的invoke方法触发切面方法
- 附录:BeanPostProcessor接口在spring中创建bean的关键位置图: