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 的標準的路徑資源