Spring框架为何这么叼?

共 13764字,需浏览 28分钟

 ·

2020-05-08 23:22


    Spring作为一个优秀的企业级应用框架,提供了企业开发过程中大多数基础功能的支持,如:事务,切面,web支持,数据库支持,ORM,OXM等等等等。但它不仅仅只是提供了这些基础功能,而是在提供这些基础功能的同时,给开发人员留下了许许多多的功能扩展点,使用者可以利用这些扩展点随意加入自己想要的其他功能,这也是Spring开闭原则的精髓。比如:常见的一些企业中间件例如:Dubbo,JSF,MyBatis,又或者是近些年比较流行的微服务框架SpringCloud等,其功能都是在Spring提供的扩展点之上扩展出来的新功能。那么Spring中经常使用到的扩展点有哪些呢?1、扩展原理    Spring功能扩展的前提是它将对象的创建及容器的初始化等过程细分为了许多的步骤【这些步骤称为容器的声明周期和Bean的声明周期】,那么假设不这样做,直接使用new关键字创建出对象,顶多两步【先new对象,然后set属性】也就结束了,就不会有扩展这一说了。而在Spring中提供了一些很特别的接口,这些接口具有一个特点,他们会在容器初始化或者Bean实例化的过程中的某些时间点【例如:bean定义加载完毕,bean实例化前后,bean初始化前后】被调用,从而完成功能的扩展,在某个时间点可以插入自定义的处理逻辑,这也就是Spring的扩展原理。2、原始扩展点
    原始扩展点此处指的是原始的接口或者抽象类,没有其他的任何父接口或者父类。常见的此类扩展点有如下几个。2.1、BeanFactoryPostProcessor
(1)扩展点方法
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
(2)扩展点的执行时机    该扩展点为一个beanFactory的后置处理器,它的执行时机是在Bean定义全部解析及加载完毕【加载完毕指的是bean定义已经被放入到了bean定义注册中心beanDefinitonMap中】,但是bean实例化之前会被执行。具体是在Spring容器初始化上下文,执行refresh方法的第5步执行的,即:
@Override
public void refresh() {
   synchronized (this.startupShutdownMonitor) {
      try {
        // ... 前4步略,后面文章介绍


        // 第5步:执行所有Bean工厂的后置处理器
        // 在该步骤中首先会去加载Bean定义,然后执行beanFactory的后置处理器        
        invokeBeanFactoryPostProcessors(beanFactory);
        // ...中间5步略,后面文章介绍


        // 第11步:完成所有单实例bean的创建及初始化
        finishBeanFactoryInitialization(beanFactory);
        // 第12步:发布容器刷新事件,例如ContextRefreshedEvent
        // SpringCloud的web服务也是在该步骤中启动的
        finishRefresh();
      } catch (BeansException ex) {
      } finally {
      }
    }
}
(3)该扩展点的作用
    使用该扩展点,可以在实例化Bean实例之前修改Bean的定义信息。例如:修改bean的类型,修改bean的作用域,是否为懒加载,是否参与自动装配或者是修改bean的工厂方法等等【在Spring中Bean定义是专门用来描述bean的一种结构,里面包括了bean的方方面面的信息,在创建bean实例以及初始化bean的过程中就是根据bean定义中的信息来完成的】。(4)扩展点使用示例
配置类ExtConfig
/**
 * @author wangbin33
 * @date Created in 12:16 2019/12/7
 */

@Configuration
@ComponentScan(basePackages = "com.wb.spring.ext")
public class ExtConfig {
}
自定义bean工厂后置处理器
/**
 * @author wangbin33
 * @date Created in 12:24 2019/12/7
 */

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      // 获取已经加载的bean定义的数量
      int count = beanFactory.getBeanDefinitionCount();
      // 获取已经加载的bean定义的名称
      String[] beanNames = beanFactory.getBeanDefinitionNames();

      // 根据bean定义的名称获取具体的bean定义
      BeanDefinition perBeanDefinition = beanFactory.getBeanDefinition("person");
      // 修改bean定义,bean定义中的许多属性此处都可以修改
      // 修改之后,创建bean实例的时候就会应用到修改之后的新属性值
      perBeanDefinition.setAutowireCandidate(false);
      System.out.println(perBeanDefinition);
   }
}
测试类TestMain
/**
 * @author wangbin33
 * @date Created in 12:16 2019/12/7
 */

public class TestMain {
   public static void main(String[] args) {
      AnnotationConfigApplicationContext acx = new AnnotationConfigApplicationContext();
      acx.register(ExtConfig.class);
      acx.refresh();
      // 容器关闭
      acx.close();
   }
}
    这样从容器中获取到的person就是使用修改之后的bean定义创建的对象。
2.2、BeanPostProcessor(1)扩展点的方法
public interface BeanPostProcessor {
    /**
     * 该方法是在Bean实例化(new)之后,初始化(设置各种属性)之前会被调用
     * 在调用afterPropertiesSet方法之前会被调用
     */

    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    /**
     * 在bean初始化之后会被调用
     * 在调用InitializingBean的afterPropertiesSet方法或者init-method指定的方法执行之后调用.
     */

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
(2)扩展点的执行时机    该扩展点是和Bean相关的一个接口,主要是用来拦截Bean的创建,接口中包括两个扩展方法,postProcessBeforeXXX以及postProcessAfterXXX。
    postProcessBeforeXXX执行时机:在Spring实例化bean并填充属性之后,执行初始化方法之前执行【初始化方法此处主要有两个:①使用init-method指定的方法;② Bean实例从InitializingBean接口中实现过来的afterPropertiesSet方法】。    postProcessAfterXXX执行时机:在Spring执行完初始化方法之后执行,如果当前Bean需要生成代理【AOP和声明式事务都会在该步骤中对目标bean对象生成代理对象】,则会在该步骤中生成。(3)该扩展点的作用    由于该扩展点是在bean实例化完成,初始化方法执行前后调用的。所以可以利用该扩展点完成一些特定标识的检查,例如判断其是否实现了自定义的某个接口,然后对bean对象作出某种定制化操作。或者说对生成的对象进行包装,生成代理对象等等。(4)使用示例    直接自定义Processor,然后实现BeanPostProcessor接口,然后实现其对应的后置处理方法即可。
/**
 * @author wangbin33
 * @date Created in 12:16 2019/12/7
 */

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyInterFace) {
            // 检查其是否实现了某个自定义接口,然后做自定义操作
            (MyInterFace(bean)).doSomeThing();
        }
        // 返回原对象,或者包装为代理对象
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }
}
2.3、ApplicationListener    Spring提供的用于事件驱动开发的一个扩展点,开发者可以给容器中发布自定义的事件,当事件发生之后,触发某个动作。(1)扩展点的方法
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEventextends EventListener {
    /**
* 自定义事件实现该接口,在容器中发布事件之后,当时间发生时,触发onApplicationEvent的回调
     */

    void onApplicationEvent(E event);
}
(2)扩展点的执行时机    容器发布默认的刷新事件时,自定义的事件会被触发执行。这个时候容器中的所有单实例bean都已经创建完成。(3)扩展点的作用    在容器中的所有单实例bean都创建完成之后,可以做一些自定义的操作,例如启动某个后台线程。再比如在SpringCloud中,内嵌的web容器就是在容器发布事件的时候启动的,再有一些RPC服务框架,也可以再事件的回调方法中去发布或者注册服务接口。
(4)扩展点使用示例① 配置类ExtConfig
/**
 * @author wangbin33
 * @date Created in 12:16 2019/12/7
 */

@Configuration
@ComponentScan(basePackages = "com.wb.spring.ext")
public class ExtConfig {
}
② 自定义事件实现
/**
 * @author wangbin33
 * @date Created in 13:13 2019/12/7
 */

@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent{
    /**
     * 当容器中发布事件之后,会触发该方法
     * @param event the event to respond to
     */

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("收到事件:" + event);
    }
}
③ 测试类
/**
 * @author wangbin33
 * @date Created in 12:16 2019/12/7
 */

public class TestMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext acx = new AnnotationConfigApplicationContext();
        acx.register(ExtConfig.class);
        acx.refresh();
        acx.publishEvent(new ApplicationEvent(new String("wb发布的事件...")) {});
        // 发布一个自定义事件.
        // 容器关闭
        acx.close();
    }
}
    在容器刷新过程中,首先会给容器中注册事件派发器,事件派发器其实就是用来执行事件回调的。然后调用publishEvent之后,底层会使用时间派发器去派发事件,即:执行事件的回调方法。
3、衍生的扩展点
3.1、InstantiationAwareBeanPostProcessor
(1)扩展点的方法
    该扩展点是继承自BeanPostProcessor接口的,所以本质上也是一个Bean的后置处理器,只是该接口中有一些特有的方法,方法列表如下:
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    /**
     * 在Bean对象实例化之前调用,可以通过实现该方法,在刚方法中加入自定义的初始化逻辑,比如在此处创建一个代理对象返回
     */

    @Nullable
    default Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        return null;
    }
    /**
     * 在bean实例化对象之后,属性值填充之前会被调用。这个时候
     */

    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }
    /**
     *  在bean实例化对象完成之后,首先会执行populateBean方法填充bean的属性【按照名称或者类型】,然后会执行该方法进行自定义属性的依赖注入
     *  使用该方法可以完成bean中一些自定义属性的解析【例如解析标注某些自定义注解的属性】;
     * 注意:该方法是Spring5.1中新增的方法,5.1之前使用的是postProcessProperties方法
     */

    @Nullable
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
            throws BeansException 
{
        return null;
    }
    /**
     * Spring5.1之前使用的方法,目前5.1版本已经过时,也是用来解析bean中的一些自定义属性
     */

    @Deprecated
    @Nullable
    default PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
 throws BeansException 
{
        return pvs;
    }
}
(2)执行时机    该扩展点是BeanPostProcessor的一个子接口,他是在Bean被实例化之前,以及填充属性过程中会被调用,具体时机如下:① postProcessBeforeInstantiation: 实例化之前调用,可以实现该方法直接使用自定义逻辑返回一个定制化的bean;
② postProcessAfterInstntiation: 在Bean对象实例化之后,普通属性被填充之前调用,【普通属性初始化可以理解为使用@Autowired或者@Resource标注的属性的初始化,经常说的依赖注入其实就是这个普通属性的初始化,可以按照属性的类型或者名称】;③ postProcessProperties: 在普通属性填充之后调用,可以在该方法中完成一些定制化属性的依赖注入,例如自定义注解的解析及注入操作。3.2、SmartInstantiationAwareBeanPostProcessor    该接口是InstantiationAwareBeanPostProcessor的一个子接口,里面提供了用于推断bean的类型以及推断给定bean所使用的构造器。(1)扩展点方法
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
    /**
     * 用于推断给定bean的类型,并且返回第一个推断成功的bean的class类型。如果无法推断出bean的类型,则会返回null。
     * 当根据bean定义无法获取到bean的真实类型时,就会调用该方法去推断出bean的实际类型。
     */

    @Nullable
    default Class predictBeanType(Class beanClass, String beanName) throws BeansException {
        return null;
    }
    /**
     * 用于推断给定bean的构造器。一个bean可能会有多个重载的构造器,然后根据自定注入的模式以及构造器的入参个数来选用合适的构造器去完成依赖注入
     */

    @Nullable
    default Constructor[] determineCandidateConstructors(Class beanClass, String beanName)
            throws BeansException {
        return null;
    }
    /**
     * 在Spring解决循环依赖【A依赖B,B依赖A】的时候。用于从单例对象工厂缓存中获取早期暴露【早期暴露出来指的是bean被实例化出来了,
     * 但是还未初始化,此时尚且不能使用,不是一个完整的Bean】出来的依赖对象。
     */

    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
(2)执行时机
① predictBeanType:在初始化所有单实例Bean的过程中,会执行该方法,会使用该方法去推断bean的类型,判断其是否为工厂Bean,即: isFactoryBean方法中会执行。简单调用链如下:
refresh()  ->  preInstantiateSingletons()  ->  isFactoryBean()
② determineCandidateConstructors:在bean的实例化过程中,会使用该方法去推断bean创建时所能够使用的构造器,推断出来之后,会选用一个合适的构造器去创建bean实例。③ getEarlyBeanReference:在bean实例化完成之后,填充属性之前,会调用该方法,获取到将实例化过程中正在创建而且是单实例而且允许循环依赖的早期对象,然后放入到早期对象缓存中,用于解决循环依赖问题。
3.3、InstantiationAwareBeanPostProcessorAdaptor    该接口是上述3.2【类名实在太长...略】的一个抽象实现类,是一个后置处理器的适配器,里面提供了上述3.1、3.2、2.1中的所有方法,并且提供默认的空实现,如果不确定需要扩展的功能,建议扩展类直接继承该类,可以随时覆写其他方法,具体的方法列表就是上述2.1、3.1、3.2方法的汇总,此处不再介绍。3.4、MergedBeanDefinitionPostProcessor    该接口是BeanPostProcessor的子接口,用于处理合并之后的bean定义。
(1)扩展的方法
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
    /**
     * 用来处理合并之后的bean定义,通常会在bean刚初始化完成,属性填充之前执行。
     * 找出带有特定注解的属性或者方法。
     */

    void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName);
    /**
     * Spring5.1之后新增的方法。用来重置【删除】给定bean名称的bean定义。例如:bean定义存在着父级bean,执行完bean的合并操作之后,如果子bean和父bean的bean名称相同,这时需要把子bean定义从bean定义注册中心删除掉
     */

    default void resetBeanDefinition(String beanName) {
    }
}
(2)执行时机
① postProcessMergedBeanDefinition:在bean实例化对象之后,填充属性之前会调用该方法。例如用于处理@Autowired,@Value,@Resource等注解的后置处理器基本上都实现了该接口,在此方法中对标注有@Autowired,@Value,@Resource注解的属性,进行预处理,将其解析为注解元数据,然后放入到一个缓存中,以备后续依赖注入时使用。
② resetBeanDefinition: 在bean定义解析完毕然后注册到bean定义注册中心之后,会调用该方法。如果bean定义中心中存在着当前bean名称相同的bean,则会使用该方法删除掉重复的bean定义。3.5、DestructionAwareBeanPostProcessor
    该后置处理器也是BeanPostProcessor的子接口,作用于具体的bean,如果bean对象实现了DisposableBean接口,则当容器关闭之前,会回调该接口的distroy方法,distroy方法中会回调postProcessBeforeDestruction方法,进行bean销毁之前的善后工作。例如:关闭释放某些系统资源,关闭某些服务等。(1)扩展点方法
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
    /**
     * 该方法会在bean实例被销毁前调用,bean实例需要实现DisposableBean接口
     */

    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
    /**
     * Spring5.1新增方法。判断给定的bean实例是否需要在容器关闭时被销毁,在创建bean的过程中,如果该方法返回true,则会将当前的bean注册到一个需要被销毁的Map中,在容器关闭时,会依次从该map中拿出需要被销毁的bean,然后依次回调bean的distroy方法
     */

    default boolean requiresDestruction(Object bean) {
        return true;
    }
}
(2)执行时机① postProcessBeforeDestruction: 在Spring容器的close方法执行时,会取出容器中需要被销毁【需要被销毁指的是创建bean的过程中requiresDestruction方法的返回值为true】的bean实例,然后依次去调用bean示例的distroy方法,在distroy方法中会去回调该后置处理器方法。
② requiresDestruction:在bean初始化完成之后,如果bean实现了Destruction...接口,而且覆写了该方法,则会调用该方法,根据该方法的返回值去判断是否需要将该bean放入到需要被销毁的map集合中。4、内置的重要后置处理器4.1、ConfigurationClassPostProcessor    该后置处理器是用来处理Spring中javaConfig的核心后置处理器,在其后置处理方法中会解析如这些常见的注解:
@Configuration:标注当前类为配置类@Import:快速给容器中导入一个组件@ImportResource:快速导入配置文件@Order:标记bean的优先级,给bean定义排序时使用@ComponentScan[s]:配置包扫描,可以配置多个@PropertySource[s]:导入properties配置文件,可以导入多个以及ImpotSelector和ImportBeanDefinitionRegister接口的实现类。
4.2、CommonAnnotationBeanPostProcessor
    用来处理自动装配及一些初始化方法和销毁方法注解的后置处理器,比如下列的注解:@Resource: 用来完成按bean名称的依赖注入。@PreDestroy: 标注在方法上,用来指定bean的销毁方法@PostConstruct: 标记在方法上,用来指定初始化方法,这个初始化方法会在bean实例化并填充属性之后被调用。注意:项目中,如果使用spring的xml来配置bean,则如下配置将会自动给容器中导入该后置处理器:【xml中一些配置的具体作用】

或者:

4.3、AutowiredAnnotationBeanPostProcessor
    用来处理Spring的一些自带注解以及JSR-330中注解的支持,常见的如下:
@Autowired: 用来完成按照类型的依赖注入
@Value: 用来完成属性值的注入,常用于解析property配置文件中的值@Inject: JSR-330中提供的注解,也是用来完成依赖注入的,使用时需要导入第三方的jar包注意:如果使用spring的xml,则在spring的xml配置文件中,如下的配置将会给容器中导入该后置处理器:【xml中一些配置的具体作用】

或者:
4.4、EventListenerMethodProcessor
    主要用来处理事件驱动开发中的@EventListener注解。使用该注解标注在某个组件的某个方法上,则如果方法的入参类型与自定义事件的类型相同,则改事件就会被自动注入到该组件中,当容器中发布该类事件时,该组件的方法就会被调用,不太好描述,如下举个简单示例: 配置类EventConfig
/**
 * @author wangbin33
 * @date 2020/5/5 20:56
 */

@Configuration
@ComponentScan("com.wb.spring.event")
public class EventConfig {
}
自定义事件TestEvent
/**
 * @author wangbin33
 * @date 2020/5/5 20:56
 */

public class TestEvent extends ApplicationEvent {
    private String text;
    public TestEvent(Object source, String text) {
        super(source);
        this.text = text;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return "TestEvent{" +
                "text='" + text + '\'' +
                '}';
    }
}
测试组件TestComponent
/**
 * @author wangbin33
 * @date 2020/5/5 20:57
 */

@Component
public class TestComponent {
    @EventListener
    public void test(TestEvent testEvent) {
        System.out.println("testEvent was invoked..." + testEvent);
    }
}
测试类TestMain
/**
 * @author wangbin33
 * @date 2020/5/5 20:56
 */

public class TestMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext acx = new AnnotationConfigApplicationContext();
        acx.register(EventConfig.class);
        acx.refresh();
        acx.publishEvent(new TestEvent(acx, "测试..."));
        acx.close();
    }
}
    运行之后,会发现,发布TestEvent事件之后,TestComponent中的test方法马上就会监监听到该事件,并输出具体的事件,如下:
testEvent was invoked...TestEvent{text='测试...'}
    至此,Spring中常见的扩展点以及一些重要的内置后置处理器介绍完毕。Spring中当然扩展点远远不止这些,还有很多很多,在后面的文章中,将会继续介绍!欢迎评论转发!


更多Java相关推文,关注Ta 


浏览 45
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报