Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
⚫ BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
⚫ BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
BeanFactoryPostProcessor只执行一次,是在所有BeanDefinition填充到BeanDefinitionMap时;
BeanPostProcessor会执行多次,每个bean实例化之后都会执行;
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
BeanFactoryPostProcessor 定义如下:
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
编写BeanFactoryPostProcessor实现类:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{
System.out.println("BeanDefinitionMap填充完毕后回调该方法,MyBeanFactoryPostProcessor执行了...");
}
}
配置BeanFactoryPostProcessor实现类:
<bean class="com.itheima.processor.MyBeanFactoryPostProcessor"/>
postProcessBeanFactory 参数本质就是 DefaultListableBeanFactory(DefaultListableBeanFactory是ConfigurableListableBeanFactory的实现类),拿到BeanFactory的引用,自然就可以对beanDefinitionMap中的BeanDefinition进行操作了 ,例如对UserDaoImpl的BeanDefinition进行修改操作
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// userDao初始的bean的定义 ,类型为UserDaoImpl
BeanDefinition userDaoBD = beanFactory.getBeanDefinition("userDao");//获得UserDao定义对象
// 修改userDao的类型为UserDaoImpl2
userDaoBD.setBeanClassName("com.itheima.dao.impl.UserDaoImpl2"); //修改class
// userDaoBD.setInitMethodName(methodName); //修改初始化方法
// userDaoBD.setLazyInit(true); //修改是否懒加载
// ... 省略其他的设置方式 ...
}
}
上面已经对指定的BeanDefinition进行了修改操作,下面对BeanDefiition进行注册操作
// 自定义类MyBeanFactoryPostProcessor还需要注册到容器中
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// 强转成子类DefaultListableBeanFactory
if(configurableListableBeanFactory instanceof DefaultListableBeanFactory){
// DefaultListableBeanFactory有注册BeanDefinition的方法registerBeanDefinition,所以需要将configurableListableBeanFactory强转
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
// 使用RootBeanDefinition对BeanDefinition进行操作
BeanDefinition beanDefinition = new RootBeanDefinition();
// 注册一个UserDaoImpl2类型的bean
beanDefinition.setBeanClassName("com.itheima.dao.UserDaoImpl2");
// 进行注册操作
beanFactory.registerBeanDefinition("userDao2", beanDefinition);
}
}
}
Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作
// 自定义类MyBeanFactoryPostProcessor2还需要注册到容器中
public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor {
// postProcessBeanFactory是顶级接口BeanFactoryPostProcessor中的方法,所以这里需要继承下来
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition = new RootBeanDefinition();
// 注册一个UserDaoImpl2类型的bean
beanDefinition.setBeanClassName("com.itheima.dao.UserDaoImpl2");
// 进行注册操作
beanDefinitionRegistry.registerBeanDefinition("userDao2", beanDefinition);
}
}
BeanFactoryPostProcessor 与 BeanDefinitionRegistryPostProcessor的执行顺序是,先调用子类接口BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry、postProcessBeanFactory方法,再调用父类接口BeanFactoryPostProcessor中的postProcessBeanFactory方法
BeanFactoryPostProcessor 在SpringBean的实例化过程中的体现:
要求如下:
⚫ 自定义@MyComponent注解,使用在类上;
⚫ 使用包扫描器工具BaseClassScanUtils完成指定包的类扫描;
⚫ 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。
自定义@MyComponent注解,使用在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
// 显示的指定Bean的beanName
String value() default "";
}
在类上使用@MyComponent
@MyComponent("otherBean")
public class OtherBean {
}
自定义BeanFactoryPostProcessor完成注解解析
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 通过扫描工具去扫描指定包及其子包下的所有类,收集使用@Mycomponent的注解的类
Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.itheima");
// 遍历Map,组装BeanDefinition进行注册
myComponentAnnotationMap.forEach((beanName,clazz)->{
// 获得beanClassName
String beanClassName = clazz.getName();//com.itheima.beans.OtherBean
// 创建BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
// 注册
beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
将自定义类MyComponentBeanFactoryPostProcessor注册到容器中
<bean class="com.itheima.processor.MyComponentBeanFactoryPostProcessor"/>
包扫描器工具BaseClassScanUtils
package com.itheima.utils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BaseClassScanUtils {
// 设置资源规则
private static final String RESOURCE_PATTERN = "/**/*.class";
public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {
// 创建容器存储使用了指定注解的Bean字节码对象
Map<String, Class> annotationClassMap = new HashMap<String, Class>();
// spring工具类,可以获取指定路径下的全部类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
// MetadataReader 的工厂类
MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
// 用于读取类信息
MetadataReader reader = refractory.getMetadataReader(resource);
// 扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
// 判断是否属于指定的注解类型
if(clazz.isAnnotationPresent(MyComponent.class)){
// 获得注解对象
MyComponent annotation = clazz.getAnnotation(MyComponent.class);
// 获得value属性值
String beanName = annotation.value();
// 判断是否为""
if(beanName != null && !beanName.equals("")){
// 存储到Map中去
annotationClassMap.put(beanName,clazz);
continue;
}
//如果没有为"",那就把当前类的类名作为beanName
annotationClassMap.put(clazz.getSimpleName(), clazz);
}
}
} catch (Exception exception) {
}
return annotationClassMap;
}
// 测试
public static void main(String[] args) {
Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.itheima");
System.out.println(stringClassMap);
}
}
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。
BeanPostProcessor的接口定义如下:
public interface BeanPostProcessor {
@Nullable
// 在属性注入完毕,init初始化方法执行之前被回调
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
// 在初始化方法执行之后,被添加到单例池singletonObjects之前被回调
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
自定义MyBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
/* 参数: bean是当前被实例化的Bean,beanName是当前Bean实例在容器中的名称
返回值:当前Bean实例对象 */
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 在这里还可以对bean进行设置值,这里只是演示作用,实际情况灵活变通
if(bean instanceof UserDaoImpl) {
UserDaoImpl userDaoImpl = (UserDaoImpl) bean;
userDaoImpl.setUserName("zhangsan");
}
System.out.println("BeanPostProcessor的before方法...");
return bean;
}
/* 参数: bean是当前被实例化的Bean,beanName是当前Bean实例在容器中的名称
返回值:当前Bean实例对象 */
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor的after方法...");
return bean;
}
}
配置MyBeanPostProcessor
<bean class="com.itheima.processors.MyBeanPostProcessor">bean>
执行顺序:
UserDaoImpl创建了…
UserDaoImpl属性填充…
BeanPostProcessor的before方法…
InitializingBean的afterpropertiesset方法
UserDaoImpl自定义的初始化init方法执行…
BeanPostProcessor的after方法…
要求如下:
⚫ Bean的方法执行之前控制台打印当前时间;
⚫ Bean的方法执行之后控制台打印当前时间。
分析:
⚫ 对方法进行增强主要就是代理设计模式和包装设计模式;
⚫ 由于Bean方法不确定,所以使用动态代理在运行期间执行增强操作;
⚫ 在Bean实例创建完毕后,进入到单例池之前,使用Proxy代替真实的目标Bean
编写BeanPostProcessor,增强逻辑编写在 after方法中
public class TimeLogBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 对Bean进行动态代理,返回的是Proxy代理对象
Object proxyBean = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(Object proxy, Method method, Object[] args) -> {
long start = System.currentTimeMillis();
System.out.println("开始时间:" + new Date(start));
// 执行目标方法,这里演示的是所有bean的方法进行增强,实际情况灵活变通
Object result = method.invoke(bean, args);
long end = System.currentTimeMillis();
System.out.println("结束时间:" + new Date(end));
return result;
});
// 返回代理对象,存入单例池
return proxyBean;
}
}
<bean class="com.itheima.processor.TimeLogBeanPostProcessor">bean>
BeanPostProcessor 在 SpringBean的实例化过程中的体现