Spring表达式语言 SpEL
- Spring 表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言。
- 语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL
- SpEL 为 bean 的属性进行动态赋值提供了便利
- 通过 SpEL 可以实现:
- 通过 bean 的 id 对 bean 进行引用
- 调用方法以及引用对象中的属性
- 计算表达式的值
- 正则表达式的匹配
- 操作字面值 整数、小数、科学计数、静态方法
- 具体代码
<?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">
<bean id="address" class="com.anqi.spel.Address">
<!-- 使用 spel 为属性赋一个字面值 -->
<property name="city" value="#{'Xinzhou'}"></property>
<property name="street" value="Wutaishanlu"></property>
</bean>
<bean id="car" class="com.anqi.spel.Car">
<property name="brand" value="Audi"></property>
<property name="price" value="500000"></property>
<!-- 使用 SpEL 引用的静态属性 -->
<property name="tyrePrimeters" value="#{T(java.lang.Math).PI * 80}">
</property>
</bean>
<bean id="person" class="com.anqi.spel.Person">
<!-- 使用 SpEL 来引用其他的 bean 相当于 value-ref="car" -->
<property name="car" value="#{car}"></property>
<!-- 使用 SpEL 来引用其他 Bean 的属性 -->
<property name="city" value="#{address.city}"></property>
<!-- 在 SpEL 中使用运算符 -->
<property name="info" value="#{car.price > 300000 ? '金领':'白领'}"></property>
<property name="name" value="Tom"></property>
</bean>
</beans>
public class Person {
private String name;
private Car car;
//引用 address 的 bean 的 city 属性
private String city;
//根据 car 的 price 来确定 info : 若 price >300000 则为金领
//否则为白领
private String info;
//..setter getter toString
}
public class Address {
private String city;
private String street;
//..setter getter toString
}
public class Car {
private String brand;
private double price;
private double tyrePrimeters;
//..setter getter toString
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-spel.xml");
Address address = (Address) ctx.getBean("address");
System.out.println(address);
//Address [city=Xinzhou, street=Wutaishanlu]
Car car = (Car) ctx.getBean("car");
System.out.println(car);
//Car [brand=Audi, price=500000.0, tyrePrimeters=251.32741228718345]
Person person = (Person) ctx.getBean("person");
System.out.println(person);
//Person [name=Tom, car=Car [brand=Audi, price=500000.0, tyrePrimeters=251.32741228718345],
//city=Xinzhou, info=金领]
}
}
IOC 容器中 Bean 的生命周期
- Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务.
- Spring IOC 容器对 Bean 的生命周期进行管理的过程:
- 通过构造器或工厂方法创建 Bean 实例
- 为 Bean 的属性设置值和对其他 Bean 的引用
- 调用 Bean 的初始化方法
- Bean 可以使用了
- 当容器关闭时, 调用 Bean 的销毁方法
- 在 Bean 的声明里设置 init-method 和 destroy-method 属性, 为 Bean 指定初始化和销毁方法.
创建 Bean 后置处理器
- Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.
- Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例. 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.
对Bean 后置处理器而言, 需要实现 BaseBeanProcessor 接口. 在初始化方法被调用前后, Spring 将把每个Bean 实例分别传递给上述接口的以下两个方法
public Object postProcessAfterInitialization(Object bean, String beanName)
public Object postProcessBeforeInitialization(Object bean, String beanName)添加 Bean 后置处理器后 Bean 的生命周期
Spring IOC 容器对 Bean 的生命周期进行管理的过程:- 通过构造器或工厂方法创建 Bean 实例
- 为 Bean 的属性设置值和对其他 Bean 的引用
- 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
- 调用 Bean 的初始化方法
- 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
- Bean 可以使用了
- 当容器关闭时, 调用 Bean 的销毁方法
- 代码
<?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">
<bean id="car" class="com.anqi.cycle.Car"
init-method="myInit" destroy-method="myDestroy" >
<property name="brand" value="fute"></property>
</bean>
<!--
实现 BeanPostProcesser 接口, 并具体提供
Object postProcessAfterInitialization(Object bean, String beanName)
init-method 之前调用
Object postProcessBeforeInitialization(Object bean, String beanName)
init-method 之后调用
bean : bean 实例本身
beanName : IOC 容器配置的 bean 本身的名字
返回值 : 是实际上返回给用户的那个 Bean, 注意:可以在以上两个方法中修改返回的 bean,
甚至返回一个新的 bean
-->
<!-- 配置 bean 的后置处理器 -->
<bean class="com.anqi.cycle.MyBeanPostProcessor"></bean>
</beans>
public class Car {
private String brand;
public Car() {
System.out.println("Car Construct..");
}
public void myInit() {
System.out.println("init...");
}
public void myDestroy() {
System.out.println("destroy...");
}
//setter、getter、toString
}
package com.anqi.cycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization.."+bean+"-"+beanName);
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization.."+bean+"-"+beanName);
Car car = (Car)bean;
car.setBrand("Chngan");
return car;
}
}
public class Main {
public static void main(String[] args) {
//因为 ApplicationContext 没有 close 方法,所以使用它的实现类
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("bean-cycle.xml");
Car car = (Car) ctx.getBean("car");
System.out.println(car);
ctx.close();
}
}
通过工厂方法来配置 bean
静态工厂方法
- 调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法,而不同关心创建对象的细节.
- 要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 constrctor-arg 元素为该方法传递方法参数
- 代码
<!--
通过静态工厂方法来配置 bean, 【注意】不是配置静态工厂方法实例, 而是配置 bean 实例
-->
<!--
class 属性 : 指向静态工厂方法的全类名
factory-method : 指向静态工厂方法的名字
construct-arg : 如果工厂方法需要传入参数, 则使用 construct-arg 来配置参数
-->
<bean id="car1" class="com.anqi.factory.StaticCarFactory"
factory-method="getCar">
<constructor-arg value="audi"></constructor-arg>
</bean>
/**
* 静态工厂方法 : 直接调用某一个类的静态方法就可以返回 Bean 的实例
*/
public class StaticCarFactory {
private static Map<String, Car> cars =
new HashMap<String, Car>();
static {
cars.put("audi", new Car("audi",29999));
cars.put("fute", new Car("fute",39999));
}
//静态工厂方法
public static Car getCar(String brand) {
return cars.get(brand);
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factory.xml");
Car car1 = (Car) ctx.getBean("car1");
System.out.println(car1);
//Car [brand=audi, price=29999.0]
}
}
实例工厂方法
<!-- 配置工厂的实例 -->
<bean id="carFactory" class="com.anqi.factory.InstanceCarFactory"></bean>
<!-- 通过实例工厂方法来配置 bean -->
<!--
factory-bean 属性 : 指向实例工厂的全类名
factory-method : 指向静态工厂方法的名字
construct-arg : 如果工厂方法需要传入参数, 则使用 construct-arg 来配置对象
-->
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="ford"></constructor-arg>
</bean>
/**
* 实例工厂方法 : 实例工厂的方法, 即需要创建工厂本身,
* 再 调用工厂的实例方法来返回 bean 的实例
*/
public class InstanceCarFactory {
private Map<String, Car> cars = null;
public InstanceCarFactory() {
cars = new HashMap<String, Car>();
cars.put("audi", new Car("audi", 300000));
cars.put("ford", new Car("ford", 400000));
}
public Car getCar(String brand) {
return cars.get(brand);
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factory.xml");
Car car2 = (Car) ctx.getBean("car2");
System.out.println(car2);
//Car [brand=ford, price=400000.0]
}
}
实现 FactoryBean 接口在 Spring IOC 容器中配置 Bean
- Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean.
- 工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法所返回的对象
<!--
在 bean 里面要引用其他 bean Factory 是最好的方法
通过 FactoryBean 来配置 Bean 的实例
class : 指向 FactoryBean 的全类名
property : 配置 Factory 的属性
但实际返回的实例却是 FactoryBean 的 getObject() 方法
-->
<bean id="car" class="com.anqi.factorybean.CarFactroyBean">
<property name="brand" value="BMW"></property>
</bean>
import org.springframework.beans.factory.FactoryBean;
/**
* 自定义的 FactoryBean 需要实现 FactoryBean 接口
*/
public class CarFactroyBean implements FactoryBean{
private String brand;
public void setBrand(String brand) {
this.brand = brand;
}
/**
* 返回 bean 的对象
*/
public Object getObject() throws Exception {
return new Car(brand,50000);
}
/**
* 返回 bean 的类型
*/
public Class getObjectType() {
return Car.class;
}
/**
* 是否是单例的
*/
public boolean isSingleton() {
return true;
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("bean-factorybean.xml");
Car car = (Car) ctx.getBean("car");
System.out.println(car);
//Car [brand=BMW, price=50000.0]
}