两种后处理器:
Bean后处理器,实现BeanPostProcessor接口
BeanFactory后处理器,实现BeanFactoryPostProcessor接口,也称为容器后处理器。
BeanPostProcessor
1、简介
Bean后处理器用来对bean的功能进行扩展、增强,对IoC容器中的所有bean都有效。
时机:执行初始化方法之前和之后处理。
生命周期如下:
代码块-->实例化-->数据装配-->初始化之前-->初始化方法-->初始化之后-->就绪-->使用-->销毁方法-->从容器中销毁
从上边bean的声明周期可见,在实例化、数据装配之后操作,因此叫做后处理器。
2、实现步骤
1、定义一个类,实现BeanPostProcessor接口
2、将该后处理器添加到IoC容器中
<?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">
<!--BeanPostProcessor-->
<bean id="springBean" class="ioc22.SpringBean" init-method="init">
</bean>
<bean id="otherBean" class="ioc22.OtherBean" init-method="init">
</bean>
<!--将SpringBeanPostProcessor后处理器加入到IoC中-->
<bean class="ioc22.SpringBeanPostProcessor"/>
</beans>
package ioc22;
public class SpringBean {
void init(){
System.out.println("SpringBean.SpringBean");
}
}
package ioc22;
public class OtherBean {
void init(){
System.out.println("OtherBean.OtherBean");
}
}
package ioc22;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class SpringBeanPostProcessor implements BeanPostProcessor {
//在bean初始化之前执行
@Override
public Object postProcessBeforeInitialization(Object obj, String s) throws BeansException {
System.out.println("SpringBeanPostProcessor.postProcessBeforeInitialization");
return obj;
}
//在bean初始化之后执行
@Override
public Object postProcessAfterInitialization(Object obj, String s) throws BeansException {
System.out.println("SpringBeanPostProcessor.postProcessAfterInitialization");
return obj;
}
}
package ioc22;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String [] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("ioc22/spring.xml");
}
}
输出测试结果:
SpringBeanPostProcessor.postProcessBeforeInitialization
SpringBean.SpringBean
SpringBeanPostProcessor.postProcessAfterInitialization
SpringBeanPostProcessor.postProcessBeforeInitialization
OtherBean.OtherBean
SpringBeanPostProcessor.postProcessAfterInitialization
3、实际操练
需求:读取properties文件,为所有的bean注入值。
info.properties
name=tom
pwd=123
email=tom@qq.com
phone=1888888
address=china
spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--练习:BeanPostProcessor-->
<bean id="springBean" class="ioc23.SpringBean">
<property name="username" value="${name}"/>
<property name="password" value="${pwd}"/>
<property name="email" value="${email}"/>
<property name="phone" value="${phone}"/>
</bean>
<bean id="otherBean" class="ioc23.OtherBean">
<property name="password" value="${pwd}"/>
<property name="address" value="${address}"/>
</bean>
<bean class="ioc23.PropertiesPostProcessor">
<property name="resource" value="classpath:ioc23/info.properties"/>
</bean>
</beans>
package ioc23;
public class SpringBean {
private String username;
private String password;
private String email;
private String phone;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "SpringBean{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
package ioc23;
public class OtherBean {
private String password;
private String address;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "OtherBean{" +
"password='" + password + '\'' +
", address='" + address + '\'' +
'}';
}
}
package ioc23;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PropertiesPostProcessor implements BeanPostProcessor {
private Resource resource;
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
@Override
public Object postProcessBeforeInitialization(Object obj, String s) throws BeansException {
return obj;//原样返回
}
//BeanPostProcessor是对IoC容器中的所有bean都有效
//无法得到bean的属性,只能通过反射获取
@Override
public Object postProcessAfterInitialization(Object obj, String s) throws BeansException {
//拿到对应的class对象
Class<?> clazz = obj.getClass();
//获取所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields){
//获取字段类型 类型可理解为类对象是啥
Class<?> fieldType = field.getType();
//只对字符串进行处理
if (fieldType == String.class){
//拿到属性的名字
String name = field.getName();
try {
//获取属性的值
field.setAccessible(true);
String s1 = field.get(obj).toString(); // ${name}
String value = reValue(s1);
field.set(obj,value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}else {
throw new RuntimeException("不支持的注入类型!");
}
}
return obj;
}
public String reValue(String value){
String regex = "\\$\\{(.*)\\}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(value);
if (matcher.matches()){
//只有一个组${},所以group(1)就是key
String key = matcher.group(1); //拿到属性名称
return getProperty(key);
}
return value;
}
private String getProperty(String key){
Properties p = new Properties();
try {
p.load(resource.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
//properties中是否有相应的key
if (p.containsKey(key)){
return p.getProperty(key);
}else {
throw new RuntimeException("未找到对应的key");
}
}
}
package ioc23;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String [] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("ioc23/spring.xml");
System.out.println(ac.getBean("springBean"));
System.out.println(ac.getBean("otherBean"));
}
}
测试输出
SpringBean{username='tom', password='123', email='tom@qq.com', phone='1888888'}
OtherBean{password='123', address='china'}
BeanFactoryPostProcessor
1、简介
容器后处理器,在bean创建之前修改bean 的定义属性( 读取bean中的元数据,根据需要进行修改),对IoC容器中所有bean有效。
生命周期如下:
BeanFactoryPostProcessor-->代码块-->实例化-->数据装配-->初始化之前-->初始化方法-->初始化之后-->就绪-->使用-->销毁方法-->从容器中销毁
注意:BeanFactoryPostProcessor中干的活是注册相应的属性编辑器,在后边装配的时候,spring框架会对匹配的属性通过这里注册的属性编辑器处理。不要以为是修改了属性点值,看看上边的声明周期就明白了。
2、实现步骤
1、定义一个类,实现BeanFactoryPostProcessor接口。
2、将该bean添加到IoC容器中。
3、定义属性编辑器PropertyEditor(转换器),实现PropertyEditor接口或继承PropertyEditorSupport父类。
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">
<!--最开始的方法:springBean中注入address-->
<!--<bean id="address" class="ioc24.Address">
<property name="city" value="nanjing"/>
<property name="province" value="jiangsu"/>
</bean>
<bean id="springBean" class="ioc24.SpringBean">
<property name="address" ref="address"/>
</bean>-->
<!--BeanFactoryPostProcessor-->
<bean id="springBean" class="ioc24.SpringBean">
<property name="address" value="[临沂-山东]"/>
</bean>
<!--将SpringBeanFacotoryPostProcessor添加到IoC容器中-->
<bean class="ioc24.SpringBeanFacotoryPostProcessor"/>
</beans>
package ioc24;
public class Address {
private String city;
private String province;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", province='" + province + '\'' +
'}';
}
}
package ioc24;
public class SpringBean {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "SpringBean{" +
"address=" + address +
'}';
}
}
package ioc24.editor;
import ioc24.Address;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorSupport;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* 定义的属性编辑器
*/
//父类中方法较多,这里只需要重写getAsText setAsText
public class AddressEditor extends PropertyEditorSupport {
//用来将Address转换为String
@Override
public String getAsText() {
return super.getAsText();
}
//将String转换为Address
@Override
public void setAsText(String text) throws IllegalArgumentException {
Pattern pattern = Pattern.compile("\\[(.*)-(.*)\\]");
Matcher matcher = pattern.matcher(text);
if (matcher.matches()){
String city = matcher.group(1);
String provience = matcher.group(2);
Address address = new Address();
address.setCity(city);
address.setProvince(provience);
//调用setValue设置值
//说白了就是把修改属性之后的对象,交给容器
setValue(address);
}
}
}
package ioc24;
import ioc24.editor.AddressEditor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
*定义容器后处理器
* 作用:通过注册属性编辑器,完成属性的转换和装配
*
*/
public class SpringBeanFacotoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("SpringBeanFacotoryPostProcessor.postProcessBeanFactory");
//向容器中注册属性编辑器
//第1个参数:要转换的属性类型 第2个参数:要使用的属性编辑器类型
//该属性编辑器在装配的时候,如果遇到Address类型的数据要装配,那就把value值经过AddressEditor进行转换,将String转换成Address类型,完成装配
configurableListableBeanFactory.registerCustomEditor(Address.class, AddressEditor.class);
}
}
package ioc24;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String [] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("ioc24/spring.xml");
System.out.println(ac.getBean("springBean"));
}
}