跳至主要內容

Javassm - 后置处理器与AOP

codejavassmSpring约 1568 字大约 5 分钟

后置处理器与AOP

PostProcessor

介绍一下PostProcessor,它其实是Spring提供的一种后置处理机制

可以让我们能够插手BeanBeanFactoryBeanDefinition的创建过程,相当于进行一个最终的处理,而最后得到的结果(比如Bean实例、Bean定义等)就是经过后置处理器返回的结果

它是整个加载过程的最后一步。

而AOP机制正是通过它来实现

BeanPostProcessor

相当于Bean初始化的一个后置动作,我们可以直接实现此接口:

// 注意它后置处理器也要进行注册
@Component
public class TestBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName);  //打印bean的名称
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }
}

我们发现,此接口中包括两个方法

一个是postProcessAfterInitialization用于在Bean初始化之后进行处理

还有一个postProcessBeforeInitialization用于在Bean初始化之前进行处理

注意这里的初始化不是创建对象,而是调用类的初始化方法,比如:

@Component
public class TestBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("我是之后:"+beanName);
        return bean;   
        // 这里返回的Bean相当于最终的结果了,我们依然能够插手修改,这里返回之后是什么就是什么了
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("我是之前:"+beanName);
        return bean;   
        // 这里返回的Bean会交给下一个阶段,也就是初始化方法
    }
}
执行顺序分析
@Component
public class TestServiceImpl implements TestService{

    public TestServiceImpl(){
        System.out.println("我是构造方法");
    }

    @PostConstruct
    public void init(){
        System.out.println("我是初始化方法");
    }

    TestMapper mapper;

    @Autowired
    public void setMapper(TestMapper mapper) {
        System.out.println("我是依赖注入");
        this.mapper = mapper;
    }
    ...
}

TestServiceImpl的加载顺序为:

我是构造方法
我是依赖注入
我是之前:testServiceImpl
我是初始化方法
我是之后:testServiceImpl

Bean加载流程

现在我们再来总结一下一个Bean的加载流程:

[Bean定义] 首先扫描Bean,加载Bean定义 -> [依赖注入] 根据Bean定义通过反射创建Bean实例 -> [依赖注入] 进行依赖注入(顺便解决循环依赖问题)

-> [初始化Bean] BeanPostProcessor的初始化之前方法 -> [初始化Bean] @PostConstruct Bean初始化方法 -> [初始化Bean] BeanPostProcessor的初始化之后方法

-> [完成]最终得到的Bean加载完成的实例

Aop实现过程

利用这种机制,理解Aop的实现过程就非常简单了,AOP实际上也是通过这种机制实现的

它的实现类是AnnotationAwareAspectJAutoProxyCreator,而它就是在最后对Bean进行了代理

因此最后我们得到的结果实际上就是一个动态代理的对象(有关详细实现过程,这里就不进行列举了,感兴趣的可以继续深入)

因此,实际上之前设计的三层缓存,都是由于需要处理AOP设计的,因为在Bean创建得到最终对象之前,很有可能会被PostProcessor给偷梁换柱!

那么肯定有人有疑问了,这个类没有被注册啊,那按理说它不应该参与到Bean的初始化流程中的,为什么它直接就被加载了呢?

还记得@EnableAspectJAutoProxy吗?我们来看看它是如何定义就知道了:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

我们发现它使用了@Import来注册AspectJAutoProxyRegistrar,那么这个类又是什么呢,我们接着来看:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 注册AnnotationAwareAspectJAutoProxyCreator到容器中
        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);
            }
        }

    }
}

它实现了接口,这个接口也是Spring提供的一种Bean加载机制,它支持直接向容器中添加Bean定义,容器也会加载这个Bean:

  • ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类。
  • 使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口中方法(一般用于注册Bean)
  • 实现该接口的类拥有注册bean的能力。

我们可以看到此接口提供了一个BeanDefinitionRegistry正是用于注册Bean的定义的。

因此,当我们打上了@EnableAspectJAutoProxy注解之后,首先会通过@Import加载AspectJAutoProxyRegistrar,然后调用其registerBeanDefinitions方法,然后使用工具类注册AnnotationAwareAspectJAutoProxyCreator到容器中

这样在每个Bean创建之后,如果需要使用AOP,那么就会通过AOP的后置处理器进行处理,最后返回一个代理对象

ImportBeanDefinitionRegistrar

我们也可以尝试编写一个自己的ImportBeanDefinitionRegistrar实现,首先编写一个测试Bean:

public class TestBean {
    
    @PostConstruct
    void init(){
        System.out.println("我被初始化了!");
    }
}
public class TestBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinition definition = BeanDefinitionBuilder.rootBeanDefinition(Student.class).getBeanDefinition();
        registry.registerBeanDefinition("lbwnb", definition);
    }
}

观察控制台输出,成功加载Bean实例。

BeanPostProcessor差不多的还有BeanFactoryPostProcessor,它和前者一样,也是用于我们自己处理后置动作的,不过这里是用于处理BeanFactory加载的后置动作

BeanDefinitionRegistryPostProcessor直接继承自BeanFactoryPostProcessor,并且还添加了新的动作postProcessBeanDefinitionRegistry,你可以在这里动态添加Bean定义或是修改已经存在的Bean定义

这里我们就直接演示BeanDefinitionRegistryPostProcessor的实现:

@Component
public class TestDefinitionProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("我是Bean定义后置处理!");
        BeanDefinition definition = BeanDefinitionBuilder.rootBeanDefinition(TestBean.class).getBeanDefinition();
        registry.registerBeanDefinition("lbwnb", definition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("我是Bean工厂后置处理!");
    }
}

在这里注册Bean定义其实和之前那种方法效果一样。

最后,我们再完善一下Bean加载流程(加粗部分是新增的):

[Bean定义]首先扫描Bean,加载Bean定义 -> [Bean定义]Bean定义和Bean工厂后置处理 -> [依赖注入]根据Bean定义通过反射创建Bean实例 -> [依赖注入]进行依赖注入(顺便解决循环依赖问题)-> [初始化Bean]BeanPostProcessor的初始化之前方法 -> [初始化Bean]Bean初始化方法 -> [初始化Bean]BeanPostProcessor的初始化之前后方法 -> [完成]最终得到的Bean加载完成的实例

上次编辑于: