摘要
看完本文你将掌握如下知识点:
- SpringBoot都帮我们做了哪些自动配置
- 我们如何接管SpringBoot的自动配置
- 注册Servlet、Filter、Listener的方法
SpringBoot系列:Spring Boot学习笔记
SpringBoot的自动配置
1.自动配置类都存放在spring-boot-autoconfigure-1.4.2.RELEASE.jar下的
org.springframework.boot.autoconfigure路径下;
2.application.properties中配置debug=true
后启动容器,可以看到服务器初始化的自动配置如下:
- DispatcherServletAutoConfiguration
注册org.springframework.web.servlet.DispatcherServlet - EmbeddedServletContainerAutoConfiguration
注册容器类型,如类路径下存在org.apache.catalina.startup.Tomcat,就会注册Tomcat容器 - ErrorMvcAutoConfiguration
注册异常处理器 - HttpEncodingAutoConfiguration
注册http编码过滤器 - HttpMessageConvertersAutoConfiguration
注册json或者xml处理器 - JacksonAutoConfiguration
注册json对象解析器 - JmxAutoConfiguration
注册JMX管理器
JMX与Spring集成
spring通过annotation注解注册MBean到JMX实现监控java运行状态
- MultipartAutoConfiguration
注册文件传输处理器 - ServerPropertiesAutoConfiguration
用于初始化容器相关的配置属性,如服务地址、端口、contextPath,并根据当前容器类型初始化各个容器的特有属性,如tomcat的maxThreads、uriEncoding等等,其对应的属性类为ServerProperties; - WebClientAutoConfiguration
注册RestTemplate - WebMvcAutoConfiguration
注册SpringMvc相关处理器,如ResourceResolver、RequestMappingHandlerAdapter、ExceptionHandlerExceptionResolver、ViewResolver、LocaleResolver,等等 - WebSocketAutoConfiguration
注册webSocket相关处理器,根据容器类型注册不同的处理器
3.如果依赖中加入了其它功能的依赖,SpringBoot还会实现这些功能的自动适配,比如我们增加数据库的JPA的功能,就会启用对JpaRepositoriesAutoConfiguration的自动配置功能。关于数据库方面的内容将在后文介绍。
org.springframework.boot
spring-boot-starter-data-jpa
说明
从各个AutoConfiguration配置类中可以看到如下注解,基于这些注解可以确定这些AutoConfiguration的初始化顺序:
- @AutoConfigureOrder(-2147483648):数越小越先初始化
- @AutoConfigureAfter({EmbeddedServletContainerAutoConfiguration.class}):在指定的配置类初始化后再加载
- @AutoConfigureBefore({WebMvcAutoConfiguration.class}):在指定的配置类初始化前加载
接管SpringBoot的自动配置
我们介绍过@SpringBootApplication这个注解,因其包含@EnableAutoConfiguration和@ComponentScan注解,可以自动扫描相关的自动配置类,从而实现自动配置功能的。
上面介绍默认情况下SpringBoot默认会初始化很多的自动配置,这些配置有些我们在项目中可能用不到,那要如何去掉呢?
去掉不需要的自动配置类
比如我们不需要开启webSocket和JMX的自动配置,我们需要在@SpringBootApplication这个注解中指定exclude属性
@SpringBootApplication(exclude = {WebSocketAutoConfiguration.class,JmxAutoConfiguration.class})
public class SpringBootWebDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootWebDemoApplication.class, args);
}
}
明确指定需要启用哪些自动配置
我们可以去掉@SpringBootApplication注解,改用@Configuration、@Import、@ComponentScan注解,在@Import注解中明确指定需要启用哪些自动配置
//@SpringBootApplication(exclude = {WebSocketAutoConfiguration.class,JmxAutoConfiguration.class})
@Configuration
@Import({
DispatcherServletAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
HttpEncodingAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JacksonAutoConfiguration.class,
MultipartAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
WebMvcAutoConfiguration.class
})
@ComponentScan
public class SpringBootWebDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootWebDemoApplication.class, args);
}
}
说明:
- 这里推荐使用第一种方式:@SpringBootApplication(exclude={});
- 实际上,开启默认的自动配置功能,只是会影响项目启动时间,所以没有特殊需要,可以不需要关闭某个自动配置功能;
- 在某些情况,比如项目需要多数据源时,在项目中就会包含多个DataSource的Bean,因为DataSourceAutoConfiguration自动配置只能绑定一个数据源,此时发现多个DataSource的Bean被Spring注册就会抛出异常。
1.这时就可以采用去掉DataSourceAutoConfiguration的方式;
2.或者也可以在某一个DataSource的Bean上声明@Primary注解,指定其为主数据源,这时DataSourceAutoConfiguration只会加载被指定@Primary注解的主数据源,这样就可以享受到SpringBoot自动配置带来的好处。
接管WebMvc自动配置
对于一个web项目,最重要的就是Mvc相关的控制,SpringBoot通过WebMvcAutoConfiguration来完成与Mvc有关的自动配置。如果希望完全接管WebMvc自动配置,可以在项目中创建一个注解了@EnableWebMvc的配置类,比如:
package com.example;
import org.apache.log4j.Logger;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.handler.SimpleServletHandlerAdapter;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import java.util.Properties;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example", useDefaultFilters = false, includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})
})
public class MvcConfig extends WebMvcConfigurationSupport {
private static final Logger logger = Logger
.getLogger(MvcConfig.class);
/**
* 描述 : <注册视图处理器>.
*
<使用方法说明>
* @return
*/
@Bean
public ViewResolver viewResolver() {
logger.info("ViewResolver");
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/jsp/function/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/**
* 描述 : <注册消息资源处理器>.
*
<使用方法说明>
* @return
*/
@Bean
public MessageSource messageSource() {
logger.info("MessageSource");
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("config.messages.messages");
return messageSource;
}
/**
* 描述 : <注册servlet适配器>.
*
<只需要在自定义的servlet上用@Controller("映射路径")标注即可>
* @return
*/
@Bean
public HandlerAdapter servletHandlerAdapter(){
logger.info("HandlerAdapter");
return new SimpleServletHandlerAdapter();
}
/**
* 描述 : <本地化拦截器>.
*
<使用方法说明>
* @return
*/
@Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
logger.info("LocaleChangeInterceptor");
return new LocaleChangeInterceptor();
}
/**
* 描述 : <基于cookie的本地化资源处理器>.
*
<使用方法说明>
* @return
*/
@Bean(name="localeResolver")
public CookieLocaleResolver cookieLocaleResolver(){
logger.info("CookieLocaleResolver");
return new CookieLocaleResolver();
}
/**
* 描述 : <添加拦截器>.
*
<使用方法说明>
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
// TODO Auto-generated method stub
logger.info("addInterceptors start");
registry.addInterceptor(localeChangeInterceptor());
logger.info("addInterceptors end");
}
/**
* 描述 : <资源访问处理器>.
*
<可以在jsp中使用/static/**的方式访问/WEB-INF/static/下的内容>
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
logger.info("addResourceHandlers");
registry.addResourceHandler("/static/**").addResourceLocations("/WEB-INF/static/");
}
/**
* 描述 : <文件上传处理器>.
*
<使用方法说明>
* @return
*/
@Bean(name="multipartResolver")
public CommonsMultipartResolver commonsMultipartResolver(){
logger.info("CommonsMultipartResolver");
return new CommonsMultipartResolver();
}
/**
* 描述 : <异常处理器>.
*
<系统运行时遇到指定的异常将会跳转到指定的页面>
* @return
*/
@Bean(name="exceptionResolver")
public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
logger.info("CP_SimpleMappingExceptionResolver");
SimpleMappingExceptionResolver simpleMappingExceptionResolver= new SimpleMappingExceptionResolver();
simpleMappingExceptionResolver.setDefaultErrorView("common_error");
simpleMappingExceptionResolver.setExceptionAttribute("exception");
Properties properties = new Properties();
properties.setProperty("java.lang.RuntimeException", "common_error");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
}
此时debug模式运行项目,会看到WebMvcAutoConfiguration没有被自动配置,说明我们自己定义的MvcConfig
已经完全接管了默认的自动配置,这是因为WebMvcAutoConfiguration有一个条件注解:
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
而我们本例中MvcConfig
就是WebMvcConfigurationSupport的实现类,同时加入@EnableWebMvc注解也会导入一个WebMvcConfigurationSupport的实现类:DelegatingWebMvcConfiguration
,所以MvcConfig
继承WebMvcConfigurationSupport不是必须的,但是可以方便我们编码。
参考:SpringMVC4零配置--Web上下文配置【MvcConfig】
如果希望可以继续使用WebMvcAutoConfiguration的自动配置,而只是需要修改或者增加MVC中的某些配置时,我们可以创建一个配置类,并继承于抽象类WebMvcConfigurerAdapter,我们可以通过实现抽象类的方法来注册自己的控制器。
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
public WebMvcConfigurerAdapter() {
}
public void configurePathMatch(PathMatchConfigurer configurer) {
}
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
public void addFormatters(FormatterRegistry registry) {
}
public void addInterceptors(InterceptorRegistry registry) {
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
public void addCorsMappings(CorsRegistry registry) {
}
public void addViewControllers(ViewControllerRegistry registry) {
}
public void configureViewResolvers(ViewResolverRegistry registry) {
}
public void addArgumentResolvers(List argumentResolvers) {
}
public void addReturnValueHandlers(List returnValueHandlers) {
}
public void configureMessageConverters(List> converters) {
}
public void extendMessageConverters(List> converters) {
}
public void configureHandlerExceptionResolvers(List exceptionResolvers) {
}
public void extendHandlerExceptionResolvers(List exceptionResolvers) {
}
public Validator getValidator() {
return null;
}
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
比如我们可以增加一个视图跳转控制器,如下:
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/demo/123").setViewName("/demo");
}
}
注册Servlet、Filter、Listener的方法
1.如果是war包项目,我们可以将Servlet、Filter、Listener注册到WebApplicationInitializer的实现类中
@Order(1)
public class CommonInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
//Log4jConfigListener
servletContext.setInitParameter("log4jConfigLocation", "classpath:log4j.properties");
servletContext.addListener(Log4jConfigListener.class);
//OpenSessionInViewFilter
OpenSessionInViewFilter hibernateSessionInViewFilter = new OpenSessionInViewFilter();
FilterRegistration.Dynamic filterRegistration = servletContext.addFilter(
"hibernateFilter", hibernateSessionInViewFilter);
filterRegistration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/");
//DemoServlet
DemoServlet demoServlet = new DemoServlet();
ServletRegistration.Dynamic dynamic = servletContext.addServlet(
"demoServlet", demoServlet);
dynamic.setLoadOnStartup(2);
dynamic.addMapping("/demo_servlet");
}
}
2.如果是jar包部署方式,则可以将其注册到任意一个@Configuration配置类中
@Configuration
public class WebConfig {
@Bean
public ServletRegistrationBean servletRegistrationBean_demo1(){
return new ServletRegistrationBean(new DemoServlet(),"/demo-servlet1");
}
@Bean
public ServletRegistrationBean servletRegistrationBean_demo2(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.addUrlMappings("/demo-servlet2");
servletRegistrationBean.setServlet(new DemoServlet2());
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new OpenSessionInViewFilter());
Set set = new HashSet();
set.add("/");
filterRegistrationBean.setUrlPatterns(set);
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean(){
ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
servletListenerRegistrationBean.setListener(new Log4jConfigListener());
servletListenerRegistrationBean.addInitParameter("log4jConfigLocation","classpath:log4j.properties");
return servletListenerRegistrationBean;
}
}
总结
一句话概括SpringBoot的自动配置--就是一组基于条件注解实现Bean注册的Spring配置类。