20190514面试SPRING扩展机制 柔情只为你懂 2022-02-01 11:23 132阅读 0赞 ## 1.SPRING扩展机制 ## * 通过基于XML和基于Java的配置扩展,可以使用户通过Spring使用我们研发的组件,提供很好的易用性。 * 通过Spring容器最常用的两个扩展点:`BeanFactoryPostProcessor`和`BeanPostProcessor`,可以使我们的程序逻辑和Spring容器紧密合作,无缝插入到用户bean的生命周期中,发挥更强大的作用。 ## 2.spring的扩展接口 ## * 1. FactroyBean 我们熟悉的AOP基础bean * 2. BeanPostProcess 在每个bena初始化成前后做操作。 * 3. InstantiationAwareBeanPostProcessor 在Bean实例化前后做一些操作。 * 4. BeanNameAware、ApplicationContextAware 和 BeanFactoryAware 针对bean工厂,可以获取上下文,可以获取当前bena的id。 * 5. BeanFactoryPostProcessor Spring允许在Bean创建之前,读取Bean的元属性,并根据自己的需求对元属性进行改变,比如将Bean的scope从singleton改变为prototype。 * 6. InitialingBean 在属性设置完毕后做一些自定义操作 DisposableBean 在关闭容器前做一些操作。 相关知识 # 一扩展方式 # ## 1。基于XML配置的扩展 ## 1.首先需要定义一套XML Schema来描述组件所提供的功能。schema中就需要描述我们期望用户提供的namespace以及namespace之间的排序等元数据。 2.除了XML Schema,我们还需要创建一个自定义的NamespaceHandler来负责解析用户在XML中的配置。 * 为了简化代码,我们一般会继承一个helper类:`NamespaceHandlerSupport`,然后在`init`方法中注册处理我们自定义节点的BeanDefinitionParser * 自定义的BeanDefinitionParser负责解析xml中的`config`节点信息,记录用户的配置信息,为后面和Spring整合做好铺垫。 3.注册Spring handler和Spring schema,让Spring解析xml配置文件的过程中识别我们的自定义节点,并且转交到我们的`NamespaceHandler`处理。 * 1)首先需要在META-INF目录下创建一个spring.handlers文件,来配置我们自定义的XML Schema Namespace到我们自定义的NamespaceHandler映射关系。 * 2)在META-INF目录下创建一个spring.schemas,来配置我们自定义的XML Schema地址到实际Jar包中的classpath映射关系(避免Spring真的去服务器上下载不存在的文件) ## 2.基于Java配置的扩展 ## 从Spring 3.0开始,一种新的基于Java的配置方式出现了。 通过这种方式,我们在开发Spring项目的过程中再也不需要去配置繁琐的xml文件了,只需要在Configuration类中配置就可以了,大大的简化了Spring的使用。 另外,这也是Spring Boot默认的配置方式,所以建议也支持这一特性。 ## 2.1 @Import注解 ## 支持Java配置扩展的关键点就是`@Import`注解,Spring 3.0提供了这个注解用来支持在Configuration类中引入其它的配置类,包括Configuration类, ImportSelector和ImportBeanDefinitionRegistrar的实现类。 我们可以通过这个注解来引入自定义的扩展Bean。 ## 2.2 自定义注解 ## 和基于XML配置类似的,我们需要提供给用户一个注解来配置需要注入到Spring Property Sources的namespaces和order。 ## 2.3 自定义ImportBeanDefinitionRegistrar实现 ## `ImportBeanDefinitionRegistrar`接口定义了`registerBeanDefinitions`方法,从而允许我们向Spring注册必要的Bean。 1. 记录用户配置的参数 2. 向Spring注册Bean:PropertySourcesProcessor,这个bean后面会实际处理用户配置的参数,从而完成配置注入到Spring中的功能 # 三扩展点 # Spring容器最常用的两个扩展点:`BeanFactoryPostProcessor`和`BeanPostProcessor`。 ## 3.1 BeanFactoryPostProcessor ## `BeanFactoryPostProcessor`提供了一个方法:`postProcessBeanFactory`。 这个方法会被Spring在容器初始化过程中调用,调用时机是所有bean的定义信息都已经初始化好,但是这些bean还没有实例化。 实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置'order'属性来控制各个BeanFactoryPostProcessor的执行次序。 注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息, <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" default-autowire="byName"> <bean id="myJavaBean" class="com.ali.caihj.postprocessor.MyJavaBean"> <property name="desc" value="测试一下啦" /> <property name="remark" value="这是备注信息啦啦啦" /> </bean> <bean id="myBeanFactoryPostProcessor" class="com.ali.caihj.postprocessor.MyBeanFactoryPostProcessor" /> </beans> public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory"); BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean"); System.out.println("属性值============" + bd.getPropertyValues().toString()); MutablePropertyValues pv = bd.getPropertyValues(); if (pv.contains("remark")) { pv.addPropertyValue("remark", "把备注信息修改一下"); } bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); } } 实际应用: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hoY2NjY2ho_size_16_color_FFFFFF_t_70][] ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hoY2NjY2ho_size_16_color_FFFFFF_t_70 1][] ## 3.2 BeanPostProcessor ## `BeanPostProcessor`提供了两个方法:`postProcessBeforeInitialization`和`postProcessAfterInitialization`,主要针对bean初始化提供扩展。 * `postProcessBeforeInitialization`会在每一个bean实例化之后、初始化(如afterPropertiesSet方法)之前被调用。 * `postProcessAfterInitialization`则在每一个bean初始化之后被调用。 我们常用的`@Autowired`注解就是通过`postProcessBeforeInitialization`实现的(AutowiredAnnotationBeanPostProcessor)。 **BeanFactoryPostProcessor在bean实例化之前执行,之后实例化bean(调用构造函数,并调用set方法注入属性值),然后在调用两个初始化方法前后,执行了BeanPostProcessor。初始化方法的执行顺序是,先执行afterPropertiesSet,再执行init-method。如下** <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" default-autowire="byName"> <bean id="myJavaBean" class="com.ali.caihj.postprocessor.MyJavaBean" init-method="initMethod"> <property name="desc" value="原始的描述信息" /> <property name="remark" value="原始的备注信息" /> </bean> <bean id="myBeanPostProcessor" class="com.ali.caihj.postprocessor.MyBeanPostProcessor" /> <bean id="myBeanFactoryPostProcessor" class="com.ali.caihj.postprocessor.MyBeanFactoryPostProcessor" /> </beans> public class MyJavaBean implements InitializingBean { private String desc; private String remark; public MyJavaBean() { System.out.println("MyJavaBean的构造函数被执行啦"); } public String getDesc() { return desc; } public void setDesc(String desc) { System.out.println("调用setDesc方法"); this.desc = desc; } public String getRemark() { return remark; } public void setRemark(String remark) { System.out.println("调用setRemark方法"); this.remark = remark; } public void afterPropertiesSet() throws Exception { System.out.println("调用afterPropertiesSet方法"); this.desc = "在初始化方法中修改之后的描述信息"; } public void initMethod() { System.out.println("调用initMethod方法"); } public String toString() { StringBuilder builder = new StringBuilder(); builder.append("[描述:").append(desc); builder.append(", 备注:").append(remark).append("]"); return builder.toString(); } } public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory"); BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean"); MutablePropertyValues pv = bd.getPropertyValues(); if (pv.contains("remark")) { pv.addPropertyValue("remark", "在BeanFactoryPostProcessor中修改之后的备忘信息"); } } } public class MyBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之前的数据: " + bean.toString()); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之后的数据:" + bean.toString()); return bean; } } ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hoY2NjY2ho_size_16_color_FFFFFF_t_70 2][] BeanPostProcessor的执行,取决于配置文件中bean的定义,如果定义的bean是singleton并且不是抽象类,也不延迟初始化,则BeanPostProcessor是在第11步中执行;而对于prototype的bean,BeanPostProcessor是在程序getBean的时候执行的。在第6步中,调用registerBeanPostProcessors方法,注册所有实现BeanPostProcessor接口的bean 参考 [https://juejin.im/post/5ba45a94f265da0aa94a0d71][https_juejin.im_post_5ba45a94f265da0aa94a0d71] [https://nobodyiam.com/2017/02/26/several-ways-to-extend-spring/][https_nobodyiam.com_2017_02_26_several-ways-to-extend-spring] [https://blog.csdn.net/qq\_38182963/article/details/78795058][https_blog.csdn.net_qq_38182963_article_details_78795058] [https://blog.csdn.net/caihaijiang/article/details/35552859][https_blog.csdn.net_caihaijiang_article_details_35552859] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hoY2NjY2ho_size_16_color_FFFFFF_t_70]: /images/20220201/08e9781faec646f9b32c4c1d82544991.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hoY2NjY2ho_size_16_color_FFFFFF_t_70 1]: /images/20220201/9d6ca5f678664dd0aa4b9d3f4887e43e.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hoY2NjY2ho_size_16_color_FFFFFF_t_70 2]: /images/20220201/53d37888f4b64014a6f0c5858b8dbf37.png [https_juejin.im_post_5ba45a94f265da0aa94a0d71]: https://juejin.im/post/5ba45a94f265da0aa94a0d71 [https_nobodyiam.com_2017_02_26_several-ways-to-extend-spring]: https://nobodyiam.com/2017/02/26/several-ways-to-extend-spring/ [https_blog.csdn.net_qq_38182963_article_details_78795058]: https://blog.csdn.net/qq_38182963/article/details/78795058 [https_blog.csdn.net_caihaijiang_article_details_35552859]: https://blog.csdn.net/caihaijiang/article/details/35552859
还没有评论,来说两句吧...