查看原文
其他

【309期】Spring Bean 容器启动耗时统计分析

Java精选 2022-08-09

点击上方“Java精选”,选择“设为星标”

别问别人为什么,多问自己凭什么!

下方有惊喜,留言必回,有问必答!

每一天进步一点点,是成功的开始...

spring bean 的生命周期

  • 实例化(instantiate), 用构造器创建一个对象
  • 字段赋值(populate)
  • 初始化(initialize), 执行bean配置里的init方法或者InitializingBean#afterPropertiesSet方法
  • 销毁(destruct)

实例化和字段赋值一般都很快,但是一些重型的bean被IOC容器创建时,需要调用远程服务或者执行耗时的操作,这些操作往往在init方法里实现。统计bean初始化耗时可以发现那些bean影响了系统的启动效率。业务方的bean可以推动业务优化,自己的bean也可以想方法优化性能。

那么如何统计初始化的耗时呢?

spring bean初始化源码分析

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean 观察执行初始化方法的逻辑

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
 if (System.getSecurityManager() != null) {
  AccessController.doPrivileged(new PrivilegedAction<Object>() {
   @Override
   public Object run() {
    invokeAwareMethods(beanName, bean);
    return null;
   }
  }, getAccessControlContext());
 }
 else {
  invokeAwareMethods(beanName, bean);
 }

 Object wrappedBean = bean;
 if (mbd == null || !mbd.isSynthetic()) {
  // 初始化前spring提供的系统钩子
  wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
 }

 try {
  // 执行初始化方法
  invokeInitMethods(beanName, wrappedBean, mbd);
 }
 catch (Throwable ex) {
  throw new BeanCreationException(
    (mbd != null ? mbd.getResourceDescription() : null),
    beanName, "Invocation of init method failed", ex);
 }
 if (mbd == null || !mbd.isSynthetic()) {
  // 初始化后spring提供的系统钩子
  wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
 }
 return wrappedBean;
}

applyBeanPostProcessorsBeforeInitialization做了什么?

取出所有实现BeanPostProcessor的bean,逐个执行一遍postProcessBeforeInitialization方法。

同理,applyBeanPostProcessorsAfterInitialization逻辑依然,只是执行的是postProcessInitialization方法。

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
  throws BeansException 
{

 Object result = existingBean;
 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
  result = beanProcessor.postProcessBeforeInitialization(result, beanName);
  if (result == null) {
   return result;
  }
 }
 return result;

spring系统钩子 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

BeanPostProcessor接口仅仅提供两个方法,用在在初始化bean的时候进行定制开发。

另外,推荐下 Spring boot 的实战开源项目:

https://gitee.com/yoodb/jing-xuan

public interface BeanPostProcessor {

 Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

bean初始化耗时功能开发demo

简单demo

package org.dubbo.server.service.tool;

import com.google.common.collect.Maps;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author LvSheng
 **/

@Component
public class TimeCostBeanPostProcessor implements BeanPostProcessor {
 
 Map<String, Long> costMap = Maps.newConcurrentMap();
 
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  costMap.put(beanName, System.currentTimeMillis());
  return bean;
 }
 
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  long start = costMap.get(beanName);
  long cost  = System.currentTimeMillis() - start;
  if (cost > 0) {
   costMap.put(beanName, cost);
   System.out.println("class: " + bean.getClass().getName()
            + "\tbean: " + beanName
            + "\ttime" + cost);
  }
  return bean;
 }
}

作者:bruce128

https://blog.csdn.net/bruce128/article/details/104419869

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

------ THE END ------

精品资料,超赞福利!

☆ 主流Java进阶技术(学习资料分享)


3000+ 道各类技术面试题在线刷,最新、最全 Java 面试题!

期往精选  点击标题可跳转

【301期】SpringBoot 一个接口同时支持 form、form-data、json 优雅写法

【302期】SpringBoot 项目鉴权的 4 种方式,你了解吗?

【303期】SpringBoot + Redis 实现搜索栏热搜、不雅文字过滤功能

【304期】全文搜索引擎 Elasticsearch,这篇文章给讲透了!

【305期】Spring Cloud 优雅下线+灰度发布

【306期】微服务 Spring Cloud 架构实现分布式日志采集方案

【307期】从实现原理来讲,Nacos 为什么这么强?

【308期】Java 实现 10 万,并发去重,优雅地处理重复请求!

 技术交流群!

最近有很多人问,有没有读者交流群!想知道如何加入?方式很简单,兴趣相投的朋友,只需要点击下方卡片,回复“加群”,即可无套路入交流群!

文章有帮助的话,在看,转发吧!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存