@Autowired对象在一个类中获取空值,而在另一类中成功连接 [英] @Autowired object gets a null value in one class, while successfully wired in another

查看:239
本文介绍了@Autowired对象在一个类中获取空值,而在另一类中成功连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Spring 3和Spring Security进行项目.我的问题是与IoC容器有关.当我为Spring Security-3编写自己的UserDetailsService实现时,问题就开始了.我检查了其他问题,但仍然无法解决问题.

I am working on a project using Spring 3, and Spring Security. My problem is with IoC container. Problem started when I wrote my own implementation of UserDetailsService for Spring Security-3. I checked the other questions but still could not solve the problem.

问题的定义是:

我有两个单独的类(一个是扩展了@ControllerUsersController.java和扩展了@ServiceProjectUserDetailsService),它们使用一个公共对象进行自动装配.但是,虽然在UsersController中成功将对象自动连线,但在ProjectUserDetailsService类中它是null,尽管该类(ProjectUserDetailsService)的对象已成功创建(我通过调试对此进行了验证).

I have two seperate classes(One is UsersController.java which extends @Controller, and ProjectUserDetailsService which extends @Service) that uses a common object to be autowired. But while object is autowired successfully in UsersController, it is null in ProjectUserDetailsService class altough the object of this class(ProjectUserDetailsService) is successfully created(I verified this by debugging).

任何建议如何解决这个问题?

Any suggestions how to solve this?

这是我的web.xmlproject-servlet.xmlproject-security.xml文件以及相关的类.

Here are my web.xml, project-servlet.xml and project-security.xml files and related classes.

Web.xml`

<?xml version="1.0" encoding="UTF-8"?>
<!--
  - Tutorial web application
  -
  -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>Ecognitio with Spring Security</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
            /WEB-INF/ecognitio-servlet.xml
            /WEB-INF/ecognitio-security.xml
        </param-value>
  </context-param>
  <context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>tutorial.root</param-value>
  </context-param>
  <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>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
  </listener>
  <servlet>
    <servlet-name>ecognitio</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>project</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>project</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

project-servlet.xml

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    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">

    <!-- Scans the classpath of this application for @Components to deploy as beans -->
    <context:component-scan base-package="com.project" />

    <!-- Configures the @Controller programming model -->
    <mvc:annotation-driven />

     <bean  id="messageSource" 
            class="org.springframework.context.support.ResourceBundleMessageSource"
            p:basename="Messages"/>

  <!-- misc -->
<!--    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="suffix" value=".jsp"/>
    </bean>  --> 


    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">

       <property name="viewClass">
         <value>
              org.springframework.web.servlet.view.tiles2.TilesView
            </value>
        </property>
    </bean>

    <bean id="tilesConfigurer"
    class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
      <property name="definitions">
             <list>
                <value>/WEB-INF/tiles.xml</value>
             </list>
      </property>
    </bean>

    <!-- Configures Hibernate - Database Config -->
    <import resource="db-config.xml" />
</beans>

project-security.xml

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

<!--
  - Sample namespace-based configuration
  -
  -->

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

    <debug />

    <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
        <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
        -->
    </global-method-security>

    <http pattern="/loggedout.jsp" security="none"/>

    <http use-expressions="true" >
        <intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/>
        <intercept-url pattern="/secure/**" access="isAuthenticated()" />

        <!--
             Allow all other requests. In a real application you should
             adopt a whitelisting approach where access is not allowed by default
          -->
        <intercept-url pattern="/login.jsp*" access="isAuthenticated()==false"/>
        <intercept-url pattern="/timeout.jsp*" access="isAuthenticated()==false"/>
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />

       <!-- <intercept-url pattern="/**" access="permitAll" /> --> 
        <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/dashboard.html" />
        <logout logout-success-url="/login.jsp" delete-cookies="JSESSIONID"/>
        <remember-me />
<!--
    Uncomment to enable X509 client authentication support
        <x509 />
-->
        <!-- Uncomment to limit the number of sessions a user can have 
        <session-management invalid-session-url="/login.jsp">
            <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
        </session-management>
        -->

    </http>

            <!-- HERE IS WHERE I USE an object of ProjectUserDetailsService -->
    <authentication-manager>
         <authentication-provider user-service-ref="userDetailsService" />   
    </authentication-manager>


 <!--  

</beans:beans>

UsersController.java(该类的UsersDAO类的对象的自动装配成功)

UsersController.java (autowiring of an object of UsersDAO class is successful for this class)

package com.project.users;

//required imports



@Controller
public class UsersControllers
{

@Autowired
private UsersDAO usersDAO;

    //Some more autowires and some class specific code


}

ProjectUserDetailsService.java(UsersDAO的自动装配无效)

ProjectUserDetailsService.java (where autowiring of UsersDAO does not work)

package com.project.security;

import java.util.ArrayList;
import java.util.Collection;

//required imports


@SuppressWarnings("deprecation")
@Service("userDetailsService") 
public class ProjectUserDetailsService implements UserDetailsService {

   @Autowired
   private UsersDAO usersDAO;
  @Autowired private Assembler assembler;

  @Transactional(readOnly = true)
  public UserDetails loadUserByUsername(String username)
      throws UsernameNotFoundException, DataAccessException {

    UserDetails userDetails = null;
    //For debugging purposes
    if(usersDAO==null){
        System.out.println("DAO IS NULL");
        System.out.println("DAO IS NULL");
        System.out.println("DAO IS NULL");

    }
    User userEntity = usersDAO.findUserbyEmail("'"+username+"'");


    if (userEntity == null)
      throw new UsernameNotFoundException("user not found");

    return assembler.buildUserFromUserEntity(userEntity);

  }
}

推荐答案

是的,将对象自动装配到从Spring安全类继承的bean中似乎是不可能的.我不知道这是否是Spring Security中的错误,或者是否出于安全性考虑或做了什么.如果有人有解释,我将有兴趣听.您可以通过xml配置手动注入bean(而不是使用@Autowired批注)来解决问题,然后它们将出现.不过要小心一点.

yes, it seems to be impossible to autowire objects into beans that inherit from spring security classes. i don't know if this is a bug in spring security, or if it's done for security or what. if anyone has an explanation I would be interested in hearing it. You can solve your problem though by manually injecting the beans via xml configuration (as opposed to using the @Autowired annotation) and then they will be present. One word of caution though..

我这样做了,我注意到上面有注释的userDao(特别是@Transactional)不再在事务中运行.我的userDao已在多个地方使用.但是,如果我将其注入到自定义的AbstractUserDetailsAuthenticationProvider中,则它将不再对使用它的任何其他类的事务进行操作.将注入删除到自定义AbstractUserDetailsAuthenticationProvider中后,当其他接收接收该对象的对象(通过@Autowired或手动xml注入)使用该注入功能时,即可将事务功能恢复到我的userDao.

I did this, and i noticed that my userDao which had annotations on it (specifically @Transactional) was no longer operating in a transaction. My userDao was being used in multiple places. If I injected it into my custom AbstractUserDetailsAuthenticationProvider though, it no longer operated in a transaction for any other class that used it. Removing the injection into the custom AbstractUserDetailsAuthenticationProvider restored the transaction functionality to my userDao when used by other objects which were receiving it (either via @Autowired or manual xml injection).

那么我如何将我的userDao放入Spring Security上下文中并仍然将其保留为@Transactional?我必须创建一个Factory类:

So how did I get my userDao into my Spring Security context and still keep it @Transactional? I had to create a Factory class:

public class UserDaoFactory {

private static UserDao userDao;

public static UserDao getUserDao() {
    return UserDaoFactory.userDao;
}

public void setUserDao(UserDao userDao) {
    UserDaoFactory.userDao = userDao;
}
}

然后将其和您的两个对象放入弹簧容器中:

Then put this and your dao two objects into the spring container:

<bean id="userDao" class="com.package.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDaoFactory" class="com.package.UserDaoFactory">
    <property name="userDao" ref="userDao" />
</bean>

因此,userDao将自动连接到您的userDaoFactory中.它将具有@Transactional的所有功能(因为spring security尚未将其剥离吗?).然后,在您的spring安全对象中,您可以执行以下操作:

So the userDao will get autowired into your userDaoFactory. It will have all of it's @Transactional capability (because spring security hasn't stripped it off?). Then in your spring security object you can do a:

userDao = UserDaoFactory.getUserDao();

我在自定义的AbstractUserDetailsAuthenticationProvider对象中实现了ServletContextAware,以便在初始化和中提琴期间执行一次以上操作.

I implemented ServletContextAware in my custom AbstractUserDetailsAuthenticationProvider object to do the above once during initialization, and viola.

因此请注意,虽然您可以通过xml配置手动将bean注入spring安全对象以克服@Autowired问题,但是如果您尝试将该DAO封装在@Transactional中,则会遇到一个新问题.

So note, while you can manually inject your bean via xml configuration into the spring security object to overcome the @Autowired problem, you will end up with a new problem if you are trying to wrap that DAO in @Transactional.

现在也许有人知道为什么会这样.这可能是我的配置错误(我承认我不是spring专家),或者可能是spring的功能.我很想听听任何人怎么说,以及如何改善这一点.

now maybe someone knows why this may be happening. it could be a mis-configuration on my part (i admit i'm not a spring expert), or it could be a feature of spring. i would love to hear what anyone has to say and how to improve this though.

这篇关于@Autowired对象在一个类中获取空值,而在另一类中成功连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆