写这篇文章的原因是本地代码运行结果和线上服务器运行结果不一致,类的加载顺序不一样,导致了意想不到的bug,由此展开了对spring自动扫描类的加载机制的探索,先看一下代码,主要涉及到RedissonProperties类和MopFictionTurntableSchedule类。
代码整体结构如下:
spring用的4.2版本,本地用jetty7.6.9.v20130131 运行,服务器用resin4.0.36运行
@Component
public class RedissonProperties {
private static final Logger logger = LoggerFactory.getLogger(RedissonProperties.class);
public static String redissonHost = null;
public static Integer redissonPort = null;
@Value("${redisson.host}")
public void setRedissonHost(String redissonHostP) {
redissonHost = redissonHostP;
}
@Value("${redisson.port}")
public void setRedissonPort(Integer redissonPortP) {
redissonPort = redissonPortP;
}
}
@Component
public class MopFictionTurntableSchedule {
private static final Logger logger = LoggerFactory.getLogger(MopFictionTurntableSchedule.class);
@Autowired
private FictionTurntableMapper fictionTurntableMapper = null;
private static RedissonClient redissonClient = null;
static {
logger.debug("start MopFictionTurntableSchedule init ");
logger.debug("redissonHost "+RedissonProperties.redissonHost );
logger.debug("redissonPort "+RedissonProperties.redissonPort);
if (redissonClient == null) {
String redissonAddress = "redis://" + RedissonProperties.redissonHost + ":" + RedissonProperties.redissonPort;
Config config = new Config();
config.useSingleServer().setAddress(redissonAddress);
redissonClient = Redisson.create(config);
}
}
//后面省略
}
项目在本地运行并没有任何问题,输出结果如下:
但是当把项目丢在服务器上却报错了,输出结果如下:
对比分析可以得出在本地环境RedissonProperties 比MopFictionTurntableSchedule 加载的早,所以RedissonProperties 的静态属性成功注入,等到MopFictionTurntableSchedule 被加载的时候就没有问题。
服务器环境MopFictionTurntableSchedule 要比RedissonProperties 加载的早,所以导致异常。
由此可见spring在使用自动扫描过程中加载类的顺序是有区别的。
下面开始源码分析:
在web项目中我们配置了
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
可见org.springframework.web.context.ContextLoaderListener这个类就是spring在web项目中的入口。
通过idea的调试功能,一步步跟进去,很快可以找到spring初始化的核心代码。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticas