(04Day)
今天我们来看看spring提供的表达式语言spel, Bean的生命周期以及前面提到过的通过工厂方法创建Bean。
(1)SpEL表达式语言
SpEL是一个支持运行时查询和操作对象的表达式语言。
SpEL语法类似于EL表达式语言,SpEL使用#{…} 作为界定符。SpEL为bean属性进行动态赋值提供了方便。
通过 SpEL 可以实现:
1)通过 bean 的 id 对 bean 进行引用
2)调用方法以及引用对象中的属性
3)计算表达式的值
4)正则表达式的匹配
下面通过一段代码来说明
<?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.spel.Address">
<!-- 通过spel为bean的属性赋字面值 -->
<property name="city" value="#{'厦门'}"></property>
<property name="street" value="集美区"></property>
</bean>
<bean id = "car" class="com.spel.Car">
<property name="brand" value="奥迪"></property>
<property name="price" value="600000"></property>
<!-- 通过spel的T引用类的静态属性 -->
<property name="tyrePerimeter" value="#{T(java.lang.Math).PI*88}"></property>
</bean>
<bean id="person" class="com.spel.Person">
<property name="name" value="张三"></property>
<property name="city" value="#{address.city}"></property>
<property name="car" value="#{car}" ></property>
<!-- 使用spel中的运算符 -->
<property name="info" value="#{car.price>300000 ? '金领' : '白领'}"></property>
</bean>
</beans>
(2)Bean的生命周期
说到生命周期我们会想到Tomcat容器也就是servlet容器的生命周期,其实Bean的生命周期和servlet的容器的生命周期类似。在IOC容器中对Bean的生命周期管理过程如下:
- 首先通过构方法或者工厂方法创建Bean实例,也就是说首先执行构造方法或者工厂方法。
- 再通过set方法等为Bean的属性赋值和对其他Bean的引用。
- 调用Bean的初始化方法,初始化方法为自己定义的方法,需要要spring配置文件中设置init-method=“你定义的初始化方法”。
- 此时Bean就创建成功。
- 当容器关闭时,调用Bean的销毁方法,和初始化方法一样,销毁方法需要自己定义且需要在spring配置文件中设置destroy-method=“你定义的销毁方法”。
在spring中还提供了Bean的后置管理器。Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理(有点像拦截器)Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理,而非单一实例. 其典型应用是:检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性。
对Bean的后置处理器而言,需要实现BeanPostProcessor接口。添加了Bean的后置处理器后其生命周期变为如下:
- 通过构造方法创建Bean的实例。
- 为Bean的属性赋值和对其他Bean的引用。
- 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法。
- 调用Bean的初始化方法
- 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法。
- Bean创建成功
- 当IOC容器关闭的时候,调用Bean的销毁方法
下面我们来看个例子:
首先创建个Car类。
package com.cycle;
public class Car {
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Car(String brand) {
super();
this.brand = brand;
}
public Car() {
System.out.println("Car is Constructor");
}
public void init2(){
System.out.println("init.....");
}
public void destroy(){
System.out.println("destroy.......");
}
@Override
public String toString() {
return "Car [brand=" + brand + "]";
}
}
创建MyBeanPostProcessor类
package com.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);
return bean;
}
}
创建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">
<bean id = "car" class="com.cycle.Car"
init-method="init2" destroy-method="destroy" >
<property name="brand" value="奔馳"></property>
</bean>
<!-- 类实现BeanPostProcessor接口,并且提供
public Object postProcessAfterInitialization(Object bean, String beanName):init-method之前被调用
public Object postProcessBeforeInitialization(Object bean, String beanName)init-method之后被调用
bean属性:bean 实例本身
beanName属性:IOC容器中配置Bean的名字
返回值:实际上是用来返回给用户的那个bean,注意:可以在上述两个方法中修改返回的bean,甚至返回一个新的bean
-->
<!-- 配置Bean后置处理器 不需要配置id,IOC容器会自动识别一个BeanPostProcessor
注意:所有的bean创建都会经过配置的后置处理器。
-->
<bean class="com.cycle.MyBeanPostProcessor"></bean>
</beans>
最后创建个测试类
package com.cycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main9 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
Car car = (Car)ctx.getBean("car");
System.out.println(car);
//将ctx转换成ClassPathXmlApplicationContext类型才能执行关闭容器的操作。
ctx.close();
}
}
(3)通过工厂方法创建Bean
通过工厂方法创建Bean有三种形式分别为通过静态工厂方法创建Bean、通过实例工厂方法创建Bean和实现FactoryBean接口在IOC容器中配置Bean。下面我们逐一看看这些方法该如何使用。
3-1静态工厂方法
调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中。 当客户端需要对象时,,只需要简单地调用静态方法, 而不同关心创建对象的细节。静态工厂方法和字面上意思一样在spring配置文件中不需要创建静态工厂方法的实例,只需要在配置实例的时候Class路径指向静态工厂方法的全类名,切在Bean的配置元素中添加factory-method=“你要执行的静态方法”。如果静态方法有参数要传入那么可以通过Bean的子元素<constructor-arg value="属性值"></constructor-arg>来逐一设置。
3-2实例工厂方法
实例工厂方法:将对象的创建过程封装到另外一个对象实例的方法里.。当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节。和静态工厂方法不一样的地方在使用实例工厂方法的时候需要先在spring配置文件中创建出一个实例工厂方法的实例。在需要用到实例工厂方法的Bean中factory-bean="你创建的实例工厂方法的名字"来指定使用哪个实例工厂方法,再通过factory-method="你要执行的方法"来调用你要执行的工厂方法。如果该方法有参数和静态工厂方法一样通过constructor-arg来传入参数。
下面我来看个例子如何使用这两个方法来创建Bean。
首先创建一个Car类
package com.factory;
public class Car {
private String brand;
private double price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Car(String brand, double price) {
super();
this.brand = brand;
this.price = price;
}
public Car() {
super();
}
@Override
public String toString() {
return "Car [brand=" + brand + ", price=" + price + "]";
}
}
创建静态工厂
package com.factory;
import java.util.HashMap;
import java.util.Map;
public class StaticCarFactory {
private static Map<String,Car> cars = new HashMap<String,Car>();
static{
cars.put("奥迪", new Car("奥迪",400000));
cars.put("奔驰", new Car("奔驰",600000));
}
public static Car getCar(String name){
return cars.get(name);
}
}
创建实例工厂
package com.factory;
import java.util.HashMap;
import java.util.Map;
/**
* 实例工厂方法;既需要创建工厂本身,再通过工厂的实例方法来返回bean的实例。
* @author User
*
*/
public class InstanceCarFactory {
private Map<String,Car> cars = null;
public InstanceCarFactory(){
cars = new HashMap<String, Car>();
cars.put("奥迪", new Car("奥迪",400000));
cars.put("奔驰", new Car("奔驰",600000));
}
public Car getCar(String brand){
return cars.get(brand);
}
}
创建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">
<!-- 通过静态工厂方法来配置Bean 注意不是配置静态工厂方法的实例,而是配置bean的实例
class:指向静态工厂方法的全类名
factory-method="getCar" 指向静态工厂方法名。
constructor-arg:如果工厂方法需要传入参数,则通过constructor-arg类配置对应的参数
-->
<bean id="car1" class="com.factory.StaticCarFactory"
factory-method="getCar">
<constructor-arg value="奥迪"></constructor-arg>
</bean>
<!-- 配置工厂的实例 -->
<bean id="carFactory" class="com.factory.InstanceCarFactory"></bean>
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="奔驰"></constructor-arg>
</bean>
</beans>
最后做个测试
package com.factory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main10 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");
Car car1 = (Car)ctx.getBean("car1");
System.out.println(car1);
Car car2 = (Car)ctx.getBean("car2");
System.out.println(car2);
}
}
3-3通过实现FactoryBean接口创建Bean
Spring 中有两种类型的 Bean,一种是普通Bean, 另一种是工厂Bean, 即FactoryBean。工厂 Bean 跟普通Bean不同,其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法所返回的对象。话不多说来看个例子就能明白什么是FactoryBean。
创建个Car类
package com.factorybean;
public class Car {
private String brand;
private double price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Car(String brand, double price) {
super();
this.brand = brand;
this.price = price;
}
public Car() {
super();
}
@Override
public String toString() {
return "Car [brand=" + brand + ", price=" + price + "]";
}
}
创建FactoryBean用来配置Car
package com.factorybean;
import org.springframework.beans.factory.FactoryBean;
public class CarFactoryBean implements FactoryBean<Car> {
private String brand;
/* 返回Bean的对象
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public Car getObject() throws Exception {
return new Car(brand,500000);
}
/* 返回bean的类型
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Car.class;
}
/* 是否单例
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
// TODO Auto-generated method stub
return true;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
创建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">
<!-- 通过factoryBean来配置Bean的实例
class:指向Factory的全类名
property配置factoryBean的属性
实际上是返回的实例是FactoryBean的getObject()方法返回的实例。
-->
<bean id="car" class="com.factorybean.CarFactoryBean">
<property name="brand" value="宝马"></property>
</bean>
</beans>
最后写给测试类
package com.factorybean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main11 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factorybean.xml");
Car car = (Car)ctx.getBean("car");
System.out.println(car);
}
}