【问题标题】:Spring beans are null, @Autowire injections do not work, what went wrong?Spring bean 为空,@Autowire 注入不起作用,出了什么问题?
【发布时间】:2016-02-21 08:32:19
【问题描述】:

我已经搜索了很多,但没有找到解决方案。这个问题会很长,有很多代码,问题是在运行时 DataSource 对象为空。这是我尝试将 dataSource bean 添加到 spring 容器的两种方式。我想这里没有错。并且实际创建了 bean。

package com.stubtech.eztaxi.eztaxiapp.config;

**imports
@Configuration
public class DBConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(DBConfig.class);

@Autowired
private Environment environment;

@Bean
public DataSource dataSource(){
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getProperty("jdbc.driverClass"));
    dataSource.setUrl(environment.getProperty("jdbc.url"));
    dataSource.setUsername(environment.getProperty("jdbc.username"));
    dataSource.setPassword(environment.getProperty("jdbc.password"));
    return dataSource;
}

@Bean(name = "jndiDataSource")
public DataSource dataSourceJndi() {
    DataSource dataSource = null;
    try {
        JndiDataSourceLookup jndiDataSourceLookup = new JndiDataSourceLookup();
        jndiDataSourceLookup.setResourceRef(true);
        dataSource = jndiDataSourceLookup.getDataSource("jdbc/EZTaxiDB");
        LOGGER.debug("Created DS");
    } catch (Exception e) {
        LOGGER.error("No DS: " + e.getMessage());
    }
    return dataSource;
}
}

我已经使用 .xml 配置文件并使用以下类进行 Web 初始化:

public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) throws ServletException {
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(WebConfig.class);

    // Manage lifecycle of the rootContext
    container.addListener(new ContextLoaderListener(rootContext));

    DispatcherServlet dispatcherServlet = new DispatcherServlet(rootContext);
    ServletRegistration.Dynamic registration = container.addServlet("dispatcherServlet", dispatcherServlet);
    registration.setLoadOnStartup(1);
    registration.addMapping("/");

    DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("springSecurityFilterChain");
    container.addFilter("springSecurityFilterChain", delegatingFilterProxy).addMappingForUrlPatterns(null, false, "/*");
}
}

同时我正在尝试使用 jjwt 令牌进行自动化,这里是 SecurityConfig:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private final ITokenAuthService tokenAuthService;
private final UserDetailsService userDetailsService;
private final ISecretReader secretReader;

public SecurityConfig() {
    super(true);
    this.secretReader = new SecretReaderImpl();
    this.userDetailsService = new UserDetailsServiceImpl();
    this.tokenAuthService = new TokenAuthServiceImpl();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .exceptionHandling().and()
            .anonymous().and()
            .servletApi().and()
            .authorizeRequests()

            // Allow anonymous resource requests
            .antMatchers("/").permitAll()
            .antMatchers("/favicon.ico").permitAll()
            .antMatchers("**/*.html").permitAll()
            .antMatchers("**/*.css").permitAll()
            .antMatchers("**/*.js").permitAll()

            // Allow anonymous logins
            .antMatchers("/auth/**").permitAll()

            // All other request need to be authenticated
            .anyRequest().authenticated().and()

            // Custom Token based authentication based on the header previously given to the client
            .addFilterBefore(new StatelessAuthenticationFilter(tokenAuthService()),
                    UsernamePasswordAuthenticationFilter.class)
            .headers().cacheControl();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

@Bean
public UserDetailsService userDetailsService() {
    return userDetailsService;
}

@Bean
public ITokenAuthService tokenAuthService() {
    return tokenAuthService;
}

@Bean
public ISecretReader secretReader() {
    return secretReader;
}
}

我正在尝试使用 servlet 进行授权:

@Component
@WebServlet("/auth/login")
public class AuthenticationServlet extends HttpServlet { // implements ServletResponse {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationServlet.class);

    private WebApplicationContext springContext;

    @Autowired
    private UserInfoDAOImpl userInfoDAO;

    @Autowired
    private AuthenticationManager authenticationManager;

    public void init(final ServletConfig config) throws ServletException {
        //this.authenticationManager = authenticationManager;
//        ApplicationContext context =
//                WebApplicationContextUtils.getRequiredWebApplicationContext(
//                        this.getServletContext());
//        authenticationManager = (AuthenticationManager) context.getBean("authenticationManagerBean");
        super.init(config);
        springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
        final AutowireCapableBeanFactory beanFactory = springContext.getAutowireCapableBeanFactory();
        beanFactory.autowireBean(this);
        LOGGER.debug("Initialised login servlet");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            UserInfo user = userInfoDAO.getUserInfoById("testuser");
            String un = request.getParameter("un");
            String pw = request.getParameter("pw");
            Authentication authrequest = new UsernamePasswordAuthenticationToken(un, pw);
            Authentication result = authenticationManager.authenticate(authrequest);
            SecurityContextHolder.getContext().setAuthentication(result);
            LOGGER.info("Successfully authenticated. Security context contains: " +
                    SecurityContextHolder.getContext().getAuthentication());
        } catch (AuthenticationException e) {
            LOGGER.error("Authentication failed: " + e.getMessage());
        }
    }
}

最后,这里有一个 NullPointer - dataSource 为空:

@Component
public class UserInfoDAOImpl implements IUserInfoDAO {

    private final static Logger LOGGER = LoggerFactory.getLogger(UserInfoDAOImpl.class);

    @Autowired
    private DataSource dataSource;

    public UserInfo getUserInfoById(String username) {
        LOGGER.debug("Get user is called");

        String sql = "SELECT u.username name, u.password pass, ur.rolename role FROM " +
                "users u INNER JOIN user_role ur on u.id=ur.userid WHERE " +
                "u.enabled = 1 and u.username = ?";

        UserInfo userInfo = null;
        try {
            userInfo = new JdbcTemplate(this.dataSource).queryForObject(sql, new Object[]{username},
                    (rs, rowNum) -> {
                        UserInfo user = new UserInfo();
                        user.setUsername(rs.getString("name"));
                        user.setPassword(rs.getString("pass"));
                        user.setRole(rs.getString("role"));
                        return user;
                    });
        } catch (Exception e) {
            LOGGER.error("Failed to fetch user: " + e.getMessage());
        }
        return userInfo;
    }
}

这是 WebConfig:

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = ("com.stubtech.eztaxi.eztaxiapp"))
@PropertySource("classpath:application.properties")
public class WebConfig extends WebMvcConfigurerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebConfig.class);

    @Autowired
    private ApplicationContext appContext;

    public WebConfig(){
        LOGGER.debug("_____APP_STARTED_____");
        LOGGER.debug("...");
    }
}

应用初始化日志:

  org.springframework.jdbc.datasource.DataSourceUtils]]
  Fetching JDBC Connection from DataSource]]
  org.springframework.jdbc.datasource.DriverManagerDataSource]]
  org.springframework.jdbc.datasource.DataSourceUtils]]
  Returning JDBC Connection to DataSource]]
  Failed to fetch user: Property 'dataSource' is required]]
  Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@596ded4b: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,webConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor,DBConfig,securityConfig,userDetailsServiceImpl,userInfoDAOImpl,authenticationServlet,dataSource,jndiDataSource,org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration,authenticationManagerBuilder,enableGlobalAuthenticationAutowiredConfigurer,org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration,delegatingApplicationListener,webSecurityExpressionHandler,springSecurityFilterChain,privilegeEvaluator,autowiredWebSecurityConfigurersIgnoreParents,org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration,objectPostProcessor,org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration,requestDataValueProcessor,authenticationManagerBean,userDetailsService,tokenAuthService,secretReader,org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration,mvcValidator,mvcUrlPathHelper,mvcViewResolver,mvcPathMatcher,httpRequestHandlerAdapter,requestMappingHandlerMapping,mvcContentNegotiationManager,viewControllerHandlerMapping,resourceHandlerMapping,defaultServletHandlerMapping,mvcConversionService,beanNameHandlerMapping,mvcResourceUrlProvider,mvcUriComponentsContributor,simpleControllerHandlerAdapter,requestMappingHandlerAdapter,handlerExceptionResolver]; root of factory hierarchy]]
  Registering bean definition for @Bean method com.stubtech.eztaxi.eztaxiapp.config.DBConfig.dataSource()]]
  Registering bean definition for @Bean method com.stubtech.eztaxi.eztaxiapp.config.DBConfig.jndiDataSource()]]
  Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@665a082b: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,webConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor,DBConfig,securityConfig,userDetailsServiceImpl,userInfoDAOImpl,authenticationServlet,dataSource,jndiDataSource,org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration,authenticationManagerBuilder,enableGlobalAuthenticationAutowiredConfigurer,org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration,delegatingApplicationListener,webSecurityExpressionHandler,springSecurityFilterChain,privilegeEvaluator,autowiredWebSecurityConfigurersIgnoreParents,org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration,objectPostProcessor,org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration,requestDataValueProcessor,authenticationManagerBean,userDetailsService,tokenAuthService,secretReader,org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration,mvcValidator,mvcUrlPathHelper,mvcViewResolver,mvcPathMatcher,httpRequestHandlerAdapter,requestMappingHandlerMapping,mvcContentNegotiationManager,viewControllerHandlerMapping,resourceHandlerMapping,defaultServletHandlerMapping,mvcConversionService,beanNameHandlerMapping,mvcResourceUrlProvider,mvcUriComponentsContributor,simpleControllerHandlerAdapter,requestMappingHandlerAdapter,handlerExceptionResolver]; root of factory hierarchy]]
  Registered injected element on class [com.stubtech.eztaxi.eztaxiapp.service.impl.UserInfoDAOImpl]: AutowiredFieldElement for private javax.sql.DataSource com.stubtech.eztaxi.eztaxiapp.service.impl.UserInfoDAOImpl.dataSource]]
  Processing injected element of bean 'userInfoDAOImpl': AutowiredFieldElement for private javax.sql.DataSource com.stubtech.eztaxi.eztaxiapp.service.impl.UserInfoDAOImpl.dataSource]]
  Creating shared instance of singleton bean 'dataSource']]
  Creating instance of bean 'dataSource']]
  org.springframework.jdbc.datasource.DriverManagerDataSource]]
  Eagerly caching bean 'dataSource' to allow for resolving potential circular references]]
  Finished creating instance of bean 'dataSource']]
  Creating shared instance of singleton bean 'jndiDataSource']]
  Creating instance of bean 'jndiDataSource']]
  org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup]]
  org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup]]
  Eagerly caching bean 'jndiDataSource' to allow for resolving potential circular references]]
  Not strongly caching class [com.sun.gjc.spi.jdbc40.DataSource40] because it is not cache-safe]]
  Finished creating instance of bean 'jndiDataSource']]
  Autowiring by type from bean name 'userInfoDAOImpl' to bean named 'dataSource']]
  Returning cached instance of singleton bean 'dataSource']]
  Returning cached instance of singleton bean 'jndiDataSource']]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource]]
  Rejected bean name 'dataSource': no URL paths identified]]
  Rejected bean name 'jndiDataSource': no URL paths identified]]
  org.springframework.jdbc.datasource.DataSourceUtils]]
  Fetching JDBC Connection from DataSource]]
  org.springframework.jdbc.datasource.DriverManagerDataSource]]
  org.springframework.jdbc.datasource.DataSourceUtils]]
  Returning JDBC Connection to DataSource]]
  Failed to fetch user: Property 'dataSource' is required]]

【问题讨论】:

    标签: spring spring-mvc dependency-injection nullpointerexception autowired


    【解决方案1】:

    很明显,您正在配置类中创建DataSource 的两个不同bean。但在注射时,您只是在执行以下操作:

    @Autowired
    private DataSource dataSource;
    

    问题是Spring不知道这里要注入两个DataSource Bean中的哪一个。您必须使用@Qualifier 让 Spring 知道要注入哪个 Bean。

    改成如下:

    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;
    

    或者,您可以将两个注释替换为单个@Resource,如下所示:

    @Resource(name = "dataSource")
    private DataSource dataSource;
    

    【讨论】:

    • Spring 创建了两个 bean:dataSource 和 jndiDataSource。在注入时,Spring 知道我要使用哪一个。那是对的?因为默认情况下它会按 id 映射。
    • 在注入时,Spring 知道我要使用哪一个。 Spring 怎么知道?
    • 自动连线和注入(1 - 按类型匹配)(2 - 按限定符限制)(3 - 按名称匹配)
    • 它将按名称匹配。无论如何,这无关紧要。如果您仍然认为这是一个问题,我已经适当地添加了 bean 名称和限定符 - 仍然是 NPE
    • 您确定在组件扫描期间创建了两个DataSource Beans 吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 2017-04-08
    • 2013-02-23
    相关资源
    最近更新 更多