Spring后处理器详细介绍

 更新时间:2023年02月08日 11:32:42   作者:tanglin_030907031026  
Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理。可以在​Spring容器通过插入一个或多个BeanPostProcessor的实现来完成实例化,配置和初始化一个​bean​之后实现一些自定义逻辑回调方法

一、概述

Spring的后处理器是Spring对外开发的重要扩展点、允许我们介入到Bean的整个实例化流程中来, 以达到动态注册 BeanDefinition, 动态修改BeanDefinition, 以及动态修改Bean的作用。Spring主要有两种后处理器:

  • BeanFactoryPostProcessor:Bean工厂后处理器, 在BeanDefinitionMap填充完毕, Bean实例化之前执行;
  • BeanPostProcessor:Bean后处理器, 一般在Bean实例化之后, 填充到单例池singletonObjects之前执行。

Bean工厂后处理器-BeanFactoryPostProcessor BeanFactoryPostProcessor是一个接口规范, 实现了该接口的类只要交由Spring容器管理的话, 那么Spring就会回调该接口的方法, 用于对BeanDefinition注册和修改的功能。

BeanFactoryPostProcessor定义如下:

public interface BeanFactoryPostProcessor{
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) ;
}

二、案例演示

①:注册BeanDefinition

1.编写MyBeanFactoryPostProcessor类,实现BeanFactoryPostProcessor接口,并实现接口方法

package com.tangyuan.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("beanDefinitionMap填充完毕后回调该方法");
        //不在主xml文件中进行配置bean
        //动态注册Beandifinition
          BeanDefinition beanDefinition=new RootBeanDefinition();
          beanDefinition.setBeanClassName("com.tangyuan.dao.impl.PeresonDaoImpl");                                     System.out.println(beanFactory);                       //org.springframework.beans.factory.support.DefaultListableBeanFactory@737996a0: defining beans
        //强转成DefaultListableBeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory= (DefaultListableBeanFactory) beanFactory;
        defaultListableBeanFactory.registerBeanDefinition("PersonDao",beanDefinition);
    }
}

2.测试

//创建ApplicationContext,加载配置文件,实例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
PeresonDao bean = applicationContext.getBean(PeresonDao.class);
System.out.println(bean);//com.tangyuan.dao.impl.PeresonDaoImpl@6ddf90b0

②:注册BeanDefinition

Spring提供了一个BeanFactoryPostProcessor的子接BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作

public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException{}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException{
BeanDefinition beanDefinition=new RootBeanDefinition() ;
beanDefinition.setBeanClassName("com.tangyuan.dao.UserDaoImp12");
beanDefinitionRegistry.registerBeanDefinition("userDao2",beanDefinition) ;
}
}

案例演示:

1.编写MyBeanDefinitionRegistryPostProcessor类,实现BeanDefinitionRegistryPostProcessor接口,并实现接口方法

package com.tangyuan.processor;
import com.alibaba.druid.support.spring.stat.SpringStatUtils;
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("MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法");
        //向容器当中注册BeanDefinition
        BeanDefinition beanDefinition=new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.tangyuan.dao.impl.PeresonDaoImpl");
         beanDefinitionRegistry.registerBeanDefinition("PersonDao",beanDefinition);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法");
    }
}

2.将MyBeanDefinitionRegistryPostProcessor类在xml文件中进行配置

<bean class="com.tangyuan.processor.MyBeanDefinitionRegistryPostProcessor"></bean>

3.测试

//创建ApplicationContext,加载配置文件,实例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
PeresonDao bean = applicationContext.getBean(PeresonDao.class);
System.out.println(bean);//com.tangyuan.dao.impl.PeresonDaoImpl@5a8e6209

ps:方法的执行顺序

1.MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法

2.MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法

3.MyBeanFactoryPostProcessor的postProcessBeanFactory方法

③:使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描

要求如下:

  • 自定义@MyComponent注解, 使用在类上;
  • 使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;
  • 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析, 解析后最终被Spring管理。

1.创建一个OtherBean类,并定义@MyComponent注解

package com.tangyuan.beans;
import com.tangyuan.anno.MyComponent;
@MyComponent("OtherBean")
public class OtherBean {
}
package com.tangyuan.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();
}

2.BaseClassScanUtils 帮助类编写

package com.tangyuan.utils;
import com.tangyuan.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.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.tangyuan");
        System.out.println(stringClassMap);
    }
}

3.编写MyComponentBeanFactoryPostProcessor类,并实现BeanDefinitionRegistryPostProcessor接口

package com.tangyuan.processor;
import com.tangyuan.utils.BaseClassScanUtils;
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 java.util.Map;
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        //通过扫描工具去扫描指定包及其包下的所有类,收集使用@MyComponent注解的类
        Map<String, Class> map = BaseClassScanUtils.scanMyComponentAnnotation(
                "com.tangyuan"
        );
        //循环遍历,组装BeanDefinition进行注册
        map.forEach((beanName,clazz)->{
       //获取BeanClassName
            String name = clazz.getName();//com.tangyuan.beans.OtherBean
        //创建BeanDefinition
            BeanDefinition beanDefinition=new RootBeanDefinition();
            beanDefinition.setBeanClassName(name);
           //注册
            beanDefinitionRegistry.registerBeanDefinition(name,beanDefinition);
        });
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }
}

4.在xml文件编写MyComponentBeanFactoryPostProcessor类的bean

<bean class="com.tangyuan.processor.MyComponentBeanFactoryPostProcessor"></bean>

5.测试

OtherBean bean1 = applicationContext.getBean(OtherBean.class);
System.out.println(bean1);//com.tangyuan.beans.OtherBean@1877ab81

6.编写一个BookBean类,并实现@MyComponent注解

package com.tangyuan.beans;
import com.tangyuan.anno.MyComponent;
import org.springframework.stereotype.Component;
@MyComponent("BookBean")
public class BookBean {
}

7.再次测试

//创建ApplicationContext,加载配置文件,实例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
BookBean bean2 = applicationContext.getBean(BookBean.class);
System.out.println(bean2);//com.tangyuan.beans.BookBean@1877ab81

三、Bean的后处理器BeanPostProcessor

Bean被实例化后, 到最终缓存到名为singletonObjects单例池之前, 中间会经过Bean的初始化过程, 例如:属性的 填充、初始方法init的执行等, 其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的 Bean工厂后处理器相似, 它也是一个接口, 实现了该接口并被容器管理的BeanPostProcessor, 会在流程节点上被 Spring自动调用。

BeanPostProcessor的接口定义如下:

public interface BeanPostProcessor{
@Nullable
//在属性注入完毕, init初始化方法执行之前被回调
default Object postProcesBeforeInitialization(Object bean, String bean Name)throws BeansException{
return bean;
}
//在初始化方法执行之后, 被添加到单例池singletonObjects之前被回调
@Nullable
default Object postProcessAfterInitialization(Object bean, String bean Name) throws BeansException{
return bean;
}
}

案例:对Bean方法进行执行时间日志增强

要求如下:

  • Bean的方法执行之前控制台打印当前时间;
  • Bean的方法执行之后控制台打印当前时间。

分析:

  • 对方法进行增强主要就是代理设计模式和包装设计模式;
  • 由于Bean方法不确定, 所以使用动态代理在运行期间执行增强操作;
  • 在Bean实例创建完毕后, 进入到单例池之前, 使用Proxy代替真是的目标Bean

思路:编写BeanPostProcessor, 增强逻辑编写在after方法中

public Object post Process After Initialization(Object bean,String beanName) throws BeansException{
//Bean进行动态代理,返回的是Proxy代理对象
Object proxy Bean=Proxy.newProxyInstance(bean.getClass() .getClassLoader() ,
bean.getClass() .getInterfaces() ,
(Object proxy, Method method,Object[] args) ->{
long start=System.currentTimeMillis() ;
system.out.println("开始时间:"+new Date(start) ) ;
//执行目标方法BIN
Object result=method.invoke(bean,args) ;
long end=System.currentTimeMillis() ;
System.out.println("结束时间:"+new Date(end) ) ;
return result;
});
//返回代理对象
return proxy Bean;
}

实现代码如下:

1.编写一个TimeLogBeanPostProcessor类,接口BeanPostProcessor实现,方法也实现

package com.tangyuan.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
public class TimeLogBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
          //使用动态代理对目标Bean进行增强,返回proxy对象,进而存储到单例池singletonObjects中
            Object beanProxy= Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //1.输出开始时间
                        System.out.println("方法:"+method.getName()+"-开始时间:"+new Date());
                        //2.执行目标方法
                        Object invoke = method.invoke(bean, args);
                        //3.输出结束时间
                        System.out.println("方法:"+method.getName()+"-结束时间:"+new Date());
                        return invoke;
                    }
                }
        );
        return beanProxy;
    }
}

2.在xml文件上完成对TimeLogBeanPostProcessor类的配置

<bean class="com.tangyuan.processor.TimeLogBeanPostProcessor"></bean>

3.编写方法

package com.tangyuan.dao;
public interface UserDao {
    void show();
}
package com.tangyuan.dao.impl;
import com.tangyuan.dao.UserDao;
import org.springframework.beans.factory.InitializingBean;
public class UserDaoImpl implements UserDao , InitializingBean {
    public UserDaoImpl() {
        System.out.println("UserDao实例化");
    }
    public void init(){
        System.out.println("init初始化方法执行");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("属性设置之后执行~~~~");
    }
    @Override
    public void show() {//ctrl+Alt+t
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("show.....");
    }
}

4.测试

//创建ApplicationContext,加载配置文件,实例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao dao = (UserDao) applicationContext.getBean("userDao");
dao.show();
方法:show-开始时间:Tue Nov 29 10:21:31 GMT+08:00 2022
show.....
方法:show-结束时间:Tue Nov 29 10:21:31 GMT+08:00 2022

四、Spring ioc整体流程总结

到此这篇关于Spring后处理器详细介绍的文章就介绍到这了,更多相关Spring后处理器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • hibernate 配置数据库方言的实现方法

    hibernate 配置数据库方言的实现方法

    这篇文章主要介绍了hibernate 配置数据库方言的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • java agent使用全解析

    java agent使用全解析

    这篇文章主要介绍了javaagent的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下
    2020-07-07
  • Hibernate延迟加载技术详解

    Hibernate延迟加载技术详解

    这篇文章主要介绍了Hibernate延迟加载技术,结合实例形式详细分析了Hibernate延迟加载所涉及的各种常用技巧,需要的朋友可以参考下
    2016-03-03
  • Spring中的事务管理及实现方式解析

    Spring中的事务管理及实现方式解析

    这篇文章主要介绍了Spring中的事务管理及实现方式解析,Spring事务管理基于底层数据库本身的事务处理机制,数据库事务的基础,是掌握Spring事务管理的基础,这篇总结下Spring事务,需要的朋友可以参考下
    2024-01-01
  • Java应用服务器之tomcat部署的详细教程

    Java应用服务器之tomcat部署的详细教程

    这篇文章主要介绍了Java应用服务器之tomcat部署,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • spring mvc+localResizeIMG实现HTML5端图片压缩上传

    spring mvc+localResizeIMG实现HTML5端图片压缩上传

    这篇文章主要为大家详细介绍了使用spring mvc+localResizeIMG实现HTML5端图片压缩上传,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • Java多线程 Producer and Consumer设计模式

    Java多线程 Producer and Consumer设计模式

    这篇文章主要介绍了Java多线程 Producer and Consumer设计模式,producer是生产者的意思:指生产数据的线程,consumer是消费者的意思,指的是使用数据的线程,下文围绕Producer及Consumer展开话题,需要的朋友可以参考一下
    2021-10-10
  • mybatis同一张表多次连接查询相同列赋值问题小结

    mybatis同一张表多次连接查询相同列赋值问题小结

    这篇文章主要介绍了mybatis同一张表多次连接查询相同列赋值问题,非常不错,具有参考借鉴价值,需要的的朋友参考下
    2017-01-01
  • 5个步骤让你明白多线程和线程安全

    5个步骤让你明白多线程和线程安全

    本文详细讲解了多线程和线程安全的实现,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Gateway集成Netty服务的配置加载详解

    Gateway集成Netty服务的配置加载详解

    这篇文章主要为大家介绍了Gateway集成Netty服务的配置加载详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02

最新评论