Java Spring Bean工厂后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
- BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
- BeanPostProcessor: Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
1. BeanFactoryPostProcessor
1.1 快速入门
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
实例化流程
MyBeanFactoryPostProcessor.java
package processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("beanDefinitionMap填充完毕后回调该方法");
}
}
beans.xml
<bean class="processor.MyBeanFactoryPostProcessor"/>
主函数断点调试
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}
执行结果
beanDefinitionMap填充完毕后回调该方法
1.2 强制修改BeanDefinition
其他设置基于上文
MyBeanFactoryPostProcessor.java
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("beanDefinitionMap填充完毕后回调该方法");
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService");
beanDefinition.setBeanClassName("org.example.UserDaoImpl");
}
}
主函数调用
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService = applicationContext.getBean("userService");
System.out.println(userService);
}
输出结果
beanDefinitionMap填充完毕后回调该方法
org.example.UserDaoImpl@3c87521
1.3 动态注入BeanDefinition
创建一个新的接口PersonDao
public interface PersonDao {
}
创建一个新的类PersonDaoImpl,并且继承PersonDao接口
public class PersonDaoImpl implements PersonDao{
}
MyBeanFactoryPostProcessor.java
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("beanDefinitionMap填充完毕后回调该方法");
//注册BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(PersonDaoImpl.class.getName());
//强转为DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
defaultListableBeanFactory.registerBeanDefinition("personDao",beanDefinition);
}
}
beans.xml
<bean class="processor.MyBeanFactoryPostProcessor"/>
主函数调用
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
PersonDao personDao = applicationContext.getBean(PersonDao.class);
System.out.println(personDao);
}
执行结果
beanDefinitionMap填充完毕后回调该方法
org.example.PersonDaoImpl@780cb77
_
Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作
直接继承BeanDefinitionRegistryPostProcessor接口的方式简化代码
MyBeanDefinitionRegistryPostProcessor.java
package processor;
import org.example.PersonDaoImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
System.out.println("beanDefinitionMap填充完毕后回调该方法");
//注册BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(PersonDaoImpl.class.getName());
beanDefinitionRegistry.registerBeanDefinition("personDao",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
beans.xml
<bean class="processor.MyBeanDefinitionRegistryPostProcessor"/>
主函数调用
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
PersonDao personDao = applicationContext.getBean(PersonDao.class);
System.out.println(personDao);
}
执行结果
beanDefinitionMap填充完毕后回调该方法
org.example.PersonDaoImpl@50a7bc6e
1.4使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
要求如下:
- 自定义@MyComponent注解,使用在类上;
- 使用资料中提供好的包扫描器工具BaseClassScanUtils 完成指定包的类扫描
- 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。
黑马程序员提供的BaseClassScanUtils.java
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);
}
}
由于我使用的是jdk19,所以使用上述文件会提示版本错误,所以在上面的基础上进行修改
package utils;
import anno.MyComponent;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;
import java.util.HashMap;
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);
for (Resource resource : resources) {
Class<?> clazz = Class.forName(resource.getFile().toString().split("classes\\\\")[1].replaceAll("\\\\",".").split(".class")[0]);
//判断是否属于指定的注解类型
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) {
System.out.println(exception);
}
return annotationClassMap;
}
public static void main(String[] args) {
Map<String, Class> stringClassMap = scanMyComponentAnnotation("beans");
System.out.println(stringClassMap);
}
}
创建注解MyComponent
package anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
String value();
}
创建类并且添加MyComponent注解,类位于beans目录下
package beans;
import anno.MyComponent;
@MyComponent("otherBean")
public class OtherBean {
}
创建新的后工厂处理器类,并且继承BeanDefinitionRegistryPostProcessor接口
package processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import utils.BaseClassScanUtils;
import java.util.Map;
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 使用扫描工具去扫描指定包以及子包下的所有类,收集使用@Mycomponent的注解的类
Map<String, Class> myComponentAnnotation = BaseClassScanUtils.scanMyComponentAnnotation("beans");
// 遍历Map,组装BeanDefinition进行注册
myComponentAnnotation.forEach((beanName,clazz)->{
// 获取类名
String beanClassName = clazz.getName();
// 创建BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
// 注册BeanDefinition
beanDefinitionRegistry.registerBeanDefinition(beanName ,beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
在beans.xml中添加后工厂处理器
<bean class="processor.MyComponentBeanFactoryPostProcessor"/>
主函数调用
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
OtherBean otherBean = applicationContext.getBean(OtherBean.class);
System.out.println(otherBean);
}
执行结果
beans.OtherBean@2133814f
2. BeanPostProcessor
2.1 快速入门
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如: 属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。
BeanPostProcessor类
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
UserDao
package org.example;
public interface UserDao {
}
UserDaoImpl
package org.example;
public class UserDaoImpl implements UserDao {
String name;
public UserDaoImpl() {
System.out.println("UserDaoImpl初始化");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("设置name的值为" + name);
this.name = name;
}
}
创建MyBeanPostProcessor并且继承BeanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before-" + beanName + ":" + bean.toString());
if (bean instanceof UserDaoImpl){
UserDaoImpl userDao = (UserDaoImpl) bean;
userDao.setName("hhhhh");
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("after-" + beanName + ":" + bean.toString());
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
beans.xml
<bean class="processor.MyBeanPostProcessor"/>
<bean class="org.example.UserDaoImpl" id="userDao"/>
主函数执行
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserDaoImpl userDao = (UserDaoImpl) applicationContext.getBean("userDao");
System.out.println(userDao.getName());
}
执行结果
UserDaoImpl初始化
before-userDao:org.example.UserDaoImpl@646be2c3
设置name的值为hhhhh
after-userDao:org.example.UserDaoImpl@646be2c3
hhhhh
2.2 before和after方法的执行时机
基于上述快速入门的文件进行修改
UserDaoImpl.java
package org.example;
import org.springframework.beans.factory.InitializingBean;
public class UserDaoImpl implements UserDao, InitializingBean {
public UserDaoImpl() {
System.out.println("UserDaoImpl初始化");
}
String name;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("设置name的值为" + name);
this.name = name;
}
public void init(){
System.out.println("init初始化方法执行");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("属性设置之后执行");
}
}
beans.xml
<bean class="org.example.UserDaoImpl" id="userDao" init-method="init"/>
<bean class="processor.MyBeanPostProcessor" />
执行结果
UserDaoImpl初始化
before-userDao:org.example.UserDaoImpl@3c0a50da
设置name的值为hhhhh
属性设置之后执行
init初始化方法执行
after-userDao:org.example.UserDaoImpl@3c0a50da
hhhhh
2.3 对Bean方法进行执行时间日志增强
需求:
- Bean的方法执行之前控制台打印当前时间
- Bean的方法执行之后控制台打印当前时间
分析:
- 对方法进行增强主要就是代理设计模式和包装设计模式
- 由于Bean方法不确定,所以使用动态代理在运行期间执行增强操作
- 在Bean实例创建完毕后,进入到单例池之前,使用Proxy代替真是的目标Bean
UserDao.java
package org.example;
public interface UserDao {
public void show();
}
UserDaoImpl.java
package org.example;
public class UserDaoImpl implements UserDao {
public void show(){
System.out.println("执行方法中");
}
}
TimeLogBeanPostProcessor.java
package processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
public class TimeLogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Object beanProxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), (Object proxy, Method method, Object[] args) -> {
//1.输出开始时间
System.out.println("方法:" + method.getName() + "-开始时间:" + new Date());
//2.执行目标方法
Object result = method.invoke(bean,args);
//3.输出结束时间
System.out.println("方法:" + method.getName() + "-结束时间:" + new Date());
return result;
});
return beanProxy;
}
}
beans.xml
<bean class="processor.TimeLogBeanPostProcessor"/>
<bean class="org.example.UserDaoImpl" id="userDao"/>
主函数调用
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.show();
}
执行结果
方法:show-开始时间:Thu Mar 02 16:13:42 CST 2023
执行方法中
方法:show-结束时间:Thu Mar 02 16:13:43 CST 2023
评论 (0)