实现ApplicationContextAware - ApplicationContext为NULL

时间:2015-08-13 08:52:14

标签: java spring

我正在编写一个Tomcat应用程序,它作为一些内部服务的代理。

我已将Spring项目从混合的基于XML和注释的配置切换到基于Java和注释的配置。

在切换配置样式之前,应用程序运行正常。现在我有两个问题。

  1. 在我的两个过滤器中执行init-methods时,ApplicationContext为空。当我调试我的应用程序时,我可以看到方法setApplicationContext已执行。

  2. EntityManagerFactory未在身份验证过滤器中注入(emf为空)

  3. 引导Spring的代码:

    import javax.servlet.FilterRegistration;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRegistration;
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    
    public class MyAppSpringBoot implements WebApplicationInitializer {
    
        @Override
        public void onStartup(ServletContext container) throws ServletException {
            initRootContext(container);
            initDispatcherContext(container);
            addFilters(container);
        }
    
        private void initDispatcherContext(ServletContext container) {
            AnnotationConfigWebApplicationContext servletContext = new AnnotationConfigWebApplicationContext();
            servletContext.register(MyAppDispatcherServletContext.class);
            ServletRegistration.Dynamic dispatcher
                    = container.addServlet("myAppDispatcherServlet", new DispatcherServlet(servletContext));
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
        }
    
        private void initRootContext(ServletContext container) {
            AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
            rootContext.register(MyAppRootContext.class);
            container.addListener(new ContextLoaderListener(rootContext));
        }
    
        private void addFilters(ServletContext container) {
            FilterRegistration.Dynamic registration
                    = container.addFilter("u3rAuthentication", UserDbAuthenticationFilter.class);
            registration.addMappingForUrlPatterns(null, false, "/entry/*");
    
            registration = container.addFilter("responseXmlFilter", ResponseTextXmlFilter.class);
            registration.addMappingForUrlPatterns(null, false, "/entry/*");
        }
    }
    

    根上下文代码:

    import java.io.File;
    import java.io.IOException;
    import java.util.Properties;
    import javax.persistence.EntityManagerFactory;
    import javax.sql.DataSource;
    import javax.xml.stream.XMLEventFactory;
    import javax.xml.stream.XMLInputFactory;
    import javax.xml.stream.XMLOutputFactory;
    import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException;
    import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.stereotype.Controller;
    
    @Configuration
    @ComponentScan(basePackages = "com.application", excludeFilters = @ComponentScan.Filter(Controller.class))
    public class MyAppRootContext {
    
        @Bean
        public DataSource userDbJpaDataSource() throws DataSourceLookupFailureException {
    
            JndiDataSourceLookup lookup = new JndiDataSourceLookup();
            return lookup.getDataSource("jdbc/userDbPostgres");
        }
    
        @Bean
        public EntityManagerFactory entityManagerFactory() {
            //return Persistence.createEntityManagerFactory(MyAppConstants.U3R_PERSISTENCE_UNIT);
            LocalContainerEntityManagerFactoryBean fb = new LocalContainerEntityManagerFactoryBean();
            fb.setDataSource(userDbJpaDataSource());
            return fb.getNativeEntityManagerFactory();
        }
    
        @Bean
        public DiskFileItemFactory diskFileItemFactory() {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setSizeThreshold(50_000 * 1024);
            factory.setRepository(new File("/WEB-INF/upload"));
            return factory;
        }
    
        @Bean
        public XMLOutputFactory xmlOutputFactory() {
            return XMLOutputFactory.newInstance();
        }
    
        @Bean
        public XMLInputFactory xmlInputFactory() {
            return XMLInputFactory.newInstance();
        }
    
        @Bean
        public XMLEventFactory xmlEventFactory() {
            return XMLEventFactory.newInstance();
        }
    
        @Bean
        public UrlPairing urlPairing() throws IOException {
            return new UrlPairing(myAppProperties().getProperty("myApp.UrlPairingFile"));
        }
    
        @Bean
        public Properties myAppProperties() throws IOException {
            Properties p = new Properties();
            p.load(MyAppRootContext.class.getResourceAsStream("/myAppConfig.properties"));
            return p;
        }
    
        @Bean
        public MyAppXmlFilterWords xmlFilterWords() throws IOException {
            MyAppXmlFilterWords words = MyAppXmlFilterWords.createFilterWords(myAppProperties().getProperty("myApp.xmlFilterWordFile"));
            return words;
        }
    
    }
    

    调度程序servlet上下文代码:

    @Configuration
    @ComponentScan(
            basePackages = "de.lgn.doorman",
            includeFilters = @ComponentScan.Filter(Controller.class)
    )
    public class MyAppDispatcherServletContext
    {
        // all beans are defined in root context
        // correct ???
    }
    

    根身份验证过滤器代码:

    @Component
    public class UserDbAuthenticationFilter implements Filter, ApplicationContextAware
    {
        private static final Logger logger = LogManager.getLogger(UserDbAuthenticationFilter.class.getName());
    
        @Autowired
        EntityManagerFactory emf;
    
        private ApplicationContext appContext;
    
        @Override
        public void init(FilterConfig filterConfig)    
        {
            logger.debug("Filter {} initialisiert. App-Context: {} {}", this.getClass().getName(),appContext.hashCode(), appContext);
            // *******************  NullPointerException here *******************
        }
    
        @Override
        public void destroy()
        { }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
        {
            appContext = applicationContext;
        }
    
        /*
        @PostConstruct    // ***************** this annotation isn't working **********************
        public void filterInit() throws ServletException
        {
            logger.debug("Filter {} initialisiert. App-Context: {} {}", this.getClass().getName(),appContext.hashCode(), appContext);
        }
    
        */
    }
    

    在我的控制器中,ApplicationContext是正确的(非空)。

    @Controller
    @RequestMapping(value = "entry/**")
    public class MyAppProxyController implements ApplicationContextAware
    {
    
        @Autowired
        Properties myAppProperties;
    
        private ApplicationContext appContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
        {
            appContext = applicationContext;
        }
    
        @PostConstruct
        public void init() throws ServletException   // this is working fine
        {
            logger.debug("Controller {} initialisiert. App-Context: {} {}", this.getClass().getName(),
                    appContext.hashCode(), appContext);
    
            logger.debug("Beans im Zugriff von Controller:");
            for (String beanName : appContext.getBeanDefinitionNames())
            {
                logger.debug("          {}", beanName);
            }
            MyAppProxyController controller = (MyAppProxyController) appContext.getBean("myAppProxyController");
            logger.debug("controller-hash im Controller={}", controller.hashCode());
        }
    }    
    

    更新Serge Ballesta的回答

    我按照你的所有指示#2。但现在我得到了这个例外:

    13-Aug-2015 13:03:27.264 INFO [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ApplicationContext.log Spring WebApplicationInitializers detected on classpath: [de.lgn.doorman.config.DmSpringBoot@c427b4f]
    13-Aug-2015 13:03:27.655 INFO [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ApplicationContext.log Initializing Spring root WebApplicationContext
    13-Aug-2015 13:03:28.308 SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.filterStart Exception starting filter responseXmlFilter
     org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'responseXmlFilter' is defined
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:698)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1174)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:283)
    

    我想知道当我使用三次DelegatingFilterProxy时,我自己的过滤器是如何连接到链的。方法name中的参数addFilter是否与bean名称相关联?

    更新2回答Serge Ballesta的答案

    这是在引导代码中创建过滤器链的代码:

    private void addFilters(ServletContext container)
    {
        FilterRegistration.Dynamic registration =
                container.addFilter("userDbAuthenticationFilter", DelegatingFilterProxy.class);
        registration.addMappingForUrlPatterns(null, false, "/mapgate/*");
    
        registration = container.addFilter("prepareRequestFilter", DelegatingFilterProxy.class);
        registration.addMappingForUrlPatterns(null, false, "/mapgate/*");
    
        registration = container.addFilter("responseTextXmlFilter", DelegatingFilterProxy.class);
        registration.addMappingForUrlPatterns(null, false, "/mapgate/*");
    }
    

    这些是我在根上下文中的过滤器bean定义:

    @Configuration
    @ComponentScan(basePackages = "com.application", excludeFilters = @ComponentScan.Filter(Controller.class))
    public class MyAppRootContext
    {
    
        @Bean
        public UserDbAuthenticationFilter userDbAuthenticationFilter()
        {
            return new UserDbAuthenticationFilter();
        }
    
        @Bean
        public PrepareRequestFilter prepareRequestFilter()
        {
            return new PrepareRequestFilter();
        }
    
        @Bean
        public ResponseTextXmlFilter responseTextXmlFilter()
        {
            return new ResponseTextXmlFilter();
        }
    
        @Bean
        public DataSource userDbJpaDataSource() throws DataSourceLookupFailureException
        {
    
            JndiDataSourceLookup lookup = new JndiDataSourceLookup();
            return lookup.getDataSource("jdbc/userDbPostgres");
        }
    
        @Bean
        public EntityManagerFactory entityManagerFactory()
        {
            LocalContainerEntityManagerFactoryBean fb = new LocalContainerEntityManagerFactoryBean();
            fb.setDataSource(userDbJpaDataSource());
            return fb.getNativeEntityManagerFactory();
        }
    }
    

    仍然没有依赖注入过滤器。是因为过滤器链是在引导阶段创建的,而bean是在根上下文中创建的吗?

    更新3回答Serge Ballesta的答案

    这是身份验证过滤器中的基本代码:

    public class U3RAuthenticationFilter implements Filter, ApplicationContextAware
    {
        private static final Logger logger = LogManager.getLogger(U3RAuthenticationFilter.class.getName());
    
        @Autowired(required = true)
        EntityManagerFactory entityManagerFactory;
    
        private ApplicationContext appContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
        {
            appContext = applicationContext;
        }
    
        @PostConstruct
        public void filterInit() throws ServletException
        {
            logger.debug("Filter {} initialisiert. App-Context: {} {}", this.getClass().getName(),appContext.hashCode(), appContext);
            logger.debug("Beans accessable by {}:", this.getClass().getName());
            for (String beanName : appContext.getBeanDefinitionNames())
            {
                logger.debug("          {}", beanName);
            }
    
            logger.debug("EntityManagerFactory: {}", (EntityManagerFactory)appContext.getBean("entityManagerFactory"));
        }
    }
    

    不会抛出任何异常。这是日志记录:

    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1] controller-hash im Controller=1481031354
    20150814-090718 INFO  [RMI TCP Connection(3)-127.0.0.1] Mapped "{[/mapgate/**],methods=[GET]}" onto protected void com.application.controller.MyAppProxyController.doGet(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
    20150814-090718 INFO  [RMI TCP Connection(3)-127.0.0.1] Mapped "{[/mapgate/**],methods=[POST]}" onto protected void com.application.controller.MyAppProxyController.doPost(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException
    20150814-090718 INFO  [RMI TCP Connection(3)-127.0.0.1] Looking for @ControllerAdvice: WebApplicationContext for namespace 'myAppDispatcherServlet-servlet': startup date [Fri Aug 14 09:07:18 CEST 2015]; parent: Root WebApplicationContext
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1] Filter com.application.filter.UserDbAuthenticationFilter initialisiert. App-Context: 641348200 WebApplicationContext for namespace 'myAppDispatcherServlet-servlet': startup date [Fri Aug 14 09:07:18 CEST 2015]; parent: Root WebApplicationContext
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1] Beans accessable by com.application.filter.UserDbAuthenticationFilter:
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.internalRequiredAnnotationProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.internalCommonAnnotationProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.internalPersistenceAnnotationProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.event.internalEventListenerProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.event.internalEventListenerFactory
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           myAppDispatcherServletContext
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           myAppRootContext
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           myAppProxyController
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           requestMappingHandlerMapping
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcContentNegotiationManager
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           viewControllerHandlerMapping
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           beanNameHandlerMapping
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           resourceHandlerMapping
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcResourceUrlProvider
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           defaultServletHandlerMapping
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           requestMappingHandlerAdapter
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcConversionService
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcValidator
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcPathMatcher
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcUrlPathHelper
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcUriComponentsContributor
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           httpRequestHandlerAdapter
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           simpleControllerHandlerAdapter
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           handlerExceptionResolver
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           mvcViewResolver
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           userDbAuthenticationFilter
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           prepareRequestFilter
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           responseTextXmlFilter
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           myAppFilterChain
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           userDbJpaDataSource
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           <b>entityManagerFactory</b>
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           diskFileItemFactory
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           xmlOutputFactory
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           xmlInputFactory
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           xmlEventFactory
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           urlPairing
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           myAppProperties
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1]           xmlFilterWords
    20150814-090718 DEBUG [RMI TCP Connection(3)-127.0.0.1] <b>EntityManagerFactory: null</b>
    

2 个答案:

答案 0 :(得分:2)

罪魁祸首是addFilter(..)的{​​{1}}方法。

如果仔细查看此LOC MyAppSpringBoot,即使FilterRegistration.Dynamic registration = container.addFilter("u3rAuthentication", UserDbAuthenticationFilter.class);已注册UserDbAuthenticationFilter作为过滤器,也很明显 不是 以任何方式与spring上下文相关联(,因为该类是直接注册的,而不是spring bean,它将解释ServletContextnull的{​​{1}}引用分别)。

虽然Spring会扫描同一个类ApplicationContext并稍后注册为bean,但<{>> emf相关联作为过滤器(从未调用此bean,因为它未注册为过滤器,这是您在调试时看到UserDbAuthenticationFilter被设置的位置

因此,同一个类ServletContext有两个实例,一个是servlet过滤器,另一个是spring bean,没有关联/链接。

这里需要的是一个使用servlet容器注册的过滤器,它也是一个spring bean。 GenericFilterBean来救你。根据您的需要进行扩展,并了解以下问题(来自API文档

  

此通用过滤器基类不依赖于Spring ApplicationContext概念。过滤器通常不加载它们自己的上下文,而是从Spring根应用程序上下文访问服务bean,可以通过过滤器的ServletContext访问(参见WebApplicationContextUtils)。

希望这会有所帮助。如果您遇到任何问题/需要进一步的帮助,请在评论中告知。

答案 1 :(得分:1)

由于应用程序上下文在控制器中正确注入,我假设Spring已正确初始化。但是你的过滤器被声明为原始过滤器,而不是spring bean,所以弹簧注释会被忽略。

您有两种方法可以访问应用程序上下文:

  1. 原始过滤器(未启用Spring)可以使用WebApplicationContext WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)访问根应用程序上下文。例如,您可以更改init方法:

    @Override
    public void init(FilterConfig filterConfig)    
    {
        ServletContex sc = filterConfig.getServletContext();
        appContext = WebApplicationContextUtils.getWebApplicationContext(sc);
        logger.debug("Filter {} initialisiert. App-Context: {} {}", this.getClass().getName(),appContext.hashCode(), appContext);
    }
    
  2. 您还可以使用DelegatingFilterProxy有效地将bean用作过滤器:

    将添加过滤器更改为:

    private void addFilters(ServletContext container) {
        FilterRegistration.Dynamic registration
                = container.addFilter("u3rAuthentication", DelegatingFilterProxy.class);
        registration.addMappingForUrlPatterns(null, false, "/entry/*");
    
        registration = container.addFilter("responseXmlFilter", DelegatingFilterProxy.class);
        registration.addMappingForUrlPatterns(null, false, "/entry/*");
    }
    

    在根上下文中添加过滤器bean:

    @Bean
    public UserDbAuthenticationFilter u3rAuthentication() { 
        return new UserDbAuthenticationFilter();
    }
    
    @Bean
    public ResponseTextXmlFilter responseXmlFilter() { 
        return new ResponseTextXmlFilter();
    }
    

    将不再调用init方法,但这可以工作:

    @PostConstruct
    public void filterInit() throws ServletException
    {
        logger.debug("Filter {} initialisiert. App-Context: {} {}", this.getClass().getName(),appContext.hashCode(), appContext);
    }