spring中xml配置与annotation注解混合

 spring的xml配置与annotation注解混合使用无法Autowired的问题

问题:

AppUserServiceImpl.java是通过@Service声明的bean,在xml配置文件中重新给AppUserServiceImpl.java配置一个别名“pap_appuser_service”。

普通类AppUserAPIImpl.java中,需要调用AppUserServiceImpl.java,但是运行的时候却报NullPointerException,说明没有注入进来。

java代码

public class AppUserAPIImpl implements AppUser {
	private static ApplicationContext context = ContextLoader.getCurrentWebApplicationContext();

	private IAppUserService service;

	public AppUserAPIImpl(String appCode, String userCode) {
		this.service = (IAppUserService) context.getBean("pap_appuser_service");
	}
}

@Service
public class AppUserServiceImpl implements IAppUserService {
	@Autowired
	private IUserService userService;	
}
配置
<bean id="pap_appuser_service" class="net.yhte.web.pap.user.service.impl.AppUserServiceImpl"/>

问题查找:

1. 查找网上资源,未果.

2. 果断debug,跟踪源码.

       将断点定位到 org.springframework.context.support.AbstractRefreshableApplicationContext#loadBeanDefinitions ,该方法是加载bean的必经之路.跟踪发现,该方法共执行两次,生成了两个不同的 org.springframework.beans.factory.support.DefaultListableBeanFactory, 并且后者的parentBeanFactory为前者,根据原设计是后者可以调用前者的bean 并完成注入.

      现在报NullPointerException,很明显是"父调用子",所以肯定拿不到.在打印的log中进行了佐证.

      在debug中发现,两次执行分别来自不同的beans资源文件: spring-servlet.xml 和 pap-service.xml, 按key查找,很容易找到了配置信息如下.

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/config/applicationContext.xml,
		/WEB-INF/config/application-*.xml,
		/WEB-INF/config/pap/pap-*.xml
	</param-value> 
</context-param>
<servlet>
	<servlet-name>monitor</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring-servlet.xml</param-value>
	</init-param>
</servlet>
 

既然,两次加载,并且加载了不同的beans,虽然有父子的层级关系,但是限制多多. 那么就尝试合二为一.

    在test中,发现因为修改了spring默认加载的文件名,所以删除任何一个配置都不能正确运行.那么就全部设置成一样的吧. test success......

解决方案:

方案一. 将配置文件路径合并, 分别指定给不同配置.

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring-servlet.xml,
		/WEB-INF/config/applicationContext.xml,
		/WEB-INF/config/application-*.xml,
		/WEB-INF/config/pap/pap-*.xml
	</param-value> 
</context-param>
<servlet>
	<servlet-name>monitor</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/spring-servlet.xml,
			/WEB-INF/config/applicationContext.xml,
			/WEB-INF/config/application-*.xml,
			/WEB-INF/config/pap/pap-*.xml
		</param-value>
	</init-param>
</servlet>

方案二. 原有配置不变,合理规划Bean的定义及合理使用.

在方案一中, 使用的简单,粗暴的解决办法. 没有考虑到spring的设计思想. 既然有ioc容器的父子级划分,那么在使用的时候,一定会有用的.

         在使用annotation定义bean 的时候,是需要增加如下代码,对使用何种注解的类才管理到ioc容器中.

<context:component-scan base-package="net.yhte.web.pap">  
   <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />  
   <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />  
   <context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />  
   <context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />  
</context:component-scan>
 

上述提到, 在 spring web的使用中, 会加载两个ioc容器,

          1. 一个是contextConfigLocation定义,用来启动spring核心框架的. 所以在该步骤中,应加载应用中的基础服务信息的bean,如 dao,Service 等等.

          2. 另外一个ioc容器是web加载的容器, 那么只需加载Controller相关的bean.

        因为在spring ioc的 DefaultListableBeanFactory类是支持父子关系,

            1. 子容器是可以访问到父容器中的bean,

            2. 然而父容器访问不了子容器的bean,

        这就保证了, Controller可以访问 Service等, 但是Service 访问不了web层的bean, 这样就将职责分开了.所以修改的配置如下:

spring-servlet.xml
<context:component-scan base-package="net.yhte.web.pap">  
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />  
</context:component-scan>

pap-service.xml
<context:component-scan base-package="net.yhte.web.pap">  
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />  
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />  
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />  
</context:component-scan>
<bean id="pap_appuser_service" class="net.yhte.web.pap.user.service.impl.AppUserServiceImpl"/>
 在开发定义bean的时候, 也需要注意,把bean定义到哪一层级.

猜你喜欢

转载自lty3039-163-com.iteye.com/blog/2292080