Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
SpringMVC教程
-> 全注解的方式 & 原理解析
1、Helloword
2、@Controller、@RequestMapping
3、接口测试利器
4、如何接受请求中的参数?
5、@RequestBody接收Json格式数据
6、多文件上传
7、返回页面常见的5种方式
8、返回json格式数据 & 通用返回值设计
9、SpringMVC返回null是什么意思?
10、异步处理请求
11、如何集成静态资源?
12、拦截器怎么用?
13、统一异常处理
14、实战篇:通用返回值 & 异常处理设计
15、全注解的方式 & 原理解析
16、源码解析SpringMVC处理请求的流程
17、源码解析SpringMVC容器的启动过程
18、RequestBodyAdvice:对@ReuqestBody进行增强
19、ResponseBodyAdvice:对@ResponseBody进行增强
20、RESTful接口详解
21、接口调用利器RestTemplate
22、参数解析器HandlerMethodArgumentResolver解密
23、@RequestParam用法及原理详解
24、@RequestBody原理解密
25、@RequestHeader详解
26、@CookieValue详解
27、@RequestAttribute详解
28、@SessionAttribute详解
29、重定向和转向详解
30、Converter转换器详解
31、跨域问题详解
32、类容协商,颠覆你的认知
33、终章
34、CORS通信
35、浏览器安全策略 & CORS
36、Http中的Content-Type详解
上一篇:实战篇:通用返回值 & 异常处理设计
下一篇:源码解析SpringMVC处理请求的流程
<div style="display:none"></div> **文末可以领取所有系列高清pdf。** 大家好,我是路人,这是SpringMVC系列第15篇。 前面写的14篇springmvc文章中都用到了配置文件,比如web.xml,springmvc的配置文件等等,使用起来比较繁琐,本文将把所有配置文件抛弃掉,采用全注解的方式使用springmvc,且会带大家了解其原理。 ## 1、本文内容 - 全注解方式使用springmvc - 全注解方式原理解析 ## 2、全注解方式使用springmvc ### 2.1、新建maven web项目 > 项目中不需要web.xml配置文件,maven配置如下 ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacode2018</groupId> <artifactId>chat12-annotation</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>chat12-annotation Maven Webapp</name> <description>springmvc全注解方式</description> <url>http://www.itsoku.com</url> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- 添加springmvc依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.6</version> </dependency> <!-- 添加jackson配置 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.11.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.4</version> </dependency> <!-- 添加servlet 依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- 日志 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!--文件上传的jar包--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> </dependencies> <build> <finalName>chat12-annotation</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <includes> <include>**/*.*</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.2</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project> ``` **注意**:上面配置中多了一个插件的配置,由于maven在web项目打包的时候,发现项目中没有web.xml,会报错,所以需要加入下面配置,让插件忽略这个问题 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/ae80b1e1-2878-4b43-a2de-82636eace94a.png) ### 2.2、创建初始化类,代替web.xml 在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为 AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。 我们来创建的 MvcInit类,需继承AbstractAnnotationConfigDispatcherServletInitializer ,项目启动的时候,servlet容器会自动加载这个类,这个类相当于 web.xml 的功能。 ```java package com.javacode2018.springmvc.chat12.config; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.Filter; /** * ①:1、创建Mvc初始化类,需要继承AbstractAnnotationConfigDispatcherServletInitializer类 */ public class MvcInit extends AbstractAnnotationConfigDispatcherServletInitializer { /** * springmvc容器的父容器spring配置类 * 实际工作中我们的项目比较复杂,可以将controller层放在springmvc容器中 * 其他层,如service层、dao层放在父容器了,bean管理起来更清晰一些 * 也可以没有父容器,将所有bean都放在springmvc容器中 * * @return */ @Override protected Class<?>[] getRootConfigClasses() { return new Class[0]; } /** * ②:2、设置springmvc容器的spring配置类 * * @return */ @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{MvcConfig.class}; } /** * ③:3、配置DispatcherServlet的url-pattern * * @return */ @Override protected String[] getServletMappings() { return new String[]{"/"}; } /** * ④:4、注册拦截器 * * @return */ @Override protected Filter[] getServletFilters() { //添加拦截器,解决乱码问题 CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); characterEncodingFilter.setForceRequestEncoding(true); characterEncodingFilter.setForceResponseEncoding(true); return new Filter[]{characterEncodingFilter}; } } ``` ### 2.3、创建配置springmvc配置类,代替springmvc配置文件 下面这个类相当于springmvc配置文件的功能,springmvc需要的各种组件可以在这个里面配置,大家注意啦,这个类的特点 1. 需要继承`WebMvcConfigurer`接口,这个接口中提供了很多方法,预留给开发者用来配置springmvc中的各种组件,springmvc容器启动的过程中,会自动调用这些方法 2. 标注有@Configuration注解,表示这是一个配置类 3. 标注有@ComponentScan注解,用来扫描组件,将bean注册到springmvc容器 4. 需要标注@EnableWebMvc注解,用来起来springmvc注解配置功能,有了这个注解,springmvc容器才会自动调用WebMvcConfigurer接口中的方法 5. WebMvcConfigurer接口中提供了一系列方法,用来配置视图解析器、静态资源处理器、拦截器 6. 在这个类中我们配置了(②视图解析器、③拦截器、④静态资源处理器、⑤文件上传解析器) ```java package com.javacode2018.springmvc.chat12.config; import com.javacode2018.springmvc.chat12.interceptor.MyInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.servlet.config.annotation.*; import org.springframework.web.servlet.view.InternalResourceViewResolver; /** * 1.开启springmvc注解配置 * 2、配置视图解析器 * 3、配置截器 * 4、配置静态资源访问 * 5、配置文件上传解析器 * 6、配置全局异常处理器 */ @Configuration @ComponentScan("com.javacode2018.springmvc.chat12") @EnableWebMvc //1:使用EnableWebMvc开启springmvc注解方式配置 public class MvcConfig implements WebMvcConfigurer { /** * ②:2、添加视图解析器(可以添加多个) * * @param registry */ @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/view/"); resolver.setSuffix(".jsp"); resolver.setOrder(Ordered.LOWEST_PRECEDENCE); registry.viewResolver(resolver); } @Autowired private MyInterceptor myInterceptor; /** * ③:3、添加拦截器(可以添加多个) * * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(this.myInterceptor).addPathPatterns("/**"); } /** * ④:4、配置静态资源访问处理器 * * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("/static/"); } /** * ⑤:5、配置文件上传解析器 * * @return */ @Bean public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(); //maxUploadSizePerFile:单个文件大小限制(byte) //maxUploadSize:整个请求大小限制(byte) commonsMultipartResolver.setMaxUploadSizePerFile(10 * 1024 * 1024); commonsMultipartResolver.setMaxUploadSize(100 * 1024 * 1024); return commonsMultipartResolver; } } ``` ### 2.4、创建自定义拦截器 上面的`MvcConfig`配置类中,我们定义了一个拦截器`MyInterceptor myInterceptor;`,这个类的代码如下 ```java package com.javacode2018.springmvc.chat12.interceptor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("这是MyInterceptor拦截器"); return true; } } ``` ### 2.5、创建全局异常处理类 异常处理,我们也给整上,添加一个类,当出错的时候,跳转到错误页面。 ```java package com.javacode2018.springmvc.chat12.config; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; /** * 异常处理 */ @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler public ModelAndView doException(Exception e) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("error"); modelAndView.addObject("ex", e); return modelAndView; } } ``` ### 2.6、测试功能 添加一个controller及几个jsp页面,测效果 ```java @Controller public class IndexController { /** * 首页 * * @return */ @RequestMapping("/") public String index() { return "index"; } /** * 测试异常情况 * * @return */ @RequestMapping("/testError") public String testError() { System.out.println(10 / 0); return "success"; } } ``` webapp/WEB-INF/view中创建3个页面 index.jsp: ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h2>全注解的方式配置springmvc</h2><br/> <a target="_blank" href="${pageContext.request.contextPath}/static/imgs/1.jpg">测试访问静态资源</a><br/> <a target="_blank" href="${pageContext.request.contextPath}/testError">测试触发全局异常处理</a> </body> </html> ``` error.jsp,错误跳转的页面,会显示异常信息 ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h2>出错啦,错误信息如下:</h2> <h3>${ex}</h3> </body> </html> ``` success.jsp ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h2>success</h2> </body> </html> ``` 在搞一个图片放在webapp/static/imgs中,稍后测试静态资源访问的效果。 ### 2.7、项目整体结构 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/b55669c5-92c6-4740-a42f-d31f9d2ac5ed.png) ### 2.8、测试效果 项目发布到tomcat,访问首页,首页上有2个连接,可以点击一下,分别用来测试静态资源是否可以访问,另外一个测试全局异常处理的效果。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/f9e898a9-8220-4edb-b760-c7b64d28efcd.png) 连接1效果: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/472df415-8ac8-477a-a514-8e8f92cf07ea.png) 连接2效果: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/15397bd7-9d22-4bb0-ab22-e61bddbf7280.png) ## 3、原理:ServletContainerInitializer接口 刚才上面2.2章节中有提到过,重点在于Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器,servlet3.0赋予了web项目免去所有配置文件(web.xml)的能力。 所以重点就在于`ServletContainerInitializer`这个接口上,springmvc全注解方式就是依靠这个接口来实现的,掌握了这个接口的用法,springmvc全注解的原理大家基本上就搞懂了,对阅读springmvc源码也是非常有利的。 下面看来这个接口的用法。 ### 3.1、ServletContainerInitializer源码 > 这个接口比较简单,只有一个onStartup方法,web容器启动的时候会自动调用这个方法,有2个参数,第1个参数稍后介绍,第2个参数ctx是servlet上下文,通过servlet上下文对象,我们可以在这个方法中实现web.xml的所有操作。 ```java public interface ServletContainerInitializer { public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; } ``` ### 3.2、ServletContainerInitializer使用 1、可以自定义一个实现ServletContainerInitializer接口,这个类必须在jar包的META-INF/services/javax.servlet.ServletContainerInitializer文件里面进行声明,这个文件的内容就是自定义类的全类名 2、Servlet容器启动会在所有jar和classes目录中扫描META-INF/services/javax.servlet.ServletContainerInitializer文件,然后找到这个文件中的具体的类,然后会自动实例化这个类,调用这个类的`onStartup`方法 ### 3.2、onStartup的第1个参数,@HandlesTypes注解 提到onStartup方法的第一个参数,这里就需要介绍一下@HandlesTypes这个注解,先来看一下其源码,比较简单,就只有一个Calss数组类型的value属性。 ```java Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface HandlesTypes { Class<?>[] value(); } ``` 1、@HandlesTypes标签用在实现ServletContainerInitializer接口的类上面,比如: ```java @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer ``` 2、servlet容器会扫描项目中的所有类(jar包和classes路径中),如果符合@HandlesTypes注解value值指定的类型,就会放在一个数组中,最终会传递给onStartup方法的第一个参数 3、当容器启动的时候,我们就可以通过拿到Set<Class<?>> c里面我们感兴趣的类,然后做一些初始化的工作 ## 3、springmvc全注解的原理 了解了`ServletContainerInitializer`接口的原理,咱们来看springmvc,spring-web.jar中包含了`META-INF/services/javax.servlet.ServletContainerInitializer文件` ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/4c677153-34bc-4975-9f60-838b4c358e44.png) 这个文件中指定的是`org.springframework.web.SpringServletContainerInitializer`这个类,重点来了,springmvc就是依靠这个类来实现注解功能的,大家可以去看看这个类的源码,在其onStartup方法中添加断点,可以看到完整清晰的启动过程。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/226/a21f64e7-d8ed-44f3-9388-4d492ec478d7.png) 后面会专门有一篇文章带大家阅读源码,一步步带大家了解springmvc容器的整个启动过程。 ## 4、总结 建议大家自己去实战一下,光看是不行的,看可能觉得什么都会了,但是抛开文章自己去试试,又是一番景象,学技术一定要多动手。 有问题欢迎留言。 ## 5、案例代码 ```html git地址:https://gitee.com/javacode2018/springmvc-series ``` <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container