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扩展点应用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java SpringBoot开发小技巧详解

    目录 一.SpringBoot开发小技巧 1.1 Lombok 1.2 dev-tools 1.3 Spring Initializr 总结 一.SpringBoot开发小技巧 1.1 Lombok 作用:在程序编译的时候,自动帮我们生成setter和getter方法以及我们的toString方法和我们的全参和无参构造器等等. 那么,怎么用呢?很简单,用下边这四个注解就行了: 1.@Data:自动生成setter和getter方法. 2.@ToString:自动生成toString方法. 3.@

  • Springboot启动扩展点超详细教程小结

    1.背景 Spring的核心思想就是容器,当容器refresh的时候,外部看上去风平浪静,其实内部则是一片惊涛骇浪,汪洋一片.Springboot更是封装了Spring,遵循约定大于配置,加上自动装配的机制.很多时候我们只要引用了一个依赖,几乎是零配置就能完成一个功能的装配. 我非常喜欢这种自动装配的机制,所以在自己开发中间件和公共依赖工具的时候也会用到这个特性.让使用者以最小的代价接入.想要把自动装配玩的转,就必须要了解spring对于bean的构造生命周期以及各个扩展接口.当然了解了bean

  • 如何利用Spring的@Import扩展点与spring进行无缝整合

    利用Spring的@Import扩展与spring进行无缝整合前言BeanFactoryPostProcessor@Import实现POM文件定义数据层Resource(dao)层的扫描注解定义我的数据层Resource使用的注解ArteryResourceImportBeanDefinitionRegistrar实现自定义扫描类ClassPathArteryResourceScanner代理注册工厂ResourceRegistryResouce的代理工厂真正的代理类方法调用类AbstractB

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

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

  • Java中Spring扩展点详解

    目录 如何在所有Bean创建完后做扩展 方式一 方式二 Spring通过initPropertySources扩展方法设置环境配置 @Import进行扩展 如何在所有Bean创建完后做扩展 方式一 Spring在容器刷新完成后会注册ContextRefreshedEvent. 所以可以自定义事件监听器监听该事件进行扩展. 监听器实现: @Component public class ContextRefreshedEventListener implements ApplicationListe

  • Java中Spring获取bean方法小结

    Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢? Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IoC的配置机制.BeanFactory使管理不同类型的Java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供

  • 详解java 中Spring jsonp 跨域请求的实例

    详解java 中Spring jsonp 跨域请求的实例 jsonp介绍 JSONP(JSON with Padding)是JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题.由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外.利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSO

  • java中Spring Security的实例详解

    java中Spring Security的实例详解 spring security是一个多方面的安全认证框架,提供了基于JavaEE规范的完整的安全认证解决方案.并且可以很好与目前主流的认证框架(如CAS,中央授权系统)集成.使用spring security的初衷是解决不同用户登录不同应用程序的权限问题,说到权限包括两部分:认证和授权.认证是告诉系统你是谁,授权是指知道你是谁后是否有权限访问系统(授权后一般会在服务端创建一个token,之后用这个token进行后续行为的交互). spring

  • java 中Spring task定时任务的深入理解

    java 中Spring task定时任务的深入理解 在工作中有用到spring task作为定时任务的处理,spring通过接口TaskExecutor和TaskScheduler这两个接口的方式为异步定时任务提供了一种抽象.这就意味着spring容许你使用其他的定时任务框架,当然spring自身也提供了一种定时任务的实现:spring task.spring task支持线程池,可以高效处理许多不同的定时任务.同时,spring还支持使用Java自带的Timer定时器和Quartz定时框架.

  • JAVA 中Spring的@Async用法总结

    JAVA 中Spring的@Async用法总结 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法. 1.  何为异步调用? 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的

  • Java中Spring Boot+Socket实现与html页面的长连接实例详解

    Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码 功能介绍 客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器给客户端发送消息(轮询方式) 注意:socket只是实现一些简单的功能,具体的还需根据自身情况,代码稍微改造下 项目搭建 项目结构图 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xml

  • Java中Spring Boot支付宝扫码支付及支付回调的实现代码

    前言:最近开发支付宝支付功能,总结一下做个分享 官方文档:https://opendocs.alipay.com/apis 支付宝沙箱地址: https://openhome.alipay.com/platform/appDaily.htm?tab=info 支付宝支付流程: 准备工作:获取支付宝沙箱数据(APPID,支付宝网关,RSA2秘,沙箱支付账号等) 集成SpringBoot,使用Java代码发起支付请求 支付宝收到支付请求后,返回HTML代码片段,用于前端展示二维码 扫码支付成功后,支

  • Java中Spring的单例模式使用

    目录 1.spring单例 V.S 设计模式的单例 2.成员变量的解决方式 3.Spring并发问题 4.对实体bean在多线程中的处理 5.spring无状态的支持 6.spring有状态的支持 7.ThreadLocal 8.ThreadLocal使用 9.ThreadLocal V.S synchronized 10.Spring使用ThreadLocal解决线程安全问题 1.spring单例 V.S 设计模式的单例 设计模式单例,在整个应用中只有一个实例 spring单例,在一个IoC容

随机推荐