Spring4.0 学习笔记(三)

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]
    }

猜你喜欢

转载自blog.csdn.net/baidu_37181928/article/details/80030149