曹工杂谈:为什么很少需要改Spring源码,因为扩展点太多了,说说Spring的后置处理器

前言

最近发了好几篇,都是覆盖框架源码,但是spring的代码,我是从没覆盖过,毕竟,如果方便扩展,没谁想去改源码,而spring就是不需要改源码的那个,真的是“对扩展开放,对修改关闭”的典范。

就我说曾经用过的,spring的扩展点,就包括了listenerbeanFactoryPostProcessorbeanPostProcessor,而spring boot的扩展点,除了propertiesymljava config覆盖自动配置、org.springframework.boot.CommandLineRunner,还包括了META-INF下的spring.factory等。

眼下就有以前的一个例子:

这次,只简单说说后置处理器,主要是beanFactoryPostProcessorbeanPostProcessor

先说说beanFactoryPostProcessor

这两个比较像,都是后置处理器,但是处理的对象不同,前者是针对beanFactory,后者是针对bean实例。

beanFactoryPostProcessor的注释如下:

Allows for custom modification of an application context's bean definitions, adapting the bean property values of the context's underlying bean factory.
Application contexts can auto-detect BeanFactoryPostProcessor beans in their bean definitions and apply them before any other beans get created.

Useful for custom config files targeted at system administrators that override bean properties configured in the application context.

See PropertyResourceConfigurer and its concrete implementations for out-of-the-box solutions that address such configuration needs.

A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead.

简单来说,允许对bean definition进行修改。

bean definition定义

所谓的bean definition呢,就是bean的元数据,比如bean的name、scope、class、是否延迟初始化(is-lazy-init)、依赖的bean等等。

负责维护bean definition 的注册表

bean definition放在哪里呢,就在org.springframework.beans.factory.support.BeanDefinitionRegistry里,看名字可以知道,这是一个注册表,具体存储来说,一般会选择我们熟悉的hashmap,key是beanDefinition的类名,value就是beanDefinition

当然,这只是个接口,其提供了增删改查的方法:

public interface BeanDefinitionRegistry extends AliasRegistry {
    //注册bean Definition
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;
    //删除bean Definition
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    //获取beanDefinition
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    //判断是否包含指定的bean Definition
    boolean containsBeanDefinition(String beanName);
    //获取所有的beanDefinition的名称
    String[] getBeanDefinitionNames();
    //获取beanDefinition的数量
    int getBeanDefinitionCount();
}

那我们再看看这个接口的实现:

这里面可以看出来,ApplicationContext就是这个接口的实现,这里可以稍微看下registerBean的实现:

org.springframework.context.support.GenericApplicationContext#registerBeanDefinition
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
    //代理给beanFactory
    this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
这里的beanFactory类型为:org.springframework.beans.factory.support.DefaultListableBeanFactory
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){

        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            //...省略无关代码
            //往hashmap里存放beanName--》beanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        //...省略无关代码
    }

beanFactoryPostProcessor`的实现类

经过上面的介绍,想来大家比较了解beanFactoryPostProcessor了,我们看看这个接口的实现类呢:

拿以上实现类来说,

org.springframework.boot.web.servlet.ServletComponentRegisteringPostProcessor

这个是处理ServletComponentScan注解,将@WebServlet,@WebFilter等注解的servlet组件,注册到applicationContext

默认情况下,spring boot web应用,会有如下这个实现类:

org.springframework.context.annotation.ConfigurationClassPostProcessor

主要就是用于处理@Configuration注解的java类。

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory
    /**
     * Prepare the Configuration classes for servicing bean requests at runtime
     * by replacing them with CGLIB-enhanced subclasses.
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        int factoryId = System.identityHashCode(beanFactory);
        this.factoriesPostProcessed.add(factoryId);
        if (!this.registriesPostProcessed.contains(factoryId)) {
            // BeanDefinitionRegistryPostProcessor hook apparently not supported...
            // Simply call processConfigurationClasses lazily at this point then.
            processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
        }
        //对configuration注解的类进行cglib代理,保证@bean注解的方法,即使多次调用,也只会有一个实例
        enhanceConfigurationClasses(beanFactory);
        //新增一个bean后置处理器
        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }

自定义beanFactoryPostProcessor,并使之生效

很简单,像下面这样,定义一个类,实现BeanFactoryPostProcessor,并保证被扫描到即可。

@Component
@Slf4j
public class CustomBeanDefinitionPostProcessor implements BeanFactoryPostProcessor{

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition definition = beanFactory.getBeanDefinition("customBeanDefinitionPostProcessor");
        log.info("definition:{}",definition);
    }
}

启动时,输出如下:

11-12 15:49:48.627 [restartedMain] INFO  c.c.cad.config.CustomBeanDefinitionPostProcessor
                    - definition:Generic bean: class [com.ceiec.cad.config.CustomBeanDefinitionPostProcessor]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [F:\working_code\****\CustomBeanDefinitionPostProcessor.class] [CustomBeanDefinitionPostProcessor.java:20]

再说说beanPostProcessor

接口定义

Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
ApplicationContexts can autodetect BeanPostProcessor beans in their bean definitions and apply them to any beans subsequently created. Plain bean factories allow for programmatic registration of post-processors, applying to all beans created through this factory.

Typically, post-processors that populate beans via marker interfaces or the like will implement postProcessBeforeInitialization(java.lang.Object, java.lang.String), while post-processors that wrap beans with proxies will normally implement postProcessAfterInitialization(java.lang.Object, java.lang.String).
//对bean的实例进行修改,或者用一个代理来包装它们,这个和上面的重要差别就出来了,一个是在bean还没实例化之前,处理beanFactory里的bean definition;一个是处理实例化后的bean。
public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}   

接口的实现类

以上有大家熟悉的,比如 ApplicationContextAwareProcessor:

    org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces
    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            //....省略无关
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

aop代理的实现

另一个广泛应用的,就是aop用到的org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor

AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization
public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (this.advisor == null || bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }

        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
                // Add our local Advisor to the existing proxy's Advisor chain...
                if (this.beforeExistingAdvisors) {
                    advised.addAdvisor(0, this.advisor);
                }
                else {
                    advised.addAdvisor(this.advisor);
                }
                return bean;
            }
        }

        if (isEligible(bean, beanName)) {
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            //是否对目标类进行代理(cglib),如果不是的话,则获取bean的接口,进行接口代理,即jdk代理
            if (!proxyFactory.isProxyTargetClass()) {
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            proxyFactory.addAdvisor(this.advisor);
            customizeProxyFactory(proxyFactory);
            return proxyFactory.getProxy(getProxyClassLoader());
        }

        // No proxy needed.
        return bean;
    }
    protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        proxyFactory.setTarget(bean);
        return proxyFactory;
    }

自定义beanPostProcessor,并生效

很简单,直接在你的代码里,像下面这样写一个类,实现BeanPostProcessor,并保证被扫描到即可。

@Component
@Slf4j
public class CustomBeanPostProcessor implements BeanPostProcessor{

    @Nullable
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RabbitTemplate) {
            log.info("hahah RabbitTemplate");
        }
        return bean;
    }
}

下面是我这边运行的效果:

总结

相同点

1、操作对象不同

前面也说了,beanFactoryPostProcessor对bean的图纸进行修改,beanPostProcessor则是对生产出来的东西,进行修改或者替换(为什么说替换,因为也可能照着生产出来的产品,搞一个代理,比如aop就是基于此实现。)

2、生效时机不同

相同点

相同点呢,就是,这都是spring给我们提供出来的扩展点,相当方便,不是吗?

原文地址:https://www.cnblogs.com/grey-wolf/p/11830319.html

时间: 2024-08-02 02:40:57

曹工杂谈:为什么很少需要改Spring源码,因为扩展点太多了,说说Spring的后置处理器的相关文章

曹工杂谈:Java 类加载还会死锁?这是什么情况?

一.前言 今天事不是很多,正好在Java交流群里,看到一个比较有意思的问题,于是花了点时间研究了一下,这里做个简单的分享. 先贴一份测试代码,大家可以先猜测一下,执行结果会是怎样的: 2 3 import java.util.concurrent.TimeUnit; 4 5 6 public class TestClassLoading { 7 public static class A{ 8 static { 9 System.out.println("class A init");

曹工杂谈:用好verbose,Jar包冲突不再难

Jar包冲突的相关文章: 了不得,我可能发现了Jar 包冲突的秘密 一.前言 jar包冲突分多种,简单理解来说,就是同package且同名的类在多个jar包内出现,如果两个jar包在同一个classloader下,那么最终的结果是,只会加载其中的一个. 有时,这个错误一般在运行时出现,报的错可能是,找不到某方法,或者呢,更隐蔽的,不会报错,但是逻辑不对. 针对运行中的应用,可以考虑使用阿里出品的arthas来处理. 我今天呢,只是简单的找不到方法的情况,所以不需要用到那个. 我这里的场景是,在学

北京赛车PK10改单源码分享,PK10改单技术分享

北京赛车PK10改单技术分享. 无需账号和密码,只需要提供网址即可做到无痕修改,大家可以放心下载使用,禁止用于非法行业 本软件支持各种网盘程序改单只要能读取到数据库成功率百分之百:支持北京赛车PK10改单.时时彩改单,快三改单 六合彩网站改单,各类高频彩改单,各种数据修改 下面我们来介绍下软件操作的详细的操作步骤 第一:需要找客户拿合作号:软件的右下角有客户的联系方式 2.拿到客户给的合作号码之后.填写进去点击登录. 3.登录之后.请填写平台的网址.以及安全吗和计划盈利即可......计划盈利不

java实现的一个很强大的彩票网站源码WAP版

后台java spring mvc架构,页面程序双分离,oracle数据库严谨数据结构.多重数据审核机制.出票机制和监控机制,html5前端技术适用移动端,后台逻辑更多以server接口可快捷实现对接pc和ap,下载会有少量图片素材丢失,附件有下载说明前端demo账户密码和后台管理地址管理员账户密码. <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op>

曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de

曹工说Spring Boot源码(24)-- Spring注解扫描的瑞士军刀,asm技术实战(上)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de

曹工说Spring Boot源码(13)-- AspectJ的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de

曹工说Spring Boot源码(15)-- Spring从xml文件里到底得到了什么(context:load-time-weaver 完整解析)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de

曹工说Spring Boot源码(16)-- Spring从xml文件里到底得到了什么(aop:config完整解析【上】)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de