接手过三个不同方式的web项目,spring mvc 配置各有不同,整理一下
最早的方式,基于web.xml配置,通过配置DispatcherServlet实现初始化,如果需要配置spring,另外添加spring ContextLoaderListener,存在双亲上下文的问题
spring 监听器配置
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
spring mvc DispatcherServlet配置
<servlet>
<servlet-name>demoDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/META-INF/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
基于注解配置spring mvc,不需要再配置web.xml,代码及原理如下:
1.实现方式,自定义WebAppInitializer继承
package ;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.util.IntrospectorCleanupListener;
import com.ly.fn.biz.fq.hybrid.config.ContextConfig;
import com.ly.fn.biz.fq.hybrid.config.ServletConfig;
import com.ly.fn.inf.trace.client.web.CatFilter;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class[] { ContextConfig.class };
}
@Override
protected Class>[] getServletConfigClasses() {
return new Class[] { ServletConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected void registerContextLoaderListener(ServletContext servletContext) {
servletContext.addListener(new IntrospectorCleanupListener());
super.registerContextLoaderListener(servletContext);
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
DelegatingFilterProxy delegateFilter = new DelegatingFilterProxy();
FilterRegistration.Dynamic reg = servletContext.addFilter("filters", delegateFilter);
reg.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*");
}
}
2.配置相关组件,同以前的spring-servlet.xml配置
package ;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "", includeFilters = {
@Filter(Controller.class), @Filter(ControllerAdvice.class), @Filter(RestController.class) })
public class ServletConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List argumentResolvers) {
}
@Override
public Validator getValidator() {
OptionalValidatorFactoryBean optional = new OptionalValidatorFactoryBean();
optional.setProviderClass(HibernateValidator.class);
return optional;
}
@Bean
@Autowired
public SpringTemplateEngine templateEngine(Set templateResolver,
Set layoutDialect) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setAdditionalDialects(layoutDialect);
engine.setTemplateResolvers(templateResolver);
return engine;
}
@Bean
public IDialect infDialect() {
return new InfDialect();
}
@Bean
public ServletContextTemplateResolver templateResolver_views() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setCacheable(false);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(1);
return resolver;
}
@Bean
@Autowired
public ThymeleafViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine);
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(0);
return resolver;
}
@Bean
public RpcwiredAutoInjectionProcessor rpcwiredProcessor() {
return new RpcwiredAutoInjectionProcessor();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
}
@Override
public void configureMessageConverters(List> converters) {
converters.add(new FormHttpMessageConverter());
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new StringHttpMessageConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
super.configureContentNegotiation(configurer);
configurer.defaultContentTypeStrategy(new DefaultContentNegotiationStrategy());
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/nohup.html").setStatusCode(HttpStatus.OK).setViewName("nohup");
}
3.启动原理
servlet 3.0 下容器启动后会通过java spi 机制寻找ServletContainerInitializer实现类,
该配置在spring-web jar 包META-INF下
文件内容
org.springframework.web.SpringServletContainerInitializer
打开该类,我们会发现其实现了onStartup方法
package org.springframework.web;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List initializers = new LinkedList();
if (webAppInitializerClasses != null) {
for (Class> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
@HandlesTypes(WebApplicationInitializer.class)
onStup方法根据类上的注解找到所有改类型的类作为参数传入,而我们的前面定义的AppInitializer也实现了该接口,
initializers.add((WebApplicationInitializer) waiClass.newInstance());
最终此处实例化我们自定义的启动类,并且在后面执行onStartUp方法
AbstractDispatcherServletInitializer.onStartup中执行了相关servlet的注册registerDispatcherServlet(servletContext);
篇幅过长,另写