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

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

之前分析 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


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

查看所有标签

猜你喜欢:

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

Head First jQuery

Head First jQuery

Ryan Benedetti , Ronan Cranley / O'Reilly Media / 2011-9 / USD 39.99

Want to add more interactivity and polish to your websites? Discover how jQuery can help you build complex scripting functionality in just a few lines of code. With Head First jQuery, you'll quickly g......一起来看看 《Head First jQuery》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

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

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具