我是Spring MVC的新手,所以我正在创建一个小型的Web应用程序,只是为了尝试它。
我正在使用Spring 4.2和Hibernate 5.
网络应用有一个Spring servlet-context.xml
和一个Spring application-context.xml
。
我有@Controller
的方法,可以使用@Service
。此服务的findAll()
方法标记为@Transactional
。
web.xml
看起来像这样:
...
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:*-context.xml
</param-value>
</context-param>
<servlet>
<servlet-name>myDispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
...
控制器:
@Controller
public class TestOneController
{
@Autowired
private UserManager userManager;
@RequestMapping("views/test")
public ModelAndView defaultResolution()
{
List<User> users = userManager.findAll();
// here I build my String msg object
return new ModelAndView("views/resolution", "msg", msg);
}
}
服务:
@Service("userManager")
public class UserManagerImpl implements UserManager
{
@Autowired
private UserDao userDao;
@Override
@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public List<User> findAll()
{
return userDao.findAll();
}
}
最后服务使用的Dao:
@Repository("userDao")
public class UserDaoImpl implements UserDao
{
@Autowired
private SessionFactory sessionFactory;
@Override
@SuppressWarnings("unchecked")
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public List<User> findAll()
{
Query query = sessionFactory.getCurrentSession().createQuery("from User ");
return query.list();
}
}
我的问题:
我已经尝试了几种配置,我认为这些配置是等效的,但是当我从页面中的链接点击映射的Controller方法时,我得到了一个org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
。有人可以解释一下,为什么在我使用配置2时会引发此异常,而不是在配置1和3上引发异常?
配置1(正常工作)
应用context.xml中
...
<tx:annotation-driven />
<context:component-scan base-package="my.package">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="packagesToScan">
<list>
<value>my.package.entities</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/myDb" />
</bean>
...
servlet的context.xml中
...
<context:component-scan base-package="my.package.inner.package">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<mvc:annotation-driven />
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
...
配置2(引发异常)
与1相同的配置,与<context:component-scan>
的{{1}}标记不同,它具有更接近根的基础包:
servlet-context.xml
请注意,这与<context:component-scan base-package="my.package">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
中的组件扫描的基本包相同。
配置3(正常工作)
与2相同(因此在application-context.xml
中使用base-package="my.package"
),区别在于我没有使用servlet-context.xml
注释我的服务,而是在@Service
中将其明确声明为bean :
application-context.xml
TL; DR
有人可以解释一下,当我用配置2而不是配置1和3命中Controller映射方法时,为什么我得到...
<bean id="userManager" class="my.package.services.impl.UserManagerImpl">
</bean>
...
?
我相信所有这三种配置都是等效的。
谢谢。
答案 0 :(得分:0)
经过几个小时的反复试验,我发现了问题。
在所有3种配置中,component-scan
的{{1}}标记需要另一个属性:servlet-context.xml
必须设置为“false”。
use-default-filters
如果将其设置为“true”(默认情况下),则基本上就像拥有这样的默认包含过滤器:
<context:component-scan base-package="my.package" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
这意味着在包扫描期间,它还加载了定义的<context:include-filter type="annotation"
expression="org.springframework.stereotype.Component"/>
(和@Service
等)。
由于服务是由Servlet上下文加载的,没有@Repository
标记,因此没有为服务启用事务上下文,并且引发了异常。
在配置1中,由于用于扫描的基本包受到限制(仅包括Controller包),因此绕过了该问题(Servlet上下文仅加载了Controller,因此使用的服务是应用程序上下文加载的服务,已启用事务上下文。
在配置3中,由于我在右侧“事务”上下文(<tx:annotation-driven />
)中使用了Service bean的“手动定义”,因此也绕过了问题。