Java中Spring技巧之扩展点的应用

 更新时间:2022年04月21日 10:43:33   作者:程序员段飞   
这篇文章主要介绍了Java中Spring技巧之扩展点的应用,下文Spring容器的启动流程图展开其内容的相关资料,具有一定的参考价值,需要的小伙伴可以参考一下

前言:

最近在看公司项目和中间件的时候,看到一些Spring扩展点的使用,写篇文章学习下,对大家之后看源码都有帮助

首先先介绍下Bean的生命周期:

我们知道Bean的生命周期分为几个主干流程

  • Bean(单例非懒加载)的实例化阶段
  • Bean的属性注入阶段
  • Bean的初始化阶段
  • Bean的销毁阶段

下面是整个Spring容器的启动流程,可以看到除了上述几个主干流程外,Spring还提供了很多扩展点

下面详细介绍下Spring的常见的扩展点

Spring常见扩展点

「BeanFactoryPostProcessor#postProcessBeanFactory」

有时候整个项目工程中bean的数量有上百个,而大部分单测依赖都是整个工程的xml,导致单测执行时需要很长时间(大部分时间耗费在xml中数百个单例非懒加载的bean的实例化及初始化过程)

解决方法:利用Spring提供的扩展点将xml中的bean设置为懒加载模式,省去了Bean的实例化与初始化时间

public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
        Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
        for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
            //设置为懒加载
            entry.getValue().setLazyInit(true);
        }
    }
}

「InstantiationAwareBeanPostProcessor#postProcessPropertyValues」

非常规的配置项比如

<context:component-scan base-package="com.zhou" />

Spring提供了与之对应的特殊解析器

正是通过这些特殊的解析器才使得对应的配置项能够生效

而针对这个特殊配置的解析器为 ComponentScanBeanDefinitionParser

在这个解析器的解析方法中,注册了很多特殊的Bean

public BeanDefinition parse(Element element, ParserContext parserContext) {
  //...
  registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    //...
  return null;
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
   BeanDefinitionRegistry registry, Object source) {
  Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
  //...
    //@Autowire
  if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
  }
  // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
   //@Resource
  if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      //特殊的Bean
   RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
  }
  //...
  return beanDefs;
 }

以@Resource为例,看看这个特殊的bean做了什么

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
  implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
      public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, 
      Object bean, String beanName) throws BeansException {
          InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass());
          try {
            //属性注入
            metadata.inject(bean, beanName, pvs);
          }
          catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
          }
          return pvs;
    }
}

我们看到在postProcessPropertyValues方法中,进行了属性注入

「invokeAware」

实现BeanFactoryAware接口的类,会由容器执行setBeanFactory方法将当前的容器BeanFactory注入到类中

@Bean
class BeanFactoryHolder implements BeanFactoryAware{
    private static BeanFactory beanFactory;
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

「BeanPostProcessor#postProcessBeforeInitialization」

实现ApplicationContextAware接口的类,会由容器执行setApplicationContext方法将当前的容器applicationContext注入到类中

@Bean
class ApplicationContextAwareProcessor implements BeanPostProcessor {
    private final ConfigurableApplicationContext applicationContext;
    public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
      this.applicationContext = applicationContext;
    }
    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
      //...
      invokeAwareInterfaces(bean);
      return bean;
    }
    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof ApplicationContextAware) {
          ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}

我们看到是在BeanPostProcessorpostProcessBeforeInitialization中进行了setApplicationContext方法的调用

class ApplicationContextHolder implements ApplicationContextAware{
    private static ApplicationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

「afterPropertySet()和init-method」

目前很多Java中间件都是基本Spring Framework搭建的,而这些中间件经常把入口放到afterPropertySet或者自定义的init中

「BeanPostProcessor#postProcessAfterInitialization」

熟悉aop的同学应该知道,aop底层是通过动态代理实现的

当配置了<aop:aspectj-autoproxy/>时候,默认开启aop功能,相应地调用方需要被aop织入的对象也需要替换为动态代理对象

不知道大家有没有思考过动态代理是如何**「在调用方无感知情况下替换原始对象」**的?

根据上文的讲解,我们知道:

<aop:aspectj-autoproxy/>

Spring也提供了特殊的解析器,和其他的解析器类似,在核心的parse方法中注册了特殊的bean

这里是一个BeanPostProcessor类型的bean

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
 @Override
 public BeanDefinition parse(Element element, ParserContext parserContext) {
    //注册特殊的bean
  AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
  extendBeanDefinition(element, parserContext);
  return null;
    }
}

将于当前bean对应的动态代理对象返回即可,该过程对调用方全部透明

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
          Object cacheKey = getCacheKey(bean.getClass(), beanName);
          if (!this.earlyProxyReferences.containsKey(cacheKey)) {
            //如果该类需要被代理,返回动态代理对象;反之,返回原对象
            return wrapIfNecessary(bean, beanName, cacheKey);
          }
        }
        return bean;
 }
}

正是利用Spring的这个扩展点实现了动态代理对象的替换

「destroy()和destroy-method」

bean生命周期的最后一个扩展点,该方法用于执行一些bean销毁前的准备工作,比如将当前bean持有的一些资源释放掉

总结

到此这篇关于Java中Spring技巧之扩展点的应用的文章就介绍到这了,更多相关Spring扩展点应用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • ZooKeeper入门教程三分布式锁实现及完整运行源码

    ZooKeeper入门教程三分布式锁实现及完整运行源码

    本文是ZooKeeper入门系列教程,分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同工具ZooKeeper,当然也有着标准的实现方式。本文介绍在zookeeper中如何实现排他锁
    2022-01-01
  • 如何修改json字符串中某个key对应的value值

    如何修改json字符串中某个key对应的value值

    这篇文章主要介绍了如何修改json字符串中某个key对应的value值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Java的MyBatis框架中对数据库进行动态SQL查询的教程

    Java的MyBatis框架中对数据库进行动态SQL查询的教程

    这篇文章主要介绍了Java的MyBatis框架中对数据库进行动态SQL查询的教程,讲解了MyBatis中一些控制查询流程的常用语句,需要的朋友可以参考下
    2016-04-04
  • 基于Spring-Security自定义登陆错误提示信息

    基于Spring-Security自定义登陆错误提示信息

    这篇文章主要介绍了Spring-Security自定义登陆错误提示信息,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • javax.validation.constraints如何校验参数合法性

    javax.validation.constraints如何校验参数合法性

    本文将深入探讨javax.validation.constraints的基本用法和高级应用,帮助读者更好地理解和运用这个强大的校验框架,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • 浅谈Spring refresh的工作流程

    浅谈Spring refresh的工作流程

    这篇文章主要介绍了浅谈Spring refresh的工作流程,refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext容器,让我们一起来学习一下吧
    2023-04-04
  • 史上最佳springboot Locale 国际化方案

    史上最佳springboot Locale 国际化方案

    今天给大家分享史上最佳springboot Locale 国际化方案,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-08-08
  • springboot2.1.7整合thymeleaf代码实例

    springboot2.1.7整合thymeleaf代码实例

    这篇文章主要介绍了springboot2.1.7整合thymeleaf代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java JDK的多版本共存实现方法

    Java JDK的多版本共存实现方法

    有时候系统中需要多个jdk版本共存,我们在做特定的操作时需要特定的版本,这篇文章主要给大家介绍了关于Java JDK的多版本共存实现 的相关资料,需要的朋友可以参考下
    2023-09-09
  • java高并发下CopyOnWriteArrayList替代ArrayList

    java高并发下CopyOnWriteArrayList替代ArrayList

    这篇文章主要为大家介绍了java高并发下CopyOnWriteArrayList替代ArrayList的使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12

最新评论