Java | Spring框架學習筆記--(1)工廠

mp-ui發表於2020-12-18

該筆記從孫哥的視訊(連結)整理而來,如果有什麼不當的地方歡迎指出!
另外推薦大佬整理的筆記:傳送門

Spring筆記(1) 工廠

什麼是Spring

Spring是⼀個輕量級JavaEE解決⽅案,整合眾多優秀的設計模式

  • 輕量級:
  1. 對於執行環境是沒有額外要求的。
    可以在開源的tomcat resion jetty裡執行,也可以在收費的weblogic websphere裡執行。
  2. 程式碼移植性高:不需要事先額外的介面。
  • JavaEE解決方案:

在這裡插入圖片描述

  • 整合設計模式(工廠、代理等)

設計模式

  1. ⼴義概念
    ⾯向物件設計中,解決特定問題的經典程式碼
  2. 狹義概念
    GOF4⼈幫定義的23種設計模式:⼯⼚、介面卡、裝飾器、⻔⾯、代理、模板…

工廠設計模式

  1. 概念:通過⼯⼚類,建立物件,不提倡通過直接new的方法的建立物件

     User user = new User();
     UserDAO userDAO = new UserDAOImpl();
    
  2. 好處:解耦合

    耦合:指定是程式碼間的強關聯關係,⼀⽅的改變會影響到另⼀⽅
    問題:不利於程式碼維護
    簡單:把接⼝的實現類,硬編碼在程式中

    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在這裡插入圖片描述

<!-- 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>
    
  3. 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");
    
  2. 傳入一個class位元組碼物件,就可以不用強制型別轉換了

    Person person = applicationContext.getBean("person",Person.class);
    
  3. 甚至可以直接只傳入一個class位元組碼物件,表示根據型別來獲取值

    Person person = applicationContext.getBean(Person.class);
    

    注意:當Spring的容器中只存在一個Person物件時,才能夠這樣子獲取,如果有多個,會報異常(他不知道你想要的是哪個)。

  4. 獲取bean的所有id

    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    System.out.println(Arrays.toString(beanDefinitionNames));
    
  5. 獲取指定類的所有id

    String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
    for (String s : beanNamesForType) {
        System.out.println(s);
    }
    
  6. 判斷是否存在指定的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屬性!

  2. 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>
      
    2. 在XML中,id屬性的命名要求:必須以字母開頭,不能以特殊字元開頭。name則沒有要求。所以name屬性會應用在特殊命名的場景下。
      但是xml發展到今天,id的限制已不存在。

    3. 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>
    
  2. 建立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的執行情況:
在這裡插入圖片描述

注入

注入:通過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">
    
  2. 基於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;
        }
    }
    
  2. 配置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工廠的配置檔案中完成。
好處:解耦合
底層實現:工廠設計模式

在這裡插入圖片描述

依賴注入

注入:通過Spring的工廠及配置檔案,為物件(bean,元件)的成員變數賦值。

依賴注⼊:當⼀個類需要另⼀個類時,就意味著依賴,⼀旦出現依賴,就可以把另⼀個類作為本類的成員變數,最終通過Spring配置⽂件進⾏注⼊(賦值)

在這裡插入圖片描述

複雜物件建立

簡單物件:可以直接通過new的方式建立
複雜物件:不能通過new的方式建立,比如Connection、SqlSessionFactory

在這裡插入圖片描述

實現FactoryBean介面

在這裡插入圖片描述

開發步驟:

  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;
        }
    }
    
  2. 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>
    

    執行程式碼,輸出:
    在這裡插入圖片描述

  • 類中提供一個普通方法,然後再在配置檔案中指定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. 如果兩種銷燬方法都存在,那麼兩種方法都執行

在這裡插入圖片描述

配置檔案引數化

把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通過型別轉換器把配置⽂件中字串型別的資料,轉換成了物件中成員變數對應型別的資料,進⽽完成了注⼊

在這裡插入圖片描述

自定義型別轉換器

當Spring內部沒有提供特定的型別轉換器,而程式設計師在應用的過程中還需要使用,那麼就需要自定義型別轉換器
如:日期格式

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

執行的時候直接報錯:

在這裡插入圖片描述

原因:缺少轉換器,沒法把字串"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;
        }
    
    }
    
  2. 在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>
    
  2. ConversionServiceFactoryBean的id值必須是conversionService

  3. Spring內建的String–>Date轉換器只支援yyyy/MM/dd

後置處理Bean

BeanPostProcessor

BeanPostProcessor 作⽤:對Spring⼯⼚所建立的物件,進⾏再加⼯。

底層實現:

在這裡插入圖片描述

程式設計師實現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;
        }
    }
    
    
  2. 在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;
    }
    
  3. 在Spring配置檔案中配置(只需要把這個物件建立出來就行了)

    <bean id="beanPostProcessor" class="com.prince.MyBeanPostProcessor"></bean>
    

相關文章