Spring奇技淫巧之扩展点的应用!

简介: 最近在看公司项目和中间件的时候,看到一些Spring扩展点的使用,写篇文章学习下,对大家之后看源码都有帮助.首先先介绍下Bean的生命周期我们知道Bean的生命周期分为几个主干流程Bean(单例非懒加载)的实例化阶段Bean的属性注入阶段Bean的初始化阶段Bean的销毁阶段下面是整个Spring容器的启动流程,可以看到除了上述几个主干流程外,Spring还提供了很多扩展点下面详细介绍下Spring的常见的扩展点Spring常见扩展点有时候整个项目工程中bean的数量有上百个,而大部分单测依赖都是整个工程的xml,导致单测执行时需要很长时间(大部分时间耗费在xml中数百个单例非懒加载的bean

前言

文章首发在公众号(月伴飞鱼),之后同步到个人网站:xiaoflyfish.cn/

觉得有收获,希望帮忙点赞,转发下哈,谢谢,谢谢

最近在看公司项目和中间件的时候,看到一些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 map = (Map) ReflectionTestUtils.getField(fac, "beanDefinitionMap");

       for (Map.Entry entry : map.entrySet()) {

           //设置为懒加载

           entry.getValue().setLazyInit(true);

       }

   }

}

「InstantiationAwareBeanPostProcessor#postProcessPropertyValues」

非常规的配置项比如

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

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

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

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

public BeanDefinition parse(Element element, ParserContext parserContext) {

 //...

 registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   //...

 return null;

}

public static Set registerAnnotationConfigProcessors(

  BeanDefinitionRegistry registry, Object source) {

 Set beanDefs = new LinkedHashSet(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);

       }

   }

}

我们看到是在BeanPostProcessor的postProcessBeforeInitialization中进行了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功能,相应地调用方需要被aop织入的对象也需要替换为动态代理对象

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

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

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持有的一些资源释放掉


最后

「写文章画图不易,喜欢的话,希望帮忙点赞,转发下哈,谢谢」

微信搜索:月伴飞鱼,交个朋友

参考书籍:

  • Spring技术内幕
  • Spring源码深度解析
相关文章
|
10天前
|
安全 Java 网络安全
当网络安全成为数字生活的守护者:Spring Security,为您的应用筑起坚不可摧的防线
【9月更文挑战第2天】在数字化时代,网络安全至关重要。本文通过在线银行应用案例,详细介绍了Spring Security这一Java核心安全框架的核心功能及其配置方法。从身份验证、授权控制到防御常见攻击,Spring Security提供了全面的解决方案,确保应用安全。通过示例代码展示了如何配置`WebSecurityConfigurerAdapter`及`HttpSecurity`,帮助开发者有效保护应用免受安全威胁。
31 4
|
24天前
|
安全 前端开发 Java
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择。依赖注入使对象管理交由Spring容器处理,实现低耦合高内聚;AOP则分离横切关注点如事务管理,增强代码模块化。Spring还提供MVC、Data、Security等模块满足多样需求,并通过Spring Boot简化配置与部署,加速微服务架构构建。掌握这些核心概念与工具,开发者能更从容应对挑战,打造卓越应用。
32 1
|
14天前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
42 0
|
10天前
|
IDE Java 开发工具
还在为繁琐的配置头疼吗?一文教你如何用 Spring Boot 快速启动,让开发效率飙升,从此告别加班——打造你的首个轻量级应用!
【9月更文挑战第2天】Spring Boot 是一款基于 Spring 框架的简化开发工具包,采用“约定优于配置”的原则,帮助开发者快速创建独立的生产级应用程序。本文将指导您完成首个 Spring Boot 项目的搭建过程,包括环境配置、项目初始化、添加依赖、编写控制器及运行应用。首先需确保 JDK 版本不低于 8,并安装支持 Spring Boot 的现代 IDE,如 IntelliJ IDEA 或 Eclipse。
39 5
|
13天前
|
安全 NoSQL Java
JeecgBoot应用Spring Authorization Server
Spring Authorizaiton Server, 简称 sas,是一个授权服务器框架,提供 OAuth2.1 与 Open Connect 1.0 认证规范及其他规范的实现,它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 Identity Provider 和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础
30 2
|
14天前
|
算法 Java UED
你的Spring Boot应用是否足够健壮?揭秘限流功能的实现秘诀
【8月更文挑战第29天】限流是保障服务稳定性的关键策略,通过限制单位时间内的请求数量防止服务过载。本文基于理论介绍,结合Spring Boot应用实例,展示了使用`@RateLimiter`注解和集成`Resilience4j`库实现限流的方法。无论采用哪种方式,都能有效控制请求速率,增强应用的健壮性和用户体验。通过这些示例,读者可以灵活选择适合自身需求的限流方案。
30 2
|
14天前
|
Prometheus 监控 Cloud Native
Spring Boot 性能护航!Prometheus、Grafana、ELK 组合拳,点燃数字化时代应用稳定之火
【8月更文挑战第29天】在现代软件开发中,保证应用性能与稳定至关重要。Spring Boot 作为流行的 Java 框架,结合 Prometheus、Grafana 和 ELK 可显著提升监控与分析能力。Prometheus 负责收集时间序列数据,Grafana 将数据可视化,而 ELK (Elasticsearch、Logstash、Kibana)则管理并分析应用日志。通过具体实例演示了如何在 Spring Boot 应用中集成这些工具:配置 Prometheus 获取度量信息、Grafana 显示结果及 ELK 分析日志,从而帮助开发者快速定位问题,确保应用稳定高效运行。
30 1
|
26天前
|
运维 Java Nacos
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
|
29天前
|
监控 Java 测试技术
代码更新不停机:Spring Boot应用实现零停机更新的新质生产力
【8月更文挑战第14天】在快节奏的软件开发与运维环境中,应用的持续部署与更新成为了提升竞争力的关键。传统的停机更新方式不仅影响用户体验,还可能造成业务中断和数据丢失。因此,实现Spring Boot应用的零停机更新成为了现代软件开发团队追求的目标。本文将深入探讨如何通过一系列技术和策略,在不影响服务可用性的前提下,实现Spring Boot应用的平滑升级。
38 2
|
11天前
|
Java Spring 监控
Spring Boot Actuator:守护你的应用心跳,让监控变得触手可及!
【8月更文挑战第31天】Spring Boot Actuator 是 Spring Boot 框架的核心模块之一,提供了生产就绪的特性,用于监控和管理 Spring Boot 应用程序。通过 Actuator,开发者可以轻松访问应用内部状态、执行健康检查、收集度量指标等。启用 Actuator 需在 `pom.xml` 中添加 `spring-boot-starter-actuator` 依赖,并通过配置文件调整端点暴露和安全性。Actuator 还支持与外部监控工具(如 Prometheus)集成,实现全面的应用性能监控。正确配置 Actuator 可显著提升应用的稳定性和安全性。
33 0