20.管理及扩展SpringMVC组件

 

管理及扩展SpringMVC组件

1. 管理SpringMVC的组件

1,前端控制器的自动管理

找到WebMvcAutoConfiguration,找到DispatcherServletAutoConfiguration.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

/**
* The default Spring MVC view prefix.
*/
public static final String DEFAULT_PREFIX = "";

/**
* The default Spring MVC view suffix.
*/
public static final String DEFAULT_SUFFIX = "";

private static final String SERVLET_LOCATION = "/";

...
}

DispatcherServletAutoConfigurationpublic DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties)方法

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
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

/**
* The bean name for a DispatcherServlet that will be mapped to the root URL "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

/**
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
/**
以上配置是创建前端控制器对象
**/

...
}

@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
/**
以上配置是进行注册
**/

}

...
}
1
2
3
4
@Component和@Bean注解
1.@Component不能初始化属性值,@Bean可以初始化属性值
2.@Component比@Bean的优先级高
3.当class类位于jar中,无法添加@Component注解,这时可以通过@Bean注解向IOC容器中创建一个该类的对象

2,控制器[controller]的自动管理

直接扫描@ComponentScan(basePackages = {})

3,视图解析器的自动管理

创建视图解析器

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
public class WebMvcAutoConfiguration {

/**
* The default Spring MVC view prefix.
*/
public static final String DEFAULT_PREFIX = "";

/**
* The default Spring MVC view suffix.
*/
public static final String DEFAULT_SUFFIX = "";

...

public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

...

@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}

@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}

...

}

...
}

contentNegotiationViewResolver是一个管理所有视图解析器的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ContentNegotiatingViewResolver {

...

@Override
protected void initServletContext(ServletContext servletContext) {
Collection<ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
...
}

...

}

4.自定义前缀和后缀

SpringBoot默认不支持jsp

application.yml

1
2
3
4
5
6
#配置视图解析器的前缀和后缀
spring:
mvc:
view:
prefix: /WEB-INF/view/
suffix: .jsp

创建jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
This is a jsp file.
</body>
</html>

在pom文件中加入相关依赖

1
2
3
4
5
6
<!--加入内嵌入tomcat对jsp的支持 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>

创建controller

1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping("index")
public class IndexController {

@RequestMapping("hello")
public String hello() {
return "hello";
}

}

5.文件上传和下载的自动配置

查看MultipartAutoConfiguration

1
2
3
4
5
6
7
8
9
10
11
@Configuration(proxyBeanMethods = false)
// 必须有这些字节码存在
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
// 如果条件都成立,默认开启文件上传的支持
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
// 必须是一个web应用程序
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {
...
}

查看MultipartProperties

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
@ConfigurationProperties(prefix = "spring.servlet.multipart", ignoreUnknownFields = false)
public class MultipartProperties {

/**
* Whether to enable support of multipart uploads.
*/
private boolean enabled = true;

/**
* Intermediate location of uploaded files.
*/
private String location;

/**
* Max file size.
*/
private DataSize maxFileSize = DataSize.ofMegabytes(1);

/**
* Max request size.
*/
private DataSize maxRequestSize = DataSize.ofMegabytes(10);

...
}

application.yml

1
2
3
4
5
6
# 文件上传的配置
servlet:
multipart:
enabled: true
location:
max-file-size: 10MB

文件大小的单位必须为MB(大写)

6.消息转化和格式化转化

6.1 消息转化【接收页面参数并类型转化】

1
2
3
4
5
6
7
8
9
public class WebMvcAutoConfiguration {
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
ApplicationConversionService.addBeans(registry, this.beanFactory);
}

}
}
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
public class ApplicationConversionService extends FormattingConversionService {
public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
Set<Object> beans = new LinkedHashSet<>();
beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values());
beans.addAll(beanFactory.getBeansOfType(Converter.class).values());
beans.addAll(beanFactory.getBeansOfType(Printer.class).values());
beans.addAll(beanFactory.getBeansOfType(Parser.class).values());
for (Object bean : beans) {
if (bean instanceof GenericConverter) {
registry.addConverter((GenericConverter) bean);
}
else if (bean instanceof Converter) {
registry.addConverter((Converter<?, ?>) bean);
}
else if (bean instanceof Formatter) {
registry.addFormatter((Formatter<?>) bean);
}
else if (bean instanceof Printer) {
registry.addPrinter((Printer<?>) bean);
}
else if (bean instanceof Parser) {
registry.addParser((Parser<?>) bean);
}
}
}
}

标出的部分为接收前台参数,将前台参数进行类型转换。

6.2 消息格式化转化【接收页面参数并格式化转化】

1
2
3
4
5
#配置日期格式化的格式
spring:
mvc:
format:
date: yyyy-MM-dd
1
2
3
4
5
6
7
8
9
10
11
12
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

private Integer id;
private String name;

@DateTimeFormat(pattern = "yyyy/MM/dd-HH:mm:ss")
private Date birthday;

}
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
@Controller
@RequestMapping("index")
public class IndexController {

@RequestMapping("showDateTime")
public String showDateTime(Date date) {
System.out.println(date);
/*
* http://127.0.0.1:8080/index/showDateTime?date=2020-12-12
* Sat Dec 12 00:00:00 CST 2020
*
* http://127.0.0.1:8080/index/showDateTime?date=2020-12-12-10-10-10
* Sat Dec 12 00:00:00 CST 2020
*/
return "hello";
}

@RequestMapping("showDateTime2")
public String showDateTime2(@DateTimeFormat(pattern = "yyyy:MM:dd:HH:mm:ss")Date date) {
System.out.println(date);
/*
* http://127.0.0.1:8080/index/showDateTime2?date=2020:12:12:11:11:11
* Sat Dec 12 11:11:11 CST 2020
*/
return "hello";
}

@RequestMapping(value = "showStudentInfo", method = RequestMethod.GET)
public String showStudentInfo(Student student) {
System.out.println(student);
/*
* http://127.0.0.1:8080/index/showStudentInfo?id=1&name=Tom&birthday=2001/10/10-09:02:01
* Student(id=1, name=Tom, birthday=Wed Oct 10 09:02:01 CST 2001)
*/
return "hello";
}

}

7.欢迎页面的默认配置

以前欢迎页面的配置:web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

<welcome-file>login.jsp</welcome-file>

<welcome-file>index.html</welcome-file>

<welcome-file>index.htm</welcome-file>

<welcome-file>default.html</welcome-file>

<welcome-file>default.htm</welcome-file>

<welcome-file>default.jsp</welcome-file>

</welcome-file-list>

欢迎页面默认为src/main/resources/static/目录下的index.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
This is the welcome index.html.
</body>
</html>
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
public class WebMvcAutoConfiguration {
public static class EnableWebMvcConfiguration {
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
return welcomePageHandlerMapping;
}

private Resource getWelcomePage() {
for (String location : this.resourceProperties.getStaticLocations()) {
Resource indexHtml = getIndexHtml(location);
if (indexHtml != null) {
return indexHtml;
}
}
ServletContext servletContext = getServletContext();
if (servletContext != null) {
return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
return null;
}

private Resource getIndexHtml(String location) {
return getIndexHtml(this.resourceLoader.getResource(location));
}

private Resource getIndexHtml(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && (resource.getURL() != null)) {
return resource;
}
}
catch (Exception ex) {
}
return null;
}

}
}