在进行日常网页开发的时候,不可避免的会用到CharacterEncodingFilter,此拦截器用于解决网页开发中的编码问题。

在SpringBoot开发中HttpEncodingAutoConfiguration就成为不得不学的一个自动配置类

配置类声明

打开该自动配置类可以看到该类上加的注解

1
2
3
4
5
6
7
8
9
10
11
12
//指明是配置类
@Configuration(proxyBeanMethods = false)
//注解的beans将自动被Environment属性配置
@EnableConfigurationProperties(HttpProperties.class)
//只有在Web环境下生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//该类存在时生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//存在该属性时生效 同时指定缺省默认值
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
}

然后就可以看到以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
//当容器中不存在此bean时生效
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
//可以通过在properties中配置编码,默认是UTF-8(在HttpProperties中可以找到)
filter.setEncoding(this.properties.getCharset().name());
//是否覆盖现有请求编码
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
//是否覆盖现有响应编码
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}

想必有人跟我一样会想到,我们在进行Spring开发时,在web.xml中有这么一段配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 编码过滤器 -->
//filter声明
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
//指定拦截器拦截请求地址
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

如果是我们自定义的Filter,可以手动指定拦截请求url(在另一篇文章中有介绍如何自定义),那SpringBoot中这些是如何指定的呢?

配置类如何生效

接着往下看

首先在项目启动时从启动类入口,很容易找到以下一段代码(为了方便删除了部分代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
//此处生成 AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
//从名字可以看出 该方法就是刷新当前的Context 从此方法进入
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();

return context;
}

接着往下能看到以下代码

1
2
3
4
5
6
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//执行此方法 (AnnotationConfigServletWebServerApplicationContext)
//执行父类(ServletWebServerApplicationContext)中refresh方法
((AbstractApplicationContext) applicationContext).refresh();
}

即以下代码

1
2
3
4
5
6
7
8
9
10
11
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
//最终可以看到其实执行的就是抽象父类中的方法
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}

最终执行代码 部分加了中文注释,其他的暂时没细看就没加—AbstractApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备刷新context
prepareRefresh();
// 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 准备在该context中使用的bean工厂
prepareBeanFactory(beanFactory);

try {

// 在特定上下文子类中初始化其他特殊bean
//****就是在这方法里面**** 接着往下
onRefresh();

// 注册监听器并注册
registerListeners();
}
}
}

这是进入ServletWebServerApplicationContext

看到这段代码,马上就接近真相了

此源码为了容易理解 我稍加修改

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
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
//进入此方法
//------------------------------------------------------------------
//以示区分
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
---------------------------------------------------------------------

}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

就是这个方法getSelfInitializer,接着会看到这段代码

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
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
//乍一看 我还是第一次遇到这么写的 搜了下其实是属于lambad表达式
//就是执行后面的方法
return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}


protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}

//-----------------------ServletContextInitializerBeans--------------------------------
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
//此处为initializers添加
addServletContextInitializerBeans(beanFactory);
//就是这个方法 用来获取Filter Servlet 与listener
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}


private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}

protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
//向bean工厂中添加Servlet
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
//向Bean工厂中添加Filter 使用FilterRegistrationBean 其父类中可以看到urls的默认值
//------------------------------------------------------------------
private static final String[] DEFAULT_URL_MAPPINGS = { "/*" };



//----------------------------------------------------------------

addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
//向bean工厂中添加Listener
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}


//------------------------------------------------------------------------------------


至此 结束。