文章目录
一、Bean实例化基本流程
二、Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
-
BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
-
BeanPostProcessor: Bean后处理器,一般在Bean实例化之后,填充到单例池sinqletonObiects之前执行。
1. Bean工厂后处理器-BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
BeanFactorvPostProcessor 定义如下:
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
其中ConfigurableListableBeanFactory继承了ListableBeanFactory,ListableBeanFactory继承了BeanFactory,所以使用ConfigurableListableBeanFactory就可以操作BeanDefinition。
那么怎么才能操作BeanDefinition呢,那就要先创建一个自己的类来实现BeanFactoryPostProcessor并重写postProcessBeanFactory了(需要配置MyBeanFactoryPostProcessor到bean):
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("beanDefinitionMap填充完毕后回调该方法");
}
}
- 修改某个BeanDefinition:
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
beanDefinition.setBeanClassName("com.zkt.dao.impl.UserDaoImpl");
当你看到这里可能就有个疑问了,为什么只能修改某个BeanDefinition呢,我能不能批量修改BeanDefinition呢,那么很抱歉,为了安全考虑,ConfigurableListableBeanFactory并没有提供关于BeanDefinitionMap的接口,所以还是老老实实的用名字去单个获取吧。
- 注册BeanDefinition(使用RootBeanDefinition,其中PersonDaoImpl是没有被注册到BeanDefinition中的):
// 注册BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.zkt.dao.impl.PersonDaoImpl");
// 强转成DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition("personDao", beanDefinition);
ConfigurableListableBeanFactory中并没有注册BeanDefinition的方法,而这里beanFactory的实质其实是DefaultListableBeanFactory,所以这里我们直接强转成DefaultListableBeanFactory。
但是每次强转类型并不好,所以Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor,专门用于注册BeanDefinition操作,其定义如下:
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
@Override
default void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
- 有了这个接口,我们就能不用强转类型就能直接注册BeanDefinition了:
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//注册BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.zkt.dao.impl.PersonDaoImpl");
registry.registerBeanDefinition("personDao", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
- 有了BeanFactoryPostProcessor了,那么Bean实例化流程就变成了
案例:使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
-
要求:
-
自定义
@MyComponent
注解,使用在类上; -
自定义包扫描器工具BaseClassScanUtils完成指定包的类扫描;
-
自定义MyComponentBeanFactoryPostProcessor完成注解
@MyComponent
的解析,解析后最终被Spring管理。
-
- 定义
@MyComponent
注解;
@Target(ElementType.TYPE) // 设置该注解使用在类上
@Retention(RetentionPolicy.RUNTIME) // 设置该注解存活到运行时
public @interface MyComponent {
String value();
}
- 定义包扫描器工具BaseClassScanUtils;
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)