applicationContext.xml和dispatch-servlet.xml的区别及引发的问题和教训

本文解析了Spring MVC环境中applicationContext.xml与dispatcher-servlet.xml的区别及如何正确配置MyBatis事务,避免重复bean定义导致事务失效的问题。
摘要由CSDN通过智能技术生成

大家知道, 在spring mvc中, 在applicationContext.xml 和 dispatch-servlet.xml中都可以进行spring 的配置, 那么他们有什么区别呢:

我们先看一下Spring 官方文档:

	Spring lets you define multiple contexts in a parent-child hierarchy.
	The applicationContext.xml defines the beans for the "root webapp context", i.e. the context associated with the webapp.
	The spring-servlet.xml (or whatever else you call it) defines the beans for one servlet's app context. There can be many of these in a webapp, one per Spring servlet (e.g. spring1-servlet.xml for servlet spring1, spring2-servlet.xml for servlet spring2).
	Beans in spring-servlet.xml can reference beans in applicationContext.xml, but not vice versa.
	All Spring MVC controllers must go in the spring-servlet.xml context.
	In most simple cases, the applicationContext.xml context is unnecessary. It is generally used to contain beans that are shared between all servlets in a webapp. If you only have one servlet, then there's not really much point, unless you have a specific use for it. 

可见, applicationContext.xml 和 dispatch-servlet.xml形成了两个父子关系的上下文,经过测试, 发现:

1) 一个bean如果在两个文件中都被定义了(比如两个文件中都定义了component scan扫描相同的package), spring会在application context和 servlet context中都生成一个实例,他们处于不同的上下文空间中,他们的行为方式是有可能不一样的(见下面描述的问题)。

 2)  如果在application context和 servlet context中都存在同一个 @Service 的实例, controller(在servlet context中) 通过 @Resource引用时, 会优先选择servlet context中的实例。

 

在Spring 里配置Mybatis 事务时出现的问题:

                 按照网上的说明, 我在applicationContext.xml文件中增加了如下的配置:

  1.       applicationContext.xml

<!--这是原先的sqlSession和dao的配置, 增加事务时候配置保持不变-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="configLocation" value="classpath:mybatis.xml"></property>
		<property name="mapperLocations" value="classpath:com/sjl/dao/*-mapper.xml"></property>
	</bean>
	
	<bean id="idao" class="com.sjl.dao.IdaoImpl">
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>        
 

 

    <!--增加的mybatis 事务控制 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
          <property name="dataSource" ref="dataSource"></property>    
    </bean> 
	<tx:annotation-driven transaction-manager="transactionManager"/>
   2.     然后在dao中使用 @Transactional 注解声明事务

package com.sjl.base;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;

import com.sjl.common.EntityClassUtil;
import com.sjl.common.Pager;
import com.sjl.dao.Idao;

public class AbstractBaseDao<T, PK extends Serializable> implements BaseDao <T, PK> {
	@Resource
	private Idao<T, PK> idao;
	
	private Class entityClass = EntityClassUtil.getEntityClass(getClass());
	<strong><span style="color:#ff0000;">@Transactional</span></strong>
	public void save(T entity) {
		
		idao.save(entity);
		throw new RuntimeException();//抛出异常, 事务回滚

	}
	public void delete(PK pk) {
		idao.delete(entityClass, pk);
		
	}
	public Pager<T> findByPage(int pageOffset, int pageSize) {
		
		HashMap<String, Integer> map = new HashMap();
		map.put("pageOffset", pageOffset);
		map.put("pageSize", pageSize);
		Pager<T> pager = idao.findByPage(entityClass, map);
		return pager;
		
		
	}

	
}
其中的idao定义如下:
<pre name="code" class="java">package com.sjl.dao;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.stereotype.Repository;

import com.sjl.common.Pager;

@Repository("idao")
public class IdaoImpl<T, PK extends Serializable> extends SqlSessionDaoSupport implements Idao<T, PK>  {

	public void save(T entity) {
		
		this.getSqlSession().insert(entity.getClass().getName()+".add", entity);
	}

	public void delete(Class<T> entityClass, PK pk) {
		this.getSqlSession().insert(entityClass.getName()+".delete", pk);
		
	}

	public Pager<T> findByPage(Class<T> entityClass, Map param){
		
		Pager<T> pager = new Pager();
		
		List<T> pageContent = this.getSqlSession().selectList(entityClass.getName()+".findPageContent", param);
		pager.setPageContent(pageContent);
		
		int count = this.getSqlSession().selectOne(entityClass.getName()+".findTotal");
		pager.setTotalCount(count);
		
		return pager;
		
	}
}

 

经过以上配置,按照道理应该没有问题了, 但是程序运行后发现事务始终没有启动,连transaction-manager都没有进入, 找了很久没有发现问题出在哪里。后来看到网上同样的配置能运行成功, 比较后发现他是在普通Java Application方式下运行成功的,我把自己的程序也在普通的Java Application方式下运行, 发现transaction起作用了,由此感觉应该是Spring MVC环境下出现的问题,而且应该是transaction 的配置没有起作用, 偶然记起在自己的配置中,dispatch-servlet.xml和applicationContext.xml中 配置的component-scan都扫描了相同的包,这样的话, 虽然在application Context中定义了transaction-manager, 但是这个transaction-manager的配置却是作用在application context中定义的bean上面的。根据前面讲到的application context和dispatcher-servlet context的关系, dispatcher-servlet context 和application context中都存在着相同类型的service bean 实例, 那么controller将优先调用dispatcher-servlet context 中的service bean,而dispatcher-servlet context 中由于没有配置transaction manager, 所以生成的service bean实例没有事务的植入(aop), 当然就无法启动事务了。

解决办法:

1. 在applicationContext.xml 中修改component-scan配置如下:

<context:component-scan base-package="com.sjl">
<span style="white-space:pre">	</span><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />  //不扫描标记@Controller的类
</context:component-scan>
2. 在dispatcher-servlet.xml中修改component-scan配置:

<context:component-scan base-package="com.sjl.controller"></context:component-scan> .//只扫描controller package


经过上面的配置, Service bean实例就只会在application Context中生成,就可以使用transaction-manager管理的声明式事务了。


教训:

在application context和dispatcher-servlet定义的bean最好不要重复, dispatcher-servlet最好只是定义controller类型的bean。



 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值