一、SpEL简述
1.1 基本定义
SpEL是一个支持运行时查询和操作对象图的强大表达式语言,语法类似于EL所有在#{}
中的的字符都被认定为SpEL.
1.2 SpEL的作用
- 通过id引用Bean
- 调用方法引用对象中的属性
- 计算表达式的值
- 正则表达式的匹配
二、SpEL使用
2.1 字面量
即直接根据字面值进行赋值,示例如下:
整数: <property name="age" value="#{25}"/>
整数: <property name="money" value="#{1e7}"/>
布尔: <property name="isValid" value="#{true}"/>
字符串:需要使用单引号或者双引号作为字符串的定界符
<property name="name" value="#{'张三'}"/>
2.2 引用Bean,属性,及方法
- 引用其他对象
<property name="prefix" value="#{prefixGenerator}"/>
- 引用其他对象的属性
<property name="suffix" value="#{prefixGenerator.suffix}"/>
- 同方法一样,可以进行链式操作。
- 逻辑运算符:
and or not !
- 算术运算符
+, -, *, /, %, ^,+还可直接连接字符串
- 算比较运算符:
<, >, ==, <=, >=, lt, gt, eq, le, ge
- if-else运算符:
?(temary)
用法同三目运算符 - 正则表达式
- 调用静态方法或属性,用法:T(java.lang.Math).abs(-20)/>
<!-- SpEL 基本示例 -->
<bean id="personSPEL" class="com.spring.learn.bean_relation.Person" depends-on="personAuto">
<property name="name" value="#{personAuto.getName()}"/>
<property name="car" value="#{personAuto.car}"/>
<property name="age" value="#{T(Math).abs(-18)*2}"/>
<property name="address" value="#{address2}"/>
</bean>
<!-- SpEL 示例 -->
<bean id="personSPEL1" class="com.spring.learn.bean_relation.Person" depends-on="personAuto">
<property name="name" value="#{personAuto.getName()}"/>
<property name="age" value="#{T(Math).abs(-18)*2}"/>
<property name="car" value="#{car.years>5?car:null}"/>
<property name="address" value="#{address2}"/>
<!-- 覆写属性 -->
</bean>
三、Bean的生命周期
3.1 Bean的生命周期过程
- 通过构造器或工厂方法创建 Bean 实例
- Bean 的属性设置值和对其他 Bean 的引用
- 调用 Bean 的初始化方法,对属性赋值。
- Bean 初始化完成,对外可用
- 当容器关闭时, 调用 Bean 的销毁方法
3.2 Bean初始化和销毁设置
在Bean声明里设置init-method
和destroy-method
属性,可以为Bean指定初始化和销毁方法,初始化前调用一次初始化方法,容器关闭前调用销毁方法,示例修改上述Bean personSPEL1
,为其添加初始化和销毁方法,需要显式调用ClassPathXmlApplicationContext的close方法
,此处直接使用子类即可,不需要复制给其父类ApplicationContext
//需要显式调用ClassPathXmlApplicationContext的关闭方法
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean-relation.xml");
Person spel = applicationContext.getBean("personSPEL1", Person.class);
System.out.println(spel);
// 关闭IOC容器
applicationContext.close();
// 输出 在初始化前调用了一次init,关闭容器前调用一次destroy
// 调用顺序先调用Bean的属性设置或者构造器方法,然后调用init
Bean的修改配置如下:
<!-- 为该Bean添加init和destroy方法 -->
<bean id="personSPEL1" class="com.spring.learn.bean_relation.Person" depends-on="personAuto" init-method="init" destroy-method="destroy">
<property name="name" value="#{personAuto.getName()}"/>
<property name="age" value="#{T(Math).abs(-18)*2}"/>
<property name="car" value="#{car.years>5?car:null}"/>
<property name="address" value="#{address2}"/>
<!-- 覆写属性 -->
</bean>
3.3 Bean的后置处理器
- Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.
- Bean 后置处理器对IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例. 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.
- 可以在后期处理器中修改Bean的属性,甚至返回一个新的Bean
- 需要实现
BeanPostProcessor
接口,Spring将每个Bean实例分别传递给上述接口的以下两个方法:
- 添加一个类实现对应的方法即可:
/**
* Created with IntelliJ IDEA.
* User: gxm
* Date: 2020/4/10
* Time: 11:59
* To change this template use File | Settings | File Templates.
* Description:
**/
package com.spring.learn.bean_relation;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 完成对Bean的检查
System.out.println("postProcessBeforeInitialization: "+bean+","+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization: "+bean+","+beanName);
return bean;
}
}
Bean的配置:
<!-- 配置Bean的后置处理器 -->
<bean class="com.spring.learn.bean_relation.MyBeanPostProcessor"/>
输出结果:
postProcessBeforeInitialization: com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2tdca1a9r9jr4i105geao|402120, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2tdca1a9r9jr4i105geao|402120, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://192.168.0.240:3306/MeasureAgent, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ],dataSourceDB
postProcessAfterInitialization: com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2tdca1a9r9jr4i105geao|402120, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2tdca1a9r9jr4i105geao|402120, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://192.168.0.240:3306/MeasureAgent, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ],dataSourceDB
postProcessBeforeInitialization: Person{name='auto', age=36, car=Car{type='红旗', yeas=1}, address=Address{city='重庆', street='南山街道'}},personSPEL
postProcessAfterInitialization: Person{name='auto', age=36, car=Car{type='红旗', yeas=1}, address=Address{city='重庆', street='南山街道'}},personSPEL
postProcessBeforeInitialization: Person{name='auto', age=36, car=null, address=Address{city='重庆', street='南山街道'}},personSPEL1
init...
postProcessAfterInitialization: Person{name='auto', age=36, car=null, address=Address{city='重庆', street='南山街道'}},personSPEL1
Person{name='auto', age=0, car=Car{type='红旗', yeas=1}, address=null}
添加Bean后置处理器后的Bean生命周期
- 通过构造器或工厂方法创建 Bean 实例
- 为 Bean 的属性设置值和对其他 Bean 的引用
- 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
- 调用 Bean 的初始化方法
- 将 Bean 实例传递给 Bean 后置处理器的
- postProcessAfterInitialization方法
- Bean 可以使用了
- 当容器关闭时, 调用 Bean 的销毁方法
四、工厂模式
测试类有如下方法:
// 静态方法
public static Person factory(String name,int age){
return new Person(name,age,null);
}
// 普通方法,返回本类
public Person createFactory(String name,int age){
return new Person(name,age,null);
}
4.1非静态方法的工厂模式
指定class和factory-method及factory-bean,使需要一个已有的Bean,然后通过该实例调用工程方法值作为另一个Bean,使用factory-bean指定该该实例,然后用factory-method指向返回该类的方法。
<!-- 工厂模式1 : 非静态方法:必须实例化工厂类(factory-bean)后才能调用工厂方法 -->
<bean id="personFactory2" class="com.spring.learn.bean_relation.Person"/>
<bean id="person22" factory-bean="personFactory2" factory-method="createFactory">
<constructor-arg index="0" value="gxm"/>
<constructor-arg index="1" value="12"/>
</bean>
4.2静态方法的工厂模式
指定class和factory-method即可
<!-- 工厂模式2 : 静态方法:直接指定类和factory-method即可 -->
<bean id="personFactory1" class="com.spring.learn.bean_relation.Person" factory-method="factory">
<constructor-arg index="0" value="gxm"/>
<constructor-arg index="1" value="15"/>
</bean>