`

Spring的两种后处理器(笔记)

阅读更多

两种后处理器

Spring 框架提供了很好的扩展性,除了可以与各种第三方框架良好整合外,其IoC容器也允许开发者进行扩展。这种扩展并不是通过实现BeanFactory或 ApplicationContext的子类,而是通过两个后处理器对IoC容器进行扩展。Spring提供了两种常用的后处理器:

  ● Bean后处理器,这种后处理器会对容器中特定的Bean进行定制,例如功能的    加强。

  ● 容器后处理器,这种后处理器对IoC容器进行特定的后处理。

 

Bean后处理器

Bean 后处理器是一种特殊的Bean,这种特殊的Bean并不对外提供服务,它无须id属性但它负责对容器中的其他Bean执行后处理例如为容器中的目标 Bean生成代理这种Bean可称为Bean后处理器,它在Bean实例创建成功后,对其进行进一步的加强处理。

Bean后处理器必须实现BeanPostProcessor接口。

BeanPostProcessor接口包含两个方法:

  ● Object postProcessBeforeInitialization(Object bean, String name)throws BeansException,该方法的第一个参数是系统即将初始化的Bean实例,第二个参数是Bean实例的名字。

  ● Object postProcessAfterInitialization(Object bean, String name)throws BeansExce- ption,该方法的第一个参数是系统刚完成初始化的Bean实例,第二个参数是Bean实例的名字。

实现该接口的Bean必须实现这两个方法,这两个方法会对容器的Bean进行后处理。两个方法会在目标Bean初始化之前和初始化之后分别调用。这两个方法用于对系统完成的默认初始化进行加强。

注意:Bean后处理器是对IoC容器一种极好的扩展,Bean后处理器可以对容器中的Bean进行后处理,这种后处理完全由开发者决定。

下面将定义一个简单的Bean后处理器,该Bean后处理器将对容器中其他Bean进行后处理。Bean后处理器的代码如下:

//自定义Bean后处理器,负责后处理容器中所有的Bean

public class MyBeanPostProcessor implements BeanPostProcessor

{

    //在初始化bean之前,调用该方法

    public Object postProcessBeforeInitialization(Object bean , String
    beanName)throws BeansException

    {

        //仅仅打印一行字符串

        System.out.println("系统正在准备对" + beanName + "进行初始化...");

        return bean;

    }

    //在初始化bean之后,调用该方法

    public Object postProcessAfterInitialization(Object bean , String
    beanName)throws BeansException

    {

        System.out.println("系统已经完成对" + beanName + "的初始化");

        //如果系统刚完成初始化的bean是Chinese

        if (bean instanceof Chinese)

        {

            //为Chinese实例设置name属性

              Chinese c = (Chinese)bean;

              c.setName("wawa"); 

         }

        return bean;

    }

}

 

下面是Chinese的源代码,该类实现了InitializingBean接口,还额外提供了一个初始化方法,这两个方法都由Spring容器控制回调。

public class Chinese implements Person,InitializingBean

{

    private Axe axe;

    private String name;

    public Chinese()

    {

        System.out.println("Spring实例化主调bean:Chinese实例...");

    }

    public void setAxe(Axe axe)

    {

        System.out.println("Spring执行依赖关系注入...");

        this.axe = axe;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public void useAxe()

    {

        System.out.println(name + axe.chop());

    }

    public void init()

    {

        System.out.println("正在执行初始化方法   init...");

    }

   public void afterPropertiesSet() throws Exception

    {

         System.out.println("正在执行初始化方法  afterPropertiesSet...");

    }

}

 

配置文件如下:

<?xml version="1.0" encoding="gb2312"?>

<!--  指定Spring 配置文件的dtd>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素 -->

<beans>

    <!-- 配置bean后处理器,可以没有id属性,此处id属性为了后面引用 -->

    <bean id="beanPostProcessor" class="lee.MyBeanPostProcessor"/>

    <bean id="steelAxe" class="lee.SteelAxe"/>

    <bean id="chinese" class="lee.Chinese" init-method="init">

        <property name="axe" ref="steelAxe"/>

    </bean>

</beans>

 

本应用的chinese具有两个初始化方法:

  ● init-method指定初始化方法。

  ● 实现InitializingBean接口,提供了afterPropertiesSet初始化方法。

MyBeanPostProcessor类实现了BeanPostProcessor接口,并实现了该接口的两个方法,这两个方法分别在初始化方法调用之前和之后得到回调。

注意:上面的配置文件配置Bean后处理器时,依然为Bean处理器指定了id属性,指定id属性是为了方便程序通过该id属性访问Bean后处理器。大部分时候,程序无须手动访问该Bean后处理器,因此无须为其指定id属性。

主程序如下:

public class BeanTest

{

    public static void main(String[] args)throws Exception

    {

        //CLASSPATH路径下的bean.xml文件创建Resource对象

        ClassPathResource isr = new ClassPathResource("bean.xml");

        //以Resource对象作为参数,创建BeanFactory的实例

        XmlBeanFactory factory = new XmlBeanFactory(isr);

        //获取Bean后处理器实例

        MyBeanPostProcessor beanProcessor = 

            (MyBeanPostProcessor)factory.getBean("beanPostProcessor");

        //注册BeanPostProcessor实例

        factory.addBeanPostProcessor(beanProcessor);

        System.out.println("程序已经实例化BeanFactory...");

        Person p = (Person)factory.getBean("chinese");

        System.out.println("程序中已经完成了chinese bean的实例化...");

        p.useAxe();

    }

}

 

如果使用BeanFactory作为Spring容器,必须手动注册Bean后处理器,因此在程序中先获取Bean后处理器实例,然后手动注册——这就是在配置文件中指定Bean后处理器id属性的原因。通过BeanFactory的addBeanPostProcessor可以注册 BeanPostProcessor实例。程序执行结果如下:

[java] 程序已经实例化BeanFactory...

[java] Spring实例化主调bean:Chinese实例...

[java] Spring实例化依赖bean:SteelAxe实例...

[java] 系统正在准备对steelAxe进行初始化...

[java] 系统已经完成对steelAxe的初始化

[java] Spring执行依赖关系注入...

[java] 系统正在准备对chinese进行初始化...

[java] 正在执行初始化方法  afterPropertiesSet...

[java] 正在执行初始化方法   init...

[java] 系统已经完成对chinese的初始化

[java] 程序中已经完成了chinese bean的实例化...

[java] wawa钢斧砍柴真快

在配置文件中配置chinese实例时,并未指定name属性值。但程序执行时,name属性有了值,这就是Bean后处理器完成的,在Bean后处理器中判断Bean是否是Chinese实例,然后设置它的name属性。

容器中一旦注册了Bean后处理器,Bean后处理器会自动启动,在容器中每个Bean创建时自动工作,完成加入Bean后处理器需要完成的工作。

实现BeanPostProcessor接口的Bean后处理器可对Bean进行任何操作,包括完全忽略这个回调。BeanPostProcessor通常用来检查标记接口或将Bean包装成一个Proxy的事情。Spring的很多工具类,就是通过Bean后处理器完成的。

从主程序中看到,采用BeanFactory作为Spring容器时,必须手动注册BeanPost Processor。而对于ApplicationContext,则无须手动注册。ApplicationContext可自动检测到容器中的Bean 后处理器,自动注册。Bean后处理器会在Bean实例创建时,自动启动。即主程序采用如下代码,效果完全一样:

public class BeanTest

{

    public static void main(String[] args)throws Exception

    {

        ApplicationContext ctx = new ClassPathXmlApplicationContext 
        ("bean.xml");

        Person p = (Person)factory.getBean("chinese");

        System.out.println("程序中已经完成了chinese bean的实例化...");

        p.useAxe();

    }

}

 

Bean后处理器的用处

实际上,Bean后处理器完成的工作更加实际,例如生成Proxy。Spring框架本身提供了大量的Bean后处理器,这些后处理器负责对容器中的Bean进行后处理。

下面是Spring提供的两个常用的后处理器:

  ● BeanNameAutoProxyCreator,根据Bean实例的name属性,创建Bean实例的代理。

  ● DefaultAdvisorAutoProxyCreator,根据提供的Advisor,对容器中所有的Bean实例创建代理。

上面提供的两个Bean后处理器,都用于根据容器中配置的拦截器创建目标Bean代理,目标代理就在目标Bean的基础上修改得到。

注意:如果需要对容器中某一批Bean进行特定的处理,可以考虑使用Bean后处理器。

容器后处理器

除了上面提供的Bean后处理器外,Spring还提供了一种容器后处理器。Bean后处理器负责后处理容器生成的所有Bean,而容器后处理器则负责后处理容器本身。

容器后处理器必须实现BeanFactoryPostProcessor接口。实现该接口必须实现如下一个方法:

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

实现该方法的方法体就是对Spring容器进行的处理,这种处理可以对Spring容器进行任意的扩展,当然也可以对Spring容器不进行任何处理。

类似于BeanPostProcessor,ApplicationContext可自动检测到容器中的容器后处理器,并且自动注册容器后处理器。但若使用BeanFactory作为Spring容器,则必须手动注册后处理器。

下面定义了一个容器后处理器,这个容器后处理器实现BeanFactoryPostProcessor接口,但并未对Spring容器进行任何处理,只是打印出一行简单的信息。该容器后处理器的代码如下:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor

{

    //容器后处理器对容器进行的处理在该方法中实现

    public void postProcessBeanFactory(ConfigurableListableBeanFactory
    beanFactory)

        throws BeansException

    {

        System.out.println("程序对Spring所做的BeanFactory的初始化没有意
        见...");

    }

}

 

将该Bean作为普通Bean部署在容器中,然后使用ApplicationContext作为容器,容器会自动调用BeanFactoryPostProcessor处理Spring容器。程序执行效果如下:

[java] 程序对Spring所做的BeanFactory的初始化没有意见...

实现BeanFactoryPostProcessor接口的Bean后处理器不仅可对BeanFactory执行后处理,也可以对ApplicationContext容器执行后处理。容器后处理器还可用来注册额外的属性编辑器。

注意:Spring没有提供ApplicationContextPostProcessor。也就是说,对于Application- Context容器,一样使用BeanFactoryPostProcessor作为容器后处理器。

Spring已提供如下两个常用的容器后处理器,包括:

  ● PropertyResourceConfigurer,属性占位符配置器。

  ● PropertyPlaceHolderConfigurer,另一种属性占位符配置器。

下面将详细介绍这两种常用的容器后处理器。

属性占位符配置器

Spring提供了PropertyPlaceholderConfigurer,它是一个容器后处理器,负责读取Java属性文件里的属性值,并将这些属性值设置到Spring容器定义中。

通过使用PropertyPlaceholderConfigurer后处理器,可以将Spring配置文件中的部分设置放在属性文件中设置。这种配置方式当然有其优势:可以将部分相似的配置(如数据库的urls、用户名和密码)放在特定的属性文件中,如果只需要修改这部分配置,则无须修改Spring配置文件,修改属性文件即可。

下面的配置文件配置了PropertyPlaceholderConfigurer后处理器,在配置数据源Bean时,使用了属性文件中的属性值。配置文件的代码如下:

<?xml version="1.0" encoding="GBK"?>

<!-- beans是Spring配置文件的根元素,并且指定了Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置一个容器后处理器Bean -->

    <bean id="propertyConfigurer"

        class="org.springframework.beans.factory.config. 
        PropertyPlaceholderConfigurer">

        <!-- locations属性指定属性文件的位置 -->

        <property name="locations">

            <list>

                <value>dbconn.properties</value>

                <!-- 如果有多个属性文件,依次在下面列出来 -->

            </list>

        </property>

    </bean>

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0. 
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="${jdbc.driverClassName}"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="${jdbc.url}"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="${jdbc.username}"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="${jdbc.password}"/>

    </bean>

</beans>

 

在上面的配置文件中,配置driverClass和jdbcUrl等信息时,并未直接设置这些属性的属性值,而是设置了${jdbc.driverClassName}和${jdbc.url}属性值。这表明Spring容器将从propertyConfigurer指定属性文件中搜索这些key对应的value,并为该Bean的属性值设置这些value值。

如前所述,ApplicationContext会自动检测部署在容器的容器后处理器,无须额外的注册,容器自动注册。因此,只需提供如下Java Properties文件:

jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://localhost:3306/j2ee

jdbc.username=root

jdbc.password=32147

通过这种方法,可从主XML配置文件中分离出部分配置信息。如果仅需要修改数据库连接属性,则无须修改主XML配置文件,只需要修改属性文件即可。采用属性占位符的配置方式,可以支持使用多个属性文件。通过这种方式,可将配置文件分割成多个属性文件,从而降低修改配置的风险。

注意:对于数据库连接等信息集中的配置,可以将其配置在Java属性文件中,但不要过多地将Spring配置信息抽离到Java属性文件中,否则可能会降低Spring配置文件的可读性。

另一种属性占位符配置器

PropertyOverrideConfigurer 是Spring提供的另一个容器后处理器,这个后处理器的额作用与上面介绍的容器后处理器作用大致相同。但也存在些许差别:PropertyOverrideConfigurer使用的属性文件用于覆盖XML配置文件中的定义。即PropertyOverrideConfigurer允许XML配置文件中有默认的配置信息。

如果PropertyOverrideConfigurer的属性文件有对应配置信息,XML文件中的配置信息被覆盖;否则,直接使用XML文件中的配置信息。使用PropertyOverrideConfigurer的属性文件,应是如下的格式:

beanName.property=value

beanName是属性占位符试图覆盖的Bean名,property是试图覆盖的属性名。看如下配置文件:

<?xml version="1.0" encoding="GBK"?>

<!-- beans是Spring配置文件的根元素,并且指定了Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置一个属性占位符Bean。ApplictionContext能自动识别
    PropertyPlaceholderConfigurer Bean -->

    <bean id="propertyOverrider"

        class="org.springframework.beans.factory.config.
        PropertyOverrideConfigurer">

        <property name="locations">

            <list>

                <value>dbconn.properties</value>

                <!-- 如果有多个属性文件,依次在下面列出来 -->

            </list>

        </property>

    </bean>

    <!-- 定义数据源Bean,使用C3P0数据源实现 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.
    ComboPooledDataSource" destroy-method="close">

        <!-- 指定连接数据库的驱动 -->

        <property name="driverClass" value="dd"/>

        <!-- 指定连接数据库的URL -->

        <property name="jdbcUrl" value="xx"/>

        <!-- 指定连接数据库的用户名 -->

        <property name="user" value="dd"/>

        <!-- 指定连接数据库的密码 -->

        <property name="password" value="xx"/>

    </bean>

</beans>

上面的配置文件中,指定数据源Bean的各种属性值时,只是随意指定了几个属性值,很明显通过这几个属性值无法连接到数据库服务。

但因为Spring容器中部署了一个PropertyOverrideConfigurer的容器后处理器,而且Spring容器使用ApplicationContext作为容器,它会自动检测容器中的容器后处理器,无须额外的注册,容器自动注册该后处理器。

PropertyOverrideConfigurer后处理器读取dbconn.properties文件中的属性,用于覆盖目标Bean的属性。因此,如果属性文件中有dataSource Bean属性的设置,则配置文件中指定的属性值将没有任何作用。

dbconn.properties属性文件如下:

dataSource.driverClassName=com.mysql.jdbc.Driver

dataSource.url=jdbc:mysql://wonder:3306/j2ee

dataSource.username=root

dataSource.password=32147

注意属性文件的格式必须是:

beanName.property=value

也就是说,dataSource必须是容器中真实存在的bean名,否则程序将出错。

注意:程序无法知道BeanFactory定义是否被覆盖。仅仅通过察看XML配置文件,无法知道配置文件的配置信息是否被覆盖。如有多个PorpertyOverrideConfigurer对同一Bean属性定义了覆盖,最后一个覆盖获胜。

分享到:
评论

相关推荐

    Spring学习笔记&源码

    6. **数据访问**:涉及JDBC模板、Hibernate、MyBatis等持久层集成,讲解事务管理的编程式和声明式两种方式。 7. **Spring Boot**:简述Spring Boot的自动化配置和起步依赖,以及如何快速构建微服务应用。 8. **...

    Spring&Mybatis&SpringMVC总结笔记-最全最基础.pdf

    Mybatis提供XML和注解两种配置方式,其中Mybatis#{}和${}的使用有不同的场景和目的,前者用于预编译语句的参数占位符,后者用于动态参数的字符串拼接。 Mybatis相比于JDBC是一个更为轻量级的解决方案。它支持简单的...

    Spring高级源码学习笔记.zip

    AOP代理有两种实现方式:JDK动态代理和CGLIB。前者针对实现了接口的类,后者则在没有接口的情况下通过子类化目标类实现。源码中,Advised接口和ProxyFactoryBean扮演着关键角色。 在数据访问/集成方面,Spring提供...

    spring笔记.zip

    主要有两种容器:ApplicationContext和BeanFactory。 - **AOP**:提供了面向切面编程的支持,允许开发者定义“切面”,即跨越多个对象的行为或责任。 - **数据访问**:包括对JDBC、ORM(如Hibernate)和OXM...

    Spring笔记

    Spring提供了后处理器接口,如BeanPostProcessor,可以在Bean实例化后和初始化前/后进行额外操作,例如属性注入后的处理或代理对象的生成。这对于实现某些特殊功能,如动态代理、属性校验等非常有用。 8. **Spring...

    Spring学习笔记(6)----编码剖析Spring依赖注入的原理

    在Spring中,DI主要通过两种方式实现:构造器注入和setter注入。构造器注入是在创建对象时通过构造函数传入依赖,而setter注入则是在对象创建后通过setter方法设置依赖。 当我们使用`@Autowired`注解时,Spring容器...

    spring学习笔记(四)

    这个类是Spring的一个Bean后处理器,它会在Bean实例化之后,查找并处理`@Value`注解,将注入的值设置到相应的字段或方法上。在处理过程中,Spring会使用`ExpressionParser`解析表达式,如果表达式包含SpEL(Spring ...

    SSH(Struts1.0+Spring+Hibernate)框架集成笔记

    ### SSH(Struts1.0+Spring+Hibernate)框架集成笔记 #### 一、概述 SSH框架集成,即Struts1.0 + Spring + Hibernate框架的整合应用,是Java Web开发中较为复杂的集成模式之一。它集合了MVC设计模式(通过Struts...

    Spring学习思维笔记.pdf

    Java的动态代理机制分为两种:基于接口的动态代理和基于类的动态代理。JDK动态代理基于接口实现,它要求被代理的类必须实现一个接口。当调用一个接口方法时,JDK动态代理会在运行时创建一个代理类,该类继承了Proxy...

    spring3.x的读书笔记-8

    - Spring 中有两种常见的 DAO 实现方式: - **SqlSessionTemplate**:使用模板方法模式封装了 SqlSession 的操作,如 `selectOne`、`insert` 等,提供了事务管理。 - **Mapper 接口**:直接定义接口,每个方法...

    Spring笔记(第九次)1

    BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor是两种重要的后置处理器,它们在bean实例化之前对BeanFactory或bean定义进行定制和修改。 首先,BeanFactoryPostProcessor(BPP)是在BeanFactory的...

    springmvc第二天课堂笔记.docx

    Spring MVC 和 Struts2 是两个不同的 MVC 框架,Spring MVC 面向方法开发的,而 Struts2 面向类开发。Spring MVC 可以单例开发,而 Struts2 只能是多例开发。 本文对 Spring MVC 框架的第二天课堂笔记进行了总结,...

    传播智客-mybatis课堂笔记(2天)

    8. MyBatis的事务管理:MyBatis支持编程式和声明式事务管理,笔记会讲解如何配置及使用这两种方式,并给出示例代码。 9. MyBatis的插件机制:MyBatis允许开发者自定义拦截器,实现对SqlSession或Executor的增强。...

    全套java笔记javaEE(上)

    8. 开发DispatcherServlet工具类:DispatcherServlet是Spring MVC的核心组件,负责接收请求并分发到相应的处理器。笔记可能讲解了如何自定义DispatcherServlet以满足特定需求,增强应用的功能。 这套笔记集合为...

    【2019版】Spring4.3入门视频课程

    自动装配、FactoryBean、两种后处理器 二、Spring AOP AOP简介、实现原理、代理技术 Spring AOP的配置实现 AspectJ表达式 三、Spring注解配置 注解简介 IOC注解 AOP注解 四、Spring整合 Spring整合Web Spring整合...

    SSM框架视频Spring4、SpringMVC4、Mybatis

    - **缓存机制**:MyBatis提供了两种级别的缓存:一级缓存默认开启,二级缓存需要手动开启。合理利用缓存机制可以显著提高应用性能。 #### 五、SSM框架整合 - **整合步骤**: 1. 引入必要的Jar包或Maven依赖。 2....

    java常用框架学习笔记

    **Spring中的代理**:Spring支持两种类型的代理:JDK动态代理和CGLIB代理。前者基于Java的反射机制,后者则基于字节码增强技术。 #### Spring2.x Spring 2.x版本引入了许多新特性,比如改进的AOP支持、增强的声明...

    springmvc+mybatis学习笔记.docx

    setter注入和构造函数注入是两种常见的实现方式。 - **BeanFactory**:Spring的核心容器,负责Bean的创建和管理。例如: ```java BeanFactory factory = new XmlBeanFactory(new FileInputStream(...

Global site tag (gtag.js) - Google Analytics