摘要
看完本文你将掌握如下知识点:
- 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的自动配置功能。关于数据库方面的内容将在后文介绍。
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
|
说明
从各个AutoConfiguration配置类中可以看到如下注解,基于这些注解可以确定这些AutoConfiguration的初始化顺序:
-
@AutoConfigureOrder(-2147483648):数越小越先初始化
-
@AutoConfigureAfter({EmbeddedServletContainerAutoConfiguration.class}):在指定的配置类初始化后再加载
-
@AutoConfigureBefore({WebMvcAutoConfiguration.class}):在指定的配置类初始化前加载
接管SpringBoot的自动配置
我们介绍过**@SpringBootApplication这个注解,因其包含@EnableAutoConfiguration和@ComponentScan**注解,可以自动扫描相关的自动配置类,从而实现自动配置功能的。
上面介绍默认情况下SpringBoot默认会初始化很多的自动配置,这些配置有些我们在项目中可能用不到,那要如何去掉呢?
去掉不需要的自动配置类
比如我们不需要开启webSocket和JMX的自动配置,我们需要在**@SpringBootApplication这个注解中指定exclude**属性
1 2 3 4 5 6
| @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**注解中明确指定需要启用哪些自动配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @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**的配置类,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
| 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);
@Bean public ViewResolver viewResolver() { logger.info("ViewResolver"); InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/jsp/function/"); viewResolver.setSuffix(".jsp"); return viewResolver; }
@Bean public MessageSource messageSource() { logger.info("MessageSource"); ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("config.messages.messages");
return messageSource; }
@Bean public HandlerAdapter servletHandlerAdapter(){ logger.info("HandlerAdapter"); return new SimpleServletHandlerAdapter(); }
@Bean public LocaleChangeInterceptor localeChangeInterceptor(){ logger.info("LocaleChangeInterceptor"); return new LocaleChangeInterceptor(); }
@Bean(name="localeResolver") public CookieLocaleResolver cookieLocaleResolver(){ logger.info("CookieLocaleResolver"); return new CookieLocaleResolver(); }
@Override protected void addInterceptors(InterceptorRegistry registry) { logger.info("addInterceptors start"); registry.addInterceptor(localeChangeInterceptor()); logger.info("addInterceptors end"); }
@Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { logger.info("addResourceHandlers"); registry.addResourceHandler("/static/**").addResourceLocations("/WEB-INF/static/"); }
@Bean(name="multipartResolver") public CommonsMultipartResolver commonsMultipartResolver(){ logger.info("CommonsMultipartResolver"); return new CommonsMultipartResolver(); }
@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有一个条件注解:
1
| @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
|
而我们本例中MvcConfig
就是WebMvcConfigurationSupport的实现类,同时加入**@EnableWebMvc**注解也会导入一个WebMvcConfigurationSupport的实现类:DelegatingWebMvcConfiguration
,所以MvcConfig
继承WebMvcConfigurationSupport不是必须的,但是可以方便我们编码。
参考:SpringMVC4零配置–Web上下文配置【MvcConfig】
如果希望可以继续使用WebMvcAutoConfiguration的自动配置,而只是需要修改或者增加MVC中的某些配置时,我们可以创建一个配置类,并继承于抽象类WebMvcConfigurerAdapter,我们可以通过实现抽象类的方法来注册自己的控制器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| 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<HandlerMethodArgumentResolver> argumentResolvers) { }
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { }
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { }
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { }
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { }
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { }
public Validator getValidator() { return null; }
public MessageCodesResolver getMessageCodesResolver() { return null; } }
|
比如我们可以增加一个视图跳转控制器,如下:
1 2 3 4 5 6 7
| @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的实现类中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Order(1) public class CommonInitializer implements WebApplicationInitializer {
@Override public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("log4jConfigLocation", "classpath:log4j.properties"); servletContext.addListener(Log4jConfigListener.class);
OpenSessionInViewFilter hibernateSessionInViewFilter = new OpenSessionInViewFilter(); FilterRegistration.Dynamic filterRegistration = servletContext.addFilter( "hibernateFilter", hibernateSessionInViewFilter); filterRegistration.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/");
DemoServlet demoServlet = new DemoServlet(); ServletRegistration.Dynamic dynamic = servletContext.addServlet( "demoServlet", demoServlet); dynamic.setLoadOnStartup(2); dynamic.addMapping("/demo_servlet");
} }
|
2.如果是jar包部署方式,则可以将其注册到任意一个**@Configuration**配置类中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @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<String> set = new HashSet<String>(); 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配置类。