Spring笔记(1) 工厂

什么是Spring

Spring是⼀个轻量级JavaEE解决⽅案,整合众多优秀的设计模式

  • 轻量级:
  1. 对于运行环境是没有额外要求的。
    可以在开源的tomcat resion jetty里运行,也可以在收费的weblogic websphere里运行。
  2. 代码移植性高:不需要事先额外的接口。
  • JavaEE解决方案:

Android 抽象工厂案例 抽象工厂 spring_java

  • 整合设计模式(工厂、代理等)

设计模式

  1. ⼴义概念
    ⾯向对象设计中,解决特定问题的经典代码
  2. 狭义概念
    GOF4⼈帮定义的23种设计模式:⼯⼚、适配器、装饰器、⻔⾯、代理、模板…

工厂设计模式

  1. 概念:通过⼯⼚类,创建对象,不提倡通过直接new的方法的创建对象
User user = new User();
 UserDAO userDAO = new UserDAOImpl();
  1. 好处:解耦合
    耦合:指定是代码间的强关联关系,⼀⽅的改变会影响到另⼀⽅
    问题:不利于代码维护
    简单:把接⼝的实现类,硬编码在程序中
UserService userService = new UserServiceImpl();

比如说我们目前使用UserServiceImpl作为UserService的一个实现类,当突然有一天我们需要升级或者其他说明原因,需要把实现类改成UserServiceImpl2,就还需要再改动一次代码,就还需要重新编译一次。

public static void main(String[] args) {
    UserService userService = new UserServiceImpl();
    userService.login("abcabc","123456");
}

一个比较好的解决方法就是使用工厂来创建对象

public static void main(String[] args) {
    UserService userService = Factory.getUserService();
    userService.login("abcabc","123456");
}

总结:

Spring本质:⼯⼚ ApplicationContext (applicationContext.xml)

第一个Spring

环境搭建

jar包

在maven的中心仓库,搜索spring,导入相应的依赖,这里选择spring context

Android 抽象工厂案例 抽象工厂 spring_ioc_02

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.0</version>
</dependency>
配置文件

配置文件放置的位置:任意位置
配置文件的命名:没有硬性要求,建议applicationContext.xml

以后使用Spring框架时,需要进行配置文件路径的设置。

<?xml version="1.0" encoding="UTF-8"?>
<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">

</beans>

核心API

ApplicationContext:Spring提供的一个工厂对象,用于对象的创建。好处:解耦合。

ApplicationContext是一个接口类型,屏蔽实现的差异。

在非web环境下,主要使用ClassPathXmlApplicationContext实现类,在web环境下主要使用XmlWebApplicationContext实现类

ApplicationContext工厂对象需要占用大量的内存(重量级),所以我们不会频繁地创建这个对象(一个应用智慧创建一个工厂对象),而且这个对象一定是线程安全的。

程序开发

  1. 新建一个类
  2. 在applicationContext.xml中引入
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!--
        id:唯一
        class:全限定类名
    -->
    <bean id="person" class="com.prince.Person"></bean>
</beans>
  1. Main
public class TestPerson {
    @Test
    public void test(){
        //创建Spring的工厂对象,并且在构造方法中指定配置文件的位置
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
        //根据id创建对象
        Person person = (Person)applicationContext.getBean("person");
        //测试
        System.out.println(person);
    }
}

细节分析

由Spring创建的对象,叫做Bean或者Component

常见方法
  1. 直接通过id值获得bean对象,但是需要强制转换
Person person = (Person)applicationContext.getBean("person");
  1. 传入一个class字节码对象,就可以不用强制类型转换了
Person person = applicationContext.getBean("person",Person.class);
  1. 甚至可以直接只传入一个class字节码对象,表示根据类型来获取值
Person person = applicationContext.getBean(Person.class);

注意:当Spring的容器中只存在一个Person对象时,才能够这样子获取,如果有多个,会报异常(他不知道你想要的是哪个)。

  1. 获取bean的所有id
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
  1. 获取指定类的所有id
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String s : beanNamesForType) {
    System.out.println(s);
}
  1. 判断是否存在指定的id
applicationContext.containsBeanDefinition("person");//当配置文件显式定义了id时,只判断id 不判断name  ,没有显式定义id时,也可以判断name
applicationContext.containsBean("person");//id 和 name 都可以判断
配置文件
  1. 只配置class属性,不配置id属性
<bean class="com.prince.Person"></bean>

问:没有手动设置id值,他有没有id
验证:

String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

输出结果为:

com.prince.Person#0

可以看到,当不指定id值的时候,spring 默认会为我们提供一个id值
如果这个bean只需要使用一次,那么久可以省略id值。如果会使用多次,或者需要被其他bean引用时,就必须要指定id属性!

  1. name属性:用于给bean定义别名(小名)
    相同点:在getBean()方法,可以同时传id和name来获取对象。(name可以取代id的作用)
<bean name="p" class="com.prince.Person"></bean>
Person p = applicationContext.getBean("p",Person.class);
System.out.println(p);

区别:

  1. 别名可以定义多个(用逗号分隔),但是ID只能定义一个
<bean name="p1,p2,p3" class="com.prince.Person"></bean>
  1. 在XML中,id属性的命名要求:必须以字母开头,不能以特殊字符开头。name则没有要求。所以name属性会应用在特殊命名的场景下。
    但是xml发展到今天,id的限制已不存在。
  2. containsBeanDefinitioncontainsBean的区别。

Spring与日志框架

Spring与⽇志框架进⾏整合,⽇志框架就可以在控制台中,输出Spring框架运⾏过程中的⼀些重要的信息。
好处:便于了解Spring框架的运⾏过程,利于程序的调试

如何整合日志框架?

默认情况下:

Spring 1 2 3等早期版本都是使用commons-logging.jar
Spring 5.x默认整合的是 logback log4j2

如何整合log4j(我们不要log4j2)?

  1. 引入log4j的jar包
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>
  1. 创建log4j.properties
log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

之后就可以很清楚地看到Spring的运行情况:

Android 抽象工厂案例 抽象工厂 spring_后端_03

注入

注入:通过Spring工厂及配置文件,为所创建的成员变量赋值。

平常的时候为成员变量赋值(使用代码的方式,存在耦合问题):

@Test
public void test4(){
    Person p = applicationContext.getBean("p",Person.class);
    p.setName("xiaoming");
    p.setAge(18);
    System.out.println(p);
}

使用Spring的配置文件来注入:

<bean name="p1" class="com.prince.Person">
    <property name="name">
    	<value>aaa</value>
    </property>
    <property name="age">
    	<value>18</value>
    </property>
</bean>
<bean name="p1" class="com.prince.Person">
    <property name="name" value="aaa"/>
    <property name="age" value="18" />
</bean>

好处:解耦合

Set注入

Spring调用Set方法,通过配置文件,为成员变量赋值。

String+8种基本类型

property标签里面嵌套value即可。

<property name="name">
    <value>aaa</value>
</property>
数组

property标签里面嵌套list,然后再在list里嵌套多个value即可。

<property name="emails">
    <list>
        <value>123456@qq.com</value>
        <value>123456@163.com</value>
        <value>123456@126.com</value>
        <value>123456@gmail.com</value>
    </list>
</property>
Set集合

property标签里面嵌套set,然后再在set里嵌套多个value即可。

<property name="tels">
    <set>
        <value>18888888888</value>
        <value>18888888889</value>
        <value>18888888890</value>
        <value>18888888890</value>
    </set>
</property>

细节:由于Set集合本身是无序的,所以最终输出的顺序不一定会和这个一样。由于Set是无重复的,即使加入了重复的元素,也会自动去重。

List集合

和数组一样,都是property里嵌套list

Map集合
<property name="map">
    <map>
        <entry key="k1" value="v1"/>
        <entry>
            <key><value>k2</value></key>
            <value>v2</value>
        </entry>
        <entry>
        	<key><ref bean=""></ref></key>
            <ref bean=""></ref>
        </entry>
    </map>
</property>

注意:key有专属的标签,写在key里面的内容就是key,因为第5行我的key是String类型,所以我在里面嵌套value标签,如果key是一个对象,那么key标签里面嵌套的是ref标签。
key外面的都是value。

Property
<property>
	<props>
    	<prop key="k1">v1</prop>
    </props>
</property>
用户自定义类型

第一种方式:直接在property里面加bean 标签即可,因为那个bean仅使用一次,所以不需要id属性

<bean id="userServer" class="com.prince.basic.UserServiceImpl">
    <property name="userDao">
        <bean class="com.prince.basic.UserDaoImpl"></bean>
    </property>
</bean>

第二种方式:

<bean id="userDao" class="com.prince.basic.UserDaoImpl"></bean>

<bean id="userServer" class="com.prince.basic.UserServiceImpl">
    <property name="userDao">
        <ref bean="userDao"></ref>
    </property>
</bean>
简化方法
  1. 基于属性简化
<property name="name" value="aaa"/>
<property name="userDao" ref="userDao">
  1. 基于p命名空间简化
<bean name="p" class="com.prince.Person" p:name="bbb" p:age="180"></bean>
<bean id="userServer" class="com.prince.basic.UserServiceImpl" p:userDao-ref="userDao"></bean>

直接写在bean标签上,p是property的缩写。

构造注入

Spring调用构造方法来赋值。前提:提供有参构造方法。

  1. 必须提供有参构造方法。
public class People {
    public String name;
    public int age;

    public People() {
    }
    
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  1. 配置bean标签的时候,里面不再是property标签,而是constructor-arg
<bean id="people" class="com.prince.People">
    <constructor-arg>
        <value>zhangsan</value>
    </constructor-arg>
    <constructor-arg>
        <value>18</value>
    </constructor-arg>
</bean>

这里需要注意的是,constructor-arg标签的个数,和顺序,必须要和构造方法里的保持一致!

注:当构造函数的参数个数不一样时,可以通过<constructor-arg>标签的个数进行区分。

当参数个数一样时,需要指定type属性,如果不指定,他将会随机选一个来注入。

<bean id="people1" class="com.prince.People">
    <constructor-arg type="int">
        <value>123</value>
    </constructor-arg>
</bean>

反转控制与依赖注入

反转控制

控制:对于成员变量赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中转移到Spring工厂的配置文件中完成。
好处:解耦合
底层实现:工厂设计模式

Android 抽象工厂案例 抽象工厂 spring_后端_04

依赖注入

注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值。

依赖注⼊:当⼀个类需要另⼀个类时,就意味着依赖,⼀旦出现依赖,就可以把另⼀个类作为本类的成员变量,最终通过Spring配置⽂件进⾏注⼊(赋值)

Android 抽象工厂案例 抽象工厂 spring_ioc_05

复杂对象创建

简单对象:可以直接通过new的方式创建
复杂对象:不能通过new的方式创建,比如Connection、SqlSessionFactory

Android 抽象工厂案例 抽象工厂 spring_java_06

实现FactoryBean接口

Android 抽象工厂案例 抽象工厂 spring_Android 抽象工厂案例_07

开发步骤:

  1. 新建一个类,继承FactoryBean接口。使用FactoryBean接口的时候,要指定泛型。
public class ConnectionFactory implements FactoryBean<Connection> {

    /**
     * 创建复杂对象的过程放在这,Spring会拿它的返回值来当做要创建的对象。
     * @return 复杂对象
     * @throws Exception
     */
    @Override
    public Connection getObject() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        return DriverManager.getConnection("jdbc:mysql:///mydata","root","root");
    }

    /**
     * 
     * @return 复杂对象的Class字节码文件
     */
    @Override
    public Class<?> getObjectType() {
        return Connection.class;
    }

    /**
     * 是否单例
     * @return true或者false
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}
  1. Spring配置文件的配置
<!--错误认知:通过getBean获取conn,得到的是ConnectionFactory对象。
        其实,Spring会对者做特殊处理,如果class指定的是FactoryBean接口,那么通过getBean获取到的就是那个复杂对象。
    -->
    <bean id="conn" class="com.factorybean.ConnectionFactory"></bean>

细节:如果就是想获取FactoryBean对象,而不是对应的复杂对象,可以在getBean里面的id前面加&

applicationContext.getBean("&conn");

依赖注入改造

Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection("jdbc:mysql:///mydata","root","root");

这四个参数对于Connection来说都是非常重要的,也就是依赖。

我们可以通过依赖注入的方式改造这个类:

public class ConnectionFactory1 implements FactoryBean<Connection> {

    public String driverName;
    public String url;
    public String username;
    public String password;
	//getter setter略

    @Override
    public Connection getObject() throws Exception {
        Class.forName(driverName);
        return DriverManager.getConnection(url,username,password);
    }
	//getObjectType isSingleton 略

}

配置文件中通过Set注入,好处:解耦合!!

<bean id="conn1" class="com.factorybean.ConnectionFactory1">
    <property name="driverName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///mydata"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>

FactoryBean总结:FactoryBean是Spring中用于创建复杂对象的一种方式,后续讲Spring整合其他框架会大量使用这种模式!

实例工厂

为什么使用实例工厂?

  1. 避免Spring框架的侵入(FactoryBean是Spring框架提供的)
  2. 整合遗留系统

开发步骤:

  1. 创建一个工厂类,创建一个getInstance()或者getXxxx()方法来创建一个对象。
/**
 * 使用实例工厂创建
 */
public class ConnectionFactory2 {
    public Connection getConnection(){
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql:///mydata","root","root");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
        }
        return conn;
    }
}
  1. Spring配置文件,factory-bean填的是工厂类的bean,factory-method填的是工厂类创建工厂的方法。
<!--实例工厂-->
<bean id="connfactory" class="com.factorybean.ConnectionFactory2"></bean>
<bean id="conn2" factory-bean="connfactory" factory-method="getConnection"></bean>

静态工厂

静态工厂和实例工厂的区别是:实例工厂的类的创建对象的方法不是静态的,而静态工厂的方法是静态的,所以使用静态工厂就省去了创建工厂类这一步。

<bean id="conn3" class="com.factorybean.ConnectionFactory3" factory-method="getConnection"></bean>

控制Spring工厂创建对象的次数

简单对象

添加一个scope属性即可

<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>
sigleton:只会创建⼀次简单对象 默认值
prototype:每⼀次都会创建新的对象

复杂对象

FactoryBean{
    isSingleton(){
        return true 只会创建⼀次
        return false 每⼀次都会创建新的
    }
}
如没有isSingleton⽅法 还是通过scope属性 进⾏对象创建次数的控制

对象的生命周期

生命周期:指的是一个对象创建、存活、消亡的一个完整过程

创建阶段

创建阶段:Spring工厂何时创建对象

  • scope="singleton" Spring工厂创建的同时,创建对象
  • scope="prototype" Spring工厂在获取对象的同时,创建对象

验证:xml文件里:

<bean id="pro" class="com.life.Product" scope="singleton"></bean>

效果:在执行完下面代码后,对象创建(可以看到控制台中输出了构造方法打印的文字)

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/life.xml");

scope="prototype"的对象,只有调用了getBean()方法之后才会创建对象。

如果,singleton的对象也想实现prototype的那种效果(只有调用了getBean()才会创建对象),可以加入懒加载属性lazy-init

<bean id="pro" class="com.life.Product" scope="singleton" lazy-init="true"></bean>

初始化阶段

Spring工厂在创建完对象后,会调用对象的初始化方法,完成对应的初始化操作。

初始化方法,由程序员根据需求来提供;初始化方法的调用,由Spring来完成。

实现初始化的方式
  • 实现InitializingBean接口,重写afterPropertiesSet()方法,在方法里面执行初始化语句。
public class Product implements InitializingBean {
    public Product() {
        System.out.println("Product对象已创建");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化");
    }
}
<bean id="pro" class="com.life.Product" scope="singleton"></bean>

执行代码,输出:

Android 抽象工厂案例 抽象工厂 spring_spring_08

  • 类中提供一个普通方法,然后再在配置文件中指定init-method属性
    我这里配置的初始化方法是init()
<bean id="pro1" class="com.life.Product1" scope="singleton" init-method="init"></bean>

细节:

  1. 如果一个对象实现InitializingBean接口的同时,又提供普通的初始化方法,那么两个初始化方法都会执行。
  2. 注入在初始化之前执行

销毁阶段

Spring销毁对象前,会调用对象的销毁方法,完成销毁操作。

Spring什么时候销毁对象? 答:工厂关闭的时候

((ClassPathXmlApplicationContext)applicationContext).close();
实现销毁的方式
  • 实现DisposableBean接口
public class Product2 implements DisposableBean {
    public Product2() {
        System.out.println("Product对象已创建");
    }
    

    @Override
    public void destroy() throws Exception {
        System.out.println("销毁方法");
    }
}
  • 自定义销毁方法
<bean id="pro3" class="com.life.Product3" scope="singleton" destroy-method="myDestroy"></bean>

细节:

  1. 销毁方法只使用于scope="singleton"
  2. 如果两种销毁方法都存在,那么两种方法都执行

Android 抽象工厂案例 抽象工厂 spring_spring_09

配置文件参数化

把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中。

1. Spring的配置⽂件中存在需要经常修改的字符串?
	存在 以数据库连接相关的参数 代表
2. 经常变化字符串,在Spring的配置⽂件中,直接修改
	不利于项⽬维护(修改)
3. 转移到⼀个⼩的配置⽂件(.properties)
	利于维护(修改)

比如一个druid的链接池:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///mydata"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>

原来是那些value都整合在一个配置文件中,可以通过下面的方法来把那些value分离出去

dp.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///mydata
username=root
password=root

applicationContext.xml

<context:property-placeholder location="dp.properties"></context:property-placeholder>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${driverClassName}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</bean>

类型转换器

类型转换器

作⽤: Spring通过类型转换器把配置⽂件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进⽽完成了注⼊

Android 抽象工厂案例 抽象工厂 spring_ioc_10

自定义类型转换器

当Spring内部没有提供特定的类型转换器,而程序员在应用的过程中还需要使用,那么就需要自定义类型转换器
如:日期格式

<bean id="person" class="com.prince.Person">
    <property name="id" value="111" />
    <property name="birthday" value="2020-11-11"/>
</bean>

运行的时候直接报错:

Android 抽象工厂案例 抽象工厂 spring_Android 抽象工厂案例_11

原因:缺少转换器,没法把字符串"2020-11-11"转成Date对象
解决方法:添加自定义类型转换器(实现Converter接口,然后在Spring配置文件中注册)

  1. 新建一个类,实现Converter接口
    可以发现Converter是一个泛型,Converter<S,T>中,S表示源类型,T表示要转换的类型。
    踩坑:Converter是org.springframework.core.convert.converter.Converter,不要导错包。
public class MyConvert implements Converter<String, Date> {

    @Override
    public Date convert(String s) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return sdf.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

}
  1. 在Spring配置文件中注册,让Spring知道有这个类
<!--第一步:创建这个转换器类的对象(最基本)-->
<bean id="myConvert" class="com.prince.MyConvert" />
<!--第二步:创建ConversionServiceFactoryBean对象,注入那个转换器,用于告诉Spring-->
<!--踩坑:id一定是conversionService,否则不起作用-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <!--观察源码发现,converters是一个Set集合-->
        <set>
            <ref bean="myConvert" />
        </set>
    </property>
</bean>

细节

  1. 自定义类型转换器的"yyyy-MM-dd"可以用依赖注入。
public class MyConvert implements Converter<String, Date> {
    public String pattern;

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public Date convert(String s) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            return sdf.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

}
<bean id="myConvert" class="com.prince.MyConvert" >
    <property name="pattern" value="yyyy-MM-dd"/>
</bean>
  1. ConversionServiceFactoryBean的id值必须是conversionService
  2. Spring内置的String–>Date转换器只支持yyyy/MM/dd

后置处理Bean

BeanPostProcessor

BeanPostProcessor 作⽤:对Spring⼯⼚所创建的对象,进⾏再加⼯。

底层实现:

Android 抽象工厂案例 抽象工厂 spring_java_12

程序员实现BeanPostProcessor规定接⼝中的⽅法:

    Object postProcessBeforeInitiallization(Object bean String beanName)
    作⽤: Spring创建完对象,并进⾏注⼊后,可以运⾏Before⽅法进⾏加⼯
    获得Spring创建好的对象 :通过⽅法的参数
    最终通过返回值交给Spring框架
    
    Object postProcessAfterInitiallization(Object bean String beanName)
    作⽤: Spring执⾏完对象的初始化操作后,可以运⾏After⽅法进⾏加⼯
    获得Spring创建好的对象 :通过⽅法的参数
    最终通过返回值交给Spring框架

开发步骤

  1. 新建一个类,实现BeanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Person person = (Person)bean;
        person.setId(9999);
        return person;
    }
}
  1. 在Spring配置文件中配置(只需要把这个对象创建出来就行了)
<bean id="beanPostProcessor" class="com.prince.MyBeanPostProcessor"></bean>

注意:

  1. 不是Person类实现BeanPostProcessor接口,而是重新写一个类来实现这个接口
  2. 一旦配置了这个BeanPostProcessor,那么这个工厂里创建的所有bean都会经过这个处理器,所以为了避免类型转换异常,需要加一个判断:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if(bean instanceof Person){
        Person person = (Person)bean;
        person.setId(9999);
    }
    return bean;
}
  1. 在Spring配置文件中配置(只需要把这个对象创建出来就行了)
<bean id="beanPostProcessor" class="com.prince.MyBeanPostProcessor"></bean>