第三章:SpringMCV的異常處理
說明:也是SpringMVC架構提供的異常處理。
基本思路:
處理步驟
1、編寫自定義異常類
package com.jtl.exception;
/**
* @author JT.L
* @date 2019/12/16 14:01:18
* @description 自定義異常類
*/
public class SysException extends Exception {
/**
* 存儲提示資訊
*/
private String message;
public SysException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2、編寫異常處理器
/**
* @author JT.L
* @date 2019/12/16 14:38:08
* @description 異常處理器
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 處理異常業務邏輯
*
* @param httpServletRequest
* @param httpServletResponse
* @param o 目前處理器這個對象 用的很少
* @param ex
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
// 擷取到異常對象
SysException e = null;
if (ex instanceof SysException) {
e = (SysException) ex;
} else {
e = new SysException("系統正在維護...");
}
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg", e.getMessage());
mv.setViewName("error");
return mv;
}
}
3、配置異常處理器
在springmvc.xml中配置:就是一個普通的bean标簽對象
<!--配置異常處理器對象-->
<bean id="sysExceptionResolver" class="com.jtl.exception.SysExceptionResolver"/>
其他、Controller方法已經error頁面
@RequestMapping("/testException")
public String testException() throws SysException{
System.out.println("execute testException...");
try {
// 模拟異常
int a = 10 / 0;
} catch (Exception e) {
// 列印異常資訊(控制台)
e.printStackTrace();
// 抛出自定義異常資訊
// 模拟調用service方法出錯
throw new SysException("查詢所有使用者出現錯誤了...");
}
return "success";
}
在相應位置建立一個error.jsp檔案,作為友好的錯誤提示頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${errorMsg}
</body>
</html>
插曲:
對于這個函數的說明,雖然形參是Exception(SysException的父類),但是如果你傳入的是SysException這個子類對象,是不會将SysException轉成Exception對象的,它本身還是SysException這個對象,可以通過instanceof來進行判斷傳入的具體對象是哪一個,是不是子類對象!例:
public static void main(String[] args) {
function(new Son());
}
private static void function(Father f){
if (f instanceof Son){
System.out.println("This is son.");
}else {
System.out.println("This is father.");
}
}
說明:Father是Son的父類,當實參傳入Son時,列印結果為:This is son.
第四章:SpringMVC架構中的攔截器
說明:攔截器功能上類似與Servlet開發中的過濾器Filter,過濾器可以去攔截你背景的資源。補充:Spring MVC 的處理器攔截器類似于 Servlet 開發中的過濾器 Filter,用于對處理器(就是Controller這個類)進行預處理和後處理。
流程圖:
說明:
(預處理)在請求Controller之前會先經過攔截器,(後處理)Controller執行完往某個頁面去跳它還會再回來再經過攔截器。
當你發請求,攔截器會先執行放行之前的代碼,代碼走完,然後放行,後面的Controller就正常執行;當Controller執行完想往頁面跳轉回來會執行放行之後的代碼,最後才是跳轉到某某頁面中去。
攔截器與過濾器差別:
過濾器:什麼都可以攔截,且任何java web工程都能用。
攔截器:隻能攔控制器方法,隻能在SpringMVC架構中去使用。
攔截器能做的事過濾器全部都能做,過濾器能做的事攔截器就不一定做的了(比如,攔截jsp、js、html、css、image等)。
4.1 快速入門程式
4.1.1 編寫攔截器類
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 預處理,Controller方法執行前
* return true 放行,執行下一個攔截器,如果沒有(下一個攔截器),執行Controller中的方法
* return false不放行,通過request&response對象直接跳轉到某個頁面去(等于沒執行Controller方法),作一些提示資訊,如:您沒權限通路。(跳轉到提示權限頁面)
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("execute MyInterceptor1 preHandle...");
return true;
}
}
4.1.2 配置攔截器
在springmvc.xml配置檔案中配置:
<!--配置攔截器-->
<mvc:interceptors>
<!--配置(具體某個)攔截器-->
<mvc:interceptor>
<!--要攔截的具體方法-->
<mvc:mapping path="/user/**"/><!--或者這樣寫:<mvc:mapping path="/user/*"/>-->
<!--所有方法全部攔截:<mvc:mapping path="/**"/>-->
<!--不要攔截的方法
<mvc:exclude-mapping path=""/>
這兩者配置其中一個即可
-->
<!--配置攔截器對象,把類配上表明攔截器注入成功-->
<bean class="com.jtl.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
4.1.3 其他
Controller類:
@RequestMapping("/testInterceptor")
public String testInterceptor(){
System.out.println("execute testInterceptor...");
return "success";
}
success.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>執行成功</h3>
<%System.out.println("execute success.jsp");%>
</body>
</html>
4.1.4 控制台列印結果
說明:先執行攔截器的預處理;然後是Controller方法;最後是跳轉頁面裡的方法。
4.2 攔截器中的其他方法
一種有三個方法。
4.2.0 不放行示範
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("execute MyInterceptor1...");
request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
return false;
}
4.2.1 postHandle方法
/**
* 後處理方法,Controller方法執行後,success.jsp執行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("execute MyInterceptor1 postHandle...");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
//modelAndView.setViewName("error");注意控制台的輸出資訊與上面請求轉發的作對比,其實就是少了success.jsp頁面的輸出
}
4.2.2 afterCompletion方法
/**
* success.jsp頁面執行後,該方法會執行(也就是最後執行的一個方法)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("execute MyInterceptor1 afterCompletion...");
// 這個時候就不能跳轉頁面了,會出現如下錯誤(控制台):Cannot forward after response has been committed
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
}
4.2.3 總結:
1、預處理:在Controller之前,可以做一些邏輯的判斷,如:使用者登沒登入邏輯判斷,如果登入直接放行,否則跳到登入頁面去。
2、後處理:也可以跳頁面。
3、afterCompletion():可以釋放一些資源,如:關閉流。
4.3 拓展 --- 兩個攔截器
4.3.1 配置第二個攔截器
<!--配置攔截器-->
<mvc:interceptors>
<!--配置(具體某個)攔截器-->
<mvc:interceptor>
<!--要攔截的具體方法-->
<mvc:mapping path="/user/**"/>
<!--配置攔截器對象,把類配上表明攔截器注入成功-->
<bean class="com.jtl.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<!--配置第二個攔截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!--注冊攔截器對象-->
<bean class="com.jtl.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
4.3.2 建立一個新的攔截器
MyInterceptor2裡面的内容與MyInterceptor1一樣隻是列印資訊修改一下。
4.3.3 執行流程結果:
說明:就是按照一開始最上面那個流程圖來執行的。
附:SSM整合
思路圖:
說明:
表現層就是Controller,注解:@Controller
業務層就是service,注解:@Service
持久層就是dao,注解:@Repository。dao層隻需要提供接口,因為在IOC容器中會自動生成一個代理對象去執行具體的事。
1、Spring整合SpringMVC
去web.xml配置上一個監聽器,并指明spring配置檔案的路徑
<!--配置Spring的監聽器,預設隻加載WEB-INF目錄下的applicationContext.xml配置檔案-->
<listener>
<!--這個類來自:<artifactId>spring-web</artifactId>這個坐标-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--設定配置檔案的路徑-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
2、Spring整合Mybatis
去spring配置檔案中,配置上SqlSessionFactory工廠,目的就是為了讓dao接口的代理對象存入到IOC容器中。
<!--Spring整合Mybatis架構(将dao接口的代理對象存入容器,用于service層注入dao對象)【說白了就是将dao放入容器中】-->
<!--配置連接配接池(c3p0)-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///ssm_demo1"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置SqlSessionFactory工廠-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--這個類可以通過連接配接池來建構session工廠-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置AccountDao接口所在的包-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.jtl.dao"/>
</bean>
3、dao代理對象的進一步說明
這是一段測試Mybatis的代碼,充分說明了dao代理對象的作用以及它生成的過程,必須經過SqlSessionFactory工廠。
@Test
public void run2() throws Exception {
Account account = new Account();
account.setName("JTL");
account.setMoney(100D);
// 加載配置檔案 這種方式就是Mybatis方式 沒用有spring 後面把SqlMapConfig.xml注釋掉後,相當于将Mybatis交給spring管理了
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 建立SqlSessionFactory對象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 建立SqlSession對象
SqlSession session = factory.openSession();
// 擷取到代理對象
AccountDao dao = session.getMapper(AccountDao.class);
// 儲存
dao.saveAccount(account);
// 送出事務(做增删改需要自己送出事務)
session.commit();
// 關閉資源
session.close();
in.close();
}
最後,SSM整合項目github位址:SSM架構整合demo