spring aop(一):aop 示例 demo 及 @EnableAspectJAutoProxy
注:本系列源码分析基于spring 5.2.2.RELEASE,本文的分析基于 annotation 注解方式,gitee仓库链接:gitee.com/funcy/sprin….
前面的文章分析了spring的启动流程,从本文开始,我们来分析spring aop功能。
1. 示例demo
1.1 准备demo
aop的示例demo依旧位于spring-learn
模块,包名是org.springframework.learn.demo02
,目录结构如下:
- 准备 annotation 首先,我们准备一个annotation用来标注切面:
package org.springframework.learn.demo02;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopAnnotation {
}
- 编写切面方法
package org.springframework.learn.demo02;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* {这里添加描述}
*
* @author chengyan
* @date 2020-07-19 6:00 下午
*/
@Aspect
@Component
public class AopAspectj {
@Pointcut("@annotation(org.springframework.learn.demo02.AopAnnotation)")
public void testAop(){
}
@Around("testAop()")
public Object around(ProceedingJoinPoint p){
System.out.println("before execute...");
Object o = null;
try {
o = p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("after execute...");
return o;
}
}
这个类表示,标注了 @AopAnnotation
的方法,将运行其运行前后运行一些代码,around(ProceedingJoinPoint)
指定了运行前后的代码。
- 启用proxy
package org.springframework.learn.demo02;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@EnableAspectJAutoProxy
@Component
public class AopAnnotationConfig {
}
这个类关键在于上面的两个注解:@EnableAspectJAutoProxy
、@Component
,前者表示启用代理,后者表示该类为spring bean.
- 准备三个类
- 继承了接口且标有注解
AopAnnotation
的类AopBean1
- 未继承接口但标有注解
AopAnnotation
的类AopBean2
- 未继承接口也未标有注解
AopAnnotation
的类AopBean3
这几个类的内容分别如下:
IAopBean.java
package org.springframework.learn.demo02;
public interface IAopBean {
void hello();
}
AopBean1.java
package org.springframework.learn.demo02;
import org.springframework.stereotype.Component;
@Component
public class AopBean1 implements IAopBean {
@Override
@AopAnnotation
public void hello() {
System.out.println("实现了接口IAopService的hello()");
}
}
AopBean2.java
package org.springframework.learn.demo02;
import org.springframework.stereotype.Component;
@Component
public class AopBean2 {
@AopAnnotation
public void hello() {
System.out.println("未实现接口IAopService的hello()");
}
}
AopBean3.java
package org.springframework.learn.demo02;
import org.springframework.stereotype.Component;
@Component
public class AopBean3 {
public void hello() {
System.out.println("未实现接口IAopService、也未进行aop的hello()");
}
}
- 启动类
package org.springframework.learn.demo02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo02Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(
"org.springframework.learn.demo02");
IAopBean obj1 = (IAopBean)context.getBean("aopBean1");
AopBean2 obj2 = (AopBean2)context.getBean("aopBean2");
AopBean3 obj3 = (AopBean3)context.getBean("aopBean3");
System.out.println("obj1:" + obj1.getClass());
obj1.hello();
System.out.println("-----------------------");
System.out.println("obj2:" + obj2.getClass());
obj2.hello();
System.out.println("-----------------------");
System.out.println("obj3:" + obj3.getClass());
obj3.hello();
}
}
1.2 运行结果及说明
运行main(String[])
方法,结果如下:
obj1:class com.sun.proxy.$Proxy19
before execute...
实现了接口IAopService的hello()
after execute...
-----------------------
obj2:class org.springframework.learn.demo02.AopBean2$$EnhancerBySpringCGLIB$$e068ccef
before execute...
未实现接口IAopService的hello()
after execute...
-----------------------
obj3:class org.springframework.learn.demo02.AopBean3
未实现接口IAopService、也未进行aop的hello()
首先,可以看到,AopBean1
与AopBean2
在执行hello()
方法前后均输出了内容,表明已正确实现了aop功能;AopBean3
方法执行前后未实现任何内容,因为我们并未在AopBean3
的hello()
方法上添加@AopAnnotation
。
接着再来看的class:AopBean1
的class是com.sun.proxy.$Proxy19
,这是jdk提供的动态代理类;AopBean2
的class是org.springframework.learn.demo02.AopBean2$$EnhancerBySpringCGLIB$$e068ccef
,从名称上EnhancerBySpringCGLIB
可以看出,这个类通过spring cglib
类增强的类;AopBean3
的class是org.springframework.learn.demo02.AopBean3
,因为我们并未在AopBean3
的hello()
方法上添加@AopAnnotation
。
对以上现象,总结如下:
-
spring 开启了 aop 后,只会对需要代理的类创建代理对象。在上述代码中,通过
@EnableAspectJAutoProxy
开启了aop功能,但并不是所有的类都创建了代理对象,只有AopBean1
与AopBean2
创建了代理对象,AopBean3
还是原来的对象,可见代理对象也是“按需”创建的; -
如果类实现了接口,那么spring在创建代理对象时,会采用jdk提供的动态代理功能;如果类未实现接口,spring会使用cglib来创建代理对象。
1.3 强制使用 cglib 创建代理对象
实际上,是否需要采用jdk提供的动态代理功能来创建代理对象,spring也提供了一个配置,该配置位于@EnableAspectJAutoProxy
中:
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* proxyTargetClass属性,默认false,尝试采用JDK动态代理织入增强(如果当前类没有实现接口则还是会使用CGLIB);
* 如果设为true,则强制采用CGLIB动态代理织入增强.
*/
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
如果我们需要强制使用cglib创建代理对象,可以这样设置:
package org.springframework.learn.demo02;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
/**
* {这里添加描述}
*
* @author chengyan
* @date 2020-07-19 5:59 下午
*/
// 强制使用cglib创建动态代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
public class AopAnnotationConfig {
}
运行结果如下:
obj1:class org.springframework.learn.demo02.AopBean1$$EnhancerBySpringCGLIB$$cccd444c
before execute...
实现了接口IAopService的hello()
after execute...
-----------------------
obj2:class org.springframework.learn.demo02.AopBean2$$EnhancerBySpringCGLIB$$e068ccef
before execute...
未实现接口IAopService的hello()
after execute...
-----------------------
obj3:class org.springframework.learn.demo02.AopBean3
未实现接口IAopService、也未进行aop的hello()
可以看到,AopBean1
也使用cglib来创建代理对象了。
2. @EnableAspectJAutoProxy
在spring中,@EnableXxx
表示启用某项功能,如@EnableAsync
表示启用异步功能,@EnableCaching
表示启用缓存功能,这里的@EnableAspectJAutoProxy
表示启用 aspectJ
的自动代理。熟悉@EnableXxx
注解功能的伙伴们应该知道,EnableXxx
类其上的@Import
注解才是启用该功能的关键,如@EnableAspectJAutoProxy
:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// Import 进来的类才是关键
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
...
}
这里@Import
进来的类是 AspectJAutoProxyRegistrar.class
,我们可以看看这个类做了什么。
如果不清楚
@EnableXxx
注解的功能,可以查看 spring探秘之spring是如何实现 @EnableXxx 功能的?
2.1 AspectJAutoProxyRegistrar
功能
AspectJAutoProxyRegistrar
内容如下:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//注册一个AOP代理实现的Bean
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
AspectJAutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
接口,在ImportBeanDefinitionRegistrar
提供的registerBeanDefinitions
中,调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)
往spring容器中注册了自动代理实现类。
我们跟进AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)
方法,看看这个类做了什么。一中跟下去,最终执行的方法为AopConfigUtils#registerOrEscalateApcAsRequired
:
// 自动代理创建类的名称
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
// 这里的 cls 是 AnnotationAwareAspectJAutoProxyCreator.class
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//如果已存在这个bean
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//判断优先级,如果优先级较高则替换原先的bean
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
//注册AnnotationAwareAspectJAutoProxyCreator到容器中,此类负责基于注解的AOP动态代理实现
// 这里的cls是AnnotationAwareAspectJAutoProxyCreator
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;
}
这个方法主要做的事就是往spring容器中注册AnnotationAwareAspectJAutoProxyCreator
的beanDefinition
,后续spring在处理bean时,就能实例化该bean了。
2.2 AspectJAutoProxyRegistrar
执行时机
AspectJAutoProxyRegistrar
中只有一个方法:registerBeanDefinitions(...)
,讨论AspectJAutoProxyRegistrar
的执行时机,其实就是讨论org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions
的执行时机。
我们这里通过打断点调度的方式来探索:
调用栈如下:
|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)
|-AbstractApplicationContext#refresh
// 注意这一行代码,这里是ApplicationContext的refresh里的流程了
|-AbstractApplicationContext#invokeBeanFactoryPostProcessors
|-PostProcessorRegistrationDelegate
#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)
|-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
|-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
|-ConfigurationClassPostProcessor#processConfigBeanDefinitions
|-ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
|-ConfigurationClassBeanDefinitionReader
#loadBeanDefinitionsForConfigurationClass
|-ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
|-ImportBeanDefinitionRegistrar#registerBeanDefinitions(
AnnotationMetadata, BeanDefinitionRegistry, BeanNameGenerator)
|-AspectJAutoProxyRegistrar#registerBeanDefinitions(
AnnotationMetadata, BeanDefinitionRegistry)
可以看到,AspectJAutoProxyRegistrar
执行是在AbstractApplicationContext#refresh
的invokeBeanFactoryPostProcessors
中执行的。
3. 总结
本文提供了一个aop的示例,同时分析了@EnableAspectJAutoProxy
注解的作用,分析就先到这里了。
本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
本系列的其他文章
转载自:https://juejin.cn/post/7160107696878780430