在Spring项目中,通常会在一些组件中做一些初始化的操作,例如初始化一些缓存,初始化加载数据到内存,初始化数据库中的数据,初始化分布式锁等等等等。但是初始化这个操作如果耗时,可能会导致项目启动特别慢,甚至出现web容器启动过程中被阻塞,服务长时间无法访问的情况,下面将举例说明这个问题。
温馨提示:本博客已经发布小程序,可在微信小程序中搜索”百变码农”,手机上也能看!
1、示例代码
配置类BeanConfig:
@Configuration @ComponentScan(basePackages = "com.wb.spring.propertyvalue") public class BeanConfig { @Bean public Person person() { return new Person(); } }
实体类Person:
public class Person { @PostConstruct public void initTask() { System.out.println("******"); try { Thread.sleep(300000L); } catch (Exception e) { e.printStackTrace(); } System.out.println("init finished..."); } }
测试类:
public class TestMain { public static void main(String[] args) { ApplicationContext acx = new AnnotationConfigApplicationContext(BeanConfig.class); Object person = acx.getBean("person"); System.out.println(person); } }
运行测试类会发现,打印出”******”之后,然后一直处于阻塞状态,无法继续向下运行。
原因:Spring在初始化bean的时候,会扫描@PostConstruct标注的初始化方法,此时初始化方法比较耗时,阻塞初始化线程。
2、Web应用中使用@PostConstruct注解
(1)现象
如果在@PostConstruct中运行了同步的耗时方法,将会导致tomcat启动线程被阻塞。
(2)原因
Web应用中,Tomcat启动的时候,会扫描类路径下HadlerTypes注解标注的子类或者间接子类,然后执行容器刷新操作,去完成Bean的创建和依赖注入。使用@PostConstruct注解的时候,而在刷新Spring容器的时候,初始化@PostConstruct注解标注的初始化方法,将会导致tomcat用于初始化容器的线程被阻塞,无法继续完成其他初始化操作,而导致阻塞。
(3)解决办法
① 用法上,在@PostConstruct中的初始化应该尽可能简单,例如:初始化分布式锁,初始化jvm缓存,异步启动一些后台线程等;
② 如果需要在初始化时执行一些耗时的操作,可以考虑开启线程,使用异步的方式,此时不会阻塞住初始化容器的线程;
(4)示例代码
上述1中的实例代码中的Person类可以修改为如下:
public class Person { @PostConstruct public void initTask() { System.out.println("******"); new Thread(()-> { try { Thread.sleep(300000L); } catch (Exception e) { e.printStackTrace(); } }).start(); System.out.println("init finished..."); } }
会立刻输出”******”和”init finished…”,不会同步阻塞主线程的执行。
至此,由@PostConstruct注解导致的tomcat启动阻塞问题说明完毕,欢迎转发!关于@PostConstruct的应用可以参考文章Spring常用注解之Bean生命周期相关注解。
注意:文章属于原创,如果转发请标注文章来源:个人小站【www.jinnianshizhunian.vip】
另外提供一些优秀的Java架构师及IT开发视频,书籍资料。无需注册,无需登录即可下载,免费下载地址:https://www.592xuexi.com