我正在编写一个Tomcat应用程序,它作为一些内部服务的代理。
我已将Spring项目从混合的基于XML和注释的配置切换到基于Java和注释的配置。
在切换配置样式之前,应用程序运行正常。现在我有两个问题。
在我的两个过滤器中执行init-methods时,ApplicationContext
为空。当我调试我的应用程序时,我可以看到方法setApplicationContext
已执行。
EntityManagerFactory
未在身份验证过滤器中注入(emf
为空)
引导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());
}
}
我按照你的所有指示#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名称相关联?
这是在引导代码中创建过滤器链的代码:
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是在根上下文中创建的吗?
这是身份验证过滤器中的基本代码:
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>
答案 0 :(得分:2)
罪魁祸首是addFilter(..)
的{{1}}方法。
如果仔细查看此LOC MyAppSpringBoot
,即使FilterRegistration.Dynamic registration = container.addFilter("u3rAuthentication", UserDbAuthenticationFilter.class);
已注册UserDbAuthenticationFilter
作为过滤器,也很明显 不是 以任何方式与spring上下文相关联(,因为该类是直接注册的,而不是spring bean,它将解释ServletContext
和null
的{{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,所以弹簧注释会被忽略。
您有两种方法可以访问应用程序上下文:
原始过滤器(未启用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);
}
您还可以使用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);
}