这几天实习生同学遇到一个spring注入一直为null的情况。
代码:
监听器代码 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@WebListener
public class ServletListener implements ServletContextListener{
private org.apache.log4j.Logger logger = org.apache.log4j.Logger
.getLogger(getClass());
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
@Override
public void contextInitialized(ServletContextEvent sce) {
SingleCheck check = new SingleCheck();
check.run();
}
}主入口代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62public class SingleCheck {
private Logger logger = Logger
.getLogger(getClass());
@Resource (name = "MonitorMasterService")
private MonitorMasterService monitorMasterService;
@Resource (name = "MonitorSlaveService")
private MonitorSlaveService monitorSlaveService;
public void run() {
try {
monitorMasterService.MOnline();
System.out.println("master 检查完成");
} catch (Exception e) {
e.printStackTrace();
String strUri =
"http://xxx:8080/oc_alarm.php?"
+ "mail=xx@xx.com"
+ "&oc_msg=主库连接失败";
try {
URI uri = new URI(strUri);
Desktop.getDesktop().browse(uri);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
try {
monitorSlaveService.SOnline();
} catch (Exception e) {
e.printStackTrace();
}
try {
monitorSlaveService.testSlaveValues();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("test");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("ctx>>" + ctx);
MonitorMasterServiceImpl mo = (MonitorMasterServiceImpl) ctx.getBean("monitorMasterServiceImpl");
System.out.println("MonitorMasterService>>" + mo);
mo.MOnline();
}
}MonitorMasterService
1
2
3
4
5public interface MonitorMasterService {
int MOnline();
}MonitorMasterServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13@Service
public class MonitorMasterServiceImpl implements MonitorMasterService{
@Resource
private MonitorMasterMapper monitorMasterMapper;
@Override
public int MOnline(){
System.out.println("aaaaaaa");
return monitorMasterMapper.testOnline();
}
}MonitorMasterMapper
1
2
3
4
5
6public interface MonitorMasterMapper {
@Select("select 1;")
int testOnline();
}applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60<?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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:config.properties" />
<context:component-scan base-package="monitor" />
<context:annotation-config />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_username}" />
<property name="password" value="${jdbc_password}" />
<!--maxActive: 最大连接数量 -->
<property name="maxActive" value="150" />
<!--minIdle: 最小空闲连接 -->
<property name="minIdle" value="5" />
<!--maxIdle: 最大空闲连接 -->
<property name="maxIdle" value="20" />
<!--initialSize: 初始化连接 -->
<property name="initialSize" value="30" />
<!-- 连接被泄露时是否打印 -->
<property name="logAbandoned" value="true" />
<!--removeAbandoned: 是否自动回收超时连接 -->
<property name="removeAbandoned" value="true" />
<!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="removeAbandonedTimeout" value="10" />
<!--maxWait: 超时等待时间以毫秒为单位 1000等于60秒 -->
<property name="maxWait" value="1000" />
<!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->
<property name="timeBetweenEvictionRunsMillis" value="10000" />
<!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
<property name="numTestsPerEvictionRun" value="10" />
<!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
<property name="minEvictableIdleTimeMillis" value="10000" />
<property name="validationQuery" value="SELECT NOW() FROM DUAL" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="monitor.alias"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="monitor.master_slave.mapper,monitor.master.mapper,monitor.slave.mapper" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
过程中遇到的问题整理
@Resource 和 @Autowired 的区别及优劣。
@Autowired + @Autowired = @Resource
@Resource的注解是J2EE提供的
一般建议使用@ResourceMybatis注入异常
1
2
3
4
5
6
7
8
9
10
11
12
13Exception in thread "main" org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): monitor.master.mapper.MonitorMasterMapper.testOnline
at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:189)
at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:43)
at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:58)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:51)
at com.sun.proxy.$Proxy10.testOnline(Unknown Source)
at monitor.master.service.impl.MonitorMasterServiceImpl.MOnline(MonitorMasterServiceImpl.java:23)
at monitor.master_slave.check.SingleCheck.main(SingleCheck.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
后来发现IDEA在编译的时候没有把mapper的xml文件编译进war包里所有会有这个错误,
改成注解的形式就没有这个问题了。
- MyBatis注入异常续集
1
2
3
4
5
6
7
8
9
10
11
12/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("test");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("ctx>>" + ctx);
MonitorMasterServiceImpl mo = (MonitorMasterServiceImpl) ctx.getBean("monitorMasterServiceImpl");
System.out.println("MonitorMasterService>>" + mo);
mo.MOnline();
}
在main里面能跑通了,但是启动web 依然报了null 的错误。
按理说注入应该是没问题了。但程序放在监听器里就是有问题,由于之前没有Spring项目的经验。
猜测是不是执行到那一步没有加载applicationContext.xml
debug之后发现已经存在这个容器
补充下spring ApplicationContext配置文件的加载过程
ContextLoaderListener
位于spring-web-{$version}.RELEASE.jar中,ServletContextListener
实现了ServletContextListener
接口,系统启动时会回调contextInitialized()
方法。
在这个方法中,利用context-param
中指定的配置文件初始化ApplicationContext
,然后以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
为key,注册为servletContext
的一个属性。
更具体的可以看参考链接
加载容器也没问题,那就只能是Listener的问题。
看listener的写法:1
2
3
4
5
6@Override
public void contextInitialized(ServletContextEvent sce) {
SingleCheck check = new SingleCheck();
check.run();
}
按我过去的经验,spring是IOC框架,实习生同学居然用了new的方式生成一个对象,感觉这里应该是有问题的,猜测应该是使用new方式生成的对象是不会加入到application context容器里的。
于是改成:1
2
3
4
5
6
7
8
9
10
11
12@Override
public void contextInitialized(ServletContextEvent sce) {
try {
MonitorMasterServiceImpl mo = (MonitorMasterServiceImpl) WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext())
.getBean("monitorMasterServiceImpl");
mo.MOnline();
} catch (Exception e) {
e.printStackTrace();
}
}
果然就完成了。
至于是不是我猜测的那样,继续查了下资料。
new的对象 是不能注入东西的,能注入其他组件的的必须是在application context中加载的单例对象。