Spring 源码(四):扩展点之 BeanFactoryPostProcessor

栏目: IT技术 · 发布时间: 4年前

之前分析 IoC 容器的启动流程时,夹杂在启动流程中我们发现 Spring 给我们提供了大量的扩展点,基于这些扩展点我们就可以实现很多灵活的功能定制需求。这篇我们首先来看下 BeanFactoryPostProcessor 这个扩展点,它是非常重要的一个扩展点,面向 IoC 容器进行扩展。

类结构

BeanFactoryPostProcessorBeanFactory 的后置处理器,针对 BeanFactory 实现各种功能扩展。 BeanFactoryPostProcessor 又分为两类: BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessorBeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor 接口,对其进行了扩展。

Spring 源码(四):扩展点之 BeanFactoryPostProcessor

执行时机

前面分析 IoC 容器启动流程中, refresh() 方法中 invokeBeanFactoryPostProcessors(beanFactory) 处触发 BeanFactoryPostProcessor 扩展类的执行。

执行逻辑总结:
1、先执行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法,其中BeanDefinitionRegistryPostProcessor执行优先级如下:
a、addBeanFactoryPostProcessor()传入到优先级最高,因为不需要实例化,直接可以获取到对象进行执行;
b、然后从IoC容器中获取PriorityOrdered接口的BeanDefinitionRegistryPostProcessor,实例化并 排序 后执行postProcessBeanDefinitionRegistry方法
c、然后从IoC容器中获取Ordered接口的BeanDefinitionRegistryPostProcessor,实例化并排序后执行postProcessBeanDefinitionRegistry方法
d、然后从IoC容器中获取剩余的BeanDefinitionRegistryPostProcessor,实例化后执行postProcessBeanDefinitionRegistry方法;注意这个处理步骤存在一个循环,主要是存在执行前面的BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法时,存在可能会向IoC容器中注册新的BeanDefinitionRegistryPostProcessor,通过循环保证都会被执行;
2、然后执行BeanDefinitionRegistryPostProcessor#postProcessBeanFactory方法,执行顺序参照步骤1中执行顺序;
3、最后才会执行BeanFactoryPostProcessor#postProcessBeanFactory,执行优先级和BeanDefinitionRegistryPostProcessor一致:
a、addBeanFactoryPostProcessor()传入到优先级最高,因为不需要实例化,直接可以获取到对象进行执行;
b、然后从IoC容器中获取PriorityOrdered接口的BeanFactoryPostProcessor,实例化并排序后执行postProcessBeanFactory方法
c、然后从IoC容器中获取Ordered接口的BeanFactoryPostProcessor,实例化并排序后执行postProcessBeanFactory方法
d、然后从IoC容器中获取剩余的BeanFactoryPostProcessor,实例化后执行postProcessBeanFactory方法

总结两句话:

  • 1、不同方法执行优先级:`BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` > `BeanDefinitionRegistryPostProcessor#postProcessBeanFactory` > `BeanFactoryPostProcessor#postProcessBeanFactory`;
    
  • 2、同方法执行优先级:`addBeanFactoryPostProcessor` > `PriorityOrdered` > `Ordered` > 非排序
    

使用场景

这两个接口都比较简单,都只定义一个方法。首先看下 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 这个方法,主要使用场景是:向 IoC 容器中注册 BeanDefinition 。这里面有个非常重要的实现类: ConfigurationClassPostProcessor ,完成各种将 Bean 解析成 BeanDefinition 注入到 IoC 容器功能。

BeanDefinitionRegistryPostProcessor

案例一

首先,我们来看个例子,自定义 BeanDefinition 方式注册到 IoC 容器中。

1、定义一个 Bean

public class TestService02 {

private TestService01 testService01;

@Autowired
private TestService03 testService03;

private String name;

//省略setter、toString方法
}

2、由于该 Bean 没有 @Component 等注解,所以我们可以定义一个 BeanDefinitionRegistryPostProcessor 扩展注册到 IoC 容器中:

@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("===MyBeanFactoryPostProcessor#postProcessBeanDefinitionRegistry===");
//创建一个BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestService02.class);
//获取MutablePropertyValues,用于添加依赖注入
MutablePropertyValues pvs = beanDefinition.getPropertyValues();
//为依赖注入指定固定值方式
pvs.addPropertyValue("name", "张三");
//为依赖注入指定beanName,自动将beanName指向IoC中的Bean注入进去
pvs.addPropertyValue("testService01", new RuntimeBeanReference("testService01"));
//将生成的BeanDefinition注册到IoC容器中
registry.registerBeanDefinition("testService02", beanDefinition);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("===MyBeanFactoryPostProcessor#postProcessBeanFactory===");
}

}

3、输出结果:

myConfig
testService01
testService03
myBeanFactoryPostProcessor
testService02
===TestService02属性信息===
TestService02{testService01=xxx.bean.TestService01@56ef9176, testService03=xxx.bean.TestService03@4566e5bd, name='张三'}

从输出结果可以看到, TestService02 已被注册到 IoC 容器中,且其属性字段都已成功进行依赖注入。通过这种方式,我们就可以更加灵活的向 IoC 容器注册 Bean ,实现各种功能的扩展。

案例二

我们可以模拟 @ComponentScan@Component 注解方式实现:将指定路径下的带有指定注解的Bean注册到IoC容器中。

1、首先,模仿 @ComponentScan@Component 定义两个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface MyComponent {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyComponentScan {

@AliasFor("basePackages")
String[] value() default {};

@AliasFor("value")
String[] basePackages() default {};

Class<?>[] basePackageClasses() default {};

}

2、定义一个 BeanDefinitionRegistryPostProcessor 实现类:

@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
private List<String> basePackageList = new ArrayList<>();

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("===MyBeanFactoryPostProcessor#postProcessBeanFactory===");
}

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

Arrays.stream(registry.getBeanDefinitionNames()).forEach(x -> parseMyComponentScan(registry.getBeanDefinition(x)));
if(basePackageList.isEmpty()){//没有扫描路径情况
return;
}
//定义一个BeanDefinitionScanner,扫描指定路径下Bean
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
scanner.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class));
scanner.setIncludeAnnotationConfig(false);
String[] basePackages = basePackageList.stream().toArray(String[]::new);
int beanCount = scanner.scan(basePackages);
System.out.println("注册成功:"+beanCount);
}

/**
* 遍历所有的BeanDefinition,然后解析出有@MyComponentScan注解信息,并提取basePackageClasses属性值,
* 解析成扫描路径存储到basePackageList集合中
* @param beanDefinition
*/

private void parseMyComponentScan(BeanDefinition beanDefinition){
if(AnnotatedBeanDefinition.class.isAssignableFrom(beanDefinition.getClass())){
AnnotatedBeanDefinition bdf = (AnnotatedBeanDefinition) beanDefinition;
Class<?> beanClass;
try {
beanClass = Class.forName(bdf.getBeanClassName());
AnnotationMetadata metadata = AnnotationMetadata.introspect(beanClass);
Map<String, Object> config = metadata.getAnnotationAttributes(MyComponentScan.class.getName());
if(config != null && config.containsKey("basePackageClasses")){
Object obj = config.get("basePackageClasses");
Class clz = ((Class[]) obj)[0];
basePackageList.add(clz.getPackage().getName());
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}

3、测试:

@Configuration
@ComponentScan(basePackageClasses = MyConfig.class)
@MyComponentScan(basePackageClasses = MyConfig.class)
public class MyConfig {
}

@ComponentScan 可以把常规的 @Component 注册到 IoC 容器中, @MyComponentScan 可以把指定包路径下带有 @MyComponent 注解的 Bean 注册到 IoC 容器中。

BeanFactoryPostProcessor

上面我们利用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 扩展方法实现了 @MyComponentScan@MyComponent 注解功能,实现将指定包路径下带有 @MyComponent 注解的Bean注册到 IoC 容器中。 BeanDefinitionRegistryPostProcessor 接口中还有一个继承父接口 BeanFactoryPostProcessor 中的方法: postProcessBeanFactory

上一个案例实现了 @MyComponent 注解的 Bean 注册到 IoC 功能,现在我们来实现对所有 @MyComponent 注解的类进行 AOP 增强。

1、定义一个增强类:

public class MyMethodInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable
{
try {
before(method);//前置通知
Object ret = methodProxy.invokeSuper(obj, args);//目标方法执行
after(method, ret);//后置通知
return ret;
} catch (Exception e) {
exception();//异常通知
} finally {
afterReturning();//方法返回通知
}
return null;
}
//前置增强
private void before(Method method) {
System.out.printf("before execute:%s\r\n", method.getName());
}
//后置增强
private void after(Method method, Object ret) {
System.out.printf("after execute:%s, ret:%s\r\n", method.getName(), ret);
}
//异常增强
private void exception() {
System.out.println("execute failure");
}
//after返回增强
private void afterReturning() {
System.out.println("execute finish");
}
}

2、基于 Spring Enhancer 工具类,实现一个传入 Class ,创建一个带有增强逻辑的 Class 返回出来 工具 类:

public class EnhancerUtil  {
//定义增强实例
private static final Callback[] CALLBACKS = new Callback[] {
new MyMethodInterceptor(),
NoOp.INSTANCE
};

private static final Class[] CALLBACKTYPES = new Class[] {
new MyMethodInterceptor().getClass(),
NoOp.INSTANCE.getClass()
};


public static Class createProxyClass(Class targetClz){
Enhancer enhancer = new Enhancer();
//基于继承方式代理:设置代理类的父类,就是目标对象,创建出来的对象就是目标对象的子类
enhancer.setSuperclass(targetClz);
//设置回调过滤器,返回值是callbacks数组的下标
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
/**
* 屏蔽掉Object类中定义方法的拦截
*/

if (method.getDeclaringClass() == targetClz) {
return 0;
}
return 1;
}
});
/**
* NoOp回调把对方法调用直接委派给这个方法在父类中的实现,即可理解为真实对象直接调用方法
*/

enhancer.setCallbackTypes(CALLBACKTYPES);
//设置类加载器
enhancer.setClassLoader(targetClz.getClassLoader());
//创建代理对象
Class clz = enhancer.createClass();
Enhancer.registerStaticCallbacks(clz, CALLBACKS);
return clz;
}

}

3、下面我们来实现 BeanFactoryPostProcessor#postProcessBeanFactory 方法:

@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
private List<String> basePackageList = new ArrayList<>();

/**
* 基于该扩展实现AOP增强
* @param beanFactory
* @throws BeansException
*/

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//遍历IoC中BeanDefinition
Arrays.stream(beanFactory.getBeanDefinitionNames())
.forEach(x -> enhancer(beanFactory.getBeanDefinition(x)));
}

/**
* 判断beanDefinition是否有@MyComponent注解,如果有则调用EnhancerUtil.createProxyClass创建一个增强后的类,
* 并替换到BeanDefinition中的beanClass
* @param beanDefinition
*/

private void enhancer(BeanDefinition beanDefinition){
if(ScannedGenericBeanDefinition.class.isAssignableFrom(beanDefinition.getClass())){
ScannedGenericBeanDefinition bdf = (ScannedGenericBeanDefinition) beanDefinition;
try {
String beanClassName = bdf.getBeanClassName();
Class beanClass = Class.forName(beanClassName);
if (beanClass != null){
AnnotationMetadata metadata = AnnotationMetadata.introspect(beanClass);
if(metadata.hasAnnotation(MyComponent.class.getName())){
Class enhancedClass = EnhancerUtil.createProxyClass(beanClass);
if(enhancedClass != beanClass){
bdf.setBeanClass(enhancedClass);
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}

}

4、定义Bean

@MyComponent
public class TestService02 {

public String hi(String name){
System.out.println("===TestService02#hi===");
return "hello,"+name;
}

}

5、 context.getBean(TestService02.class).testService02.hi("zhangsan") 调用方法时,会发现方法前后都已被加入增强逻辑:

before execute:hi
===TestService02#hi===
after execute:hi, ret:hello,zhangsan
execute finish
返回结果:hello,zhangsan

总结

通过使用场景案例分析,我们对 BeanFactoryPostProcessor 扩展点的使用大致情况应该有了一些了解:

  • BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
    IoC
    Bean
    IoC
    Bean
    
  • BeanFactoryPostProcessor#postProcessBeanFactory
    IoC
    BeanDefinition
    postProcessBeanDefinitionRegistry
    Bean
    BeanDefinition
    IoC
    BeanDefinition
    BeanDefinition
    beanClass
    Enhancer
    AOP
    Spring
    AOP
    

Spring 源码(四):扩展点之 BeanFactoryPostProcessor


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

怪诞行为学2

怪诞行为学2

[美]丹·艾瑞里 / 赵德亮 / 中信出版社 / 2010-1-9 / 42.00元

《怪诞行为学2:非理性的积极力量》编辑推荐:尝试用“非理性”的决策方式,彻底颠覆工作和生活中的“逻辑”,你将获得意想不到的成就感与幸福感!畅销书《怪诞行为学》作者卷土重来,掀起新一轮“非理性”狂潮。 《写给中国人的经济学》作者王福重、著名行为经济学家董志勇倾情作序。 诺贝尔经济学奖得主阿克尔洛夫、《免费》作者安德森高度评价。 《纽约时报》《哈佛商业评论》《波士顿环球报》等全球顶级......一起来看看 《怪诞行为学2》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具