Spring(六)- BeanFactoryPostProcessor 与 BeanPostProcessor 后处理器

文章目录

  • 一、Spring 的后处理器
    • 1. Bean工厂后处理器 – BeanFactoryPostProcessor
      • (1)应用:使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
    • 2. Bean后处理器 – BeanPostProcessor
      • (1)应用:对Bean方法进行执行时间日志增强

一、Spring 的后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
⚫ BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
⚫ BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
BeanFactoryPostProcessor只执行一次,是在所有BeanDefinition填充到BeanDefinitionMap时;
BeanPostProcessor会执行多次,每个bean实例化之后都会执行;

1. Bean工厂后处理器 – BeanFactoryPostProcessor

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的实例化过程中的体现:

Spring(六)- BeanFactoryPostProcessor 与 BeanPostProcessor 后处理器_第1张图片

(1)应用:使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描

要求如下:
⚫ 自定义@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);
    }
}

2. Bean后处理器 – BeanPostProcessor

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方法…

(1)应用:对Bean方法进行执行时间日志增强

要求如下:
⚫ 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的实例化过程中的体现

Spring(六)- BeanFactoryPostProcessor 与 BeanPostProcessor 后处理器_第2张图片

你可能感兴趣的:(Spring,spring,java,后端)