注:本系列源码剖析基于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/… ,限于作者个人水平,文中不免有过错之处,欢迎指正!原创不易,商业转载请联系作者取得授权,非商业转载请注明出处。
本系列的其他文章
【spring源码剖析】spring源码剖析系列目录