我使用基于java的Spring Mvc配置。
我在 WebApplicationInitializer 实现中注册 Spring 调度程序 servlet。 加载 Spring ApplicationContext 配置文件。 Spring 配置文件管理的逻辑是在 ApplicationContextInitializer 实现中实现的。而且效果很好。
以下是原始文件的完整示例: Web应用程序初始化器
public class SpringMvcExampleWebApplicationInitializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerDispatcherServlet(servletContext);
registerHiddenHttpMethodFilter(servletContext);
}
private void registerDispatcherServlet(final ServletContext servletContext) {
WebApplicationContext dispatcherContext = createContext(WebMvcContextConfiguration.class, InfrastructureContextConfiguration.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(dispatcherContext);
dispatcherServlet.setContextInitializers(new SpringMvcExampleProfilesInitializer());
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, dispatcherServlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private WebApplicationContext createContext(final Class<?>... annotatedClasses) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(annotatedClasses);
return context;
}
private void registerHiddenHttpMethodFilter(ServletContext servletContext) {
FilterRegistration.Dynamic registration = servletContext.addFilter("hiddenHttpMethodFilter", HiddenHttpMethodFilter.class);
registration.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD),
false, DISPATCHER_SERVLET_NAME);
}
}
SpringMvcExampleProfiles初始化器
public class SpringMvcExampleProfilesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext ctx) {
ConfigurableEnvironment environment = ctx.getEnvironment();
List<String> profiles = new ArrayList<String>(getProfiles());
if( profiles == null || profiles.isEmpty() )
{
throw new IllegalArgumentException("Profiles have not been configured");
}
environment.setActiveProfiles(profiles.toArray( new String[0]));
}
//TODO add logic
private Collection<String> getProfiles() {
return Lists.newArrayList("file_based", "test_data");
}
}
基础设施上下文配置
@Configuration
@ComponentScan(basePackages = {"com.savdev.springmvcexample.repository", "com.savdev.springmvcexample.config"})
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.savdev.springmvcexample.repository"})
public class InfrastructureContextConfiguration {
@Configuration
@Profile(value = "file_based")
@PropertySource("classpath:/db/config/file_based.properties")
public static class FileBasedConfiguration {
@Inject
private Environment environment;
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();
dataSource.setDriverClassName(environment.getProperty("jdbc.driver"));
dataSource.setUrl(environment.getProperty("jdbc.url"));
dataSource.setUsername(environment.getProperty("jdbc.username"));
dataSource.setPassword(environment.getProperty("jdbc.password"));
return dataSource;
}
}
@Bean
public SpringLiquibase liquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:/db/liquibase/changelog/db.changelog-master.xml");
liquibase.setDropFirst(true);
return liquibase;
}
然后我将 Spring Security 上下文配置添加到应用程序中。要使用它,必须加载
DelegatingFilterProxy
。我更新了配置:
添加了新方法并在
onStartup
中调用它:
private void registerSpringSecurityFilterChain(ServletContext servletContext) {
FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter(
BeanIds.SPRING_SECURITY_FILTER_CHAIN,
new DelegatingFilterProxy());
springSecurityFilterChain.addMappingForUrlPatterns(null, false, "/*");
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
...
registerDispatcherServlet(servletContext);
...
registerSpringSecurityFilterChain(servletContext);
}
现在,当我尝试请求任何网址时,我收到错误:
message No WebApplicationContext found: no ContextLoaderListener registered?
description The server encountered an internal error that prevented it from fulfilling this request.
exception
java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:251)
好的,我添加了以下内容:
private static final Class<?>[] configurationClasses = new Class<?>[]{
WebMvcContextConfiguration.class, InfrastructureContextConfiguration.class};
...
private void registerListener(ServletContext servletContext) {
WebApplicationContext rootContext = createContext(configurationClasses);
servletContext.addListener(new ContextLoaderListener(rootContext));
}
并从以下位置调用它:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerListener(servletContext);
registerDispatcherServlet(servletContext);
registerHiddenHttpMethodFilter(servletContext);
registerSpringSecurityFilterChain(servletContext);
}
错误已经消失。
但是所有依赖于Spring配置文件的bean现在都没有加载。添加
ContextLoaderListener
破坏了 SpringMvcExampleProfilesInitializer
逻辑。
No qualifying bean of type [javax.sql.DataSource] found for dependency
我可以做什么来解决这个问题?请问有什么想法吗?
这是完整更新的 Web 初始值设定项类:
public class SpringMvcExampleWebApplicationInitializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final Class<?>[] configurationClasses = new Class<?>[]{
WebMvcContextConfiguration.class, InfrastructureContextConfiguration.class};
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerListener(servletContext);
registerDispatcherServlet(servletContext);
registerHiddenHttpMethodFilter(servletContext);
registerSpringSecurityFilterChain(servletContext);
}
private void registerSpringSecurityFilterChain(ServletContext servletContext) {
FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter(
BeanIds.SPRING_SECURITY_FILTER_CHAIN,
new DelegatingFilterProxy());
springSecurityFilterChain.addMappingForUrlPatterns(null, false, "/*");
}
private void registerDispatcherServlet(final ServletContext servletContext) {
WebApplicationContext dispatcherContext = createContext(WebMvcContextConfiguration.class, InfrastructureContextConfiguration.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(dispatcherContext);
dispatcherServlet.setContextInitializers(new SpringMvcExampleProfilesInitializer());
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, dispatcherServlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private WebApplicationContext createContext(final Class<?>... annotatedClasses) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(annotatedClasses);
// context.refresh();
return context;
}
private void registerListener(ServletContext servletContext) {
WebApplicationContext rootContext = createContext(configurationClasses);
servletContext.addListener(new ContextLoaderListener(rootContext));
// servletContext.addListener(new RequestContextListener());
}
private void registerHiddenHttpMethodFilter(ServletContext servletContext) {
FilterRegistration.Dynamic registration = servletContext.addFilter("hiddenHttpMethodFilter", HiddenHttpMethodFilter.class);
registration.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD),
false, DISPATCHER_SERVLET_NAME);
}
}
正如 M.Deinum 建议的那样,我将配置文件初始值设置为
ServletContext
,而不是将其设置为 DispatcherServlet
。这是更新后的配置:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
configureServletContext( servletContext );
registerListener(servletContext);
registerDispatcherServlet(servletContext);
...
}
private void configureServletContext(ServletContext servletContext) {
String initializerClasses = servletContext.getInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM);
String profilesInitClassName = SpringMvcExampleProfilesInitializer.class.getName();
if (StringUtils.hasText(initializerClasses)) {
initializerClasses += " " + profilesInitClassName;
}
else {
initializerClasses = profilesInitClassName;
}
servletContext.setInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, initializerClasses);
}