SpringMVC筆記 SpringMVC介紹 基本介紹 SpringMVC 是WEB 層框架, 接管了Web 層組件, 支持MVC 的開發模式/開發架構 SpringMVC 通過註解,讓POJO 成為控制器,不需要繼承類或者實現介面 SpringMVC 採用低耦合的組件設計方式,具有更好擴展和靈活 ...
SpringMVC筆記
SpringMVC介紹
基本介紹
- SpringMVC 是WEB 層框架, 接管了Web 層組件, 支持MVC 的開發模式/開發架構
- SpringMVC 通過註解,讓POJO 成為控制器,不需要繼承類或者實現介面
- SpringMVC 採用低耦合的組件設計方式,具有更好擴展和靈活性.
- 支持REST 格式的URL 請求.
Spring MVC和Spring之間的關係
- Spring MVC 只是Spring 處理WEB 層請求的一個模塊/組件, Spring MVC 的基石是Servlet 【Java WEB】
- Spring Boot 是為了簡化開發者的使用, 推出的封神框架(約定優於配置,簡化了Spring的配置流程), SpringBoot 包含很多組件/框架,Spring 就是最核心的內容之一,也包含SpringMVC
- Spring Boot > Spring > Spring MVC
@RequestMapping
基本介紹
@RequestMapping 註解可以指定控制器/處理器的某個方法的請求的url
@RequestMapping(value = "/buy")
public String buy(){
System.out.println("購買商品");
return "success";
}
其它使用方式
-
@RequestMapping 可以修飾方法和類
- @RequestMapping 註解可以修飾方法,還可以修飾類當同時修飾類和方法時,
請求的url 就是組合/類請求值/方法請求值
@RequestMapping(value = "/user" ) @Controller public class UserHandler { @RequestMapping(value = "/buy") public String buy(){ System.out.println("購買商品"); return "success"; } }
此時url = /user/buy
- @RequestMapping 註解可以修飾方法,還可以修飾類當同時修飾類和方法時,
-
@RequestMapping 可以指定請求方式
- @RequestMapping 還可以指定請求的方式(post/get/put/delete..), 請求的方式需
要和指定的一樣,否則報錯
- @RequestMapping 還可以指定請求的方式(post/get/put/delete..), 請求的方式需
@RequestMapping(value = "/find", method = RequestMethod.GET)
public String search(String bookId) {
System.out.println("查詢書籍bookId= " + bookId);
return "success";
}
- SpringMVC 控制器預設支持GET 和POST 兩種方式, 也就是你不指定method , 可以接收
GET 和POST 請求
@RequestMapping(value = "/buy")//預設支持GET 和POST 兩種方式
public String buy(){
System.out.println("購買商品");
return "success";
}
3.@RequestMapping 可指定params 和headers 支持簡單表達式
-
param1: 表示請求必須包含名為param1 的請求參數
-
!=param1: 表示請求不能包含名為param1 的請求參數
-
param1 != value1: 表示請求包含名為param1 的請求參數,但其值不能為value1
-
{"param1=value1", "param2"}: 請求必須包含名為param1 和param2 的兩個請求參數,
且param1 參數的值必須為value1
@RequestMapping(value = "/find", params = "bookId", method = RequestMethod.GET)
public String search(String bookId) {
System.out.println("查詢書籍bookId= " + bookId);
return "success";
}
4.@RequestMapping 支持Ant 風格資源地址
- ?:匹配文件名中的一個字元
- *:匹配文件名中的任意字元
- **:匹配多層路徑
- Ant 風格的url 地址舉例
/user/*/createUser: 匹配/user/aaa/createUser、/user/bbb/createUser 等URL
/user/**/createUser: 匹配/user/createUser、/user/aaa/bbb/createUser 等URL
/user/createUser??: 匹配/user/createUseraa、/user/createUserbb 等URL
- Ant 風格的url 地址舉例
5.@RequestMapping 可配合@PathVariable 映射URL 綁定的占位符
- 不需要在url 地址上帶參數名了,更加的簡潔明瞭
// 我們希望目標方法獲取到username 和userid, value="/xx/{username}" -@PathVariable("username")
@RequestMapping(value = "/reg/{username}/{userId}")
public String register(@PathVariable("username") String username,
@PathVariable("userId") String userId){
System.out.println("接收到參數--" + "username= " + username + "--" + "usreid= " + userId);
return "success";
}
<a href="user/reg/kristina/300">占位符的演示</a>
6.請求的簡寫形式
@GetMapping @PostMapping @PutMapping @DeleteMapping
Rest請求風格
基本介紹
- REST:即Representational State Transfer。(資源)表現層狀態轉化。是目前流行的請求方式。它結構清晰, 很多網站採用。
- HTTP 協議裡面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:
- GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE 用來刪除資源。
傳統的請求方法
在url中:
getBook?id=1 GET
delete?id=1 GET
update POST
add POST
說明: 傳統的url 是通過參數來說明crud 的類型,rest 是通過get/post/put/delete 來說明crud 的類型
REST 的核心過濾器
- 當前的瀏覽器只支持post/get 請求,因此為了得到put/delete 的請求方式需要使用Spring
提供的HiddenHttpMethodFilter 過濾器進行轉換. - HiddenHttpMethodFilter:瀏覽器form 表單隻支持GET 與POST 請求,而DELETE、PUT
等method 並不支持,Spring 添加了一個過濾器,可以將這些請求轉換為標準的http 方
法,使得支持GET、POST、PUT 與DELETE 請求 - HiddenHttpMethodFilter 能對post 請求方式進行轉換
- 過濾器需要在web.xml 中配置
配置過濾器
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
當我們需要 用delete/put 的方式時,
name="_method" 名字需要寫成_method 因為後臺的HiddenHttpMethodFilter
就是按這個名字來獲取hidden 域的值,從而進行請求轉換的.
<form action="user/book/100" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="修改書籍~">
</form>
public String delBook(@PathVariable("id") String id) {
System.out.println("刪除書籍id= " + id);
//return "success"; [如果這樣返回會報錯JSPs only permit GET POST orHEAD]
return "redirect:/user/success"; //重定向到一個沒有指定method 的Handler 方法
}
註意事項
- HiddenHttpMethodFilter, 在將post 轉成delete / put 請求時,是按_method 參數名來讀取的
- 如果web 項目是運行在Tomcat 8 及以上,會發現被過濾成DELETE 和PUT 請求,到達控制器時能順利執行,但是返回時(forward)會報HTTP 405 的錯誤提示:消息JSP 只允許GET、POST 或HEAD。
- 因此,將請求轉發(forward)改為請求重定向(redirect):重定向到一個Handler,由Handler 轉發到頁面
SpringMVC映射請求數據
獲取參數值
/**
* @RequestParam(value="name", required=false)
* 1.@RequestParam : 表示說明一個接受到的參數
* 2.value="name" : 接收的參數名是name
* 3.required=false : 表示該參數可以有,也可以沒有,如果required=true,表示必須傳遞該參數.
* 預設是required=true
*/
@RequestMapping("vote01")
public String test01(@RequestParam(value = "name",required = false) String username){
System.out.println("得到的username= "+username);
return "success";
}
- @RequestParam 表示會接收提交的參數
- value = "name" 表示提交的參數名是name
- required = false 表示該參數可以沒有,預設是true 表示必須有這個參數
- 當我們使用@RequestParam(value = "name",required = false) 就表示 請求的參數名和目標方法的形參名 可以不一致
獲取Http請求消息頭
@RequestMapping(value = "/vote02")
public String test02(@RequestHeader(value = "Accept-Encoding")String ae,
@RequestHeader(value = "Host")String host) {
System.out.println("Accept-Encoding =" + ae);
System.out.println("Host =" + host);
//返回到一個結果
return "success";
}
獲取javabean 形式的數據
@RequestMapping(value = "/vote03")
public String test03(Master master) {
System.out.println("主人信息= " + master);
//返回結果
return "success";
}
-
方法的形參 (Master master) 用對應的對象類型指定即可,SpringMVC會自動的進行封裝
-
如果自動的完成封裝,要求提交的數據參數名和對象的欄位名保持一致
-
支持級聯數據獲取: 如果對象的屬性任然是一個對象,就通過 欄位名.欄位名 來提交
比如Master[Pet] ,可以通過 pet.id ,pet.name 來指定
-
如果參數名和欄位名不匹配,則對象的屬性就是null
ServletAPI
- 開發中原生的ServletAPI仍然可以使用
@RequestMapping(value = "/vote04")
public String test04(HttpServletRequest request,HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username= "+username+" ,password= "+password);
//返回結果
return "success";
}
- 除了HttpServletRequest, HttpServletResponse 還可以其它對象也可以這樣的形式獲取
- HttpSession、java.security.Principal,InputStream,OutputStream,Reader,Writer
模型數據
數據放入Request域
通過HttpServletRequest放入Request域
- 通過HttpServletRequest放入Request域是預設機制
@RequestMapping(value = "/vote05")
public String test05(Master master) {
return "vote_ok";
}
- 當我們提交的數據和對象名保持一致,那麼SpringMVC回自動封裝成一個對象【在前面 獲取javabean 形式的數據 講過】
- SpringMVC還會有一個機制,會自動將封裝的這個對象【model模型】放入Request域
- 也可以手動將對象放入Request域
- 會以 k-v 的形式放入Request域,k 是類名首字母小寫,v 是對象
-
如果我們需要向Request域中添加新的屬性/對象
request.setAttribute("address","beijing");
-
如果我們要修改預設機制自動放入的對象的屬性
master.setName("pp");
通過請求的方法參數Map<String,Object>放入Request域
@RequestMapping(value = "/vote06")
public String test06(Master master, Map<String,Object> map) {
map.put("address","tianjing");
map.put("master",null);
//返回到一個結果
return "vote_ok";
}
- SpringMVC會遍歷Map,然會將map中的 k-v 存放到Request域
- 如果 map.put("master",null); 會覆蓋預設機制的master,為null
通過返回ModelAndView對象實現Request域數據
@RequestMapping(value = "/vote07")
public ModelAndView test07(Master master) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("address","shanghai");
modelAndView.addObject("master",null);
//指定跳轉的視圖名稱
modelAndView.setViewName("vote_ok");
return modelAndView;
}
- SpringMVC會遍歷ModelAndView,然會將ModelAndView中的 k-v 存放到Request域
- 如果 modelAndView.addObject("master",null); 會覆蓋預設機制的master,為null
註意事項:
- 從本質上看,請求響應的方法 return "xx",是返回了一個字元串,其實本質是返回一個ModelAndView對象,只是預設被封裝起來了
- ModelAndView對象既可以包含model數據,也可以包含視圖信息
- ModelAndView對象的addObject("","");方法 可以添加key -value數據,預設在Request域中
- ModelAndView對象setView方法是指定 視圖名稱
數據放入Session域
@RequestMapping(value = "/vote08")
public String test08(Master master,HttpSession session){
session.setAttribute("master",master);
session.setAttribute("address","guangzhou");
return "vote_ok";
}
@ModelAttribute
@ModelAttribute
public void prepareModel(){
System.out.println("prepareModel()-----完成準備工作-----");
}
- 在某個方法上加上 @ModelAttribute 註解,那麼在調用該Handler的任何方法都會調用這個方法
- 類似Aop的前置通知
視圖和視圖解析器
基本介紹
- 在springMVC 中的目標方法最終返回都是一個視圖(有各種視圖).
- 返回的視圖都會由一個視圖解析器來處理(視圖解析器有很多種)
自定義視圖
- 在預設情況下,我們都是返回預設的視圖, 然後這個返回的視圖交由SpringMVC 的 InternalResourceViewResolver 視圖處理器來處理的
首碼 value="/WEB-INF/pages/" 和 尾碼 value=".jsp" 之後會拼接 返回給視圖解析器的返回值
<!-- 配置預設視圖解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置屬性 prefix 首碼 和 suffix 尾碼-->
<!-- 這裡的首碼 value="/WEB-INF/pages/" 和 尾碼 value=".jsp" 之後會拼接 返回給視圖解析器的返回值
例如 UserSerlet return "login ok";
就會拼接成 /WEB-INF/pages/login_ok.jsp 從而進行跳轉-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
2. 在實際開發中,我們有時需要自定義視圖,這樣可以滿足更多更複雜的需求.
需要在spring配置文件 , 增加自定義視圖解析器
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="99"></property>
</bean>
- name="order" :表示給這個解析器設置優先順序,
- 我們自己的視圖解析優先順序高,Order 值越小,優先順序越高
編寫自己的視圖
- 繼承 AbstractView 就可以作為一個視圖使用
- @Component(value = "myView")會作為id= myView 的一個組件 註入到容器中
@Component(value = "zyView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> map,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//該方法完成視圖渲染
//並且可以確定我們要跳轉的頁面 /WEB-INF/pages/my_view.jsp
System.out.println("進入到自己的視圖..");
//請求轉發到 /WEB-INF/pages/my_view.jsp
//第一個斜杠會解析成 工程路徑-> springmvc/
httpServletRequest.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
.forward(httpServletRequest,httpServletResponse);
}
}
進行跳轉
@RequestMapping("/goods")
@Controller
public class GoodsHandler {
@RequestMapping(value = "/buy")
public String buy() {
System.out.println("--buy()被調用--");
return "zyView";
}
}
自定義視圖小結
-
自定義視圖: 創建一個View 的bean, 該bean 需要繼承自AbstractView, 並實現 renderMergedOutputModel 方法.
-
並把自定義View 加入到IOC 容器中
-
自定義視圖的視圖處理器,使用BeanNameViewResolver, 這個視圖處理器也需要配置到ioc 容器
-
BeanNameViewResolver 的調用優先順序需要設置一下,設置order 比Integer.MAX_VAL 小的值. 以確保其在InternalResourceViewResolver 之前被調用
自定義視圖-工作流程
-
SpringMVC 調用目標方法, 返回自定義View 在IOC 容器中的id
-
SpringMVC 調用BeanNameViewResolver 解析視圖: 從IOC 容器中獲取返回id 值對應的bean, 即自定義的View 的對象
-
SpringMVC 調用自定義視圖的renderMergedOutputModel 方法渲染視圖
-
如果在SpringMVC 調用目標方法, 返回自定義View 在IOC 容器中的id不存在, 則仍然按照預設的視圖處理器機制處理
自定義解析器的執行流程-源碼
/**
* 自定義解析器的執行流程
* 1.
* @RequestMapping(value = "/buy")
* public String buy() {
* System.out.println("--buy()被調用--");
* return "zyView"; -->
* }
* 2.
* ApplicationContext context = obtainApplicationContext();
* if (!context.containsBean(viewName)) {//判斷viewName是否在容器中
* // Allow for ViewResolver chaining...
* return null;
* }
* if (!context.isTypeMatch(viewName, View.class)) {//判斷是否繼承了 AbstractView 實際上是判斷是否實現了View介面 因為AbstractView實現了View介面
* if (logger.isDebugEnabled()) {
* logger.debug("Found bean named '" + viewName + "' but it does not implement View");
* }
* // Since we're looking into the general ApplicationContext here,
* // let's accept this as a non-match and allow for chaining as well...
* return null;
* }
* return context.getBean(viewName, View.class); -->
* }
* 3.
* protected void renderMergedOutputModel(Map<String, Object> map,
* HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
*
* //該方法完成視圖渲染
* //並且可以確定我們要跳轉的頁面 /WEB-INF/pages/my_view.jsp
* System.out.println("進入到自己的視圖..");
*
* //請求轉發到 /WEB-INF/pages/my_view.jsp
* //第一個斜杠會解析成 工程路徑-> springmvc/
* httpServletRequest.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
* .forward(httpServletRequest,httpServletResponse); -->
* }
* }
* 4.
* <h1>進入到my_view頁面</h1>
* <p>是從自定義視圖來的...</p>
*
*
*/
預設解析器的執行流程-源碼
/**
* 預設解析器的執行流程
* 1.
* public String buy() {
* System.out.println("--buy()被調用--");
* return "zyView";->
* }
* 2.
* public InternalResourceViewResolver(String prefix, String suffix) {
* this();
* setPrefix(prefix);//用於拼接
* setSuffix(suffix);//用於拼接
* }
* 3.
* protected AbstractUrlBasedView buildView(String viewName) throws Exception {
* InternalResourceView view = (InternalResourceView) super.buildView(viewName);//調用super.buildView(viewName)
* if (this.alwaysInclude != null) {
* view.setAlwaysInclude(this.alwaysInclude);
* }
* view.setPreventDispatchLoop(true);
* return view;
* }
* 4.
* 找不到 報錯404
*/
找不到自定義解析器 會調用預設解析器
/**
* 找不到自定義解析器 會調用預設解析器
* 1.
* public String buy() {
* System.out.println("--buy()被調用--");
* return "zyView"; -->
* }
* 2.
* ApplicationContext context = obtainApplicationContext();
* if (!context.containsBean(viewName)) {//找不到返回null
* // Allow for ViewResolver chaining...
* return null;-->
* }
* 3.
* if (this.viewResolvers != null) {
* for (ViewResolver viewResolver : this.viewResolvers) { //遍歷解析器
* View view = viewResolver.resolveViewName(viewName, locale);//此時以及走預設解析器那一套了
* if (view != null) {
* return view;//進行拼接首碼和尾碼 但找不到
* }
* }
* 4.
* 拼接後找不到 報錯 404
*/
預設解析器一旦解析 不會去自定義解析器
- 因為預設解析器會拼接 view != null 就return了
* if (this.viewResolvers != null) {
* for (ViewResolver viewResolver : this.viewResolvers) {
* View view = viewResolver.resolveViewName(viewName, locale);
* if (view != null) {
* return view;
* }
* }
目標方法直接指定轉發或重定向
- 預設返回的方式是請求轉發,然後用視圖處理器進行處理
@RequestMapping(value = "/buy")
public String buy(){
return "success";
}
- 也可以在目標方法直接指定重定向或轉發的url 地址
請求轉發:return "forword:路徑"
return "forword:/WEB-INF/pages/my_view.jsp"
重定向:return "redirect:路徑"
return "redirect:login.jsp"
註意事項:
-
對於重定向,不能重定向到WEB-INF目錄下
重定向在響應頭返回的URL中是 /工程路徑/login.jsp【此時,瀏覽器會將第一個 / 處理成 IP:埠/工程路徑/login.jsp】
-
對於請求轉發,是在伺服器內部進行,第一個 / 解析成 工程路徑,進行轉發
數據格式化
基本介紹
引出:在我們提交數據(比如表單時)SpringMVC 怎樣對提交的數據進行轉換和處理的??
- 基本數據類型可以和字元串之間自動完成轉換
- Spring MVC 上下文中內建了很多轉換器,可完成大多數Java 類型的轉換工作
基本數據類型和字元串自動轉換
<form:form action="save" method="post" modelAttribute="monster">
妖怪名字: <form:input path="name"/> <form:errors path="name"/> <br><br>
妖怪年齡~: <form:input path="age"/> <form:errors path="age"/><br><br>
電子郵件: <form:input path="email"/> <form:errors path="email"/><br><br>
妖怪生日: <form:input path="birthday"/> <form:errors path="birthday"/>要求以"9999-11-11"的形式<br><br>
妖怪薪水: <form:input path="salary"/> <form:errors path="salary"/>要求以"123,890.12"的形式<br><br>
<input type="submit" value="添加妖怪"/>
</form:form>
- SpringMVC 表單標簽在顯示之前必須在request 中有一個bean, 該bean 的屬性和表單標簽的欄位要對應!
- request 中的key 為: form 標簽的modelAttribute 屬性值, 比如這裡的monsters
- SpringMVC 的form:form 標簽的action 屬性值中的/ 不代表WEB 應用的根目錄.
@RequestMapping(value = "/addMonsterUI")
public String addMonsterUI(Map<String, Object> map) {
//這裡需要給request 增加一個monster , 因為jsp 頁面的modelAttribute="monster"需要
//是springMVC 的內部的檢測機制即使是一個空的也需要,否則報錯
map.put("monster", new Monster());
return "datavalid/monster_addUI";
}
說明:
- 當我們在瀏覽器發送 age=10 時,會把10轉換成String類型,到達後端後,又會把String轉成 int/Integer
- 而發送 age=aaa 時,會把aaa轉成 string類型,到達後端後,把String類型 的 aaa轉換成 int/Integer,此時會報錯
特殊數據類型和字元串間的轉換
-
特殊數據類型和字元串之間的轉換使用註解(比如日期,規定格式的小數比如貨幣形式等)
-
對於日期和貨幣可以使用@DateTimeFormat 和@NumberFormat 註解. 把這兩個註解標記在欄位上即可.(JavaBean上)
@DateTimeFormat(pattern = "yyy-MM-dd") private Date birthday;
驗證國際化
- 對輸入的數據(比如表單數據),進行必要的驗證,並給出相應的提示信息
- 對於驗證表單數據,springMVC 提供了很多實用的註解, 這些註解由JSR 303 驗證框架提供.
JSR 303 驗證框架
-
JSR 303 是Java 為Bean 數據合法性校驗提供的標準框架,它已經包含在JavaEE 中
-
JSR 303 通過在Bean 屬性上標註類似於@NotNull、@Max 等標準的註解指定校驗規則,
並通過標準的驗證介面對Bean 進行驗證 -
JSR 303 提供的基本驗證註解有:
Hibernate Validator 擴展註解
-
Hibernate Validator 和Hibernate 沒有關係,只是JSR 303 實現的一個擴展.
-
Hibernate Validator 是JSR 303 的一個參考實現,除支持所有標準的校驗註解外,它還支
持以下的擴展註解: -
擴展註解有如下:
@NotNull(message = "年齡必須1-100")
@Range(min = 1,max = 100)
private Integer age;
@NotNull
private String name ;
@NotNull(message = "生日必須符合格式")
@DateTimeFormat(pattern = "yyy-MM-dd")
private Date birthday;
@NotNull(message = "salary必須符合格式")
@NumberFormat(pattern = "###,###.##")
private Float salary;
@RequestMapping(value = "/save")
public String save(@Valid Monster monster, Errors errors, Map<String,Object> map) {}
- @Valid Monster monster:表示對monster 接收的數據進行校驗
- Errors errors: 表示如果校驗出現錯誤,將校驗的錯誤信息保存到 errors 中
- Map<String,Object> map:如果校驗出現錯誤,會將校驗的錯誤信息保存到map,並且同時保存 monster對象
- 在前端使用<form:errors path="name"/> 回顯錯誤信息
妖怪名字: <form:input path="name"/> <form:errors path="name"/> <br><br>
妖怪年齡~: <form:input path="age"/> <form:errors path="age"/><br><br>
電子郵件: <form:input path="email"/> <form:errors path="email"/><br><br>
妖怪生日: <form:input path="birthday"/> <form:errors path="birthday"/>要求以"9999-11-11"的形式<br><br>
妖怪薪水: <form:input path="salary"/> <form:errors path="salary"/>要求以"123,890.12"的形式<br><br>
自定義驗證錯誤信息
1.需要在Spring配置文件中配置相關bean
<!-- 配置國際化錯誤信息的資源處理bean -->
<bean id="messageSource" class=
"org.springframework.context.support.ResourceBundleMessageSource">
<!-- 配置國際化文件名字
如果你這樣配的話,表示messageSource 回到src/i18nXXX.properties 去讀取錯誤信息
-->
<property name="basename" value="i18n"></property>
</bean>
2.需要在src目錄下創建 i18nxxx.properties去讀取錯誤信息
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\u0030\u4e4b\u95f4
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e
3.用工具將希望自定義的錯誤信息轉換成Unicode編碼填入i18nxxx.properties
註意事項
-
在需要驗證的Javabean/POJO 的欄位上加上相應的驗證註解.
-
目標方法上,在JavaBean/POJO 類型的參數前, 添加@Valid 註解. 告知SpringMVC該bean 是需要驗證的
@RequestMapping(value = "/save")
public String save(@Valid Monster monster, Errors errors, Map<String,Object> map) {}
- 在@Valid 註解之後, 添加一個Errors 或BindingResult 類型的參數, 可以獲取到驗證的錯誤信息
- 需要使用<form:errors path="email"></form:errors> 標簽來顯示錯誤消息, 這個標簽,需要寫在form:form 標簽內生效.
<form:form action="save" method="post" modelAttribute="monster">
妖怪名字: <form:input path="name"/> <form:errors path="name"/> <br><br>
</form:form>
-
錯誤消息的國際化文件i18n.properties , 中文需要是Unicode 編碼,使用工具轉碼.
格式: 驗證規則.表單modelAttribute 值.屬性名=消息信息NotEmpty.monster.name=\u540D\u5B57\u4E0D\u80FD\u4E3A\u7A7A
typeMismatch.monster.age=\u7C7B\u578B\u4E0D\u5339\u914D -
註解@NotNull 和@NotEmpty 的區別說明
-
@NotEmpty 是判斷 null 的 並且可以接收 任何類型
-
@NotNull 是判斷null 和 empty的,接收String,collection,map和array
-
如果是字元串驗證空, 建議使用@NotEmpty
- SpingMVC 驗證時,會根據不同的驗證錯誤, 返回對應的信息
註解組合使用
- 使用@NotNull + @Range 組合使用
@NotNull(message = "年齡必須1-100")
@Range(min = 1,max = 100)
private Integer age;
取消屬性綁定 @InitBinder 註解
- 不希望接收到某個表單對應的屬性的值,則可以通過@InitBinder 註解取消綁定.
- 使用@InitBinder 標識的該方法,可以對WebDataBinder 對象進行初始化。
- WebDataBinder 是DataBinder 的子類,用於完成由表單欄位到JavaBean 屬性的綁定
- @InitBinder 方法不能有返回值,它必須聲明為void。
- @InitBinder 方法的參數通常是是WebDataBinder
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("name");//表示取消屬性name的綁定; 這裡可以填寫多個欄位
}
- 取消屬性的綁定,那麼在JavaBean中的校驗註解也應該去掉
中文亂碼問題處理
- Spring提供的過濾器處理中文
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
處理JSON格式
@ResponseBody
- 伺服器 -----JSON數據-----> 客戶端/瀏覽器
@RequestMapping(value = "/json/dog")
@ResponseBody
public Dog getJson(){
Dog dog = new Dog("大黃", "貝加爾");
return dog;
}
@RequestBody
客戶端/瀏覽器-----JSON數據-----> 伺服器
@RequestMapping(value = "/save2")
@ResponseBody
public User save2(@RequestBody User user){
System.out.println("user= "+user);
return user;
}
- 將客戶端/瀏覽器發送的json字元串數據封裝成 JavaBean對象
- 再把這個 JavaBean對象 以 json對象形式返回
- (@RequestBody User user)在形參上指定
- SpringMVC就會將提交的json字元串數據填充給指定JavaBean
註意事項
- 目標方法正常返回json需要的數據,可以是對象也可以是集合
- @ResponseBody可以寫在類上,這樣對該類所有的方法生效
- @ResponseBody + @Controller 可以直接寫成@RestController
HttpMessageConverter
- SpringMVC 處理JSON-底層實現是依靠HttpMessageConverter
來進行轉換的
工作機制簡圖
-
使用HttpMessageConverter
將請求信息轉化並綁定到處理方法的入參中, 或將響應結果轉為對應類型的響應信息,Spring 提供了兩種途徑: -
使用@RequestBody / @ResponseBody 對目標方法進行標註
-
使用HttpEntity
/ ResponseEntity 作為目標方法的入參或返回值
-
-
當控制器處理方法使用到@RequestBody/@ResponseBody 或HttpEntity
/ResponseEntity 時, Spring 首先根據請求頭或響應頭的Accept 屬性選擇匹配的HttpMessageConverter, 進而根據參數類型或泛型類型的過濾得到匹配的HttpMessageConverter, 若找不到可用的HttpMessageConverter 將報錯
SpringMVC文件上傳
- 在SpringMVC 中,通過返回ResponseEntity
的類型,可以實現文件下載的功能 - 需要構建 ResponseEntity 對象,需要1.得到http響應頭 2.http響應狀態 3.下載文件的數據
@RequestMapping(value="/downFile")
public ResponseEntity<byte[]> downFile(HttpSession session) throws Exception{
//1.先獲取到要下載 的 InputStream
InputStream resourceAsStream =
session.getServletContext().getResourceAsStream("/img/1.jpg");
//2.開闢存放文件的byte數組 -> 支持二進位數據
//resourceAsStream.available() 返回資源文件的大小
byte[] bytes = new byte[resourceAsStream.available() ];
//3. 將要下載的文件數據讀入到byte數組
resourceAsStream.read(bytes);
/*
ResponseEntity 的構造器:
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
this(body, headers, (Object) status);
}
*/
//準備構造ResponseEntity 對象
//4.構建httpStatus
HttpStatus httpStatus = HttpStatus.OK;
//5.構建http響應頭
HttpHeaders headers = new HttpHeaders();
//指定瀏覽器以附件的形式處理回送數據
headers.add("Content-Disposition","attachment;filename=1.jpg");
ResponseEntity<byte[]> responseEntity =
new ResponseEntity<>(bytes, headers, httpStatus);
return responseEntity;
}
-
文件下載響應頭的設置
content-type 指示響應內容的格式
content-disposition 指示如何處理響應內容,一般有兩種方式:1. inline:直接在頁面顯示 2.attchment:以附件形式下載
SpringMVC文件上傳
基本介紹
- Spring MVC 為文件上傳提供了直接的支持,
- 這種支持是通過即插即用的MultipartResolver 實現的。
- Spring 用Jakarta Commons FileUpload 技術實現了一個MultipartResolver 實現類:CommonsMultipartResovler
- Spring MVC 上下文中預設沒有裝配MultipartResovler,因此預設情況下不能處理文件的上傳工作,
- 如果想使用Spring 的文件上傳功能,需現在上下文中配置MultipartResolver
<!--文件上傳-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
- 需要在form表單添加屬性 enctype="multipart/form-data"
<form action="<%=request.getContextPath()%>/fileUpload" method="post" enctype="multipart/form-data">
文件介紹:<input type="text" name="introduce"><br>
選擇文件:<input type="file" name="file"><br>
<input type="submit" value="上傳文件">
</form>
文件上傳
//編寫方法 處理文件上傳的請求
@RequestMapping(value = "/fileUpload")
public String fileUpload(@RequestParam(value = "file") MultipartFile file,
HttpServletRequest request, String introduce) throws IOException {
//接收到提交的文件名
String originalFilename = file.getOriginalFilename();
System.out.println("提交的文件名= " + originalFilename);
System.out.println("文件介紹= " + introduce);
//獲取到文件保存的目標位置/路徑
String filePath =
request.getServletContext().getRealPath("/img/" + originalFilename);
//創建文件
File saveToFile = new File(filePath);
//將上傳的文件轉存到 saveToFile
file.transferTo(saveToFile);
return "success";
}
攔截器
基本介紹
- Spring MVC 也可以使用攔截器對請求進行攔截處理,
- 用戶可以自定義攔截器來實現特定的功能.
- 自定義的攔截器必須實現HandlerInterceptor 介面
自定義攔截器的三個方法
-
preHandle():這個方法在業務處理器處理請求之前被調用,在該方法中對用戶請求request 進行處理。
-
postHandle():這個方法在目標方法處理完請求後執行
-
afterCompletion():這個方法在完全處理完請求後被調用,可以在該方法中進行一些資源
清理的操作。
在Spring配置文件中配置攔截器
預設配置是都所有的目標方法都進行攔截, 也可以指定攔截目標方法, 比如只是攔截hi
<mvc:interceptors>
<ref bean="myInterceptor01"/>//直接引用對應攔截器
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
</mvc:interceptor>
mvc:mapping 支持通配符, 同時指定不對哪些目標方法進行攔截
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
- 這樣配置會攔截h打頭的url指定的方法
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor02"/>
</mvc:interceptor>
- 這樣配置會攔截h打頭的url指定的方法並且排除 url是hello的方法
自定義攔截器執行流程分析圖
● 自定義攔截器執行流程說明
- 如果preHandle 方法返回false, 則不再執行目標方法, 可以在此指定返回頁面
- postHandle 在目標方法被執行後執行. 可以在方法中訪問到目標方法返回的 ModelAndView 對象
- 若preHandle 返回true, 則afterCompletion 方法在渲染視圖之後被執行.
- 若preHandle 返回false, 則afterCompletion 方法不會被調用
- 在配置攔截器時,可以指定該攔截器對哪些請求生效,哪些請求不生效
註意事項
- 攔截器需要配置才生效,不配置是不生效的.
- 如果preHandler() 方法返回了false, 就不會執行目標方法(前提是你的目標方法被攔截了), 程式員可以在這裡根據業務需要指定跳轉頁面.
多個攔截器
註意事項
-
如果第1 個攔截器的preHandle() 返回false , 後面都不在執行
-
如果第2 個攔截器的preHandle() 返回false , 就直接執行第1 個攔截器的
afterCompletion()方法, 如果攔截器更多,規則類似 -
前面說的規則,都是目標方法被攔截的前提
異常處理
基本介紹
-
Spring MVC 通過HandlerExceptionResolver 處理程式的異常,包括Handler 映射、數據綁定以及目標方法執行時發生的異常。
-
主要處理Handler 中用@ExceptionHandler 註解定義的方法。
-
ExceptionHandlerMethodResolver 內部若找不到@ExceptionHandler 註解的話, 會找@ControllerAdvice 類的@ExceptionHandler 註解方法, 這樣就相當於一個全局異常處理器
-
如果不去處理異常,tomcat會預設機制處理,用戶看到的頁面非常不友好
異常處理的優先順序
局部異常 > 全局異常 > SimpleMappingExceptionResolver > tomcat預設機制
局部異常
//局部異常就是直接在這個Handler 中編寫即可
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String localException(Exception ex,HttpServletRequest request){
System.out.println("異常信息是~" + ex.getMessage());
//如何將異常的信息帶到下一個頁面.
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
全局異常
@ControllerAdvice//@ControllerAdvice 表示了該註解,就是全局異常
public class MyGlobalException {
@ExceptionHandler({NumberFormatException.class, ClassCastException.class})
public String globalException(Exception ex, HttpServletRequest request){
System.out.println("全局異常處理---"+ex.getMessage());
request.setAttribute("reason",ex.getMessage());
return "exception_mes";
}
- @ControllerAdvice 表示了該註解,就是全局異常
自定義異常
- 通過@ResponseStatus 註解, 可以自定義異常
- 格式:@ResponseStatus(reason = "異常原因",value = httpStatus狀態 )
@ResponseStatus(reason = "年齡需要在1-120之間",value = HttpStatus.BAD_REQUEST )
public class AgeException extends RuntimeException{//需要繼承RuntimeException/Exception
}
- 自定義異常類需要繼承RuntimeException/Exception
- httpStatus會有很多狀態
-
如果想在其他頁面看到reason信息,加上帶String構造器即可
public AgeException(String message) { super(message); }
統一處理異常信息
基本介紹
-
如果希望對所有異常進行統一處理,可以使用SimpleMappingExceptionResolver
-
它將異常類名映射為視圖名,即發生異常時使用對應的視圖報告異常
-
需要在ioc 容器中配置
<!-- 統一處理異常-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">allEx</prop>
</props>
</property>
</bean>
- 在這個標簽內 就可以配置出現異常需要跳轉的頁面
allEx - key="java.lang.Exception" 是異常的範圍,這樣設置可以對未知異常進行統一處理,也就是所有異常都處理
SpringMVC執行流程以及源碼剖析
SpringMVC執行流程示意圖
執行流程-源碼剖析
-
發出請求url
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request);
-
調用處理器映射器
//getWebApplicationContext() 就是Spring容器 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); //走完這一步前端控制器就有了Spring容器 doDispatch(request, response);//進入分發 2.1 HandlerExecutionChain mappedHandler = null;//有屬性 處理器鏈 2.2 mappedHandler = getHandler(processedRequest);// mappedHandler中已經有 目標Handler 和 攔截器鏈
-
調用處理器適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//得到處理器適配器 String method = request.getMethod();得到請求方式
-
調用Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//調用Handler mav = invokeHandlerMethod(request, response, handlerMethod); invocableMethod.invokeAndHandle(webRequest, mavContainer);//調用Handler中的目標方法了 ModelAndView modelAndView = new ModelAndView();//到達目標方法 return invoke0(this.method, var1, var2);//目標方法執行完畢返回 return getModelAndView(mavContainer, modelFactory, webRequest); return mav;//返回給前端處理器 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//回到起點,返回了一個 ModelAndView
-
調用視圖解析器
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); render(mv, request, response);//進行渲染 //進入render 方法 String viewName = mv.getViewName();//拿到視圖名稱 view = resolveViewName(viewName, mv.getModelInternal(), locale, request);//進行視圖解析 //調用視圖解析器 if (this.viewResolvers != null) { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale);//解析完畢返回視圖 if (view != null) { return view;//返回視圖給前端控制器 } } }
-
視圖渲染
view.render(mv.getModelInternal(), request, response); renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);//渲染合併 輸出模型 //進入方法 RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);//獲得RequestDispatcher
-
返迴響應
rd.forward(request, response);//返迴響應
手寫SpringMVC底層機制!
-
前景提要:實現的是SpringMVC核心機制
-
對一些細枝末節的代碼做了簡化,比如字元串的處理...
-
完成哪些機制
- 機制一: 通過@RequestMapping ,可以標記一個方法,編寫路徑url,瀏覽器就能通過url完成調用
- 機制二: 進行依賴註入,使之不需要傳統的new 一個對象,而是直接從IOC容器中獲得
- 機制三:通過@RequestParam,如果瀏覽器傳遞的參數名和目標方法的形參不一致,可以通過value設置進行匹配
- 機制四:在目標方法完成後,跳轉到相關頁面 請求轉發/重定向
- 機制五:在目標方法完成後,通過@Response註解,向瀏覽器發送JSON格式數據
手寫添加配置
思路
- 需要配置pom.xml的依賴
- 需要寫一個Servlet 作為前端控制器
- 需要配置Web.xml 中的前端控制器 1).url 2)配置spring容器配置文件的classpath: 3)跟隨Tomcat 自啟動
- 需要配置spring容器配置文件
- 需要配置spring容器配置文件 掃描的路徑 <component-scan ...>
實現
- 需要配置pom.xml的依賴
<!-- 配置原生Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
<!-- <scope> 表示引入的jar的作用範圍
provided 表示該項目在打包 放到生產環境時,不需要帶上 servlet-api.jar
因為tomcat本身有 servlet-api.jar,到時直接使用tomcat本身的 servlet-api.jar-->
</dependency>
<!-- 配置dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.1</version>
</dependency>
<!-- 配置常用的工具類-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
- 需要寫一個Servlet 作為前端控制器
public class ZyDispatcherServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ZyDispatcherServlet-doPost--");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ZyDispatcherServlet-doGet--");
}
}
-
需要配置Web.xml 中的前端控制器 1).url 2)配置spring容器配置文件的classpath: 3)跟隨Tomcat 自啟動
-
需要配置spring容器配置文件 掃描的路徑 <component-scan ...>
<servlet>
<servlet-name>ZyDispatcherServlet</servlet-name>
<servlet-class>com.code_study.zyspringmvc.servlet.ZyDispatcherServlet</servlet-class>
<!--配置參數,指定要操作的spring配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:zyspringmvc.xml</param-value>
</init-param>
<!--跟隨tomcat自啟動-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ZyDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 需要配置spring容器配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!-- 要掃描的包-->
<component-scan base-package="com.code_study.controller,com.code_study.service"></component-scan>
</beans>
完成瀏覽器可以請求控制層
思路
- 創建@Controller和自己的Controller
- 編寫工具類XMLParser,解析spring容器配置文件
- 開發自己的 Spring容器,得到掃描類的全路徑列表
- 開發自己的 前端控制器,實例化對象到容器中
- 完成請求的URL和控制器方法的映射關係
- 完成前端控制器分發請求到對應控制器
- 自定義註解@Controller
- 創建Controller
- 需要寫一個工具類XMLParser來解析在spring容器配置文件 掃描的路徑 <component-scan ...> 的包 返回所有的路徑
- 這個所有的路徑 一個split(",")分隔,都進行掃描
- 需要寫自己的 前端控制器
- 需要寫自己的 Spring容器
- 在前端控制器中 需要添加方法 scanPackage() 掃描 XMLParser 解析出來的路徑
- 在Spring容器中 需要添加一個屬性 classFullPathList 來保存掃描出來的類的全路徑
- 需要添加一個屬性 ioc 來存放反射生成的bean對象 也就是過濾classFullPathList 中沒有@Controller註解的一些路徑 並實例化
- 需要添加類Handler 這個類要保存 一個url 對應的 一個控制器的方法的 映射 ,也就是說,根據這個url,可以找到對應控制器的對應方法
- 需要添加一個屬性 HandlerList 用於 保存Handler 【url 和 控制器的映射】
- 需要添加三個方法 一個是initHandlerMapping(),完成 url 對應的 一個控制器的方法的 映射,即 將ioc 中bean 中的 方法進行反射,獲取url,將 url,method,bean 封裝成Handler 放入HandlerList 保存
- 添加第二個方法 getHandler(),需要將瀏覽器發送的request請求中的 uri拿出來,遍歷HandlerList 進行配對,如果有 就返回對應的Handler
- 添加第三個方法 executeDispatch(),進行分發處理,需要 調用getHandler() 獲取瀏覽器發送的request請求 對應的 Handler ,獲取Handler 中的method 進行反射調用,method .invoke() 實現分發請求。
實現
- 自定義註解@Controller
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
- 創建Controller
@Controller
public class MonsterController {
}
- 需要寫一個工具類XMLParser來解析在spring容器配置文件 掃描的路徑 <component-scan ...> 的包 返回所有的路徑
public class XMLParser {
public static String getBasePackage(String xmlFile){
SAXReader saxReader = new SAXReader();
ClassLoader classLoader = XMLParser.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream(xmlFile);
try {
Document document = saxReader.read(resourceAsStream);
Element rootElement = document.getRootElement();
Element element = rootElement.element("component-scan");
String basePackage = element.attribute("base-package").getText();
return basePackage;
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
}
- 這個所有的路徑 一個split(",")分隔,都進行掃描
- 需要寫自己的 前端控制器
- 需要寫自己的 Spring容器
- 在前端控制器中 需要添加方法 scanPackage() 掃描 XMLParser 解析出來的路徑
public void scanPackage(String pack) {
//獲得包所在的工作路徑 [絕對路徑]
URL url =
this.getClass().getClassLoader().//獲取類的載入器
//得到指定包對應的工作路徑 [絕對路徑]
getResource("/" + pack.replaceAll("\\.", "/"));
// System.out.println("url= "+url);
//根據得到的路徑,對其進行掃描,把類的全路徑 保存到 classFullPathList
String path = url.getFile();
//在io中 把目錄也是為一個文件
File file = new File(path);
//遍歷file 【遍歷出文件和子目錄】
for (File f : file.listFiles()) {
if (f.isDirectory()) {//如果是目錄
//需要遞歸掃描 找子目錄
scanPackage(pack + "." + f.getName());
} else {
//的確是個文件
//掃描到的文件可能是 .class 文件 也可能是其他文件
//就算是.class 文件 也需要判斷是否需要註入容器 有無加 @Controller註解
//目前無法拿到註解 因為沒法反射 所以先把文件的全路徑都保存到 classFullPathList 之後在註入對象到容器時再處理
String classFullPath =
//類的全路徑不需要.class 去掉.class
pack + "." + f.getName().replaceAll(".class", "");
//保存到 classFullPathList
classFullPathList.add(classFullPath);
}
}
}
- 在Spring容器中 需要添加一個屬性 classFullPathList 來保存掃描出來的類的全路徑
//保存掃描的包/子包類的全路徑
private List<String> classFullPathList =
new ArrayList<>();
- 需要添加一個屬性 ioc 來存放反射生成的bean對象 也就是過濾classFullPathList 中沒有@Controller註解的一些路徑 並實例化
//定義屬性 ioc -> 存放反射生成的bean對象 比如Controller / Service /Dao
public ConcurrentHashMap<String, Object> ioc =
new ConcurrentHashMap<>();
- 編寫方法,將掃描到的類,在滿足情況下 反射到ioc容器
//編寫方法,將掃描到的類,在滿足情況下 反射到ioc容器
public void executeInstance() {
if (classFullPathList.size() == 0) {
//說明沒有掃描到類
return;
}
//遍歷classFullList
for (String classFullPath : classFullPathList) {
try {
Class<?> clazz = Class.forName(classFullPath);
if (clazz.isAnnotationPresent(Controller.class)) {//處理@Controller
String className = clazz.getSimpleName();
Object instance = clazz.newInstance();
String value = clazz.getAnnotation(Controller.class).value();
if (!"".equals(value)) {
className = value;
} else {
className = StringUtils.uncapitalize(className);
}
ioc.put(className, instance);
}
else if (clazz.isAnnotationPresent(Service.class)) {//處理@Service
String className = clazz.getSimpleName();//類名
Service serviceAnnotation = clazz.getAnnotation(Service.class);
String value = serviceAnnotation.value();
if (!"".equals(value)) {
className = value;
Object instance = clazz.newInstance();
ioc.put(className, instance);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
- 需要添加類Handler 這個類要保存 一個url 對應的 一個控制器的方法的 映射 ,也就是說,根據這個url,可以找到對應控制器的對應方法
ZyHandler {
private String url;
private Method method;
private Object controller;
public ZyHandler() {
}
public ZyHandler(String url, Method method, Object controller) {
this.url = url;
this.method = method;
this.controller = controller;
}
//需要提供getter和setter方法...
- 需要添加一個屬性 HandlerList 用於