一.概述
MVC:Model+View+Controller(数据模型+视图+控制器)
三层架构:Presentation tier+Application tier+Data tier(展示层+应用层+数据访问层)
在spring mvc里,有个专门的类叫Model,用来和V之间的数据交互,传值;V指的是试图页面,包含jsp,freeMarker,Velocity,Thymeleaf,Tile等;C当然就是控制器(Spirng MVC的注解@Controller类)
二.项目快速搭建
1.构建maven项目
pom.xml
<dependencies>
<dependency>
<groupId></groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provied</scope>
</dependency>
<!--spring mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.18.RELEASE</version>
</dependency>
<!--other web-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--spring and transactions-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!--slf4j和LogBack-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins<groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</verison>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configruation>
</plugin>
</plugins>
</build>
2.日志配置
/*
Logback 配置文件的语法非常灵活。正因为灵活,所以无法用 DTD 或 XML schema 进行定义。尽管如此,可以这样描述配置文件的基本结构:以<configuration>开头,后面有零个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素。
*/
<?xml version="1.0" encoding="UTF-8" ?>
/*
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
*/
<configuration scan="true" scanPeriod="1 seconds">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<jmxConfigurator/>
/*
负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名
*/
<appender name="console" class="ch.qos.logback.core.ConsoleAppender" >
<encoder>
<pattern>logbak:%d{HH:mm:ss.SSS} %logger{36} - %msg%n </pattern>
</encoder>
</appender>
/*
用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>。<loger>仅有一个name属性,一个可选的level和一个可选的addtivity属性。可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个loger
name: 用来指定受此loger约束的某一个包或者具体的某一个类。
level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前loger将会继承上级的级别。
addtivity: 是否向上级loger传递打印信息。默认是true。同<loger>一样,可以包含零个或多个 <appender-ref>元素,标识这个appender将会添加到这个loger。
*/
<logger name="org.springframework.web" level="DEBUG" />
/*
它也是<loger>元素,但是它是根loger,是所有<loger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。
*/
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>
3.演示页面
在src/main/resources下建立views目录,并在此目录下新建index.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head></head>
<body>
<pre>
welcome to spring mvc world
</pre>
</body>
</html>
4.spring mvc配置
@Configuration
@EnableWebMvc //开启一些默认配置,如ViewResolver或者MeaaageConverter等
@ComponentScan("com.shm.springdemo")
public class MyMvcCofig{
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
}
5.web配置
/**
WebApplicationInitializer是spring提供用来配置servlet3.0+的接口,从而实现了替代web.xml的位置。实现此接口将会自动被SpringServletContainerInitializer(用来启动Servlet3.0容器)获取到。
*/
public class WebInitializer implements WebApplicationInitializer{
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//注册配置类,并作关联
AnnotationConfigWebApplication ctx = new AnnotationConfigWebApplication();
ctx.register(MyMvcConfig.class);
ctx.setServletContext(servletContext);
//注册Spring MVC的DispatcherServlet
Dynamic servlet = servletContext.addServlet("dispatcher",new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
6.控制器
@Controller
public class HelloController{
@RequestMapping("/index")
public String hello(){
return "index";
}
}
三.Spring MVC的常用注解
- @Controller 注解在类上,表明这个类是spring mvc里的controller,将其声明为spring的一个bean,DiapathcServlet会自动扫描注解了此注解的类并将web请求映射到注解了@RequestMapping的方法上。
- @RequestMapping 是用来映射web请求,处理类和方法的。可注解在类和方法上
- @ResponseBody 支持将返回值放在response体内,而不是返回一个页面。可注解在返回值前或者方法上。
- @RequestBody 允许request的参数在request体内,而不是在直接连接在地址后面。注解在参数前。
- @PathVariable 用来接收路劲参数。注解在参数前。
- @RestController 是一个组合注解,组合了@Controller和@ResponseBody。
添加jackson及其以来获得对象和json的转换
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</verison>
</dependency>
@Controller //1 声明此类事一个控制器
@RequestMapping("/anno") //2 映射此类的访问路径是/anno
public class DemoAnnoController{
@RequestMapping(produces="text/plain;charset=URF-8") //3 未标注路径,故使用类级别的路径。procuces可定制返回的response的媒体类型和字符集。
public @ResponseBody String index(HttpServletRequest request){ //4 接受HttpServletRequest 作为参数
return "url:"+request.getRequestURL+" can access";
}
@RequestMapping(value="/pathvar/{str}",produces="text/plain;charset=URF-8") //5 接受路径参数,并在方法参数前结合@PathVariable 使用。
public @ResponseBody String demoPathVar(@PathVariable String str,HttpServletRequest request){
return "url:"+request.getRequestURL+" can access,str:"+str;
}
@RequestMapping(value="/requestParam",produces="text/plain;charset=URF-8") //6 常规的request参数获取
public @ResponseBody String passRequestParam(Long id,HttpServletRequest request){
return "url:"+request.getRequestURL+" can access,id:"+id;
}
@RequestMapping(value="/obj",produces="application/json;charset=URF-8") //7 解释参数到对象
@ResponseBody //8 也可以用在方法上
public String passObj(DemoObj obj,HttpServletRequest request){
return "url:"+request.getRequestURL+" can access,obj id:"+obj.getId()+"obj name:"+obj.getName();
}
@RequestMapping(value={"/name1","name2"},produces="text/plain;charset=URF-8") //9映射不同的路径到相同的方法
public @ResponseBody String remove(HttpServletRequest request){
return "url:"+request.getRequestURL+" can access";
}
}
四.spring mvc基本配置
Spring MVC的定制配置需要我们实现接口WebMvcConfigurer,并在此类使用@EnableWebMvc注解,来开启对spring mvc的配置支持,我们实现相应的方法,来完成我们的常用配置。
1.静态资源映射
在src/main/resources下建立assets/js目录
配置:
@Configuration
@EnableWebMvc //开启一些默认配置,如ViewResolver或者MeaaageConverter等
@ComponentScan("com.shm.springdemo")
public class MyMvcCofig implements WebMvcConfigurer{ //实现WebMvcConfigurer接口,重写其方法可以springmvc进行配置
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
@Verride
public void addResourceHandlers(ResourceHanlerRegistry registry){
registry.addResourceHandler("/assets/**").addResourceLocations("calsspath:/assets/"); //addResourceLocations指的是文件存放的目录,addResourceHandler指的是对外暴露的访问路径
}
}
2.拦截器配置
拦截器(Interceptor)实现对每一个请求处理前后进行的相关业务处理,类似于servlet的Filter
可让普通的Bean实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类来实现自定义拦截器
通过重写WebMvcConfigurer的addInterceptors方法来注册自定义的拦截器。
拦截器:
public class DemoInterceptor extends HandlerInterceptorAdapter{ //继承HandlerInterceptorAdapter来实现自定义拦截器
@Verride
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{ //重写preHandle方法,在请求发生前
long startTime = System.currentTimeMillis();
request.setAttribute("startTime",startTime)
}
@Verride
public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModeAndView modelAndView) throws Exception{ //重写postHandle方法,在请求完成后执行
long startTime = (Long)request.getAttribute("startTime");
request.removeAttribute("startTime");
long endTime = System.currentTimeMills();
System.out.println("本次请求处理时间为:"+new Long(endTime-startTime)+"ms");
request.setAttribute("handlingTime",endTime-startTime)
}
}
配置:
@Bean //配置拦截器的bean
public DemoInterceptor demoInterceptor(){
return new DemoInterceptor ();
}
@Overide //重写addInterceptor方法,注册拦截器
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(demoInterceptor());
}
3.@ControllerAdvice 全局控制
通过@ControlerAdvice,我们可以将对于控制器的全局配置放在同一个位置,注解了@Controller的类的方法可以使用@ExceptionHandler,@InitBinder,@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效。
- @ExceptionHandler 用于全局处理控制器里的异常。
- @InitBinder 用来设置WebDataBinder,自动绑定前台请求参数到model中。
- @ModelAttribute 本来的作用是绑定键值对到model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对。
定制ControllerAdvice
@ControllerAdvice //1 声明一个控制器建言,组合了@Component
public class ExceptionHandleAdvice{
@ExceptionHandler(value=Exception.class) //2在此定义全局处理,value属性可过滤拦截的条件
pbulic ModelAndView exception(Exception exception,WebRequest request){
ModelAndView modelAndView = ModelAndView("error"); //error页面
modelAndView.addObject("""errorMessage",exception.getMessage());
return modelAndView;
}
@ModelAttribute //3 注解将键值对添加到全局,所有注解了@RequestMapping的方法可获的此键值对
public void addAttributes(Model model){
model.addAttribute("msg","额外信息");
}
@InitBinder //4 注解定制WebDataBinder
public void initBinder(WebDataBinder webDataBinder){
webDataBinder.setDisalloweFields("id"); //5 忽略request参数中的id
}
}
控制器
@Controller
public class AdviceController{
@RequestMapping("/advice")
public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){
throw new IllegalArgumentException("非常抱歉,参数有误:"+"来自@ModelAttribute"+msg);
}
}
异常页面
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head></head>
<body>
<pre>
${errorMessage}
</pre>
</body>
</html>
四.其他配置
1.快捷的ViewController
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("index").setViewName("/index");
}
2.路径匹配参数配置
路径参数如果带“.”的话,“.”后面的4值将被沪铝,入股哦让其不被忽略,配置
@Verride
pbulic void configurePathMatch(PathMatchCOnfigurer configurer){
configurer.setUseSuffixPatternMatch(false);
}
3.更多配置![](https://img-blog.csdnimg.cn/20190829092816832.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2phdmFfc2ht,size_16,color_FFFFFF,t_70)
五.spring mvc的高级配置
1.文件上传配置
spring mvc通过配置要给MultipartResolver来上传文件。
在spring的控制器中,通过MultipartFile file来接收文件,通过MultipartFile[] files接收多个文件上传。
依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</verison>
</dependency>
<!--非必须,可简化I/O操作-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</verison>
</dependency>
页面:
<div>
<form action="upload" enctype="multipart/form-data" method="post">
<input type="file" name="file" /> <br/>
<input type="submit" value="上传" />
</form>
</div>
配置:
@Bean
public MultipartResolver multipartResolver(){
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1024*1024); //限制上传文件大小1M,单位B
multipartResolver.setDefaultEncoding("utf-8"); //中文乱码处理
return multipartResolver;
}
控制器
@Controller
public class uploadController{
@RequestMapping(value="/upload",method=RequestMethod.POST)
public @ResponseBody String upload(MultipartFile){
FileUtils.writeByteArrayToFile(new File("D:/upload/"+file.getOriginalFilename()),file.getBytes());
return "ok";
}
}
2.自定义HttpMessageConverter
HttpMessageConverter是用来处理request和response里的数据的。spring为我们内置了大量的HttpMessageConverter,列如,MappingJackson2HttpMessageConverter,StringHttpMessageConverter。
3.服务器端推送技术
当客户端向服务端发送请求,服务端会抓住这个请求不放,等有数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求,周而复始。
六.spring mvc的测试
1.测试依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.8.RELEASE</verison>
<scope>test</scope> <!--test说明这些包的存活是在test周期,发布时将不包含这些jar包-->
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</verison>
<scope>test</scope>
</dependency>
2.演示服务
@Service
public class DemoService{
public String saySomething(){
return "hello";
}
}
/*********************************************************************/
@Controller
public class NormalController{
@Autowired
DemoService demoService;
@RequestMapping("/normal")
public String testPage(Model model){
model.addAttribut("msg",demoService.saySomething());
return "page";
}
}
/**********************************************************************/
@RestController
public class MyTestController{
@Autowired
DemoService demoService;
@RequestMapping(value="/testRest",produces="text/plain;charset=utf-8")
public @ResponseBody String testRest(){
return demoService.saySomething();
}
}
3.在src/main/resources/vies下新建页面page.jsp
4.测试用例
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContestConfiguration(classes={MyMvcConfig.class}) //springmvc配置java文件
@WebAppConfiguration("src/main/resources") //声明是一个WebAppConfiguration,他的属性指定的是web资源的位置
public class TestControllerTests{
private MockMvc mockMvc; //模拟mvc对象
@Autowired
private DmeoService demoService;
@Autowired
WebApplicationContext wac;
@@Autowired
MockHttpSession session;
@Autowired
MockHttpServletRequest request;
@Before //测试前,做的初始化工作
public void setup(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();//初始化mvc对象
}
@Test
public void testNormalController(){
mockMvc.perform(get("/normal")) //模拟向“/normal”进行get请求
.addExpect(status().isOk()) //预期控制返回状态为200
.addExpect(view().name("page")) //预期view的名称
.addExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp")) //预取也买你转向的真正路径
.addExpect(model().attribute("msg",demoService.saySomething())); //预取model里的值是方法返回的值“hello”
}
@Test
public void testRestConteroller{
mockMvc.perform(get("/normal")) //模拟向“/normal”进行get请求
.addExpect(status().isOk()) //预期控制返回状态为200
.addExpect(content().contentType("text/plain;charset=UTF-8")) //预期返回值的媒体类型
.addExpect(content().string(demoService.saySomething())); //预取返回值内容为方法返回的值“hello”
}
}