SpringMVC
學習目標:
1.SpringMVC簡介
1)web訪問流程
- 1.web服務器通過瀏覽器訪問頁面
- 2.前端頁面使用異步提交的方式發送請求到后端服務器
- 3.后端服務器采用:表現層—業務層—數據層的架構進行開發
- 4.頁面請求由表現層進行接收,獲取用戶的請求后將參數傳遞到業務層,再由業務層訪問數據層獲取數據庫中的數據,返回給表現層
- 5.得到用戶需要訪問的數據之后,表現層通過JSON的格式發送數據到前端頁面
- 6.前端頁面得到數據之后,解析數據并組織對應的頁面渲染到瀏覽器展示給用戶
2)SpringMVC概述
- 是一種基于Java實現的MVC模型的輕量級web框架(表現層框架技術,主要用于表現層開發)
- 優點:
- 使用簡單,開發便捷
- 靈活性強
2.SpringMVC入門案例
1)案例實施
- 1.導入
SpringMVC
和Servlet
的坐標
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.10.RELEASE</version></dependency></dependencies>
配置tomcat
插件:
<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><path>/</path></configuration></plugin></plugins></build>
- 2.創建SpringMVC控制器類:
@Controller
public class controller {@RequestMapping("/print")@ResponseBodypublic String print() {System.out.println("user print ...");return "{user: name}";}
}
- 3.初始化SpringMVC環境,設定SpringMVC加載對應的bean:
SpringMVCConfig
:
@Configuration
@ComponentScan("com.springmvclearn.controller")
public class SpringMvcConfig {
}
- 4.初始化Servelet容器,加載SpringMVC環境,并設置SpringMVC技術處理的請求:
SpringMvcContainersInitConfig
:
public class ServeletContainersInitConfig extends AbstractDispatcherServletInitializer {//加載SpringMVC容器配置@Overrideprotected WebApplicationContext createServletApplicationContext() {AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();context.register(SpringMvcConfig.class);return context;}//設置哪些請求歸屬springMVC處理@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}@Overrideprotected WebApplicationContext createRootApplicationContext() {return null;}
}
涉及的注解介紹
2)入門案例工作流程
3)不同層次結構Bean的加載控制
- 在開發中,Spring一般加載業務層和數據層的bean,SpringMVC一般加載表現層的bean,而入門案例中配置時,Spring加載時會直接掃描整個包下的bean,包括表現層的bean
- 為了避免Spring錯誤的加載到SpringMVC的bean:我們需要在Spring加載bean時,排除掉SpringMVC的bean
- 1.方式一:Spring加載的bean設定掃描范圍為com.itheima,排除掉controller包內的bean(SpringBoot中會使用這種方式進行bean的細粒度控制)
@Configuration
@ComponentScan(value = "com.springmvclearn", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
)
public class SpringConfig {
}
- 2.方式二:Spring加載的bean設定掃描范圍為精確的包:比如直接對應
service包
,dao包
等。
@Configuration
@ComponentScan({"com.springmvclearn.service", "com.springmvclearn.dao"})
public class SpringConfig {
}
4)簡化開發的書寫格式
5)PostMan
-
網頁調試與發送網頁http請求的Chrome插件
-
常用于進行接口測試
-
在workspaces中可以云備份歷史請求操作
-
創建請求:
-
保持請求,并放入到一個集合中,方便分類復用:
3.請求與響應
1.請求映射路徑
-
當相同層級下的controller有相同的路徑時(
/save
),在訪問時就會有路徑沖突問題 -
一般解決就是在不同controller的路徑前加入不同模塊的請求路徑前綴:
/user/save
、/book/save
-
通過
RequestMapping
在控制類上統一設置當前控制類下的方法請求路徑前綴,然后在每個控制器方法上再加每個控制方法對應的請求訪問路徑
2.請求參數
1)普通參數請求
//請求參數
@Controller
public class UserController {//普通參數:請求參數與形參名稱對應即可完成參數傳遞@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name ,int age){System.out.println("普通參數傳遞 name ==> "+name);System.out.println("普通參數傳遞 age ==> "+age);return "{'module':'common param'}";}//普通參數:請求參數名與形參名不同時,使用@RequestParam注解關聯請求參數名稱與形參名稱之間的關系@RequestMapping("/commonParamDifferentName")@ResponseBodypublic String commonParamDifferentName(@RequestParam("name") String userName , int age){System.out.println("普通參數傳遞 userName ==> "+userName);System.out.println("普通參數傳遞 age ==> "+age);return "{'module':'common param different name'}";}
}
- 1.GET 請求:
- 2.POST請求:
- 3.POST中文參數亂碼問題
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {protected Class<?>[] getRootConfigClasses() {return new Class[0];}protected Class<?>[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}protected String[] getServletMappings() {return new String[]{"/"};}//亂碼處理@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");return new Filter[]{filter};}
}
2)POJO參數傳遞
- 當大量參數需要傳遞時,一般將參數封裝成一個實體類,在參數傳遞時,直接將參數賦值給實體類對象,然后從實體類對象中拿取對應參數值
User
:
package com.itheima.domain;public class User {private String name;private int age;@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
在controller中:
//請求參數
@Controller
public class UserController {//POJO參數:請求參數與形參對象中的屬性對應即可完成參數傳遞@RequestMapping("/pojoParam")@ResponseBodypublic String pojoParam(User user){System.out.println("pojo參數傳遞 user ==> "+user);System.out.println("userName ==> "+user.getName());System.out.println("userAge ==> "+user.getAge());return "{'module':'pojo param'}";}
}
3)嵌套POJO參數傳遞
- 當POJO中還有引用的POJO時,如何傳遞參數
- 也就是在POJO中將該POJO注入,并給出setter和getter方法:
user
:
package com.itheima.domain;public class User {private String name;private int age;private Address address;@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", address=" + address +'}';}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
然后在controller中同樣接收POJO對象即可:
//請求參數
@Controller
public class UserController {//嵌套POJO參數:嵌套屬性按照層次結構設定名稱即可完成參數傳遞@RequestMapping("/pojoContainPojoParam")@ResponseBodypublic String pojoContainPojoParam(User user){System.out.println("pojo嵌套pojo參數傳遞 user ==> "+user);System.out.println("userName ==> "+user.getName());System.out.println("userAge ==> "+user.getAge());System.out.println("userAddress ==> "+user.getAddress());return "{'module':'pojo contain pojo param'}";}
}
4)數組參數傳遞
- 需要傳入一個數組類型的參數時:
//請求參數
@Controller
public class UserController {//數組參數:同名請求參數可以直接映射到對應名稱的形參數組對象中@RequestMapping("/arrayParam")@ResponseBodypublic String arrayParam(String[] hobby){System.out.println("數組參數傳遞 hobby ==> "+ Arrays.toString(hobby));return "{'module':'array param'}";}
}
5)集合類型參數
- 需要集合類型參數時:我們需要設置該集合參數為
RequestParam
,否則無法識別需要傳遞的是集合
//請求參數
@Controller
public class UserController {//集合參數:同名請求參數可以使用@RequestParam注解映射到對應名稱的集合對象中作為數據@RequestMapping("/listParam")@ResponseBodypublic String listParam(@RequestParam List<String> likes){System.out.println("集合參數傳遞 likes ==> "+ likes);return "{'module':'list param'}";}
}
請求則同上,結果也是一樣的。
3.JSON格式參數傳遞
- 首先要加入解析JSON的坐標:
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency>
1)JSON格式的集合參數
- 1.開啟支持集合JSON格式的數據類型自動轉換:
@Configuration
@ComponentScan("com.itheima.controller")
//開啟json數據類型自動轉換
@EnableWebMvc
public class SpringMvcConfig {
}
- 2.加上在參數前面加入
@RequestBody
,表示傳入的參數是在JSON的body中
//請求參數
@Controller
public class UserController {//集合參數:json格式//1.開啟json數據格式的自動轉換,在配置類中開啟@EnableWebMvc//2.使用@RequestBody注解將外部傳遞的json數組數據映射到形參的集合對象中作為數據@RequestMapping("/listParamForJson")@ResponseBodypublic String listParamForJson(@RequestBody List<String> likes){System.out.println("list common(json)參數傳遞 list ==> "+likes);return "{'module':'list common for json param'}";}
}
2)JSON格式的POJO參數傳遞(以及嵌套POJO)
- 接收參數格式:
//請求參數
@Controller
public class UserController {//POJO參數:json格式//1.開啟json數據格式的自動轉換,在配置類中開啟@EnableWebMvc//2.使用@RequestBody注解將外部傳遞的json數據映射到形參的實體類對象中,要求屬性名稱一一對應@RequestMapping("/pojoParamForJson")@ResponseBodypublic String pojoParamForJson(@RequestBody User user){System.out.println("pojo(json)參數傳遞 user ==> "+user);return "{'module':'pojo for json param'}";}
}
-
參數請求格式:
-
結果:
3)JSON格式對象集合參數傳遞
- 接收參數格式:
//請求參數
@Controller
public class UserController {//集合參數:json格式//1.開啟json數據格式的自動轉換,在配置類中開啟@EnableWebMvc//2.使用@RequestBody注解將外部傳遞的json數組數據映射到形參的保存實體類對象的集合對象中,要求屬性名稱一一對應@RequestMapping("/listPojoParamForJson")@ResponseBodypublic String listPojoParamForJson(@RequestBody List<User> list){System.out.println("list pojo(json)參數傳遞 list ==> "+list);return "{'module':'list pojo for json param'}";}
}
- 參數請求格式:
- 結果:
4.對比
5.日期型參數傳遞
- 接收參數格式:
//請求參數
@Controller
public class UserController {//日期參數//使用@DateTimeFormat注解設置日期類型數據格式,默認格式yyyy/MM/dd@RequestMapping("/dataParam")@ResponseBodypublic String dataParam(Date date,@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){System.out.println("參數傳遞 date ==> "+date);System.out.println("參數傳遞 date1(yyyy-MM-dd) ==> "+date1);System.out.println("參數傳遞 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);return "{'module':'data param'}";}
}
- 參數請求格式:
- 結果:
6.響應
- 1.響應頁面
- 2.響應文本
- 3.響應POJO
- 4.響應POJO集合
使用@ResponseBody
- 將其注解在SpringMVC控制器方法定義的上方
- 作用:設置當前控制器返回值為響應體(POJO轉JSON,POJO集合轉JSON)
//請求參數
@Controller
public class UserController {//返回頁面@RequestMapping("/pageParam")public String pageParam() {System.out.println("返回頁面");return "index.jsp";}//返回文本數據@RequestMapping("/textParam")@ResponseBodypublic String totext() {System.out.println("返回文本數據");return "context text";}//返回POJO@RequestMapping("/userParam")@ResponseBodypublic User userParam(User user) {System.out.println("返回POJO");User new_user = new User();new_user.setName(user.getName());new_user.setAge(user.getAge());new_user.setAddress(user.getAddress());return new_user;}//返回POJO集合@RequestMapping("/usersParam")@ResponseBodypublic List<User> usersParam(List<User> user) {System.out.println("返回POJO集合");List<User> new_users = new ArrayList<>();new_users.addAll(user);return new_users;}
}
4.REST風格
-
是一種定義資源描述的形式
-
REST風格訪問資源時,使用
行為動作
區分對資源進行了何種操作:路徑+請求方式
1)RESTful
-
在請求時通過不同的請求方式
-
@PathVariable
接收路徑參數或者路徑變量(路徑中的參數獲取:http://localhost/users/3
-> 3的獲取)
package com.itheima.controller;import com.itheima.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
public class UserController {//設置當前請求方法為POST,表示REST風格中的添加操作@RequestMapping(value = "/users",method = RequestMethod.POST)@ResponseBodypublic String save(){System.out.println("user save...");return "{'module':'user save'}";}//設置當前請求方法為DELETE,表示REST風格中的刪除操作//@PathVariable注解用于設置路徑變量(路徑參數),要求路徑上設置對應的占位符,并且占位符名稱與方法形參名稱相同@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)@ResponseBodypublic String delete(@PathVariable Integer id){System.out.println("user delete..." + id);return "{'module':'user delete'}";}//設置當前請求方法為PUT,表示REST風格中的修改操作@RequestMapping(value = "/users",method = RequestMethod.PUT)@ResponseBodypublic String update(@RequestBody User user){System.out.println("user update..."+user);return "{'module':'user update'}";}//設置當前請求方法為GET,表示REST風格中的查詢操作//@PathVariable注解用于設置路徑變量(路徑參數),要求路徑上設置對應的占位符,并且占位符名稱與方法形參名稱相同@RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET)@ResponseBodypublic String getById(@PathVariable Integer id){System.out.println("user getById..."+id);return "{'module':'user getById'}";}//設置當前請求方法為GET,表示REST風格中的查詢操作@RequestMapping(value = "/users",method = RequestMethod.GET)@ResponseBodypublic String getAll(){System.out.println("user getAll...");return "{'module':'user getAll'}";}
}
2)@RequestBody
、@RequestParam
、@PathVariable
3)RESTful快速開發
-
在1)RESTful的編寫中,同一個路徑前綴等在每個控制器方法上都有,為了簡化書寫,以下注解和簡化寫法是標準的RESTful開發:
-
標準RESTful開發:
package com.itheima.controller;import com.itheima.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;//@Controller
//@ResponseBody配置在類上可以簡化配置,表示設置當前每個方法的返回值都作為響應體
//@ResponseBody
@RestController //使用@RestController注解替換@Controller與@ResponseBody注解,簡化書寫
@RequestMapping("/books")
public class BookController {// @RequestMapping( method = RequestMethod.POST)@PostMapping //使用@PostMapping簡化Post請求方法對應的映射配置public String save(@RequestBody Book book){System.out.println("book save..." + book);return "{'module':'book save'}";}// @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)@DeleteMapping("/{id}") //使用@DeleteMapping簡化DELETE請求方法對應的映射配置public String delete(@PathVariable Integer id){System.out.println("book delete..." + id);return "{'module':'book delete'}";}// @RequestMapping(method = RequestMethod.PUT)@PutMapping //使用@PutMapping簡化Put請求方法對應的映射配置public String update(@RequestBody Book book){System.out.println("book update..."+book);return "{'module':'book update'}";}// @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)@GetMapping("/{id}") //使用@GetMapping簡化GET請求方法對應的映射配置public String getById(@PathVariable Integer id){System.out.println("book getById..."+id);return "{'module':'book getById'}";}// @RequestMapping(method = RequestMethod.GET)@GetMapping //使用@GetMapping簡化GET請求方法對應的映射配置public String getAll(){System.out.println("book getAll...");return "{'module':'book getAll'}";}
}
4)案例:基于RESRful頁面數據交互
-
1.制作控制器,通過PostMan測試接口:
-
2.放行靜態資源訪問:
由于SpringMVC設置了訪問路徑攔截,當訪問前端靜態頁面時,會被攔截,導致無法訪問到,于是我們需要制作放行器,將對應的靜態數據進行放行:
SpringMVCSupport
:
package com.itheima.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;@Configuration
public class SpringSupport extends WebMvcConfigurationSupport {//當訪問/pages/????的時候,走/pages目錄下的內容@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");registry.addResourceHandler("/css/**").addResourceLocations("/css/");registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");}
}
- 3.異步提交訪問后端控制器獲取數據:
5.攔截器
- 動態攔截方法調用的機制,在SpringMVC中動態攔截控制器的執行
- 作用:
- 在指定的方法調用前后執行預先設定的代碼
- 阻止原始方法的執行(權限控制)
1)攔截器與過濾器區別
- 歸屬不同:Filter屬于Serverlet技術,Interceptor屬于SpringMVC技術
- 攔截內容不同:Filter針對所有訪問進行增強,Interceptor僅針對SpringMVC的訪問進行增強
2)攔截器入門案例
- 1.聲明攔截器的bean,并實現HandlerInterceptor接口:
@Component
public class MyProjectInterceptor implements HandlerInterceptor {@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("MyProjectInterceptor posHandle");}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("MyProjectInterceptor preHandle");return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("MyProjectInterceptor afterCompletion");}
}
- 2.定義配置類,繼承WebMvcConfigurationSupport,實現addInterceptor方法:
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {@Overridepublic void addInterceptors(InterceptorRegistry registry) {}
}
- 3.添加攔截器并設定攔截的訪問路徑,路徑可以通過可變參數設置多個:
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {@Autowiredprivate MyProjectInterceptor projectInterceptor;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/page/**").addResourceLocations("classpath:/pages/");}@Overridepublic void addInterceptors(InterceptorRegistry registry) {//攔截器配置,當遇到哪些路徑時,會將其攔截下來執行對應的postHandle和preHandle方法registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");}
}
- 4.發送請求:
- 5.攔截器攔截對應的路徑下方法的調用,并在方法調用前后執行預先定義的方法:
攔截器執行流程:
3)攔截器參數(了解即可,主要用到的可能是前置處理)
- 前置處理,拿到handler(原始方法對象的封裝)可以對原始方法進行一些處理
- 主要用處為在方法執行前作校驗
4)攔截器鏈(了解即可)
- 當配置多個攔截器是,形參攔截器鏈
- 攔截器鏈的運行順序參照攔截器添加的順序為準
*注:上述內容均來自黑馬程序員SSM框架的課程學習,僅用作學習交流,不作為商業用途,如有侵權,聯系刪除。