前言
继续学习中,坚持写作,不求快,只求深
源码分析
aop应用代码
配置类
@Configuration
@ComponentScan("com.xxsc.cn.aopstudy")
@EnableAspectJAutoProxy(exposeProxy = true)
public class Myconfig {
}
需要加强类的方法
@Component
public class JoinPointDemo {
public void test1(){
System.out.println("test1方法执行了");
}
}
切面
@Aspect
@Component
public class MyAsepect {
/**
* 对连接点的描述,个人理解筛选增强的方法
*/
@Pointcut("execution(* com.xxsc.cn.aopstudy.JoinPointDemo.*(..))")
public void pointCut(){
}
/**
* 前置通知:在Pointcut筛选的join point连接点执行前执行
*/
@Before(value = "pointCut()")
public void methodBefore(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("前置通知执行的目标方法为"+methodName);
}
/**
* 后置通知 在Pointcut筛选的join point连接点执行后执行,无论是否异常
*/
@After(value = "pointCut()")
public void methodAfter(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("后置通知执行的目标方法为"+methodName);
}
/**
* 返回通知:在Pointcut筛选的join point连接点执行正常情况下执行,异常不执行
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void methodReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("返回通知执行的目标方法为"+methodName);
}
/**
* 异常通知 :在Pointcut筛选的join point连接点执行异常情况下执行,正常返回不执行
*/
@AfterThrowing(value = "pointCut()")
public void methodAfterThrowing(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("异常通知执行的目标方法为"+methodName);
}
}
测试类
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
JoinPointDemo joinPointDemo = context.getBean("joinPointDemo", JoinPointDemo.class);
joinPointDemo.test1();
}
}
执行结果
前置通知执行的目标方法为test1
test1方法执行了
返回通知执行的目标方法为test1
后置通知执行的目标方法为test1
对于以上代码,非常基础,不做过多解释。
aop源码分析入口
对于以上配置类,发现有一个注解,@EnableAspectJAutoProxy(exposeProxy = true) 这个就是SpringAOP源码分析入口。
源码片段一
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
发现 @Import(AspectJAutoProxyRegistrar.class) 注解,我们知道@Import注解可以往容器中添加bean,那么@Import添加bean有三种方式,分别如下:
①、直接import类的class
@Import(Student.class),那么Spring就会往容器中添加Student这个bean组件。
②、import实现ImportSelector接口的类
@Import(MyPort.class)
public class MyPort implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"com.xxsc.demo.Demo1","com.xxsc.demo.Demo2","com.xxsc.demo.Demo3","com.xxsc.demo.Demo4"};
}
}
它会把返回的数组的Demo1、Demo2、Demo3、Demo4这四个类都添加到Spring容器中。
③、import实现ImportBeanDefinitionRegistrar接口的类
@Import(MyRegistrar.class)
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//手动注册
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Student.class);
registry.registerBeanDefinition("student",rootBeanDefinition);
}
}
代码清晰可见手动注册一个bean到容器当中。
此时再看AOP的代码@Import(AspectJAutoProxyRegistrar.class),查看AspectJAutoProxyRegistrar类就容易理解了,是属于以上第三种方式,一起看看是如何手动注册的。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//关键代码
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);
}
}
}
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
//这块代码和以上Demo类似,很容易理解,这个class根据源码得知:是这个类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;
}
以上代码,只看最后几行即可,能找到需要添加Bean的类为:AnnotationAwareAspectJAutoProxyCreator ,这个类就是我们分析源码AOP最为关键的类。
这里补充一点,既然学习源码,就在深一点,如Spring是如何通过@Import注解导入这个组件的
//AbstractApplicationContext类的refresh()中,以下代码是bean扫描把bean定义添加到容器中
invokeBeanFactoryPostProcessors(beanFactory);
//AbstractApplicationContext类的refresh()中,以下代码是getBean初始化Bean
finishBeanFactoryInitialization(beanFactory);
那么从上面IOC的源码分析之后,我们只关注Spring是如何扫描到这个AnnotationAwareAspectJAutoProxyCreator 的bean定义并把它加到容器当中去的。
//ConfigurationClassPostProcessor类的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法的下面几行代码,不关键的在这省略
...
//下面这行代码核心逻辑:解析我们的配置类Myconfig,通过@ComponentScan注解找到所有的候选bean
//挨个判断是否需要添加到Spring容器,但是它只关注@Service @Compont @Contoller @Register 注解的组件
//找到有@Import 和 @Bean 注解的类,个人理解,先找了个地方保存起来,并没有注册Bean定义
parser.parse(candidates);
.....
//这行代码,找出通过@Import和@Bean注解的方式导入Spring容器的组件
this.reader.loadBeanDefinitions(configClasses);
分析this.reader.loadBeanDefinitions(configClasses); 这行代码
//核心代码
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
......
//把保存的候选bean进行遍历,判断是否是通过@Import的方式导入的
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//把保存的候选bean进行遍历,判断是否是通过@Bean的方式导入的
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//加载bean定义是从ImportedResources方式
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//加载ImportBean资源
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
因为此时,我们的bean就是三个:配置类、切面、需要增强的类 ,并没有通过@Import @Bean方式加入,但是配置类有@Impor 方式要导入的类,所以断定走loadBeanDefinitionsFromRegistrars这行代码
分析loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); 这行代码
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
//去注册,先不看后面怎么注册的源码,走debug再返回来分析接下来的源码
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
DEBUG运行分析:
断点位置:
此时容器中的bean定义组件有这么多,我们自己的,还有系统的