Spring(二)-生命周期 + 自動裝配(xml) +自動裝配(註解)
1、生命周期
**Spring容器的 bean **的生命周期;
1.1 默認生命周期
1.1.1 生命周期
- 調用構造方法,創建實例對象;
- set方法,給實例對象賦值;
- init 初始化方法 初始化對象;(手寫並配置到bean上init-method=””)
- 使用容器中的bean對象;
- destroy 銷毀方法 銷毀對象 (手寫並配置到bean上destroy-method=””)
1.1.2 bean 實體類
Truck
@Data
@ToString
public class Truck {
//品牌
private String brand;
//廠商
private String factory;
//價格
private Double price;
public Truck() {
//空參構造方法,觀察bean什麼時候實例化
System.out.println("------ 1.調用構造方法,創建實例對象 ------\n");
}
public void setBrand(String brand) {
//任意一個set方法,觀察bean什麼時候注入參數
System.out.println("------ 2.set方法,給實例對象賦值 ------");
this.brand = brand;
}
public void initTruck(){
//init初始化方法,觀察bean什麼時候初始化
//需要再配置bean的時候,配置init初始化方法
System.out.println("------ 3.Truck init 初始化方法 初始化對象 ------\n");
this.brand = "大運";
}
public void destroyTruck(){
//destory方法,觀察bean什麼時候銷毀
//需要再配置bean的時候,配置destory銷毀方法
System.out.println("------ 5.Truck destroy 銷毀方法 銷毀對象 ------\n");
}
//這裡方法上標註的序號是測試後得來的;
}
1.1.3 bean 配置
spring-lifecycle.xml
<!-- spring容器中bean的生命周期 默認生命周期 -->
<bean id="truck" class="com.kgc.spring.lifecycle.Truck" init-method="initTruck" destroy-method="destroyTruck">
<property name="brand" value="江淮"></property>
<property name="factory" value="安徽"></property>
<property name="price" value="200000"></property>
</bean>
1.1.4 測試
public class TestSpringLifeCycle {
//定義全局容器對象,如果需要關閉容器對象,
//必須使用ApplicationContext的子介面 ConfigurableApplicationContext
//ApplicationContext介面主要各種屬性的get方法;
//ConfigurableApplicationContext重在對各種屬性的配置;
// private ApplicationContext context;
private ConfigurableApplicationContext context;
@Before
public void initApplicationContext(){
context = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
}
//測試spring 容器的bean的生命周期,默認和加了處理器兩種場景
@Test
public void testSpringBeanLifeCycle(){
//從容器中,獲取Truck的是實例對象
Truck truck = context.getBean("truck", Truck.class);
//使用對象
System.out.println("------ 4.使用容器中的bean對象"+truck +" ------");
//關閉容器
context.close();
}
}
輸出結果:
//可以得出 spring中bean的 默認生命周期
------ 1.調用構造方法,創建實例對象 ------
------ 2.set方法,給實例對象賦值 ------
------ 3.Truck init 初始化方法 初始化對象 ------
------ 4.使用容器中的bean對象Truck(brand=大運, factory=安徽, price=200000.0) ------
------ 5.Truck destroy 銷毀方法 銷毀對象 ------
1.1.5 ApplicationContext 和 ConfigurableApplicationContext
參考部落格: ApplicationContext和ConfigurableApplicationContext解析
-
ApplicationContext介面主要 各種屬性的get方法;
-
ConfigurableApplicationContext重在對 各種 屬性的配置;
1.2 增加後置處理器
1.2.1 生命周期
1.調用構造方法,創建實例對象;
2.set方法,給實例對象賦值;
3-1.後置處理的 before 方法;
3.初始化方法 初始化對象;
3+1.後置處理器的的 after 方法;
4.使用容器中的bean對象;
5.destroy 銷毀方法 銷毀對象;
1.2.2 後置處理器
-
要求:必須實現 BeanPostProcessor 介面
-
自定義 bean 的 後置處理器,對容器中所有的bean統一處理(生效),
-
要生效的話,必須將此處理器放到容器中(配置到spring的核心配置文件中,增加處理器的實例配置);
注意:
當前案例,只對容器中的一個實例處理;
MyBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//在容器中bean的實例對象調用 初始化方法 前 自動調用(init方法可以沒有,不影響)
//模擬處理容器中的bean,接下來的寫法,僅限於當前的用法案例(容器中就 只有一個 卡車實例)
Truck truck = (Truck)bean;
System.out.println("++++++ 容器中的卡車對象 "+truck+"++++++");
System.out.println("++++++ 3-1,後置處理的 before 方法 ++++++");
truck.setBrand("後置處理的before方法");
System.out.println("++++++ 處理後的 卡車對象 "+truck+" ++++++\n");
return truck;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//bean 初始化方法 執行 後,調用此方法處理bean
Truck truck = (Truck)bean;
System.out.println("++++++ 初始化容器中的卡車對象 "+truck+"++++++");
System.out.println("++++++ 3+1,後置處理器的的 after 方法 ++++++");
truck.setBrand("after");
System.out.println("++++++ 初始化後 處理後的 卡車對象 "+truck+" ++++++\n");
return truck;
}
}
1.2.3 bean 配置
在配置文件中配置 MyBeanPostProcessor;
<!-- 配置後置處理器的實例,自動放入容器中,可以自動生效 (容器中所有的實例生效) -->
<bean class="com.kgc.spring.lifecycle.MyBeanPostProcessor"></bean>
1.2.4 測試
跟默認生命周期的測試程式碼一致;
輸出結果:
------ 1.調用構造方法,創建實例對象 ------
------ 2.set方法,給實例對象賦值 ------
++++++ 容器中的卡車對象 Truck(brand=江淮, factory=安徽, price=200000.0)++++++
++++++ 3-1,後置處理的 before 方法 ++++++ ------ 2.set方法,給實例對象賦值 ------
++++++ 處理後的 卡車對象 Truck(brand=後置處理的before方法, factory=安徽, price=200000.0) ++++++
------ 3.Truck init 初始化方法 初始化對象 ------
++++++ 初始化容器中的卡車對象 Truck(brand=大運, factory=安徽, price=200000.0)++++++
++++++ 3+1,後置處理器的的 after 方法 ++++++ ------ 2.set方法,給實例對象賦值 ------
++++++ 初始化後 處理後的 卡車對象 Truck(brand=after, factory=安徽, price=200000.0) ++++++
------ 4.使用容器中的bean對象Truck(brand=after, factory=安徽,
------ 5.Truck destroy 銷毀方法 銷毀對象 ------
1.2.3 BeanPostProcesso
參考部落格:BeanPostProcessor簡介
BeanPostProcessor官方定義為工廠鉤子,我們也俗稱後置處理器。它允許自定義修改新的bean實例,例如檢查標記介面或用代理包裝它們。應用程式上下文可以在其bean定義中自動檢測BeanPostProcessor bean,並將它們應用於隨後創建的任何bean。
BeanPostProcessor 的 前置處理 和 後置處理;
 +自動裝配(註解).assets/image-20220826150821502.png)
2、自動裝配(xml)
2.1 bean 實體類
Person
@Data
public class Person {
//昵稱
private String nickName;
//車子
private Car car;
//房子
private House house;
}
Car
@Data
public class Car {
//品牌
private String brand;
//廠商
private String factory;
//價格
private Double price;
}
House
@Data
public class House {
//戶型
private String type;
//面積
private double area;
//價格
private Integer price;
}
2.2 bean 配置 (byType)
autowire=”byType”:根據屬性 的 類型自動裝配;
spring-auto.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">
<!-- spring的自動裝配方式,基於xml配置文件方式,掌握 -->
<!-- 容器中實例化一個容器的Car對象 -->
<bean id="car" class="com.kgc.spring.auto.Car">
<property name="brand" value="Bnw520"></property>
<property name="factory" value="華晨"></property>
<property name="price" value="450000"></property>
</bean>
<!-- 容器中實例化一個容器的House對象 -->
<bean id="house" class="com.kgc.spring.auto.House">
<property name="type" value="三室一廳"></property>
<property name="area" value="96"></property>
<property name="price" value="2800000"></property>
</bean>
<!-- 根據類型自動裝配 -->
<bean id="person" class="com.kgc.spring.auto.Person" autowire="byType">
<property name="nickName" value="huayu"></property>
</bean>
</beans>
2.3 測試
public class TestSpringAutoUserXml {
private ApplicationContext context;
@Before
public void initApplicationContext(){
context = new ClassPathXmlApplicationContext("spring-auto.xml");
}
@Test
public void testSpringAuto(){
Person person = context.getBean("person", Person.class);
//使用對象
System.out.println("容器中的person對象:"+person);
}
}
輸出結果:
容器中的person對象:Person(
nickName=huayu,
car=Car(brand=Bnw520, factory=華晨, price=450000.0),
house=House(type=三室一廳, area=96.0, price=2800000)
)
2.4 bean 配置 (多個同類型bean)
bean 配置:
其他不變,多增加一個Car類型的實例bean;
<bean id="car" class="com.kgc.spring.auto.Car">
<property name="brand" value="Bnw520"></property>
<property name="factory" value="華晨"></property>
<property name="price" value="450000"></property>
</bean>
<bean id="carNew" class="com.kgc.spring.auto.Car">
<property name="brand" value="AudiA6"></property>
<property name="factory" value="一汽"></property>
<property name="price" value="450000"></property>
</bean>
測試,報錯資訊:
No qualifying bean of type ‘com.kgc.spring.auto.Car’ available: expected single matching bean but found 2: car,carNew
總結
:autowire=”byType” 當有多個相同類型的bean時,無法確定要裝配的 bean;
2.5 bean 配置(byName)
其他配置資訊不變,設置 autowire=”byName” ,根據 屬性 的 名字 自動裝配;
<bean id="person" class="com.kgc.spring.auto.Person" autowire="byName">
<property name="nickName" value="hauyu"></property>
</bean>
測試輸出結果:
容器中的person對象:Person(
nickName=huayu,
car=Car(brand=Bnw520, factory=華晨, price=450000.0),
house=House(type=三室一廳, area=96.0, price=2800000)
)
總結
:
- byType:根據類型自動裝配:
- 根據實體屬性的 類型,到容器中,根據 bean類型 進行唯一匹配,如果可以匹配到對應類型的bean的實例,就會執行自動裝配, 如果不能唯一匹配(同類型的bean有多個),會報錯;
- byName: 根據名稱自動裝配:
- 根據屬性的 屬性名,到容器中,根據 bean的id 屬性值,進行唯一匹配,如果能夠成功匹配,執行自動裝配, 如果匹配不到,不執行自動裝配,實體屬性為null;
3、自動裝配 (註解)
3.1 註解
- @Component 普通組件註解;
- @Repository 持久層註解
- @Service 業務層註解
- @Controller 控制層註解
3.3.1 註解的原理
默認情況下:spring自動將分層組件(@Controller,@Service,@Repository,@component)標識的類(不是介面),自動創建實例對象放入容器中,使用bean的標識id值為 對應類名首字母小寫 就相當於,幫我們手動添加了配置 :
<bean id=”分層註解標識類的類名首字母小寫” class=”分層註解標識類的全類名”> … <bean>
3.1.2 自定義id 屬性
如果不想使用默認的類名首字母小寫,我們可以使用註解的value屬性執行一個自定義的id值;
比如:@Service(value=”自定義的id值”),註解只有value屬性,可以省略value執行,簡化為@Service(“自定義的id值”)
3.1.3 分層組件的目的
分層組件的目的,就僅僅是為了方便開發人員明確當前註解所在的類所對應的角色,在使用上,建議使用,按照官方定義的使用,防止模糊不清;在springMVC框架中@Controller有特殊含義;
3.2 配置文件
spring創建容器對象時,如果解析到 component-scan 組件掃描配置,會將base-package指定的基包(父包)及其子包所有增加了分層組件的類,自動創建實例,放進容器中;
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xmlns="//www.springframework.org/schema/beans"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd
//www.springframework.org/schema/context //www.springframework.org/schema/context/spring-context.xsd
">
<!-- 組件掃描:註解標識的組件,必須通過組件掃描配置,才可以添加到spring的容器中-->
<context:component-scan base-package="com.kgc.spring.acnocation" > </context:component-scan>
</beans>
3.3 默認id屬性 測試
3.3.1 實體
@Component
public class User {
//用戶名
@Value("huayu") //@Value() 自動裝配參數
private String userName;
//用戶密碼
@Value("123")
private String userPwd;
}
3.3.2 測試
@Test
public void testSpringAutoUserAnnotation(){
User user = context.getBean("user", User.class);
System.out.println(user);
//User(userName=huayu, userPwd=123)
}
3.4 自定義id屬性 測試
3.4.1 實體(自定義id屬性)
@Component(value = "myUser")
public class User {
//用戶名
private String userName;
//用戶密碼
private String userPwd;
}
3.4.2 測試
@Test
public void testSpringAutoUserAnnotation(){
//User user = context.getBean("user", User.class);
//自定義id後,默認id不能使用: No bean named 'user' available
//必須使用自定義id
User user = context.getBean("myUser", User.class);
System.out.println(user);
//User(userName=huayu, userPwd=123)
}
3.5 自動裝配
3.5.1 @Autowired
- 組件自動裝配,可以實現實體屬性類型的自動裝配,自動到spring的容器中,根據當前屬性的類型或者名稱進行注入,如果容器中能匹配到,就直接將實例對象注入到當前實體屬性上,無序手動指定;
- @Autowired自動裝配原理:首先會根據byType方式,進行自動裝配,
- 如果不能唯一匹配(存在同類型多個實例對象),會再次嘗試使用byName方式,根據當前實體屬性名,到容器中進行匹配(容器中bean的id值),如果能唯一匹配,直接執行自動裝配,
- 默認情況下,@Autowired註解標識的實體屬性,必須被裝配
- 如果裝配失敗,就直接拋出異常;
- 如果不需要校驗必須被裝配(項目啟動,如果裝配失敗,項目是起不來);
- 通過指定required = false,去除必須執行自動裝配的校驗(即便容器中找不到裝配的實例,也不會拋出異常);
- 如果自動裝配,容器中存在多個同類型的bean對象,可以使用註解@Qualifier(“容器中同類型多個bean的某個id值”),實現指定到容器中,找對應的bean實例對象,進行自動裝配;
- 底層是如何做的:在指定要掃描的包時,<context:component-scan> 元素會自動註冊一個bean的後置處理器:AutowiredAnnotationBeanPostProcessor的實例。該後置處理器可以自動裝配標記了@Autowired、@Resource或@Inject註解的屬性。
3.5.2 實體
People
@Data
@Component("myPeople")
public class People {
//昵稱
@Value("huayu")
private String name;
//玩具
@Autowired
private Toy toy;
}
Toy介面
public interface Toy {
//得到玩具
public void getToy();
}
ToyImpl1
@Component("toy1")
public class ToyImpl1 implements Toy {
@Value("玩具車")
private String toyName;
@Override
public void getToy() {
System.out.println(this.toyName);
}
}
3.5.3 測試
注意
:可以通過介面類型獲取實現類(推薦使用);
@Test
public void testAutowired (){
People people = context.getBean("myPeople", People.class);
people.getToy().getToy(); //玩具車
}
3.5.4 存在多個相同類型的bean
當存在多個相同類型的bean,不能唯一匹配,會自動裝配錯誤;
在寫一個Toy實現類,ToyImpl2
@Component("toy2")
public class ToyImpl2 implements Toy {
@Value("尤克里里")
private String toyName;
@Override
public void getToy() {
System.out.println(this.toyName);
}
}
3.5.4.1 測試
報錯資訊(項目無法啟動):
No qualifying bean of type ‘com.kgc.spring.acnocation.bean.Toy’ available: expected single matching bean but found 2: toy1,toy2
主要資訊:類型無法唯一匹配;
3.5.4.2 required = false 允許不裝配
People
@Data
@Component("myPeople")
public class People {
//昵稱
@Value("huayu")
private String name;
//玩具
@Autowired (required = false)
private Toy toy;
}
項目可以啟動但是還是報錯(一般項目中不會有兩個相同類型的實現類;)
3.5.4.3 @Quailfy
People
@Data
@Component("myPeople")
public class People {
//昵稱
@Value("huayu")
private String name;
//玩具
@Autowired
@Qualifier("toy2") //指定bean的id值
private Toy toy;
}
3.5.4.4 測試
@Test
public void testAutowired (){
People people = context.getBean("myPeople", People.class);
System.out.println(people.getToy());
//com.kgc.spring.acnocation.bean.ToyImpl2@15d9bc04
people.getToy().getToy();
//尤克里里
}
3.6 指定掃描 排除掃描
3.6.1 指定掃描
include-filter
指定掃描(包含掃描):
- 只會掃描指定的類或者某類組件(使用分組掃描),加入到容器中;
- 但是必須配合父標籤的user-default-filter使用,默認值是true,就是全部掃描;
- 指定掃描,如果要生效必須改為false;
- 指定掃描某類組件,type=”annotation” expression=”某類組件註解的全類名”;
- 指定掃描某個類,type=”assignable” expression=”某個類的全類名”;
3.6.1.1 指定掃描某類組件
type=”annotation”
- org.springframework.stereotype.Component
- org.springframework.stereotype.Repository
- org.springframework.stereotype.Service
- org.springframework.stereotype.Controller
<!-- 指定掃描 @Component 組件 -->
<context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="false" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
3.6.1.2 指定掃描某個類
type=”assignable”
<!-- 指定掃描 ToyImpl1 -->
<context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="false" >
<context:include-filter type="assignable" expression="com.kgc.spring.acnocation.bean.ToyImpl1"/>
</context:component-scan>
3.6.2 排除掃描
exclude-filter
- 排除掃描(剔除掃描):排除指定的類或某類組件,不加入到容器中,處理排除外的其他組件,仍然會被添加到容器中;
- 不需要配合父標籤,use-default-filters=”true” 因為,默認就是在全部掃描的基礎上剔除;
- 排除掃描某類組件,type=”annotation” expression=”某類組件註解的全類名”;
- 排除掃描某個類,type=”assignable” expression=”某個類的全類名”;
3.6.2.1 排除掃描某類組件
type=”annotation”
<!-- use-default-filters="true" 可寫可不寫 -->
<!-- 排除掃描 @Component組件 -->
<context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="true" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
3.6.2.2 排除掃描個類
type=”assignable”
<!-- 排除掃描 ToyImpl1 -->
<context:component-scan base-package="com.kgc.spring.acnocation" >
<context:exclude-filter type="assignable" expression="com.kgc.spring.acnocation.bean.ToyImpl1"/>
</context:component-scan>