本人博客: https://blog.onfree.cn (●ˇ?ˇ●)
11分鐘詳析Spring MVC
我手中的魔法,是守護摯愛的力量,是堅定這個信念所必須的力量,我一定會拯救你的,無論在何時、何地。
Spring MVC是當前最優秀的MVC框架,支持注解配置,易用性有了大幅度的提高,使用簡單,學習成本低,靈活性高,易拓展。
1. 工作流程
<!-- 配置處理器 Handle,映射 “/controller1”請求 -->
<bean name="/controller1" class="com.controller.Controller1" />
<!-- 已下默認不用寫 在Spring 4.0 會自動使用默認的來處理 -->
<!-- 處理器映射器 將處理器的name作為URL請求進行查找-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 處理器適配器 配置對處理器中handleRequest()方法的調用 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
2. DispatcherServlet
<!-- 配置Springmvc -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
// 1為啟動程序立即加載該
// / 為攔截所有URL交給DispatcherServlet處理
3.Controller 層
3.1實現接口 org.springframework.web.servlet.mvc.Controller
public class Controller1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
ModelAndView modelAndView =new ModelAndView();
modelAndView.addObject("msg", "hello world");
modelAndView.setViewName("WEB-INF/jsp/index1.jsp");
return modelAndView;
}
<!-- 配置處理器 Handle,映射 “/controller1”請求 -->
<bean name="/controller1" class="com.controller.Controller1" />
3.2 使用注解 @Controller @RequestMapping
@Controller
@RequestMapping(value="/hello") //標注在類上時所有請求前必須有定義的名稱下"/hello
public class Controller1{
//跳轉登錄界面
@RequestMapping(value="/login",method=RequestMethod.GET) //使用 /login 即可調用
public String toLogin(){
return "login";
}
//獲取登錄視圖傳遞來的信息進行配對 成功后視圖跳轉
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(User user,Model model,HttpSession session) {
System.out.println("當前用戶: "+user);
String name=user.getName();
String password=user.getPassword();
if(name.equals("jz")&&password.equals("123")) {
session.setAttribute("session_user", user);
//redirect 重定向的意思時跳轉到相應的方法而不是跳到jsp視圖
return "redirect:main";
}else if(name.equals("aa")&&password.equals("123")) {
session.setAttribute("session_user", user);
return "redirect:upload";
}
model.addAttribute("msg", "用戶名或密碼錯誤");
return "login";
}
//組合注解: GetMapping PostMapping PutMapping DeleteMapping PatchMapping
4. 視圖解析器
<!-- 定義視圖 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
5.數據綁定
5.1綁定默認數據類型
- HttpServletRequest
- HttpServletResponse
- HttpSession
- Model/ModelMap
//model是接口 ModelMap是接口實現 作用是將數據填充到Request
5.2綁定簡單數據類型
String 、Interget、double..
//視圖傳遞來的name屬性要和控制器方法的形參相同
//不同要使用注解 @RequestParam(value="name") String name 定義
5.3綁定POJO類型
public String login(User user) {}
賬號:
密碼:
//解決請求參數中中文亂碼問題
<!-- 字符過濾為UTF-8 -->
<filter>
<filter-name>Encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5.4綁定包裝POJO類型
public class Order {
private Integer id;
private User user;
}
//獲取order.jsp界面傳遞來的參數
@RequestMapping("/order")
public String order(Order order) {
System.out.println(order);
return "index2";
}
<form action="${pageContext.request.contextPath}/hello/order " method="post">
訂單號: <input type="text" name="order.id"><br/>
用戶名: <input type="text" name="user.name"><br/>
<input type="submit" value="繼續">
<input type="reset" value="重置">
</form>
//兩種規范:
1.查詢條件參數是包裝類的直接基本屬性 則參數名直接用對應的屬性名
2.查詢條件參數是包裝類的POJO的子屬性 則參數名必須為 【對象.屬性】對象為包裝類里POJO的對象名
5.5自定義綁定數據
5.5.1 Converter轉換器 //源格式可以為任何格式
public interface Converter<S, T>{
T convert(S source)
} //S 為源格式 T為目標格式
public class DateConverter implements Converter<String, Date> {
public Date convert(String source) {
String datePattern="yyyy-MM-dd HH:MM:SS";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(datePattern);
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException("無效格式,請使用這種格式"+datePattern);
}
}
}
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"
<!-- 顯示裝配的自定義的轉換器 -->
<mvc:annotation-driven conversion-service="conversionservice1"></mvc:annotation-driven>
<!-- 自定義類型轉換器配置 converters-->
<bean id="conversionservice1" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.converter.DateConverter"></bean>
</set>
</property>
</bean>
//通過自定義綁定數據Converter或DateFormat 獲取Date
@RequestMapping("customDate")
public String customDate(Date date) {
System.out.println(date);
return "index2";
}
5.5.2 Formatter格式化 //源格式只能為String
public interface Formatter extends Printer,Parser{}
public class DateFormatter implements Formatter<Date> {
private String datePattern="yyyy-MM-dd HH:MM:SS";
private SimpleDateFormat simpleDateFormat;
//返回目標對象的字符串
public String print(Date date, Locale locale) {
return new SimpleDateFormat().format(date);
}
//利用指定的local將一個String類型解析成目類型
public Date parse(String source, Locale locale) throws ParseException {
simpleDateFormat=new SimpleDateFormat(datePattern);
return simpleDateFormat.parse(source);
}
}
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"
<!-- 顯示裝配的自定義的轉換器 -->
<mvc:annotation-driven conversion-service="conversionservice1"></mvc:annotation-driven>
<!-- 自定義類型轉換器配置formatters-->
<bean id="conversionservice2" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.converter.DateFormatter"></bean>
</set>
</property>
</bean>
5.6綁定數組
//獲取綁定要執行刪除的數組
@RequestMapping("/delID")
public String delID(Integer[] ids) {
if(ids!=null) {
for (Integer id : ids) {
System.out.println("你刪除了第 "+id+"數據");
}
}else{
System.out.println("ids為null");
}
return "editUser";
}
<form action="${pageContext.request.contextPath}/hello/delID" method="post">
<table border=1 width="20%">
<thead>刪除用戶</thead>
<tbody>
<tr>
<td>選擇</td>
<td>用戶名</td>
</tr>
<tr>
<td> <input type="checkbox" name="ids" value="1"></input> </td>
<td>aa</td>
</tr>
<tr>
<td> <input type="checkbox" name="ids" value="2"></input> </td>
<td>bb</td>
</tr>
</tbody>
</table>
<br/><input type="submit" value="確定">
</form>
5.6綁定集合
//后臺不允許使用集合作為形參 只能用包裝類來包裝一個集合
public class UserList {
private List userList;
}
//獲取綁定要執行修改數據的集合
@RequestMapping("/editUser")
public String editUser(UserList userList1) {
List<User> users=userList1.getUserList();
for (User user : users) {
if(user.getId()!=null) {
System.out.println("修改了第 "+user.getId()+"數據");
System.out.println("用戶名為:"+user.getName());
}
}
return "index2";
}
<form action="${pageContext.request.contextPath}/hello/editUser" method="post">
<table border=1>
<thead>修改用戶</thead>
<tr>
<td>選擇</td>
<td>用戶名</td>
</tr>
<tr>
<td> <input type="checkbox" name="userList[0].id" value="1"></input> </td>
<td> <input type="text" name="userList[0].name" value="jz"></input></td>
</tr>
<tr>
<td> <input type="checkbox" name="userList[1].id" value="2"></input> </td>
<td> <input type="text" name="userList[1].name" value="aa"></input></td>
</tr>
</table>
<br/><input type="submit" value="確定">
</form>
6.JSON 和RESTful
6.1 JSON 格式
對象結構:
{
key1:value1,
key2:value2
}
數組結構:
[
value1,
value2
]
6.2JSON數據交換
包類:
jackson-annotations-2.9.9.jar
jackson-core-2.9.9.jar
databind-2.9.9.jar
注解:
@RequestBody //將請求體的數據綁定在形參上 作用在形參
@ResponseBody //返回JSON格式 作用在方法上
<!-- 配置注解驅動
自動注冊 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
和org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
-->
<mvc:annotation-driven />
<!--第1種. 配置靜態資源的訪問映射 使其不被前端控制器攔截 -->
<mvc:resources location="/js/" mapping="/js/**" />
<!-- 第2種.使用使用默認的服務器請求處理器自動判斷篩選 -->
<!-- <mvc:default-servlet-handler/> -->
?
//測試JSON
@RequestMapping(value="/login2",method=RequestMethod.POST)
@ResponseBody //返回JSON格式
public User login2(@RequestBody User user) {
System.out.println("login2 測試");
System.out.println(user);
return user;
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
function check() {
var name=$("#name").val();
var password=$("#password").val();
if(name==null||password==null||name==""||password==""){
alert("賬號或密碼不能為空!!");
return false;
}
return true;
}
//使用JQuery的AJAX傳遞JSON格式
function login2(){
//alert("1");
var name=$("#name").val();
var password=$("#password").val();
var id=1;
if(check()){
$.ajax({
//url前往路徑
url:"${pageContext.request.contextPath}/hello/login2",
type:"post",
//data請求發送的數據
data:JSON.stringify({id:id,name:name,password:password}),
//json格式時 必須為application/json
contentType:"application/json;charset=UTF-8",
//定義回調屬性為json
dataType:"json",
success:function(data){
alert(data.name+data.password);
}
});
}
}
</script>
</head>
<body>
<!-- ${pageContext.request.contextPath}<br/>-->
<div style="color: red">${msg}</div>
<form action="${pageContext.request.contextPath}/hello/login " method="post" onsubmit="return check()">
賬號: <input type="text" name="name" id="name"><br/>
密碼: <input type="password" name="password" id="password"><br/>
<input type="submit" value="登錄">
<input type="reset" value="重置">
<input type="button" value="登錄2" onclick="login2()" />
</form>
</body>
</html>
6.3RESTful風格
如:http://hello/items/1
//參數寫在連接之后
//用RESTful風格測試JSON
@RequestMapping(value="/user/{id}",method=RequestMethod.GET)
@ResponseBody
public User selectUser(@PathVariable("id") Integer id) {
User user1=new User();
//模擬在數據庫找到此ID數據
if(id.equals(1)) {
user1.setName("jzz");
user1.setId(id);
System.out.println("ID: "+user1.getId()+" 用戶名: "+user1.getName());
}else {
System.out.println("沒有找到此ID的用戶");
}
return user1;
}
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
function select1(){
//alert("1");
var id=$("#number").val();
$.ajax({
url:"${pageContext.request.contextPath}/hello/user/"+id,
type:"GET",
dataType:"json",
success:function(data){
if(data.id!=null&&data.name!=null){
alert("id: "+data.id+"用戶名: "+data.name);
}else{
alert("沒有找到此ID的用戶");
}
}
})
}
</script>
7.攔截器interceptor
-
// 攔截器需實現接口 HandlerInterceptor 或它的實現類 HandlerInterceptorAdapter
-
//或者 實現接口WebRequestInterceptor或實現類WebRequestHandlerInterceptorAdapter
public class LoginInterceptor extends HandlerInterceptorAdapter { //此方法在控制器方法前執行 返回true時繼續執行 fasle時中斷控制器方法及攔截器方法 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception { //判斷是否登錄 /login 連接 String url=request.getRequestURI(); System.out.println("當前連接:"+url); if(url.indexOf("/login")>=0) { return true; } if(url.indexOf("/login2")>=0) { return true; } //判斷session是否有數據User用戶 HttpSession session=request.getSession(); User user =(User) session.getAttribute("session_user"); if(user!=null) { return true; } //不符合條件重新轉發到登錄界面 request.setAttribute("msg", "你還沒有登錄,請登錄!"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } //此方法在控制器調用之后 視圖解析前執行 以便對模型和視圖進行修改 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } //此方法在視圖解析之后進行 可進行資源清理、日子記錄等等 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3) throws Exception { // TODO Auto-generated method stub } } <!-- 配置攔截器 --> <mvc:interceptors> <!-- 全局攔截器 --> <!--<bean class="" /> --> <mvc:interceptor> <mvc:mapping path="/**"/> <!-- 配置攔截器作用的路徑 --> <mvc:exclude-mapping path=""/> <!-- 配置攔截器不作用的路徑 --> <bean class="com.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
/* 假設配置多個攔截器interceptor1 interceptor2
則其攔截器的方法的調用的順序是:
interceptor1(preHandle) - interceptor2(preHandle) - handleAdapter(Handle) -interceptor2(postHandle) - interceptor1(postHandle) - DispatcherServlet(reader) - interceptor2(afterCompletion) - interceptor1(afterCompletion) */
7.文件上傳
類包:
-
commons-fileupload-1.4.jar
-
commons-io-2.6.jar
<!-- 因為CommonsMultipartResolver內部是引用MultipartResolver字符串來完成文件解析 所以指定的ID必須為multipartResolver -->
<!-- enctype屬性必須為"multipart/form-data" multiple="multiple" 表示可以多文件上傳 -->
<form id="form1" action="${pageContext.request.contextPath}/hello/upload" enctype="multipart/form-data" method="post" onsubmit="return check()" >
上傳人: <input type="text" id="name1" name="name1" /> <br/>
<input type="file" multiple="multiple" name="ufile" id="ufile" />
<input type="submit" value="上傳" />
</form>
//上傳文件處理
@RequestMapping(value="/upload" ,method=RequestMethod.POST)
public String upload(@RequestParam(value="name1") String name1,@RequestParam(value="ufile") List<MultipartFile> ufile,HttpServletRequest request,HttpSession session) {
//System.out.println("1成功");
User user=(User) session.getAttribute("session_user");
//System.out.println(user);
//判斷上傳文件是否存在
if(!ufile.isEmpty()&&ufile.size()>0) {
for (MultipartFile multipartFile : ufile) {
//獲取上傳文件的源名字
String sfilename=multipartFile.getOriginalFilename();
//上傳文件的最后的保存路徑
String dirPath=request.getServletContext().getRealPath("/upload")+"/"+user.getName();
System.out.println("dirPath :"+dirPath);
//假設目錄不存在 就創建目錄
File pathfile=new File(dirPath);
if(!pathfile.exists()) {
pathfile.mkdirs();
}
//上傳文件的最后的文件名 上傳人_UUID_源文件名
String lfilename=name1+"_"+UUID.randomUUID()+" "+sfilename;
try {
multipartFile.transferTo(new File(dirPath,lfilename));
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("錯誤"+e);
}
}
return "index2";
}else {
System.out.println("錯誤2");
return null;
}
}
// MultipartFile 接口常用方法
- byte[] getBytes() 返回文件的內容作為一個字節數組
- String getContentType() 返回文件的內容類型
- InputStream getInputStream() 返回InputStream讀取文件的內容
- String getName() 返回參數的名稱多部分的形式
- String getOriginalFilename() 返回原來的文件名
- long getSize() 返回文件的大小,以字節為單位
- boolean isEmpty() 返回是否上傳文件是空
- void transferTo(File dest) 接收到的文件轉移到給定的目標文件。
8.文件下載
// 使用 ResponseEntity<byte[]> 對象設置 HttpHeaders 和 HttpStatus 配置信息下載
<%@ page import="java.net.URLEncoder"%>
...
<a href="${pageContext.request.contextPath}/hello/download?
filename=<%=URLEncoder.encode("看.jpg","UTF-8")%>">下載</a>
//下載文件處理
@RequestMapping(value="/download")
public ResponseEntity<byte[]> download(@RequestParam("filename") String filename,HttpSession session,HttpServletRequest request) throws IOException{
System.out.println("download 開始!");
//System.out.println(filename);
User user =(User) session.getAttribute("session_user");
//下載文件路徑
//String pathname=request.getServletContext().getRealPath("/upload")+"/"+user.getName();
//File pathfile=new File(pathname+File.separator+filename);
File pathfile=new File("C:\\Users\\jz\\Pictures\\Camera Roll\\"+filename);
System.out.println(pathfile);
if(!pathfile.exists()) {
System.out.println("文件為空");
}
//設置頭信息
HttpHeaders headers = new HttpHeaders();
//中文文件名處理
String filename1=new String(filename.getBytes("utf-8"),"ISO-8859-1");
//設置請求頭內容,告訴瀏覽器以下載方式打開窗口
headers.setContentDispositionFormData("attachment", filename1);
//定義以流的形式返回下載數據
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(pathfile),
headers, HttpStatus.OK);
}
本博客原文:https://blog.onfree.cn/posts/65754b0a.html
轉載請申明原作者Athink,謝謝!
浙公網安備 33010602011771號