问题描述
最近接手的一个较老的项目,在其上进行开发时遇到了一个spring懒加载导致的问题,下面进行简单的总结。
为了方便查看,下面是经过简化处理过新增的几个类,主要的实现是业务处理类SomeService在使用B的实例时,会根据不同的事件类型去选择具体的执行器AA的实现类去做具体的执行。
@Service
public class SomeService {
@Autowired
private B b;
public void doSomething() {
// 做一些业务逻辑....
Event event = new Event();
event.setType("xxx");
event.setXxx(..);
b.process(event);
}
}
@Component
public class B {
private Map<String, AA>> listenersMap = new ConcurrentHashMap<>();
public void registerXxx(String name, AA a) {
listenersMap.put(name, a);
}
public void process(Event event) {
AA a = listenersMap.get(event.getType());
if (null != a) {
a.doProcess();
}
}
}
public interface AA {
void doProcess(Event event);
}
@Service
public class A implements AA{
@Autowired
private B b;
@PostConstruct
public void init() {
b.registerXxx(A.class.getName(), this);
}
@Override
public void doProcess(Event event) {
}
}
如果是在启动项目时去加载并实例化所有的bean(不延迟加载),那么上面的实现是没有问题。但是,这个项目使用的是spring xml的配置方式,且有如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd"
default-lazy-init="true">
省略具体内容
</beans>
最关键的是有default-lazy-init="true"
,由于使用的是懒加载的方式,所以只有在第一次使用bean时才会实例化,所以,在SomeService在使用B时,会实例化B,而此时由于B没有直接去依赖AA,AA不会进行实例化,那么就不会将自己注册到B的listenersMap中,导致实际上没有调用到AA的doProcess。
这里的实现其实就是将B和AA接口的实现类通过反向注册实现解耦,在懒加载的机制下就会出现问题。
总结
懒加载的好处是服务在启动是会节省初始化bean的时间,启动速度更快,同时可以节省内存,在后续第一次用到时才会进行实例化。
但是,使用懒加载可能会掩盖住问题,比如上面案例中,还有可能有某些bean在实例化时会出错的情况,不符合fast-fail原则。所以,慎用懒加载。