之前分析IoC
容器的启动流程时,夹杂在启动流程中我们发现Spring
给我们提供了大量的扩展点,基于这些扩展点我们就可以实现很多灵活的功能定制需求。这篇我们首先来看下BeanFactoryPostProcessor
这个扩展点,它是非常重要的一个扩展点,面向IoC
容器进行扩展。
【资料图】
BeanFactoryPostProcessor
是BeanFactory
的后置处理器,针对BeanFactory
实现各种功能扩展。BeanFactoryPostProcessor
又分为两类:BeanFactoryPostProcessor
和BeanDefinitionRegistryPostProcessor
。BeanDefinitionRegistryPostProcessor
继承了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
容器中:
@Componentpublic 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、输出结果:
myConfigtestService01testService03myBeanFactoryPostProcessortestService02===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@Indexedpublic @interface MyComponent { String value() default "";}
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interface MyComponentScan { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class>[] basePackageClasses() default {};}
2、定义一个BeanDefinitionRegistryPostProcessor
实现类:
@Componentpublic class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { private List 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 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
方法:
@Componentpublic class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { private List 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
@MyComponentpublic 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,zhangsanexecute 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
功能。 标签:
精彩推荐
1、多玩大脚蘑菇等都是适合新手使用的通用型插件,里面功能齐全,而且对原本界面的改动不大。2、但如果...
1、川井宪次是日本著名作曲家,曾为大量动画、电影名作配乐。2、《一刻公寓》、《乱马1 2》、《无责任...
26日晚,“相约闽江边”音乐嘉年华在“闽江之心”青年广场举办。这是“闽江之心”周末文化惠民演出的重...
红三兵是连续收出三根阳线的K线组合,它是一种很常见的K线组合,多数出现在底部区域,偶尔也会出现在上...
1、社保: 社会保险是指国家为了预防和强制社会多数成员参加的,具有所得重分配功能的非营利性的社会...
直播吧3月27日讯姆巴佩在此前已经为法国队打进了38球,超越本泽马(37球)升至法国队史射手榜第5名。姆...
人均超人?瓦格纳雇佣兵正面硬刚乌军10个旅,战斗力为何这么强?,乌军,硬刚,俄军,马里,士兵,瓦格纳,雇佣兵
1、广饶县孙武湖放心蔬菜基地成立于2009年,占地面积750亩,投资1600万元。2、基地建设办公场所20间,铺设花
iphone云服务是什么,苹果手机的云服务叫什么很多人还不知道,现在让我们一起来看看吧!解答:1、Iphone...
【高清组图】贵州望谟:以茶促旅以旅带茶首届“紫茶文化节”举行
我国首座深远海浮式风电平台“海油观澜号”26日在广东珠海福陆码头启航前往海南文昌海域。这标志着我国...
最近,多地警方接到不少市民报案称自己收到了陌生快递快递盒子平平无奇里面是一个杯子有这样的↓↓↓或...
1、《极品太监猎美传奇》,作者是小强哥哥;2、《极品假太监混后宫》,作者是东风破;3、《混在后宫假太...
1、您好,胎儿体重的计算公式有多个,胎儿体重(克)=-4973 72+260 69*HC(头围)胎儿体重(克)
【230亿元!利率最高的50年期国债来了】近日,财政部招标发行3期国债,其中,2023年记账式附息(七期)国...
1、A类违规12分,一个节点处罚,屏蔽店铺7天,处罚期结束考试通过之后店铺可以恢复。2、打造爆款与A类处...
1、准备好做最简单贺卡的工具和材料。做最简单贺卡的工具和材料有:剪刀、铅笔、橡皮、彩色笔、彩色纸,...
郭艾伦、胡金秋、徐杰、王奕博、张宁、曾凌铉、焦泊乔、崔永熙、赵柏清、袁堂文、崔晓龙、周子昂(特邀...
大连海警勇救被困落水车内群众---获救村民张任伟对该舰执法员孙海鹏休假期间不顾生命危险,冲入荷塘从落...
为方便市民前往我市各大墓园进行清明扫墓祭奠,公交集团清明节期间的公交运营组织方案如下:一、加强岛...
1、临渊羡鱼下一句是:不如退而结网。2、意思是站在水边想得到鱼,不如回家去结网。比喻只有愿望而没有...
央视网消息:春天的中国,春意盎然,万象更新。下面,我们跟随镜头去祖国大地看一看。现在,我们来到重...
纯欲是什么意思,纯欲到底是什么意思很多人还不知道,现在让我们一起来看看吧!解答:1、纯粹的欲望是什...
1、是要加一个带蒙板的渐变图层还是在图层蒙板上加渐变?我想你要的是后者吧:首先打开你要做的图。2、...
【全国两会精神在基层】西宁西关大街街道:巧用“加减乘除”法解好两会精神方程式
资讯News
06-20
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
聚焦Policy
当好农民工的“护薪人” 近日,罗某等7名农民工在收到被拖欠的工资后,纷纷打电话向江西省南昌市...
“通讯录里所有人都知道我欠钱了” □ 本报记者 韩丹东 □ 本报见习记者 张守坤 ...
大连宝马车撞人案肇事司机被判死刑 本报讯 记者韩宇 10月29日,辽宁省大连市中级人民法院一审...
医院财务迷上网络赌博输光5000万元公款 □ 本报记者 马维博 □ 本报通讯员 汪宇堂 曹...
辊环车削 雕琢毫厘(工匠绝活) 【绝活看点】 23年来,雷虎始终扎根一线,改进钢材轧制工艺...
交警严查超标电动自行车挪用“白牌” 截至昨晚6时,处罚电动自行车违法行为共计6585笔;下一步将...
明起寒潮来袭 北方气温普降10℃以上 中央气象台预计,本周日北京平原地区最低气温降至-4℃左右...
多种蔬菜价格降幅达五成 包括菠菜、蒿子秆等 预计本月中旬蔬菜恢复供需平衡 本报讯(记者...
北京周日最低气温或达-4℃ 本报讯(记者 赵婷婷)北京青年报记者昨天从中央气象台获悉,新一股...
昌平一家四口确诊新冠肺炎 天通北苑第二社区升级为中风险地区 朝阳两涉疫校区及16所学校停课 ...