Spring IOC官方文档学习笔记(八)之容器扩展点
  oTtxkpRl76Le 2023年11月01日 88 0

1.通过BeanPostProcessor来自定义bean

(1) BeanPostProcessor用于在容器完成了对bean的实例化,配置及初始化后来实现一些自定义逻辑,它是用于操纵由容器创建的每个bean实例的,即在容器实例化了一个bean后以及该bean的初始化回调(如InitializingBean.afterPropertiesSet()等)被执行之前,会将这个bean交由BeanPostProcessor来进行处理。通过BeanPostProcessor,我们可以对bean实例进行任何操作,包括忽略掉后续的初始化回调等,BeanPostProcessor通常用来检查回调接口,或用来生成某个bean的代理对象,因此Spring AOP的实现基础就是BeanPostProcessor,如下是简单使用BeanPostProcessor的一个例子

//让ExampleA实现3个初始化回调
public class ExampleA implements InitializingBean {

    private String name;

    public ExampleA() {
        System.out.println("ExampleA的构造方法被调用");
        System.out.println("----------------------------------------");
    }

    //这个方法只用于IOC的属性注入
    public void setName(String name) {
        System.out.println("IOC对ExampleA的name属性进行注入,值为:" + name);
        System.out.println("----------------------------------------");
        this.name = name;
    }
    
    //这个方法用于我们自己手动注入
    public void setNameInOtherWay(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "ExampleA{" +
                "name='" + name + '\'' +
                '}';
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("正在执行初始化回调PostConstruct,此时的ExampleA为:" + this);
        this.setNameInOtherWay("zzz2");
        System.out.println("执行完毕,此时的ExampleA为:" + this);
        System.out.println("----------------------------------------");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("正在执行初始化回调InitializingBean.afterPropertiesSet,此时的ExampleA为:" + this);
        this.setNameInOtherWay("zzz3");
        System.out.println("执行完毕,此时的ExampleA为:" + this);
        System.out.println("----------------------------------------");
    }

    public void init() {
        System.out.println("正在执行初始化回调init-method,此时的ExampleA为:" + this);
        this.setNameInOtherWay("zzz4");
        System.out.println("执行完毕,此时的ExampleA为:" + this);
        System.out.println("----------------------------------------");
    }
}

//实现BeanPostProcessor,自定义后置处理器来操纵bean实例(注意:需要把我们的自定义处理器注入到容器中),它主要提供了2个方法
public class Processor implements BeanPostProcessor {

    /**
     * 该方法作用于bean实例创建配置好后,初始化回调执行前,来自定义一些逻辑
     * @param bean 实例化并配置好后的bean
     * @param beanName  bean的名称
     * @return  自定义操作完成后的bean
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("正在执行postProcessBeforeInitialization,此时的ExampleA为:" + bean);
        ((ExampleA) bean).setNameInOtherWay("zzz1");
        System.out.println("执行完毕,此时的ExampleA为:" + bean);
        System.out.println("----------------------------------------");
        return bean;
    }
    
    /**
     * 该方法作用于初始化回调执行后
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("正在执行postProcessAfterInitialization,此时的ExampleA为:" + bean);
        ((ExampleA) bean).setNameInOtherWay("zzz5");
        System.out.println("执行完毕,此时的ExampleA为:" + bean);
        System.out.println("----------------------------------------");
        return bean;
    }
}

<!-- xml配置文件 -->
<beans ....>

    <!-- 开启注解扫描,否则@PostConstruct注解不生效 -->
    <context:annotation-config></context:annotation-config>
    
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" init-method="init">
        <property name="name" value="zzz"></property>
    </bean>
    
    <!-- 注意仅仅实现接口是不行的,我们必须把自定义后置处理器注册成bean,它才会生效 -->
    <bean id="processor" class="cn.example.spring.boke.Processor"></bean>
</beans>

//启动容器后,打印结果为
ExampleA的构造方法被调用
----------------------------------------
IOC对ExampleA的name属性进行注入,值为:zzz
----------------------------------------
正在执行postProcessBeforeInitialization,此时的ExampleA为:ExampleA{name='zzz'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz1'}
----------------------------------------
正在执行初始化回调PostConstruct,此时的ExampleA为:ExampleA{name='zzz1'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz2'}
----------------------------------------
正在执行初始化回调InitializingBean.afterPropertiesSet,此时的ExampleA为:ExampleA{name='zzz2'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz3'}
----------------------------------------
正在执行初始化回调init-method,此时的ExampleA为:ExampleA{name='zzz3'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz4'}
----------------------------------------
正在执行postProcessAfterInitialization,此时的ExampleA为:ExampleA{name='zzz4'}
执行完毕,此时的ExampleA为:ExampleA{name='zzz5'}
----------------------------------------

综上可见,添加了BeanPostProcessor后,bean的初始化流程为:执行bean的构造函数 -> IOC进行属性注入 -> BeanPostProcessor.postProcessBeforeInitialization -> 三大初始化回调 -> BeanPostProcessor.postProcessAfterInitialization

(2) 我们可以向容器中注入多个自定义BeanPostProcessor,并通过实现Ordered接口来控制这些BeanPostProcessor的执行顺序,如下所示

public class ExampleA { }

//让该自定义后置处理器实现Ordered接口,指定它在所有自定义后置处理器中的执行顺序
public class Processor0 implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Order值为0的postProcessBeforeInitialization执行...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Order值为0的postProcessAfterInitialization执行...");
        return bean;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

public class Processor1 implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Order值为1的postProcessBeforeInitialization执行...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Order值为1的postProcessAfterInitialization执行...");
        return bean;
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

<!-- xml配置文件 -->
<beans ....>
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
    <bean id="processor0" class="cn.example.spring.boke.Processor0"></bean>
    <bean id="processor1" class="cn.example.spring.boke.Processor1"></bean>
</beans>

//启动容器,输出如下
Order值为0的postProcessBeforeInitialization执行...
Order值为1的postProcessBeforeInitialization执行...
Order值为0的postProcessAfterInitialization执行...
Order值为1的postProcessAfterInitialization执行...

由上可见,getOrder返回值越小,自定义的后置处理器就越先执行,同时也可以发现,post-processor bean全部作用于普通bean,即一个post-processor bean不会作用于容器中的另一个post-processor bean

(3) IOC容器会自动检测容器中的post-processor bean并提前实例化,以作用于容器中的其他bean,当我们向一个容器中注入了一个BeanPostProcessor,那么该BeanPostProcessor仅对该容器中的bean进行后置处理,例如,即父容器中的BeanPostProcessor不会作用于子容器中的bean

(4) BeanPostProcessor作用于bean实例化并配置好了之后,换句话说,在BeanPostProcessor起作用时,bean实例已经存在了,因此,如果我们想要修改bean的配置元数据(即BeanDefinition,此时的bean还未被创建),则需要实现BeanFactoryPostProcessor接口,它与BeanPostProcessor类似,只不过作用时机不同

(5) 可通过容器ConfigurableBeanFactory中的addBeanPostProcessor方法编程式的向容器注册一个BeanPostProcessor实例,但需要注意,通过该方法注入的BeanPostProcessor实例不会遵守Ordered接口,即如果是编程式的向容器中注册BeanPostProcessor实例,那么其注册的顺序就是后置处理器实际执行的顺序,如下所示

<!-- 其余不变,修改xml文件如下 -->
<beans ...>
    
    <!-- 注意这里设置了懒加载属性,因为我们是要编程式的添加BeanPostProcessor,故而要先创建容器,再调用容器的addBeanPostProcessor方法,但singleton bean会随容器创建而被创建,此时addBeanPostProcessor方法还未被执行,因此BeanPostProcessor就无法作用于这些提前创建bean了,所以我们设置为懒加载或不用singleton作用域 -->
    <bean id="exampleA" class="com.lb.spring5.ExampleA" lazy-init="true"></bean>
</beans>

//测试,先注册Processor1,再注册Processor0
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
ctx.getBeanFactory().addBeanPostProcessor(new Processor1());
ctx.getBeanFactory().addBeanPostProcessor(new Processor0());
ctx.getBean(ExampleA.class);

//打印结果,可以发现先注册的BeanPostProcessor先执行
Order值为1的postProcessBeforeInitialization执行...
Order值为0的postProcessBeforeInitialization执行...
Order值为1的postProcessAfterInitialization执行...
Order值为0的postProcessAfterInitialization执行...

(6) BeanPostProcessor与自动代理

在Spring官方文档中,提到了这么一句话:Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor instances nor the beans they directly reference are eligible for auto-proxying and, thus, do not have aspects woven into them. 这句话的大意就是AOP是基于BeanPostProcessor实现的,因此我们无法对BeanPostProcessor实例及其依赖的bean进行自动代理,织入通知,如下例所示

//定义一个切面MyAspect,对下面的BeanPostProcessor实例进行织入
public class MyAspect {
    // 前置通知
    public void before(){
        System.out.println("before...");
    }
}

//定义一个Bean后置处理器Processor,在该处理器中我们定义了一个方法aop,作为切入点,进行织入
public class Processor implements BeanPostProcessor, Ordered {

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

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return 0;
    }
    
    //切入点
    public void aop() {
        System.out.println("Processor aop...");
    }
}

<!-- xml配置文件 -->
<beans ....>
    <!-- 切面 -->
    <bean id="myAspect" class="cn.example.spring.boke.MyAspect"></bean>

    <!-- aop配置,对指定方法进行切入 -->
    <aop:config>
        <aop:aspect id="advice" ref="myAspect">
            <!-- 声明前置通知 -->
            <aop:before method="before" pointcut="execution(public * cn.example.spring.boke.*.aop(..))"></aop:before>
        </aop:aspect>
    </aop:config>

    <bean id="processor" class="cn.example.spring.boke.Processor"></bean>
</beans>

//测试
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
ctx.getBean(Processor.class).aop();

//控制台输出如下,可见前置通知before方法并未执行,此时aop失效
Processor aop...


//然后我们将上面的Processor类变更一下,使它成为一个普通的bean,如下
public class Processor {
    public void aop() {
        System.out.println("Processor aop...");
    }
}

//此时,再启动容器,控制台打印如下,可见,如果是一个普通bean的话,aop是生效的
before...
Processor aop...

对BeanPostProcessor所依赖的bean,它的aop不会生效吗? 答案是不一定,有些情况下会生效,有些情况下不会生效,得结合实际情况具体分析,最好详看这一方面的源码,例子如下

//定义一个普通的bean exampleA,用于测试aop织入
public class ExampleA {

    public void aop() {
        System.out.println("ExampleA aop...");
    }
}

//对于上面的例子,我们把Processor修改一下,让它依赖一个bean exampleA
public class Processor implements BeanPostProcessor,Ordered {

    private ExampleA exampleA;
    
    public void setExampleA(ExampleA exampleA) {
        this.exampleA = exampleA;
    }
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

<!-- xml配置文件 -->
<beans ....>
    <!-- 切面保持不变 -->
    <bean id="myAspect" class="cn.example.spring.boke.MyAspect"></bean>

    <aop:config>
        <aop:aspect id="advice" ref="myAspect">
            <aop:before method="before" pointcut="execution(public * cn.example.spring.boke.*.aop(..))"></aop:before>
        </aop:aspect>
    </aop:config>

    <bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
    
    <!-- 通过自动装配机制,向容器中注入依赖项 -->
    <bean id="processor" class="cn.example.spring.boke.Processor" autowire="byType"></bean>
</beans>

//测试
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
ctx.getBean(ExampleA.class).aop();

//打印结果如下,aop前置通知未能进行织入成功
ExampleA aop...
//此时,Spring也会给出一条提醒:Bean 'exampleA' of type [cn.example.spring.boke.ExampleA] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying), 提示我们的Bean exampleA可能未被所有的BeanPostProcessor处理


//对于上面的Processor类,我们移除掉Ordered接口,只实现BeanPostProcessor接口,其他属性和方法保持不变
public class Processor implements BeanPostProcessor {

    private ExampleA exampleA;

    public void setExampleA(ExampleA exampleA) {
        this.exampleA = exampleA;
    }

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

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

//这时,再次启动容器,观察控制台打印,虽然这次Spring也给出了一条关于exampleA可能未被所有的BeanPostProcessor处理的提醒,但可以发现aop前置通知成功执行,说明对BeanPostProcessor实例所依赖的bean,是可以进行自动代理的
before...
ExampleA aop...

总结一下: Spring官方文档,只是笼统的说明了一下:无法对BeanPostProcessor实例及其依赖的bean进行自动代理,织入通知. 然而实际上bean是否会被执行自动代理,与BeanPostProcessor何时被Spring处理有着很大联系,至于具体的机制,详见源码

2.通过BeanFactoryPostProcessor来自定义bean的配置元数据

(1) BeanFactoryPostProcessor用于读取容器中bean的配置元数据,并在这些bean被实例化之前来修改它们的配置元数据(此时bean还未被创建),同上面所提及的BeanPostProcessor,BeanFactoryPostProcessor也仅对它所属容器中的bean进行处理,同时我们也可以向容器中注入多个BeanFactoryPostProcessor实例并通过实现Ordered接口来规定它们的执行顺序,如下所示

//实现BeanFactoryPostProcessor接口,该接口提供了一个方法,用于获取配置BeanDefinition
public class Processor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //获取到exampleA的BeanDefinition,并为其name属性赋予值exampleAAA
        configurableListableBeanFactory.getBeanDefinition("exampleA").getPropertyValues().addPropertyValue("name", "exampleAAA");
    }
}

//普通的bean
public class ExampleA {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

<!-- xml配置文件 -->
<beans ....>
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
    <!-- BeanFactoryPostProcessor实例也需要注入到容器中 -->
    <bean id="processor" class="cn.example.spring.boke.Processor"></bean>
</beans>

//启动容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
System.out.println(ctx.getBean(ExampleA.class).getName());

//打印日志如下,可见BeanFactoryPostProcessor实例成功执行,向我们的bean中注入了属性值
exampleAAA

将上面的Processor类稍微变更一下,其它保持不变

public class Processor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //增添了一个getBean方法,获取一下容器中的bean exampleA
        configurableListableBeanFactory.getBean("exampleA");
        configurableListableBeanFactory.getBeanDefinition("exampleA").getPropertyValues().addPropertyValue("name", "exampleAAA");
    }
}

//之后,我们重新启动容器,控制台打印如下,可见,此时属性注入失效,这是因为我们使用了getBean方法,导致容器过早的实例化了bean exampleA,从而违反了容器标准的生命周期,绕过了后置处理器
null

3.通过FactoryBean来自定义bean的实例化逻辑

(1) FactoryBean,即工厂Bean,使用了工厂模式,一般用于生产初始化过程很复杂的bean,从而避免编写冗杂的xml配置文件,如下例所示

//一个普通的bean
public class ExampleA {}

//实现FactoryBean接口,作为ExampleA的工厂,用于生成ExampleA实例,该接口主要提供了3个方法如下所示
public class ExampleAFactory implements FactoryBean {
    /**
     * 返回该工厂所生产的bean实例
     */
    @Override
    public Object getObject() throws Exception {
        return new ExampleA();
    }

    /**
     * 返回该工厂所生产的bean实例的类型
     */
    @Override
    public Class<?> getObjectType() {
        return ExampleA.class;
    }

    /**
     * 返回的bean实例是否为单例
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

<!-- xml配置文件 -->
<beans ....>
    <!-- 将工厂Bean注入进容器中 -->
    <bean id="exampleAFactory" class="cn.example.spring.boke.ExampleAFactory"></bean>
</beans>

//启动容器,获取这个工厂Bean
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
System.out.println(ctx.getBean("exampleAFactory"));

//打印结果如下,我们可以发现,我们向容器中注入的是工厂Bean ExampleAFactory实例,结果getBean方法取到的不是这个工厂Bean,而是它所生产的ExampleA
cn.example.spring.boke.ExampleA@1a968a59

//当然,我们也可以获取到这个工厂Bean,而非它所生产的实例,如下,只需在它的name前加一个&符号即可
System.out.println(ctx.getBean("&exampleAFactory"));

//打印结果如下,可见这次我们获取到的是工厂Bean ExampleAFactory本身
cn.example.spring.boke.ExampleAFactory@1a968a59
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  2Vtxr3XfwhHq   2024年05月17日   54   0   0 Java
  Tnh5bgG19sRf   2024年05月20日   109   0   0 Java
  8s1LUHPryisj   2024年05月17日   46   0   0 Java
  aRSRdgycpgWt   2024年05月17日   47   0   0 Java
oTtxkpRl76Le