spring(SpEL、 IOC 容器中 Bean 的生命週期方法、配置bean(多種方法))

上一篇

Spring表達式語言:SpEL

Spring 表達式語言(簡稱SpEL):是一個支持運行時查詢和操作對象圖的強大的表達式語言。
語法類似於 EL:SpEL 使用 #{…} 作爲定界符,所有在大框號中的字符都將被認爲是 SpEL
SpEL 爲 bean 的屬性進行動態賦值提供了便利
通過 SpEL 可以實現:
通過 bean 的 id 對 bean 進行引用
調用方法以及引用對象中的屬性
計算表達式的值
正則表達式的匹配

Spring 表達式語言(簡稱SpEL):是一個支持運行時查詢和操作對象圖的強大的表達式語言。

語法類似於 EL:SpEL 使用 #{…} 作爲定界符,所有在大框號中的字符都將被認爲是 SpEL
SpEL 爲 bean 的屬性進行動態賦值提供了便利
通過 SpEL 可以實現:
通過 bean 的 id 對 bean 進行引用
調用方法以及引用對象中的屬性
計算表達式的值
正則表達式的匹配

SpEL:字面量

字面量的表示:
整數:< property name=“count” value="#{5}"/>
小數:< property name=“frequency” value="#{89.7}"/>
科學計數法:< property name=“capacity” value="#{1e4}"/>
String可以使用單引號或者雙引號作爲字符串的定界符號:< property name=“name” value="#{‘Chuck’}"/> 或 < property name=‘name’ value=’#{“Chuck”}’/>
Boolean:< property name=“enabled” value="#{false}"/>

SpEL:引用 Bean、屬性和方法(1)

引用其他對象:在這裏插入圖片描述
引用其他對象的屬性
在這裏插入圖片描述

調用其他方法,還可以鏈式操作在這裏插入圖片描述在這裏插入圖片描述

SpEL支持的運算符號(1)

算數運算符:+, -, *, /, %, ^:
在這裏插入圖片描述
加號還可以用作字符串連接:
在這裏插入圖片描述
比較運算符: <, >, ==, <=, >=, lt, gt, eq, le, ge
在這裏插入圖片描述
邏輯運算符號: and, or, not, |
在這裏插入圖片描述
if-else 運算符:?: (ternary), ?: (Elvis)
在這裏插入圖片描述
if-else 的變體
在這裏插入圖片描述
正則表達式:matches
在這裏插入圖片描述

SpEL:引用 Bean、屬性和方法(2)

調用靜態方法或靜態屬性:通過 T() 調用一個類的靜態方法,它將返回一個 Class Object,然後再調用相應的方法或屬性:
在這裏插入圖片描述
例子:

//bean Car
package wo;
public class Car {

	private String brand;
	private int maxSpeed;
	private double price;
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public int getMaxSpeed() {
		return maxSpeed;
	}
	public void setMaxSpeed(int maxSpeed) {
		this.maxSpeed = maxSpeed;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	public Car(String brand, int maxSpeed, double price) {
		super();
		this.brand = brand;
		this.maxSpeed = maxSpeed;
		this.price = price;
	}
	@Override
	public String toString() {
		return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + ", price="
				+ price + "]";
	}
	public Car() {
		super();
		// TODO Auto-generated constructor stub
	}
}
/////////////////////////////////////////////////////////
//bean Person
package wo;

import java.util.List;

public class Person {
private String name;
private Car car;
@Override
public String toString() {
	return "Person [car=" + car + ", name=" + name + "]";
}
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
public Car getCar() {
	return car;
}
public void setCar(Car car) {
	this.car = car;
}
}
/////////////////////////////////////////////////////////////////////.
//全局配置文件/spring1/src/beans-spel.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"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!--使用spel爲屬性賦-個字面值-->
<!--使用SpEL引用類的靜態屬性-->
<bean id="car" class="wo.Car">
<property name="brand" value="#{'dazhong'}"></property>
<property name="maxSpeed" value="2"></property>
<property name="price" value="#{T(java.lang.Math).PI*80}"></property>
</bean>
<!--使用SpFL來應用其他的Bean的屬性-->
<!--在SpEL中使用運算符-->
<!--使用SpEL來應用其他的Bean -->
<bean id="person" class="wo.Person">
<property name="name" value="#{car.price>2.0?'putong':'gao'}"></property>
<property name="car" value="#{car}"></property>
</bean>
</beans>
////////////////////////////////////////////////////////
//測試
package wo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		
//1. 創建 Spring 的 IOC 容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-spel.xml");
System.out.println(ctx.getBean("person"));

	}
}

IOC 容器中 Bean 的生命週期方法

Spring IOC 容器可以管理 Bean 的生命週期, Spring 允許在 Bean 生命週期的特定點執行定製的任務.
Spring IOC 容器對 Bean 的生命週期進行管理的過程:
通過構造器或工廠方法創建 Bean 實例
爲 Bean 的屬性設置值和對其他 Bean 的引用
調用 Bean 的初始化方法
Bean 可以使用了
當容器關閉時, 調用 Bean 的銷燬方法
在 Bean 的聲明裏設置 init-method 和 destroy-method 屬性, 爲 Bean 指定初始化和銷燬方法.
例子(在上面基礎上):

//bean car 改動
	public Car() {
		super();
		// TODO Auto-generated constructor stub
		System.out.println("car' constructor");
	}
public void init(){
	System.out.println("car' init");	
}
public void destroy(){
	System.out.println("car' destroy");	
}	
	public void setPrice(double price) {
		System.out.println("price");	
		this.price = price;
	}
///////////////////////////////////////////////////////////////
////全局配置文件/spring1/src/beans-spel.xml
<bean id="car" class="wo.Car" init-method="init" destroy-method="destroy">
<property name="brand" value="#{'dazhong'}"></property>
<property name="maxSpeed" value="2"></property>
<property name="price" value="#{T(java.lang.Math).PI*80}"></property>
</bean>
////////////////////////////////////////////////////////////////////
//測試
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-spel.xml");
System.out.println(ctx.getBean("car"));
ctx.close();

創建 Bean 後置處理器

Bean 後置處理器允許在調用初始化方法前後對 Bean 進行額外的處理.
Bean 後置處理器對 IOC 容器裏的所有 Bean 實例逐一處理, 而非單一實例. 其典型應用是: 檢查 Bean 屬性的正確性或根據特定的標準更改 Bean 的屬性.
對Bean 後置處理器而言, 需要實現 在這裏插入圖片描述 接口. 在初始化方法被調用前後, Spring 將把每個 Bean 實例分別傳遞給上述接口的以下兩個方法:
在這裏插入圖片描述
添加 Bean 後置處理器後 Bean 的生命週期
Spring IOC 容器對 Bean 的生命週期進行管理的過程:
通過構造器或工廠方法創建 Bean 實例
爲 Bean 的屬性設置值和對其他 Bean 的引用
將 Bean 實例傳遞給 Bean 後置處理器的 postProcessBeforeInitialization 方法
調用 Bean 的初始化方法
將 Bean 實例傳遞給 Bean 後置處理器的 postProcessAfterInitialization方法
Bean 可以使用了
當容器關閉時, 調用 Bean 的銷燬方法
例子(在上面基礎上):

// 後置處理器
package wo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class Bean implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object arg0, String arg1)
			throws BeansException {
		// TODO Auto-generated method stubs
		System.out.println("before");
		return arg0;
	}

	@Override
	public Object postProcessBeforeInitialization(Object arg0, String arg1)
			throws BeansException {
		// TODO Auto-generated method stub
		System.out.println("after");
		return arg0;
	}

}
///////////////////////////////////////////////////////////
////全局配置文件/spring1/src/beans-spel.xml
<bean id="car" class="wo.Car" init-method="init" destroy-method="destroy">
<property name="brand" value="#{'dazhong'}"></property>
<property name="maxSpeed" value="2"></property>
<property name="price" value="#{T(java.lang.Math).PI*80}"></property>
</bean>
<!--

實現BeanPostProcessor接口,並具體提供Object postProcessBeforeInitialization(object bean, String beanName): init-method 之前被調用Object postProcessAfterInitialization(0bject bean, String beanName): init-method 之後被調用的實現
bean: bean 實例本身
beanName: IOC 容器配置的bean的名字。
返回值:是實際上返回給用戶的那個Bean,注意:可以在以上兩個方法中修改返回的bean,甚至返回一個新的bean
-->
<!--配置bean的後置處理器:不需要配置id, IOC 容器自動識別是一一個BeanPostProcessor -->
<bean class="wo.Bean"></bean>

配置bean

通過調用靜態工廠方法創建 Bean

調用靜態工廠方法創建 Bean是將對象創建的過程封裝到靜態方法中. 當客戶端需要對象時, 只需要簡單地調用靜態方法, 而不同關心創建對象的細節.要聲明通過靜態方法創建的 Bean, 需要在 Bean 的 class 屬性裏指定擁有該工廠的方法的類, 同時在 factory-method 屬性裏指定工廠方法的名稱. 最後, 使用 < constrctor-arg> 元素爲該方法傳遞方法參數.
例子(在上面基礎上):

//靜態工廠
package wo;

import java.util.HashMap;
import java.util.Map;
//*靜態工廠方法:直接調用某一個類的靜態方法就可以返回Bean的實例
public class StaticFactory {
static Map<String,Car> a=new HashMap<String,Car>();
static{
	a.put("I", new Car("I",26,32));
}
//靜態工廠方發
public static Car getCar(String l)
{
	return a.get(l);}
}
////////////////////////////////////////////////////////////////////
////全局配置文件/spring1/src/beans-spel.xml

<!-- 通過靜態工廠方法來配置bean.注意不是配置靜態工廠方法實例,而是配置bean 實例--><!--

class屬性:指向靜態工廠方法的全類名

factory- method:指向靜態工廠方法的名字

constructor-arg:如果工廠 方法需要傳入參數,則使用constructor-arg來配置參數--<bean id ="car1" class="wo.StaticFactory" factory-method="getCar">
<constructor-arg value="I"></constructor-arg>
</bean>
///////////////////////////////////////////////////////////
//測試
System.out.println(ctx.getBean("car1"));

通過調用實例工廠方法創建 Bean

實例工廠方法: 將對象的創建過程封裝到另外一個對象實例的方法裏. 當客戶端需要請求對象時, 只需要簡單的調用該實例方法而不需要關心對象的創建細節.
要聲明通過實例工廠方法創建的 Bean
在 bean 的 factory-bean 屬性裏指定擁有該工廠方法的 Bean
在 factory-method 屬性裏指定該工廠方法的名稱
使用 construtor-arg 元素爲工廠方法傳遞方法參數
例子(在上面基礎上):

//工廠方法
package wo;
import java.util.HashMap;
import java.util.Map;
//實例工廠方法:實例工廠的方法。即現需要創建工廠本身,再調用工廠的實例方法來返回bean的屬性
public class Instance {
	private Map<String,Car> a=new HashMap<String,Car>();
	public Instance(){
		a.put("I", new Car("I",26,32));
	}
	public  Car getCar(String l)
	{
		return a.get(l);}
}
//////////////////////////////////////////////////////////////////////
//全局配置文件/spring1/src/beans-spel.xml
<bean id="factory" class="wo.Instance"></bean>
<bean id="car2" factory-bean="factory" factory-method="getCar">
<constructor-arg value="I"></constructor-arg>
</bean>
///////////////////////////////////////////////////////////
System.out.println(ctx.getBean("car2"));

實現 FactoryBean 接口在 Spring IOC 容器中配置 Bean

Spring 中有兩種類型的 Bean, 一種是普通Bean, 另一種是工廠Bean, 即FactoryBean.
工廠 Bean 跟普通Bean不同, 其返回的對象不是指定類的一個實例, 其返回的是該工廠 Bean 的 getObject 方法所返回的對象
在這裏插入圖片描述
例子(在上面基礎上):

//FactoryBean
package wo;

import org.springframework.beans.factory.FactoryBean;

public class CarBean implements FactoryBean<Car> {
private String brand;

	public void setBrand(String brand) {
	this.brand = brand;
}

	@Override
	public Car getObject() throws Exception {
		// TODO Auto-generated method stub
		return new Car(brand,26,38.5);
	}

	@Override
	public Class<?> getObjectType() {
		// TODO Auto-generated method stub
		return Car.class;
	}

	@Override
	public boolean isSingleton() {
		// TODO Auto-generated method stub
		return true;
	}
}
//////////////////////////////////////////////////////////////////
//全局配置文件/spring1/src/beans-spel.xml
<!--
FactoryBean配置 Bean的實例class:指向FactoryBean的全類名
property: 配置FactoryBean 的屬性
但實際返回的實例確實FactoryBean的getobject()方法返回的實例
-->
<bean id="car3" class="wo.CarBean">
<property name="brand" value="aodi"></property>
</bean>

(通過註解配置)在 classpath 中掃描組件

組件掃描(component scanning): Spring 能夠從 classpath 下自動掃描, 偵測和實例化具有特定註解的組件.
特定組件包括:
@Component: 基本註解, 標識了一個受 Spring 管理的組件
@Respository: 標識持久層組件
@Service: 標識服務層(業務層)組件
@Controller: 標識表現層組件
對於掃描到的組件, Spring 有默認的命名策略: 使用非限定類名, 第一個字母小寫. 也可以在註解中通過 value 屬性值標識組件的名稱
例子:

//各個bean
//1.
package ta;

import org.springframework.stereotype.Component;

@Component
public class TestObject {

}
//2.
package ta.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {
public void execute(){
	System.out.println("controller");
}
}
//3.
package ta.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
public void add(){
	System.out.println("add");
}
}
//4.
package ta.you;

public interface UserRepository {
public void save();
}

package ta.you;

import org.springframework.stereotype.Repository;

@Repository("userRepository")
public class UserAction implements UserRepository {

	@Override
	public void save() {
		// TODO Auto-generated method stub
System.out.println("save");
	}

}
////////////////////////////////////////////////////////////
//全局配置文件/spring1/src/beans-annotation.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- 配置自動掃描的包: 需要加入 aop 對應的 jar 包 -->
<context:component-scan base-package="ta"></context:component-scan>
</beans>
//////////////////////////////////////////////////////////
//測試
package ta;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import ta.controller.UserController;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
		System.out.println(ctx.getBean("testObject"));
		System.out.println(ctx.getBean("userRepository"));
		((UserController) ctx.getBean("userController")).execute();
		ctx.close();
	}

}

當在組件類上使用了特定的註解之後, 還需要在 Spring 的配置文件中聲明 < context:component-scan> :
base-package 屬性指定一個需要掃描的基類包,Spring 容器將會掃描這個基類包裏及其子包中的所有類.
當需要掃描多個包時, 可以使用逗號分隔.
如果僅希望掃描特定的類而非基包下的所有類,可使用 resource-pattern(base-package的子包) 屬性過濾特定的類,示例(在上面的基礎上):

////全局配置文件/spring1/src/beans-annotation.xml
<context:component-scan base-package="ta" resource-pattern="controller/*.class"></context:component-scan>

< context:include-filter> 子節點表示要包含的目標類
< context:exclude-filter> 子節點表示要排除在外的目標類
< context:component-scan> 下可以擁有若干個 < context:include-filter> 和 < context:exclude-filter> 子節點
< context:include-filter> 和 < context:exclude-filter> 子節點支持多種類型的過濾表達式:
在這裏插入圖片描述
示例(在上面的基礎上):

////全局配置文件/spring1/src/beans-annotation.xml
//1.
<context:component-scan base-package="ta">
<!-- context: exclude-filter子節點指定排除哪些指定表達式的組件-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
//2.
<!--context:include-filter子節點指定包含哪些表達式的組件,該子節點需要use-default-filters配合使用-->
<context:component-scan base-package="ta" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
//3
<context:component-scan base-package="ta">
<context:exclude-filter type="assignable" expression="ta.controller.UserController"/>
</context:component-scan>
</beans>
//4.
<context:component-scan base-package="ta" use-default-filters="false">
<context:include-filter type="assignable" expression="ta.controller.UserController"/>
</context:component-scan>

組件裝配

context:component-scan 元素還會自動註冊 AutowiredAnnotationBeanPostProcessor 實例, 該實例可以自動裝配具有 @Autowired 和 @Resource 、@Inject註解的屬性

使用 @Autowired 自動裝配 Bean

@Autowired 註解自動裝配具有兼容類型的單個 Bean屬性
構造器, 普通字段(即使是非 public), 一切具有參數的方法都可以應用@Authwired 註解
默認情況下, 所有使用 @Authwired 註解的屬性都需要被設置. 當 Spring 找不到匹配的 Bean 裝配屬性時, 會拋出異常, 若某一屬性允許不被設置, 可以設置 @Authwired 註解的 required 屬性爲 false
例子(在之前例子基礎上):

////全局配置文件/spring1/src/beans-annotation.xml
<context:component-scan base-package="ta">
</context:component-scan>
///////////////////////////////////////////////////////////////////
//修改bean  
//1.
 import ta.service.UserService;
@Controller
public class UserController {
	@Autowired//也可放到對應的set方法中 當不被設置時
	//@Autowired(required=false)
	private  UserService a;
	
public void execute(){
	System.out.println("controller");
	a.add();
}
}
//2.
import ta.you.UserRepository;
@Service
public class UserService {
	@Autowired
	private UserRepository ab;
public void add(){
	System.out.println("add");
	ab.save();
}
}
///////////////////////////////////////
//測試
((UserController) ctx.getBean("userController")).execute();

默認情況下, 當 IOC 容器裏存在多個類型兼容的 Bean 時, 通過類型的自動裝配將無法工作.(1.屬性名和註解名一致)

@Service
public class UserService {
	@Autowired(required=false)
	private UserRepository userRepository;
public void add(){
	System.out.println("add");
	userRepository.save();
}
}
////////////////////////////////////////////////
@Repository("userRepository")
public class UserAction implements UserRepository {

	@Override
	public void save() {
		// TODO Auto-generated method stub
System.out.println("save");
	}
}

此時可以在 @Qualifier 註解裏提供 Bean 的名稱. Spring 允許對方法的入參標註@Qualifier 已指定注入 Bean 的名稱
例子:(在前面的基礎上)

//修改bean/spring1/src/ta/service/UserService.java
public class UserService {
	@Autowired
	@Qualifier("userRepository")
	private UserRepository ab;
public void add(){
	System.out.println("add");
	ab.save();
}
}
//2./spring1/src/ta/you/UserAction.java
@Repository("userRepository")
public class UserAction implements UserRepository {

	@Override
	public void save() {
		// TODO Auto-generated method stub
System.out.println("save");
	}

}

@Autowired
@Qualifier(“userRepository”)

也可都在在對應的set方法前或者@Autowired在set前,(public void setxx(@Qualifier(“userRepository”)A a))
@Authwired 註解也可以應用在數組類型的屬性上, 此時 Spring 將會把所有匹配的 Bean 進行自動裝配.
@Authwired 註解也可以應用在集合屬性上, 此時 Spring 讀取該集合的類型信息, 然後自動裝配所有與之兼容的 Bean.
@Authwired 註解用在 java.util.Map 上時, 若該 Map 的鍵值爲 String, 那麼 Spring 將自動裝配與之 Map 值類型兼容的 Bean, 此時 Bean 的名稱作爲鍵值

使用 @Resource 或 @Inject 自動裝配 Bean

Spring 還支持 @Resource 和 @Inject 註解,這兩個註解和 @Autowired 註解的功用類似
@Resource 註解要求提供一個 Bean 名稱的屬性,若該屬性爲空,則自動採用標註處的變量或方法名作爲 Bean 的名稱
@Inject 和 @Autowired 註解一樣也是按類型匹配注入的 Bean, 但沒有 reqired 屬性
建議使用 @Autowired 註解

泛型依賴注入

詳細說明
Spring 4.x 中可以爲子類注入子類對應的泛型類型的成員變量的引用
在這裏插入圖片描述

整合多個配置文件

Spring 允許通過 將多個配置文件引入到一個文件中,進行配置文件的集成。這樣在啓動 Spring 容器時,僅需要指定這個合併好的配置文件就可以。
import 元素的 resource 屬性支持 Spring 的標準的路徑資源
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章