Spring容器启动耗时分析 (Bean启动耗时)

VSole2022-04-23 15:05:46

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() {
   @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的时候进行定制开发。
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 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;
 }
}
推荐
主流Java进阶技术(学习资料分享)
Java面试题宝典
加入Spring技术开发社区


PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!
初始化bean
本作品采用《CC 协议》,转载必须注明作者和本文链接
spring bean 的生命周期 实例化(instantiate), 用构造器创建一个对象 字段赋值(populate) 初始化(initialize), 执行bean配置里的init方法或者InitializingBean#afterPropertiesSet方法 销毁(destruct) 实例化和字段赋值一般都很快,但是一些重型的bean被IOC容器创建时,需要调用远程服务或者执行耗时的操
Springboot更是封装了Spring,遵循约定大于配置,加上自动装配的机制。让使用者以最小的代价接入。当然了解了bean的各个生命周期也能促进我们加深对spring的理解。在网上搜索spring扩展点,发现很少有博文说的很全的,只有一些常用的扩展点的说明。并且整理出了一个bean在spring内部从被加载到最后初始化完成所有可扩展点的顺序调用图。这个点允许被用户自己扩展。用户可以在整个spring容器还没被初始化之前做一些事情。
Spring MVC和Spring WebFlux都通过RquestMappingHandlerMapping和RequestMappingHndlerAdapter两个类来提供对@RequestMapping注解的支持。
以下两个工具方式的差别是,前者在获取失败时抛出异常。然后在代码中就可以获取spring容器bean了。
所以,这就要求Spring事务能支持上面各种场景,这就是Spring事务传播机制的由来。
spring-boot下的thymeleaf模板注入挺有趣的,本文尝试对该漏洞一探究竟,如有谬误,还请同学们指教。
收集内存马打入方式
2023-05-29 09:42:33
收集内存马打入方式
S2-007的漏洞原理是在处理类型转换的错误时会存入错误到内存中,在后续调用流程中触发OGNL表达式注入。
前言在前面的一章中,主要在理论上进行了各种内存马的实现,这里就做为上一篇的补充,自己搭建反序列化的漏洞环境来进行上文中理论上内存马的注入实践。这是内存马系列文章的第十四篇。在/unser路由中获取了请求体输入流进行了反序列化调用。在debug过程中,发现是因为不能够找到他的构造方法而报错,更改后的注入方式。
fastjson反序列化已经是近几年继Struts2漏洞后,最受安全人员欢迎而开发人员抱怨的一个漏洞了。
VSole
网络安全专家