Java Spring Bean工厂后处理器

1585364631
2023-03-02 / 0 评论 / 122 阅读 / 正在检测是否收录...

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注册和修改的功能。


实例化流程

image-20230228211434647


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");
}

image-20230228193103706

执行结果

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自动调用。

image-20230302162019495

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

评论 (0)

取消