Spring MVC + Hibernate 4 + Spring Security

Posted

技术标签:

【中文标题】Spring MVC + Hibernate 4 + Spring Security【英文标题】: 【发布时间】:2012-12-05 04:06:23 【问题描述】:

从现在开始,我一直在努力完成所有这些工作,不知道该怎么办。我相信我在 SO 上浏览了关于该主题的每一篇文章,并浏览了数十个教程......

这是我此时收到的信息:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fruitController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.controller.FruitController.setFruitManager(com.service.FruitManager); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.service.FruitManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: 

我过去已经能够解决这个错误,但只是有一个新的“没有为当前线程找到会话”。当没有这个时,我的 Assembler 和 UserDetailsS​​erviceImpl bean 无法在我的 spring-security.xml 文件中被识别出一些问题......

我认为问题不是来自我的代码,我只是无法正确设置我的配置文件,我可能在这里遗漏了一些东西。

这里是配置文件:

web.xml:

<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Spring MVC Application</display-name>

    <!-- Spring MVC -->
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/mvc-dispatcher-servlet.xml,
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>

    <!-- Spring Security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:annotation-config/>
    <!-- Load everything except @Controllers -->
    <context:component-scan base-package="com">
        <context:exclude-filter expression="org.springframework.stereotype.Controller"
            type="annotation" />
    </context:component-scan>

    <tx:annotation-driven transaction-manager="transactionManager" />

    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="save*" />
            <tx:method name="*" read-only="false" />
        </tx:attributes>
    </tx:advice>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>com.dao.HibernateFruitDAO</value>
            </list>
        </property>
        <property name="packagesToScan">
            <list>
                <value>com.service</value>
                <value>com.controller</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.mysql5Dialect</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/basename" />
        <property name="username" value="xxx" />
        <property name="password" value="yyy" />
    </bean>

</beans>

mvc-dispatcher-servlet.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:annotation-config />

    <context:property-placeholder location="classpath:hibernate.properties" />

    <!-- Load @Controllers only -->
    <context:component-scan base-package="com.controller"
        use-default-filters="false">
        <context:include-filter expression="org.springframework.stereotype.Controller"
            type="annotation" />
    </context:component-scan>

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"></bean>
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"></bean>

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>mymessages</value>
            </list>
        </property>
    </bean>

    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

</beans>

spring-security.xml http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <beans:bean id="userDetailsService" class="com.service.UserDetailsServiceImpl">
    </beans:bean>

    <beans:bean id="assembler" class="com.service.Assembler">
    </beans:bean>


    <http auto-config='true' use-expressions='true'>
        <intercept-url pattern="/login*" access="isAnonymous()" />
        <intercept-url pattern="/secure/**" access="hasRole('ROLE_Admin')" />
            <logout logout-success-url="/listing.htm" />
        <form-login login-page="/login.htm" login-processing-url="/j_spring_security_check"
            authentication-failure-url="/login_error.htm" default-target-url="/listing.htm"
            always-use-default-target="true" />
    </http>

    <beans:bean id="com.daoAuthenticationProvider"
        class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <beans:property name="userDetailsService" ref="userDetailsService" />
    </beans:bean>

    <beans:bean id="authenticationManager"
        class="org.springframework.security.authentication.ProviderManager">
        <beans:property name="providers">
            <beans:list>
                <beans:ref local="com.daoAuthenticationProvider" />
            </beans:list>
        </beans:property>
    </beans:bean>

    <authentication-manager>
        <authentication-provider user-service-ref="userDetailsService">
            <password-encoder hash="plaintext" />
        </authentication-provider>
    </authentication-manager>
</beans:beans>

水果控制器:

package com.controller;

@Controller
public class FruitController

    protected final Log logger = LogFactory.getLog(getClass());


    private FruitManager fruitManager;

    @Autowired
    public void setFruitManager(FruitManager FruitManager) 
        this.fruitManager = fruitManager;
    

    @RequestMapping(value = "/listing", method = RequestMethod.GET)
    public String getFruits(ModelMap model) 
        model.addAttribute("fruits", this.fruitManager.getFruits());
        return "listing";
    

水果道: 公共接口 FruitDAO

public List<Fruit> getFruitList();

public List<Fruit> getFruitListByUserId(String userId);

public void saveFruit(Fruitprod);

public void updateFruit(Fruitprod);

public void deleteFruit(int id);

public Fruit getFruitById(int id);

HibernateFruitDAO

package com.dao;

@Repository("fruitDao")
public class HibernateFruitDAO implements FruitDAO 


private SessionFactory sessionFactory;

        @Autowired
        public void setSessionFactory(SessionFactory sessionFactory) 
            this.sessionFactory = sessionFactory;
        

    public List<Fruit> getList() 
        return (List<Fruit>) getSession().createCriteria ( Fruit.class ).list();
    

    public List<Fruit> getFruitListByUserId(String userId) 
        return (List<Fruit>)sessionFactory.getCurrentSession().createCriteria("from Fruit where userId =?", userId).list();
    

    public void saveFruit(Fruit fruit) 
        sessionFactory.getCurrentSession().save(fruit);
    

    public void updateFruit(Fruit fruit) 
        sessionFactory.getCurrentSession().update(fruit);
    

    public void deleteFruit(int id) 
        Fruit fruit = (Fruit) sessionFactory.getCurrentSession().load(Fruit.class, id);
        if (null != fruit) 
            sessionFactory.getCurrentSession().delete(fruit);
        
    

    public Fruit getFruitById(int id) 
        return (Fruit)sessionFactory.getCurrentSession().load(Fruit.class, id);
    

    private Session getSession()
            return sessionFactory.getCurrentSession();
        

界面水果管理器:

package com.service;

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

import com.domain.Fruit;


public interface FruitManager extends Serializable

    public List<Fruit> getFruits();

    public List<Fruit> getFruitsByUserId(String userId);

    public void addFruit(Fruit fruit);

    public void removeFruit(int id);

    public Fruit getFruitById(int id);

    public void updateFruit(Fruit fruit);

FruitManager 的实现:

package com.service;

@Repository("fruitManager")
@Transactional
public class SimpleFruitManager implements FruitManager 

    /**
     * 
     */
    private static final long serialVersionUID = ...;

    @Autowired
    private FruitDAO fruitDao;

    public List<Fruit> getFruits() 
        return fruitDao.getFruitList();
    

    public List<Fruit> getFruitsByUserId(String userId)
        return fruitDao.getFruitListByUserId(userId);
    

    public void setFruitDao(FruitDAO fruitDao) 
        this.fruitDao = fruitDao;
    

    public void addFruit(Fruit fruit) 
        fruitDao.saveFruit(fruit);
    

    public void removeFruit(int id) 
        fruitDao.deleteFruit(id);
    

    public  getFruitById(int id) 
        return fruitDao.getFruitById(id);
    

    public void updateFruit(Fruit fruit) 
        fruitDao.updateFruit(fruit);
    

【问题讨论】:

【参考方案1】:

我认为你必须在 DaoImpl 中使用它来获取会话:

@Autowired
private SessionFactory sessionFactory;

【讨论】:

【参考方案2】:

乍一看,您似乎遇到了一个常见问题,即不了解 Spring ApplicationContexts 如何组合在一起来构建 Web 应用程序。请参阅我对完全相同问题的其他答案,看看它是否可以解决问题:

Declaring Spring Bean in Parent Context vs Child Context

您也可能对类似主题的这个答案有所启发,该答案链接到我之前提到的答案以及另一个答案:

Spring XML file configuration hierarchy help/explanation

几个简短的提示可以让你朝着正确的方向前进......

按照惯例,Spring 的 ContextLoaderListener 从 WEB-INF/applicationContext.xml 加载 bean 以创建根应用程序上下文。当您覆盖默认值时,将不再加载该文件。

提示 #1:坚持常规行为。它会让你的生活更简单。

同样按照惯例,启动 Spring DispatcherServlet 从 WEB-INF/&lt;servlet name&gt;-context.xml 加载 bean 以创建用于配置调度程序 servlet 的上下文。此上下文成为根上下文的子上下文。

提示 #2:参见提示 #1

所以你看,你现在过度配置了东西。阅读链接的答案和其中链接的参考资料。学习使用 Spring 而不是反对它。

【讨论】:

感谢您的启发性回答,这对我帮助很大。我想我已经清理了大部分配置文件并理解了它是如何工作的。这次我对 spring-security.xml 有一个遗留问题:“没有定义名为 'userDetailsS​​ervice' 的 bean”。未找到 userDetailsS​​ervice bean。我确实在我的 spring-security.xml(Assembler 和 UserDetailsS​​erviceImpl)中注释了两个 bean 声明。我删除它们的原因是它们应该已经被根上下文扫描了。 这个答案和提供的链接帮助我获得了有意义的干净配置文件。我也更好地理解它应该工作的方式以及应该使用 Spring 的方式。非常有用的答案。【参考方案3】:

在您的 web.xml 文件中,applicationContext.xml 永远不会被加载。你应该把它放在context-param。将mvc-dispatcher-servlet.xml 的位置(包含与控制器相关的 bean)作为 DispatcherServlet 的 init-param 代替:

<init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
        </init-param>

【讨论】:

以上是关于Spring MVC + Hibernate 4 + Spring Security的主要内容,如果未能解决你的问题,请参考以下文章

从 Migrate 迁移到 Spring MVC 4 + Hibernate5

通用后台管理系统(ExtJS 4.2 + Spring MVC 3.2 + Hibernate)

Spring+Spring MVC+Hibernate框架搭建实例

杰克逊 JSON、Spring MVC 4.2 和 Hibernate JPA 问题的无限递归

spring mvc+spring + hibernate 整合

spring mvc+spring + hibernate 整合