实现ApplicationContextAware – ApplicationContext为NULL

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

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

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

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

  2. EntityManagerFactory未在身份validationfilter中注入( emf为null)

引导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 ??? } 

根身份validationfilter的代码:

 @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是正确的(非null)。

 @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三次时,我自己的filter是如何连接到链的。 方法addFilter中的参数name是否与bean名称相关联?

将Serge Ballesta的答案更新为2

这是在引导程序代码中创建filter链的代码

 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/*"); } 

这些是根上下文中的filterbean定义:

 @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(); } } 

仍然没有dependency injectionfilter。 是因为filter链是在引导阶段创建的,而bean是在根上下文中创建的吗?

更新3回到Serge Ballesta的回答

这是身份validationfilter中的基本代码:

 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] entityManagerFactory 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] EntityManagerFactory: null 

由于应用程序上下文正确地注入到控制器中,我假设Spring已正确初始化。 但是您的filter被声明为原始filter,而不是弹簧bean,因此弹簧注释会被忽略。

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

  1. 原始filter(不启用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作为filter:

    将添加filter更改为:

     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/*"); } 

    在根上下文中添加filterbean:

     @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); } 

罪魁祸首是MyAppSpringBoot addFilter(..)方法。

如果你仔细看看这个LOC FilterRegistration.Dynamic registration = container.addFilter("u3rAuthentication", UserDbAuthenticationFilter.class); 很明显,即使UserDbAuthenticationFilterServletContext注册为filter,它也不会以任何方式与spring上下文相关联( 因为该类是直接注册的,而不是Spring bean,它将分别解释ApplicationContextemfnull引用 ) 。

虽然相同的类UserDbAuthenticationFilter是由spring扫描并稍后注册为bean但与ServletContext 没有关联作为filter( 因为它未被注册为filter而从未调用此bean,这是您在调试时看到设置的ApplicationContext

因此,同一个类UserDbAuthenticationFilter有两个实例,一个用作servlet的filter,另一个用作spring bean,没有相互关联/链接。

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

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

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