這是我參與8月更文挑戰的第13天,活動詳情查看:8月更文挑戰css
maven 建立 webapp 程序html
一些依賴前端
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
</dependencies>
複製代碼
web.xml 配置java
<web-app>
<!--前端控制器-->
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<!--若是使用 /*,會匹配 .jsp-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
複製代碼
springmvc-servlet.xml 配置web
<!--處理器映射器-->
<!--BeanNameUrlHandlerMapping 是 SpringMVC 提供的處理器映射器的一種,按照 bean 的 name 屬性匹配-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!--處理器適配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!--視圖解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--將視圖位置設置在 WEB-IBF 下的 jsp 目錄中-->
<property name="prefix" value="/WEB-INF/jsp" />
<property name="suffix" value=".jsp" />
</bean>
複製代碼
HelloController.javaspring
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
// 轉發到 WEB-INF/jsp/hello.jsp
mv.setViewName("/hello");
return mv;
}
}
複製代碼
配置處理器json
在 spingmvc-servlet.xml 中配置api
<!--處理器-->
<bean name="/hello" class="controller.HelloController"></bean>
複製代碼
視圖文件數組
在 webapp/WEB-INF 目錄下,新建 jsp 目錄,建立 hello.jsp服務器
啓動服務器,訪問 /hello 進行測試,成功轉發到視圖 hello.jsp
配置開啓註解
<!--開啓自動掃描-->
<context:component-scan base-package="..." />
<!--啓用默認的靜態資源處理器,html、js、css、mp3 等-->
<mvc:default-servlet-handler />
<!--啓用 MVC 註解支持,如 @RequestMapping-->
<mvc:annotation-driven>
<!--設置消息轉化器默認編碼,解決前端拿到數據亂碼-->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
複製代碼
mvc:annotation-driven 幫咱們注入了 DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerMapping 兩個 bean
經常使用註解介紹
@Controller 將類標註爲控制器,不用在繼承 Controller 接口了
@RequestMapping 標註進入該控制器的匹配路徑,能夠用以類和方法
@ResponseBody 將返回內容轉爲 JSON 字符串,寫入輸入流返回給前端
@RequestParam 做用在方法參數中,從請求查詢串中解析參數內容
@RequestBody 與 @RequestParam 相似,從請求體中解析參數內容
@RestController 擁有 @Controller 功能,並將全部方法標註爲 @ResponseBody
@Controller
public class HelloController {
// 匹配請求路徑
// 相似:@GetMapping、@PostMapping...
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "來自 HelloController.hello 的消息");
// 沒使用 @ResponseBody,會走視圖解析器
// 若是使用 @ResponseBody,則直接返回字符串
return "hello";
}
}
複製代碼
@RequestParam
@ResponseBody
@RequestMapping("/hello")
public String hello(@RequestParam(value = "username", required = false) String name, @RequestParam(required = false, defaultValue = "1") Integer age) {
return name + " " + age;
}
複製代碼
value 用於定義有前端傳來的要解析的鍵名
required 表示是否必須攜帶該數據,默認爲 true 時,沒有該數據沒法正確匹配
defaultValue 用於在沒有解析到對應數據時賦予初值
@RequestParam 使用場景
可解析請求查詢串內容和請求類型爲 x-www-form-urlencoded 或 form-data 時的請求體內容
- 使用基本類型或 String 接收時,根據名稱匹配鍵值對
- 使用 String 接收時,若是查詢串和請求體內的同名鍵值對會自動以逗號拼接
- 使用 List 或數組接收時,可用一個鍵名對應多個逗號分隔的值,來綁定到 List 或數組
- 使用 List 或數組接收時,若是查詢串和請求體內的同名鍵值對會自動合併,能夠用 List、數組接收
- 使用 Map 接收時,可對查詢串和請求體內的鍵值對生成 Map
- 不可使用實體類接收
@RequestBody
@ResponseBody
@RequestMapping("/hello")
public User hello(@RequestBody User user) {
return user;
}
複製代碼
required 表示是否必須攜帶該數據,默認爲 true 時,沒有該數據沒法正確匹配
@RequestBody 只能解析請求體中的內容,且請求類型爲 appliction/json
使用 MappingJackson2HttpMessageConverter 進行解析和轉換,支持自動綁定實體類或 Map
不使用 @RequestParam 和 @RequestBody
相似於 @RequestParam,能夠綁定實體類,不能夠接收 List、Map,通常不推薦使用
ModelAndView
ModelAndView 轉發到 hello.jsp 並攜帶信息
@RequestMapping("/hello")
public ModelAndView hello() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("hello");
modelAndView.addObject("msg", "轉發到 hello.jsp");
return modelAndView;
}
複製代碼
ModelAndView 構造方法
ModelAndView mv = new ModelAndView("redirect:/404.htm"); // 重定向
ModelAndView mv = new ModelAndView("forward:/404.htm"); // 請求轉發
複製代碼
重定向的方法還有返回 String, 如 return "redirect:/404.html",配合在參數中接收 Model 做爲參數,傳遞信息
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "消息~");
return "hello";
}
複製代碼
Model是每次請求中都存在的默認參數,利用其 addAttribute() 方法傳遞內容到頁面中
同時,咱們總可以在 Controller 的方法參數中使用 HttpServletRequest 和 HttpServletResponse 對象
@RequestMapping("/hello")
public String hello( HttpServletRequest request, HttpServletResponse response) {
request.setAttribute("msg", "消息~");
response.addCookie(new Cookie("c", "cookie~"));
return "hello";
}
複製代碼
@ResponseBody
@RespnseBody 將方法結果寫入字符串,返回給前端
@ResponseBody
@RequestMapping(value = "/hello")
public User hello(@RequestBody User user) {
return user;
}
複製代碼
返回結果是一個對象會通過轉換生成 JSON 格式的字符串,返回給前端
默認使用 Jackson 進行處理,pom 引入 Jackson 後,自動調用
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.2</version>
</dependency>
複製代碼
springmvc 中由 MultipartFile 接口實現文件上傳
MultipartFile 接口有兩個繼承實現類,CommonsMultipartFile,StandardMultipartFile
pom 中引入
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
複製代碼
在 Controller 方法參數中使用 MultipartFile 接收
@ResponseBody
@RequestMapping(value = "/ipload")
public String uploadExcel(@RequestParam("file") MultipartFile file, HttpServletRequest req) throws Exception {
if (file == null) return ;
String fileName = file.getOriginalFilename();
String path = req.getServletContext().getRealPath("/upload/");
// 獲取原文件名
String fileName = file.getOriginalFilename();
// 建立文件實例
File filePath = new File(path, fileName);
// 若是文件目錄不存在,建立目錄
if (!filePath.getParentFile().exists()) {
filePath.getParentFile().mkdirs();
}
// 寫入文件
file.transferTo(filePath);
return "success";
}
複製代碼
攔截器
自定義攔截器 Interceptor 實現 HandlerInterceptor 接口
public class MyInterceptor implements HandlerInterceptor {
// 進入接口方法前調用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 經過 handler 能夠獲取要訪問的目標接口及方法
// 進行相關權限判斷,返回 false 則不能進入接口執行
}
return true;
}
// 接口方法執行後,渲染視圖前調用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
// 視圖渲染完成後調用
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
複製代碼
<!-- 配置攔截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 配置攔截器做用的路徑 -->
<mvc:mapping path="/**" />
<!--攔截器方法-->
<bean class="cade.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
複製代碼
@ControllerAdvice
經過 @ControllerAdvice 配合 @ExceptionHandler 能夠對控制層的異常進行處理
@ControllerAdvice 能夠指定 basePackages,防止影響全局(如 swagger 不能正常使用)
@ControllerAdvice
public class ExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public String handleEx(Exception e) {
return "發生了異常:"+ e.getMessage();
}
}
複製代碼
繼承 ResponseBodyAdvice 接口
對 body 進行統一處理,如返回固定格式
@ControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// 返回 true,beforeBodyWrite 方法才生效
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 對 body 進行統一處理,如返回固定格式
return body;
}
}
複製代碼
使用 AOP
註解實現對接口方法的權限驗證
@Aspect
@Component
public class RoleHandler {
// 切入點:全部帶有 HasRole 註解的方法
@Pointcut("@annotation(cade.annotation.HasRole)")
public void needAuthMethod() {
}
@Before("needAuthMethod()")
public void before(JoinPoint jp) {
//
}
}
複製代碼
AOP 中獲取 Method 對象和其上的註解
MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
HasRole hasRole = method.getAnnotation(HasRole.class);
複製代碼
AOP 中獲取 requset、response 對象
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
複製代碼
AOP 獲取任意 Bean
// 方法一
// 自動注入依賴
@Autowired
ApplicationContext context;
// context.getBean() 獲取任意 Bean
// 方法二
@Autowired
ServletContext servletContext;
// 也能夠是使用 request.getServletContext() 獲取
// WebApplicationContextUtils.getWebApplicationContext(servletContext) 獲取 WebAppclicationContext
// getBean() 獲取任意 Bean
複製代碼