前面写的14篇springmvc文章中都用到了配置文件,比如web.xml,springmvc的配置文件等等,使用起来比较繁琐,本文将把所有配置文件抛弃掉,采用全注解的方式使用springmvc,且会带大家了解其原理。
1、本文内容
- 全注解方式使用springmvc
- 全注解方式原理解析
2、全注解方式使用springmvc
2.1、新建maven web项目
项目中不需要web.xml配置文件,maven配置如下
<?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,会报错,所以需要加入下面配置,让插件忽略这个问题
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 的功能。
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需要的各种组件可以在这个里面配置,大家注意啦,这个类的特点
- 需要继承
WebMvcConfigurer
接口,这个接口中提供了很多方法,预留给开发者用来配置springmvc中的各种组件,springmvc容器启动的过程中,会自动调用这些方法 - 标注有@Configuration注解,表示这是一个配置类
- 标注有@ComponentScan注解,用来扫描组件,将bean注册到springmvc容器
- 需要标注@EnableWebMvc注解,用来起来springmvc注解配置功能,有了这个注解,springmvc容器才会自动调用WebMvcConfigurer接口中的方法
- WebMvcConfigurer接口中提供了一系列方法,用来配置视图解析器、静态资源处理器、拦截器
- 在这个类中我们配置了(②视图解析器、③拦截器、④静态资源处理器、⑤文件上传解析器)
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;
,这个类的代码如下
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、创建全局异常处理类
异常处理,我们也给整上,添加一个类,当出错的时候,跳转到错误页面。
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页面,测效果
@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:
<%@ 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,错误跳转的页面,会显示异常信息
<%@ 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
<%@ 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、项目整体结构
2.8、测试效果
项目发布到tomcat,访问首页,首页上有2个连接,可以点击一下,分别用来测试静态资源是否可以访问,另外一个测试全局异常处理的效果。
连接1效果:
连接2效果:
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的所有操作。
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属性。
Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlesTypes {
Class<?>[] value();
}
1、@HandlesTypes标签用在实现ServletContainerInitializer接口的类上面,比如:
@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文件
这个文件中指定的是org.springframework.web.SpringServletContainerInitializer
这个类,重点来了,springmvc就是依靠这个类来实现注解功能的,大家可以去看看这个类的源码,在其onStartup方法中添加断点,可以看到完整清晰的启动过程。
后面会专门有一篇文章带大家阅读源码,一步步带大家了解springmvc容器的整个启动过程。
4、总结
建议大家自己去实战一下,光看是不行的,看可能觉得什么都会了,但是抛开文章自己去试试,又是一番景象,学技术一定要多动手。
有问题欢迎留言。
5、案例代码
git地址:https://gitee.com/javacode2018/springmvc-series
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。