springboot教程-web(二)
擼了今年阿里、頭條和美團(tuán)的面試,我有一個(gè)重要發(fā)現(xiàn).......>>>
第一節(jié)
現(xiàn)在開始springboot-web開發(fā)教程。
引入依賴,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies> </project>spring-boot-starter-web已經(jīng)包含了spring-boot-starter依賴,因此只需引入這個(gè)依賴就可以了。
新建UserController.java
package com.edu.spring.springboot;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;@Controller public class UserController {@RequestMapping(value = "/user/home")@ResponseBodypublic String home() {return "user home";}}新建App.java
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}運(yùn)行App.java,則服務(wù)器正常運(yùn)行,默認(rèn)端口號(hào)是8080,通過瀏覽器訪問http://localhost:8080/user/home正常。
這樣最簡(jiǎn)單的web開發(fā)就完成了。
如果要修改端口,可以再application.properties中修改:
server.port=8081這樣端口號(hào)就修改成功了。
默認(rèn)的請(qǐng)求方式是:GET,POST,PUT方式都支持。我們可以限制他的請(qǐng)求方式:
方法一:
@RequestMapping(value = "/user/home", method = RequestMethod.GET)@ResponseBodypublic String home() {return "user home";}方法二:
使用GetMapping
@GetMapping("/user/show")@ResponseBodypublic String show() {return "user home";}@PostMapping("/user/create")@ResponseBodypublic String create() {return "user home";}GetMapping PostMapping等是spring4.3的新特性
如何傳遞參數(shù)
方法一:
修改UserController.java
@PostMapping("/user/create")@ResponseBodypublic String create(@RequestParam("username") String username, @RequestParam("password") String password) {return "user create, username: " + username + ", password: " + password;}@RequestParam注解默認(rèn)是參數(shù)必須提供,如果可以不提供可以使用required=false
可以提供一個(gè)默認(rèn)值defaultValue=""
方法二:
@GetMapping("/user/{id}")@ResponseBodypublic String show(@PathVariable("id") String id) {return "user home id: " + id;}方法三:
注入Servlet的api
@GetMapping("/user/edit")@ResponseBodypublic String edit(HttpServletRequest httpServletRequest){return "user edit: " + httpServletRequest.getRemoteHost();}我們發(fā)現(xiàn)每個(gè)方法都必須使用@ResponseBody來(lái)注釋。因此可以使用RestController來(lái)簡(jiǎn)化
新建RoleController.java
package com.edu.spring.springboot;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class RoleController {@GetMapping("/role/show")public String show(){return "role show ";} }@RestController 表明了當(dāng)前controller的方法的返回值可以直接用body輸出。
如何在springboot中使用jsp
新建LoginController.java
package com.edu.spring.springboot;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;@Controller public class LoginController {@PostMapping("/login")public String login(@RequestParam("username") String username, @RequestParam(value = "password") String password) {if (username.equals(password)) {return "ok";}return "fail";}}在main文件夾下面新建webapp,與java和resources文件夾并列。
修改application.properties
?
在webapp目錄下新建文件夾/WEB-INF/jsp,然后新建ok.jsp和fail.jsp
springboot默認(rèn)是不支持使用jsp的
?在springboot中使用jsp,需要引入依賴:
<dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency>這樣就可以成功訪問jsp了。
如何向jsp傳參數(shù)?
@GetMapping("/loginIndex")public String loginIndex(Model model) {model.addAttribute("username", "root");model.addAttribute("password", "123456");return "login";}新建login.jsp
<html> <h1>username; ${username}</h1> <h2>password: ${password}</h2> </html>在springboot中使用jsp時(shí),不能使用@RestController, 而要使用@Controller
如何在Jsp中使用模板?
添加pom.xml依賴:并且刪除jsp的依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency>在application.properties中刪除jsp的配置。
在springboot中使用freemarker的步驟:
1. 在pom中加入依賴,? ? ? ?
????????<dependency>
? ? ? ? ? ? <groupId>org.apache.tomcat.embed</groupId>
? ? ? ? ? ? <artifactId>tomcat-embed-jasper</artifactId>
? ? ? ? </dependency>
2. 默認(rèn)的freemaker的模板文件在classpath:/template/, 默認(rèn)的文件擴(kuò)展名為:ftl
新建AccountController.java
package com.edu.spring.springboot;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping;@Controller public class AccountController {@GetMapping("/reg")public String reg(){return "reg";}}在resources/template下新建reg.ftl
<h1>ftl reg </h1>可以通過訪問?http://192.168.170.132:8081/reg來(lái)獲取這個(gè)模板頁(yè)面了
如何修改模板文件的文件路徑
在application.properties中修改:
spring.freemarker.template-loader-path=classpath:/ftl/ 多個(gè)用逗號(hào)隔開在resources下新建ftl文件夾,然后將reg.ftl文件移動(dòng)到這個(gè)路徑下,就可以訪問了。
如何在模板文件中傳參數(shù)
在AccountController.java
@GetMapping("/logout")public String logout(Model model){model.addAttribute("username", "admin");model.addAttribute("logout", "true");return "logout";}在ftl目錄下新建logout.ftl文件:
<h1>logout</h1> <h2>username: ${username}</h2> <h2>logout is ${logout}</h2>這樣就傳遞參數(shù)到模板中了。
最好在項(xiàng)目中要么選擇模板,要么選擇jsp,不要二者都選。
Springboot默認(rèn)容器是Tomcat,如果想換成Jetty,如何做
首先需要把tomcat排除掉。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency>導(dǎo)入jetty依賴。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>其余都不需要改變,直接運(yùn)行,輸出:
2019-05-15 21:00:56.619 INFO 14692 --- [ main] o.e.jetty.server.handler.ContextHandler : Started o.s.b.w.e.j.JettyEmbeddedWebAppContext@37d3d232{application,/,[org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory$LoaderHidingResource@30c0ccff],AVAILABLE} 2019-05-15 21:00:56.619 INFO 14692 --- [ main] org.eclipse.jetty.server.Server : Started @2652ms 2019-05-15 21:00:56.776 INFO 14692 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-05-15 21:00:57.069 INFO 14692 --- [ main] o.e.j.s.h.ContextHandler.application : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-05-15 21:00:57.070 INFO 14692 --- [ main] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-05-15 21:00:57.075 INFO 14692 --- [ main] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms 2019-05-15 21:00:57.194 INFO 14692 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector@614aeccc{HTTP/1.1,[http/1.1]}{0.0.0.0:8081} 2019-05-15 21:00:57.196 INFO 14692 --- [ main] o.s.b.web.embedded.jetty.JettyWebServer : Jetty started on port(s) 8081 (http/1.1) with context path '/' 2019-05-15 21:00:57.198 INFO 14692 --- [ main] com.edu.spring.springboot.App : Started App in 2.8 seconds (JVM running for 3.23)說(shuō)明容器已經(jīng)變成jetty了。
添加項(xiàng)目名稱
默認(rèn)是不需要有項(xiàng)目名稱的,在application.properties文件中修改:
server.servlet.context-path=/mall在地址欄中,需要指定/mall才能訪問。例如:http://192.168.170.132:8081/mall/logout
第二節(jié)
如何在springboot中訪問靜態(tài)資源
1. src/main/webapp 下可以直接訪問
2. 默認(rèn)的靜態(tài)資源路徑是:classpath:[/META-INF/resources/, * /resources/, /static/, /public/] 源碼在org.springframework.boot.autoconfigure.web包中
3. 可以通過spring.resources.static-locations配置項(xiàng)修改默認(rèn)靜態(tài)資源路徑
方法一:
在src/main/webapp下新建user.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><h1>this is user page </h1> </body> </html>可以直接在瀏覽器訪問http://localhost:8080/user.html,說(shuō)明直接將html頁(yè)面放到webapp下面就可以直接訪問了。
在webapp下面新建目錄img,在img目錄中拷貝一張圖片進(jìn)去my.jpg,在user.html中添加圖片,<img src="img/my.jpg" />。這樣可以直接在user.html中訪問圖片了。
方法二:
在resources下新建文件夾public
在resources/public 下新建login.html,
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>login</title> </head> <body> this is login html page. 在public下 </body> </html>訪問http://localhost:8080/login.html?可以訪問成功。
在public下新建css文件夾,新建main.css
body {color: red; }在login.html中引入這個(gè)main.css文件
<link href="css/main.css" rel="stylesheet" />訪問login.html頁(yè)面可以成功訪問,字體顏色生效。
方法三:
在application.properties中添加:
spring.resources.static-locations=classpath:/html/在resources中新建文件夾html
然后在resources/html/中新建index.html頁(yè)面,
重啟以后可以直接訪問http://localhost:8080/index.html
如何在springboot中使用Servlet
新建UserServlet.java,并且繼承HTTPServlet
使用Servlet3.0注解
package com.edu.spring.springboot;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;@WebServlet("/user.do") public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().println("user servlet");} }修改App.java ,將Servlet添加到spring容器中。
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan;@ServletComponentScan @SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}運(yùn)行,可以通過瀏覽器訪問http://localhost:8080/user.do
如何在springboot容器中使用Servlet filter
新建LogFilter.java
package com.edu.spring.springboot;import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException;@WebFilter("/user.do") public class LogFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("income log filter " + servletRequest.getRemoteHost());filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {} }這個(gè)Filter可以攔截user.do請(qǐng)求。運(yùn)行訪問http://localhost:8080/user.do時(shí),控制臺(tái)輸出結(jié)果:
income log filter 0:0:0:0:0:0:0:1如何在springboot中使用Listener
新建MyContextListener.java
package com.edu.spring.springboot;import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import java.time.LocalDateTime;@WebListener public class MyContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("app start up at: " + LocalDateTime.now().toString());}@Overridepublic void contextDestroyed(ServletContextEvent sce) {} }這個(gè)監(jiān)聽器將監(jiān)聽?wèi)?yīng)用程序啟動(dòng)。啟動(dòng)程序時(shí),控制臺(tái)將會(huì)輸出:
app start up at: 2019-05-16T15:09:23.084如何不使用上述方法,實(shí)現(xiàn)Servlet的API
新建包c(diǎn)om.edu.spring.springboot.servlet,在這個(gè)包下面新建BookServlet.java
package com.edu.spring.springboot.servlet;import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;public class BookServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().println("book servlet output");} }在這個(gè)包下新建ServletConfiguration.java
package com.edu.spring.springboot.servlet;import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean;@SpringBootConfiguration public class ServletConfiguration {@Beanpublic ServletRegistrationBean createBookServlet() {ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new BookServlet(), "/book.do");return servletRegistrationBean;}}修改App.java
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}瀏覽器輸入http://localhost:8080/book.do返回結(jié)果正常
使用這個(gè)方法,不用在Servlet上使用注釋,也不用使用@ServletComponentScan注釋。
同理,可以使用這個(gè)方法使用Filter
在Servlet這個(gè)包下新建EchoFilter.java
package com.edu.spring.springboot.servlet;import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException;public class EchoFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;System.out.println("spring boot web filter " + httpServletRequest.getRequestURI());filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {} }在ServletConfiguration.java中添加bean
@Beanpublic FilterRegistrationBean createFilterRegistraionBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new EchoFilter());filterRegistrationBean.setUrlPatterns(Arrays.asList("/book.do"));return filterRegistrationBean;}瀏覽器上輸入http://localhost:8080/book.do,控制臺(tái)輸出:
spring boot web filter /book.do同理,新建StartUpListener.java
package com.edu.spring.springboot.servlet;import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener;public class StartUpListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("===========");System.out.println("application is started");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {} }在ServletConfiguration.java中添加bean
@Beanpublic ServletListenerRegistrationBean<StartUpListener> createServletListenerRegistrationBean() {ServletListenerRegistrationBean<StartUpListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean(new StartUpListener() );return servletListenerRegistrationBean;}運(yùn)行App.java,在應(yīng)用程序運(yùn)行開始,控制臺(tái)輸出:
=========== application is started總結(jié)
? ? springboot 中使用Servlet的API
方法一:? ??
? ? 1. 編寫Servlet,然后加上相應(yīng)的注解
? ? 2. 需要啟用@ServletComponentScan注解?
? ? servlet2.5以上版本 可以使用這種方法使用
? ? 這種方法更方便一些。
方法二:
? ? 1. 編寫Servlet,
? ? 2.? 裝配相應(yīng)的bean到spring容器中
????? ? Servlet ->?ServletRegistrationBean
????? ? Filter ->?FilterRegistrationBean
????? ? Listener -> ServletListenerRegistrationBean?
? ? Servlet2.5及以下版本可以使用這種方法
第三節(jié)
如何在springboot中使用攔截器
新建UserController.java
package com.edu.spring.springboot;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class UserController {@GetMapping("/user/home")public String home() {System.out.println("----user---home");return "user home";}}新建LogHandlerInterceptor.java
package com.edu.spring.springboot;import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public class LogHandlerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("=preHandle=====" + handler.getClass());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("=postHandle=====");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("=afterCompletion=====");} }新建WebConfiguration.java
package com.edu.spring.springboot;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configuration public class WebConfiguration extends WebMvcConfigurerAdapter {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LogHandlerInterceptor());} }或者:
package cn.ac.iie.authorization.config;import cn.ac.iie.authorization.interceptor.AuthorizationInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LogHandlerInterceptor());} }這里的@Configuration注釋可以替換為@SpringBootConfiguration
運(yùn)行App.java,然后在瀏覽器中輸入http://127.0.0.1:8080/user/home,正常顯示user home
控制臺(tái)輸出:
=preHandle=====class org.springframework.web.method.HandlerMethod ----user---home =postHandle===== =afterCompletion=====總結(jié):攔截器的使用步驟
? ? 1. 寫一個(gè)攔截器,實(shí)現(xiàn)HandlerInterceptor接口
? ? 2. 寫一個(gè)類,繼承WebvcConfigurereAdapter抽象類,然后重寫addInterceptors方法,并調(diào)用registry.addInterceptor把上一步的攔截器加進(jìn)去
HanderInterceptor
? ? 1. preHanle: controller執(zhí)行之前調(diào)用
? ? 2. postHandle: controller執(zhí)行之后,且頁(yè)面渲染之前調(diào)用
? ? 3. afterCompletion: 頁(yè)面渲染之后調(diào)用,一半用于資源清理操作
springboot開發(fā)中的異常處理?
將攔截器關(guān)閉,注釋W(xué)ebConfiguration.java中的@Configuration
在UserController.java中添加方法:
@GetMapping("/user/help")public String help() {throw new IllegalArgumentException("args is empty");}當(dāng)頁(yè)面請(qǐng)求/user/help的時(shí)候拋出異常,運(yùn)行App.java
瀏覽器輸入http://127.0.0.1:8080/user/help,瀏覽器顯示如下:
Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback.Sun May 19 22:03:18 CST 2019 There was an unexpected error (type=Internal Server Error, status=500). args is empty同時(shí)控制臺(tái)輸出:
java.lang.IllegalArgumentException: args is emptyat com.edu.spring.springboot.UserController.help(UserController.java:17) ~[classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_144]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_144]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_144]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_144]at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]如何使用我們自己的異常頁(yè)面?
方法一
默認(rèn)的異常頁(yè)面在ErrorMvcAutoConfiguration.java中定義,我們需要將這個(gè)類排除掉。
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class) public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}這時(shí),我們?nèi)绻跒g覽器中輸入一個(gè)不存在的網(wǎng)址時(shí)例如http://127.0.0.1:8080/user/help000,出現(xiàn)404的錯(cuò)誤。
如果在瀏覽器中輸入http://127.0.0.1:8080/user/help,出現(xiàn)500錯(cuò)誤頁(yè)面。
如何去掉springboot 默認(rèn)的異常處理邏輯?
@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)?如何使用自己的異常邏輯頁(yè)面?
在resoures下新建文件夾public,這時(shí)默認(rèn)的web頁(yè)面訪問路徑,在public文件夾下面新建404.html和500.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>404 not found</h1> </body> </html> <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>500 error</h1> </body> </html>新建CommonErrorPageRegistry.java
package com.edu.spring.springboot;import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.ErrorPageRegistrar; import org.springframework.boot.web.server.ErrorPageRegistry; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component;@Component public class CommonErrorPageRegistry implements ErrorPageRegistrar {@Overridepublic void registerErrorPages(ErrorPageRegistry registry) {ErrorPage e404 = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");ErrorPage e500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");registry.addErrorPages(e404, e500);} }瀏覽器中輸入http://127.0.0.1:8080/user/help000和http://127.0.0.1:8080/user/help分別跳轉(zhuǎn)到我們自定義的頁(yè)面
總結(jié):
? ? 使用ErrorPageRegistrar方法
? ? 寫一個(gè)類,實(shí)現(xiàn)ErrorPageRegistrar接口,然后實(shí)現(xiàn)registerErrorPage方法,在該方法里面,添加具體的錯(cuò)誤處理邏輯(類似web.xml有里面配置錯(cuò)誤處理方法)
?如果我們想單獨(dú)給IllegalArgumentException異常渲染一個(gè)頁(yè)面,如何做?
修改CommonErrorPageRegistry.java
package com.edu.spring.springboot;import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.ErrorPageRegistrar; import org.springframework.boot.web.server.ErrorPageRegistry; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component;@Component public class CommonErrorPageRegistry implements ErrorPageRegistrar {@Overridepublic void registerErrorPages(ErrorPageRegistry registry) {ErrorPage e404 = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");ErrorPage e500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");ErrorPage args = new ErrorPage(IllegalArgumentException.class, "/args.html");registry.addErrorPages(e404, e500, args);} }這樣IllegalArgumentException異常可以單獨(dú)頁(yè)面渲染了。
方法二:
首先將上一種方式屏蔽,將CommonErrorPageRegistry.java中的@Component注釋掉
新建BookController.java
package com.edu.spring.springboot;import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import java.io.FileNotFoundException;@RestController public class BookController {@ExceptionHandler(value = FileNotFoundException.class)public String error(Exception e) {return "file not found exception" + e.getMessage();}@GetMapping("/book/error1")public String error1() throws FileNotFoundException {throw new FileNotFoundException("book.txt not found");}@GetMapping("/book/error2")public String error2() throws ClassNotFoundException {throw new ClassNotFoundException("book.class not found");}}在BookController.java中定義當(dāng)前Controller中的異常,這個(gè)error方法將捕獲到FileNotFoundException并返回file not found exception,捕獲不到FileNotFound異常。并且這個(gè)只對(duì)當(dāng)前Controller生效。對(duì)UserController中的異常并不處理。
如果要對(duì)當(dāng)前Controller中的所有異常都捕獲,則@ExceptionHandler(value = Exception.class)
如何對(duì)所有Controller生效?
新建GlobalExceptionHandler.java
package com.edu.spring.springboot;import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)@ResponseBodypublic String errorHandler(Exception e) {return "global error " + e.getClass().getName();}}這樣就可以捕獲所有的Controller中的異常。
全局異常處理
? ? 1. 寫一個(gè)類,需要加上@ControllerAdvice注解
? ? 2. 寫一個(gè)異常處理方法,方法上面需要加上@ExceptionHandler(value=Exception.class)這個(gè)注解,然后在該方法里面處理異常
第四節(jié)
springboot如何定制和優(yōu)化內(nèi)嵌的Tomcat
springboot默認(rèn)集成了2種web容器分別是tomcat和jetty
新建UserController.java
package com.edu.spring.springboot;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class UserController {@GetMapping("/user/home")public String home(){return "user home";}}在application.properties中修改端口號(hào)
server.port=8081運(yùn)行應(yīng)用程序,在瀏覽器中輸入網(wǎng)址:http://127.0.0.1:8081/user/home和http://192.168.170.132:8081/user/home都可以訪問成功
在application.properties中添加:
server.port=8081 server.address=192.168.170.132運(yùn)行應(yīng)用程序,在瀏覽器中輸入http://127.0.0.1:8081/user/home就無(wú)法訪問了,說(shuō)明ip綁定成功。
可以啟用tomcat日志:
server.port=8081 server.address=192.168.170.132 server.tomcat.accesslog.enabled=true server.tomcat.accesslog.directory=F:/test如何通過代碼的方式配置tomcat
注釋application.properties中的內(nèi)容
新建MyEmbeddedServletContainerFactory.java
package com.edu.spring.springboot;import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class MyEmbeddedServletContainerFactory {@Beanpublic TomcatServletWebServerFactory servletContainer() {TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();tomcat.setPort(8081);return tomcat;} }同樣端口號(hào)修改為8081
設(shè)置tomcat連接數(shù)和線程數(shù):
package com.edu.spring.springboot;import org.apache.catalina.connector.Connector; import org.apache.catalina.valves.AccessLogValve; import org.apache.coyote.http11.Http11NioProtocol; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class MyEmbeddedServletContainerFactory {@Beanpublic TomcatServletWebServerFactory servletContainer() {TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();tomcat.setPort(8081);tomcat.addConnectorCustomizers(new MyTomcatConnectorCustomizer());return tomcat;}class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {@Overridepublic void customize(Connector connector) {Http11NioProtocol protocol=(Http11NioProtocol) connector.getProtocolHandler();//設(shè)置最大連接數(shù)protocol.setMaxConnections(2000);//設(shè)置最大線程數(shù)protocol.setMaxThreads(500);}} }添加tomcat日志,和404錯(cuò)誤重定向頁(yè)面
package com.edu.spring.springboot;import org.apache.catalina.connector.Connector; import org.apache.catalina.valves.AccessLogValve; import org.apache.coyote.http11.Http11NioProtocol; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.ErrorPage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus;@Configuration public class MyEmbeddedServletContainerFactory {@Beanpublic TomcatServletWebServerFactory servletContainer() {TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();tomcat.setPort(8081);tomcat.addContextValves(getLogAccessLogValve());tomcat.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,"/404.html"));tomcat.addInitializers(servletContext -> System.out.println("servlet start up =========="));tomcat.addConnectorCustomizers(new MyTomcatConnectorCustomizer());return tomcat;}private AccessLogValve getLogAccessLogValve(){AccessLogValve log = new AccessLogValve();log.setDirectory("F:/test");log.setEnabled(true);log.setPattern("common");log.setPrefix("springboot--");log.setSuffix(".txt");return log;}class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {@Overridepublic void customize(Connector connector) {Http11NioProtocol protocol=(Http11NioProtocol) connector.getProtocolHandler();//設(shè)置最大連接數(shù)protocol.setMaxConnections(2000);//設(shè)置最大線程數(shù)protocol.setMaxThreads(500);}} }總結(jié)
定制和優(yōu)化Tomcat,以編碼的方式設(shè)置Tomcat的各個(gè)屬性值,以及Tomcat的日志配置
TomcatServletWebServerFactory納入spring容器中管理
當(dāng)我們的springboot中沒有自定義的web容器,那么springboot使用自己的tomcat,如果我們自定義了容器,則使用我們自定義的tomcat。?原因如下:在org.springframework.boot.autoconfigure.web.embedded包下
@Configuration @ConditionalOnWebApplication @EnableConfigurationProperties(ServerProperties.class) public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {/*** Nested configuration if Tomcat is being used.*/@Configuration@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })public static class TomcatWebServerFactoryCustomizerConfiguration {@Beanpublic TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {return new TomcatWebServerFactoryCustomizer(environment, serverProperties);}}第五節(jié)
spring JDBC配置
引入pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.152.45:3306/renyuanku spring.datasource.username=root spring.datasource.password=123456在App.java中使用數(shù)據(jù)源
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);DataSource dataSource = configurableApplicationContext.getBean(DataSource.class);try {Connection connection = dataSource.getConnection();System.out.println(connection.getCatalog());connection.close();} catch (SQLException e) {e.printStackTrace();}}}運(yùn)行結(jié)果輸出數(shù)據(jù)庫(kù)名。
總結(jié):
? ? 裝配DataSource的步驟
? ? 1.? 加入數(shù)據(jù)庫(kù)驅(qū)動(dòng)
? ? 2.? 配置數(shù)據(jù)源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.152.45:3306/renyuanku spring.datasource.username=root spring.datasource.password=123456以上操作,springboot會(huì)自動(dòng)裝配好DataSource,JDBCTemplate,可以直接使用
?數(shù)據(jù)庫(kù)使用JDBCTemplate操作數(shù)據(jù)庫(kù)
新建ProductDao.java
package com.edu.spring.springboot;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository;@Repository public class ProductDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void addProduct(String id){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);}}修改App.java
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);DataSource dataSource = configurableApplicationContext.getBean(DataSource.class);try {Connection connection = dataSource.getConnection();System.out.println(connection.getCatalog());connection.close();ProductDao bean = configurableApplicationContext.getBean(ProductDao.class);bean.addProduct("123");} catch (SQLException e) {e.printStackTrace();}}}執(zhí)行App.java,查詢數(shù)據(jù)庫(kù),可以看到執(zhí)行成功
查看Springboot用的什么數(shù)據(jù)源
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);DataSource dataSource = configurableApplicationContext.getBean(DataSource.class);System.out.println(dataSource.getClass());}}輸出結(jié)果:
class com.zaxxer.hikari.HikariDataSource可以看到使用的是HikariDataSource數(shù)據(jù)源
如何使用其他數(shù)據(jù)源
在application.properties中配置
spring.datasource.type=可以指定具體使用哪種數(shù)據(jù)源,springboot默認(rèn)支持一下數(shù)據(jù)源,在類中org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,DataSourceJmxConfiguration.class })Hikari,tomcat,dbcp2,generic,放到classpath下
如何自己配置數(shù)據(jù)源
添加druid數(shù)據(jù)源依賴
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version></dependency>新建DBConfiguration.java
package com.edu.spring.springboot;import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment;import javax.sql.DataSource;@SpringBootConfiguration public class DBConfiguration {@Autowiredprivate Environment environment;@Beanpublic DataSource createDataSource() {DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setUrl(environment.getProperty("spring.datasource.url"));druidDataSource.setUsername(environment.getProperty("spring.datasource.username"));druidDataSource.setPassword(environment.getProperty("spring.datasource.password"));druidDataSource.setDriverClassName(environment.getProperty("spring.datasource.driver-class-name"));return druidDataSource;}}application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.152.45:3306/renyuanku spring.datasource.username=root spring.datasource.password=123456App.java
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);DataSource dataSource = configurableApplicationContext.getBean(DataSource.class);System.out.println(dataSource.getClass());}}運(yùn)行輸出:
class com.alibaba.druid.pool.DruidDataSource說(shuō)明數(shù)據(jù)源已經(jīng)變?yōu)镈ruid了。
springboot的特點(diǎn)是優(yōu)先使用自己的配置,然后使用spring默認(rèn)配置。
同樣可以使用JDBCTemplate
修改App.java
public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);DataSource dataSource = configurableApplicationContext.getBean(DataSource.class);try {Connection connection = dataSource.getConnection();System.out.println(connection.getCatalog());connection.close();ProductDao bean = configurableApplicationContext.getBean(ProductDao.class);bean.addProduct("124");} catch (SQLException e) {e.printStackTrace();}System.out.println(dataSource.getClass());}}成功插入數(shù)據(jù)124
Springboot對(duì)事務(wù)也做了很好的集成
修改ProductDao.java
package com.edu.spring.springboot;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional;@Repository public class ProductDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void addProduct(String id){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);}@Transactionalpublic void addProductBatch(String ...ids) throws FileNotFoundException {for(String id: ids){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);if("".equals("")) {throw new FileNotFoundException();}}}}使用事務(wù)需要在方法上添加注釋@Transactional
然后在App.java啟用事務(wù),添加注釋@EnableTransactionManagement
@SpringBootApplication @EnableTransactionManagement public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);DataSource dataSource = configurableApplicationContext.getBean(DataSource.class);try {Connection connection = dataSource.getConnection();System.out.println(connection.getCatalog());connection.close();ProductDao bean = configurableApplicationContext.getBean(ProductDao.class);try {bean.addProductBatch("111", "222", "333", "444", "555");} catch (FileNotFoundException e) {e.printStackTrace();}} catch (SQLException e) {e.printStackTrace();}System.out.println(dataSource.getClass());}}然后執(zhí)行,報(bào)異常,查詢數(shù)據(jù)庫(kù)發(fā)現(xiàn)存入了一條數(shù)據(jù) 111,說(shuō)明事務(wù)沒有生效。
原因是spring默認(rèn)會(huì)對(duì)運(yùn)行時(shí)的異常進(jìn)行事務(wù)的操作,而fileNotFound不是運(yùn)行時(shí)的異常,我們需要修改為RunTimeException。修改:
@Transactionalpublic void addProductBatch(String ...ids) throws FileNotFoundException {for(String id: ids){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);if("".equals("")) {throw new NullPointerException();}}}然后運(yùn)行App.java,報(bào)出異常,查詢數(shù)據(jù)庫(kù),沒有插入數(shù)據(jù),說(shuō)明事務(wù)生效。
事務(wù)
? ? 首先要使用@EnableTransactionManagement啟用對(duì)事務(wù)的支持
? ? 然后在需要使用事務(wù)的方法上面加上@Transactional
? ? 注意,默認(rèn)只會(huì)對(duì)運(yùn)行時(shí)異常進(jìn)行事務(wù)回滾,非運(yùn)行時(shí)異常不會(huì)回滾事務(wù)
?如何回滾非運(yùn)行時(shí)異常
使用@Transactional(rollbackFor = Exception.class)可以回滾所有異常
@Transactional(rollbackFor = Exception.class)public void addProductBatch(String ...ids) throws FileNotFoundException {for(String id: ids){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);if("".equals("")) {throw new FileNotFoundException();}}}如何不回滾某些異常
使用@Transactional(noRollbackFor = NullPointerException.class)
@Transactional(rollbackFor = Exception.class, noRollbackFor = NullPointerException.class)public void addProductBatch(String ...ids) throws Exception {for(String id: ids){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);if("".equals("")) {throw new NullPointerException();}}}注意:@Transactional必須要標(biāo)注在納入到spring容器管理bean的公有方法,例如:
@Transactional()public void addTest(String ...ids){add(ids);}@Transactional()private void add(String ...ids){for(String id: ids){String sql = "insert into test (id) values ("+ id + ")";jdbcTemplate.execute(sql);if("".equals("")) {throw new NullPointerException();}}}運(yùn)行App.java可以成功插入數(shù)據(jù)庫(kù),事務(wù)沒有生效。
注意:直接調(diào)用的方法必須要使用@Transactional注釋,否則不能回滾
第六節(jié)
SpringAOP
日志記錄、權(quán)限處理、監(jiān)控、異常處理
添加依賴pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency></dependencies> </project>新建包dao,在dao下面新建UserDao.java
package com.edu.spring.springboot.dao;import org.springframework.stereotype.Component;@Component public class UserDao {public void add (String username, String password){System.out.println("add: username:" + username + ",password:" + password);} }新建LogAspect.java
package com.edu.spring.springboot;import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;@Aspect @Component public class LogAspect {@Before("execution(* com.edu.spring.springboot.dao..*.*(..))")public void log() {System.out.println("method log done" );}}execution(* com.edu.spring.springboot.dao..*.*(..)) 表示織入到com.edu.spring.springboot.dao及其子包下面的所有的類的所有的方法。
執(zhí)行的時(shí)機(jī)就是,前置執(zhí)行。
App.java
package com.edu.spring.springboot;import com.edu.spring.springboot.dao.UserDao; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);configurableApplicationContext.getBean(UserDao.class).add("admin", "123456");configurableApplicationContext.close();}}運(yùn)行結(jié)果:
method log done add: username:admin,password:123456這是一個(gè)最簡(jiǎn)單的AOP。
AOP開發(fā)流程
? ? 1.?spring-boot-starter-aop加入依賴,默認(rèn)開啟了AOP的支持
? ? 2. 寫一個(gè)Aspect,封裝橫切關(guān)注點(diǎn)(日志,監(jiān)控等等),需要配置通知(前置通知,后置通知等等)和切入點(diǎn)(哪些包的哪些類的哪些方法等等);
? ? 3. 這個(gè)Aspect需要納入到spring容器管理,并且需要加入@Aspect注解
在application.properties中配置:
spring.aop.auto=false表示不啟用aop,默認(rèn)是為true啟用,運(yùn)行App.java,結(jié)果如下:
add: username:admin,password:123456在application.properties中配置:
spring.aop.auto=true spring.aop.proxy-target-class=falsespring.aop.proxy-target-class默認(rèn)是true,false表示使用的是JDK的動(dòng)態(tài)代理,true表示使用CGLIB的動(dòng)態(tài)代理
JDK的動(dòng)態(tài)代理需要一個(gè)接口
新建IUserDao.java
package com.edu.spring.springboot.dao;public interface IUserDao {public void add (String username, String password); }然后讓UserDao實(shí)現(xiàn)這個(gè)接口,修改App.java
package com.edu.spring.springboot;import com.edu.spring.springboot.dao.UserDao; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);System.out.println(configurableApplicationContext.getBean(UserDao.class).getClass());configurableApplicationContext.getBean(UserDao.class).add("admin", "123456");configurableApplicationContext.close();}}運(yùn)行報(bào)錯(cuò):
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.edu.spring.springboot.dao.UserDao' availableat org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)at com.edu.spring.springboot.App.main(App.java:12)原因是基于JDK的動(dòng)態(tài)代理之后,就不能根據(jù)class來(lái)獲取對(duì)象,需要根據(jù)接口來(lái)獲取對(duì)象。
@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);System.out.println(configurableApplicationContext.getBean(IUserDao.class).getClass());configurableApplicationContext.getBean(IUserDao.class).add("admin", "123456");configurableApplicationContext.close();}}運(yùn)行輸出結(jié)果如下:
class com.sun.proxy.$Proxy55 method log done add: username:admin,password:123456這是典型的動(dòng)態(tài)代理。
將spring.aop.proxy-target-class改為true,運(yùn)行結(jié)果如下:
class com.edu.spring.springboot.dao.UserDao$$EnhancerBySpringCGLIB$$62d64f2d method log done add: username:admin,password:123456總結(jié):
? ? aop默認(rèn)是使用基于JDK的動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP,默認(rèn)啟用
????spring.aop.proxy-target-class=true或者不配置,表示使用cglib的動(dòng)態(tài)代理,
? ? =false表示jdk動(dòng)態(tài)代理
? ? 如果配置了false,而類沒有借口,則依然使用cglib
將application.properties中的配置注釋掉。
如何得到aop相關(guān)參數(shù)
修改LogAspect.java
package com.edu.spring.springboot;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;import java.util.Arrays;@Aspect @Component public class LogAspect {@Before("execution(* com.edu.spring.springboot.dao..*.*(..))")public void log() {System.out.println("before method log done" );}@After("execution(* com.edu.spring.springboot.dao..*.*(..))")public void logAfter(JoinPoint point) {System.out.println("before method log done" + point.getTarget().getClass() + ", args="+ Arrays.asList(point.getArgs()) + ", method=" + point.getSignature().getName());}}輸出結(jié)果如下:
class com.edu.spring.springboot.dao.UserDao$$EnhancerBySpringCGLIB$$abbff7d9 before method log done add: username:admin,password:123456 before method log doneclass com.edu.spring.springboot.dao.UserDao, args=[admin, 123456]雖然springboot默認(rèn)支持了AOP,但是springboot依然提供了enable的注解,@EnableAspectJAutoProxy
第七節(jié) Springboot starter
新建RedisProperties.java
package com.edu.spring.springboot;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "redis") public class RedisProperties {private String host;private Integer port;public String getHost() {return host;}public void setHost(String host) {this.host = host;}public Integer getPort() {return port;}public void setPort(Integer port) {this.port = port;} }新建RedisConfiguration.java
package com.edu.spring.springboot;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.Jedis;@Configuration @ConditionalOnClass(Jedis.class) @EnableConfigurationProperties(RedisProperties.class) public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic Jedis jedis(RedisProperties redisProperties){Jedis jedis = new Jedis(redisProperties.getHost(), redisProperties.getPort());System.out.println("springbourse bean" + jedis);return jedis;} }這樣的話spring容器在裝配Jedis這個(gè)bean的時(shí)候會(huì)先從容器中獲取RedisProperties這個(gè)bean,然后傳到這個(gè)方法中去。
@ConditionalOnClass(Jedis.class)表示裝配這個(gè)bean的時(shí)候Jedis.class這個(gè)類一定要存在。
@ConditionalOnMissingBean表示沒有這個(gè)Jedis這個(gè)類的時(shí)候,我們才裝配。
新建項(xiàng)目spring-course-redis,將上面的項(xiàng)目加到這個(gè)項(xiàng)目中去:pom.xml如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course-redis</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>加入好依賴以后,在spring-course-redis項(xiàng)目中我們可以直接從容器中獲取jedis了,App.java如下:
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import redis.clients.jedis.Jedis;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);Jedis jedis = configurableApplicationContext.getBean(Jedis.class);System.out.println("springbourseredis bean" + jedis);jedis.set("id", "vincent");System.out.println(jedis.get("id"));}}新建application.properties,內(nèi)容如下:
redis.host=192.168.152.45 redis.port=6379運(yùn)行App.java輸出如下:
springbourse beanredis.clients.jedis.Jedis@60bdf15d springbourseredis beanredis.clients.jedis.Jedis@60bdf15d vincent說(shuō)明已經(jīng)成功注入進(jìn)去了。
但是在springboot1.X版本中是無(wú)法直接這樣使用的。
解決方法有兩種,
方法一:
? ? 在springbootcourse項(xiàng)目中,新建EnableRedis.java,需要使用@Import注解
package com.edu.spring.springboot;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(RedisAutoConfiguration.class) public @interface EnableRedis { }? ? 在springbootcourseredis項(xiàng)目中,添加@EnableRedis注解
@EnableRedis @SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);Jedis jedis = configurableApplicationContext.getBean(Jedis.class);System.out.println("springbourseredis bean" + jedis);jedis.set("id", "vincent");System.out.println(jedis.get("id"));}}方法二:
使用spring.factories
在springbootcourse項(xiàng)目中在resources目錄下,新建/META-INF文件夾,然后在這個(gè)文件夾下新建spring.factories文件,內(nèi)容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.edu.spring.springboot.RedisAutoConfiguration總結(jié)
? ? springboot2.x 可以直接使用
? ? springboot1.x 需要進(jìn)行配置。
自己開發(fā)一個(gè)spring boot? starter的步驟
1. 新建一個(gè)項(xiàng)目
2. 需要一個(gè)配置類,配置類里面需要裝配好需要提供出去的類
3. 使用
? ? (1)@Enable ,使用@Import導(dǎo)入需要裝配的類
? ? (2)/META-INF/spring.factories, 在org.springframework.boot.autoconfigure.EnableAutoConfiguration配置需要裝配的類
第八節(jié) springboot日志
默認(rèn)的日志輸出結(jié)果如下:
2019-05-26 14:29:27.650 INFO 641 --- [ main] com.edu.spring.springboot.App : Starting App on duandingyangdeMacBook-Pro.local with PID 641 (/Users/duandingyang/git-project/springcourse/target/classes started by duandingyang in /Users/duandingyang/git-project/springcourse) 2019-05-26 14:29:27.654 INFO 641 --- [ main] com.edu.spring.springboot.App : No active profile set, falling back to default profiles: default 2019-05-26 14:29:28.804 INFO 641 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-05-26 14:29:28.834 INFO 641 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-05-26 14:29:28.835 INFO 641 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17] 2019-05-26 14:29:28.931 INFO 641 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-05-26 14:29:28.931 INFO 641 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1230 ms 2019-05-26 14:29:29.203 INFO 641 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-05-26 14:29:29.409 INFO 641 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-05-26 14:29:29.416 INFO 641 --- [ main] com.edu.spring.springboot.App : Started App in 2.638 seconds (JVM running for 3.64)日志級(jí)別為Info ,進(jìn)程ID(PID)641 , 線程名字main,所在類,日志內(nèi)容。
新建dao包,然后在這個(gè)dao包下新建UserDao.java
package com.edu.spring.springboot.dao;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;@Component public class UserDao {private Logger logger = LoggerFactory.getLogger(UserDao.class);public void log() {logger.debug("user dao debug log");logger.info("user dao info log");logger.warn("user dao warn log");logger.error("user dao error log");} }App.java內(nèi)容如下:
package com.edu.spring.springboot;import com.edu.spring.springboot.dao.UserDao; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(App.class, args);run.getBean(UserDao.class).log();run.close();}}運(yùn)行輸出結(jié)果如下:
2019-05-26 14:35:24.170 INFO 787 --- [ main] com.edu.spring.springboot.dao.UserDao : user dao info log 2019-05-26 14:35:24.170 WARN 787 --- [ main] com.edu.spring.springboot.dao.UserDao : user dao warn log 2019-05-26 14:35:24.170 ERROR 787 --- [ main] com.edu.spring.springboot.dao.UserDao : user dao error log說(shuō)明日志的默認(rèn)級(jí)別是info。
如何調(diào)整日志級(jí)別?
方法一:
修改application.properties
logging.level.*=DEBUG可以通過logging.level.*=debug 來(lái)設(shè)置,* 可以是包,也可以是某個(gè)類。
方法二
在program arguments中設(shè)置--debug,也可以啟用DEBUG,但是這種方式無(wú)法輸出我們自己的DEBUG信息,只可以輸出Spring的debug
新建service包,然后在這個(gè)包下新建UserService.java
package com.edu.spring.springboot.service;import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class UserService {private Logger logger = LoggerFactory.getLogger(UserService.class);public void log() {logger.debug("user service debug log");logger.info("user service info log");logger.warn("user service warn log");logger.error("user service error log");} }如果我們只想在service包下面使用debug,則需要修改application.properties內(nèi)容:
logging.level.com.edu.spring.springboot.service=DEBUG在App.java中添加
run.getBean(UserService.class).log();輸出結(jié)果如下:
2019-05-26 14:54:20.149 INFO 843 --- [ main] com.edu.spring.springboot.dao.UserDao : user dao info log 2019-05-26 14:54:20.149 WARN 843 --- [ main] com.edu.spring.springboot.dao.UserDao : user dao warn log 2019-05-26 14:54:20.149 ERROR 843 --- [ main] com.edu.spring.springboot.dao.UserDao : user dao error log 2019-05-26 14:54:20.149 DEBUG 843 --- [ main] c.e.s.springboot.service.UserService : user service debug log 2019-05-26 14:54:20.149 INFO 843 --- [ main] c.e.s.springboot.service.UserService : user service info log 2019-05-26 14:54:20.149 WARN 843 --- [ main] c.e.s.springboot.service.UserService : user service warn log 2019-05-26 14:54:20.149 ERROR 843 --- [ main] c.e.s.springboot.service.UserService : user service error logservice啟用了debug,dao默認(rèn)的info
日志級(jí)別有:trace,debug,info,warn,error,fatal,off
日至級(jí)別off表示關(guān)閉日志
如何配置日志輸出文件?
application.properties
logging.file=/Users/vincent/my.log指定日志文件路徑與名字。
logging.path 也可以指定日志的路徑,此時(shí)名字為spring.log
日志文件輸出,文件的大小10M之后,就會(huì)分割了
如何指輸出日志格式
logging.pattern.console=%-20(%d{yyy-MM-dd} [%thread]) %-5level %logger{80} - %msg%n logging.file.console=%-20(%d{yyy-MM-dd HH:mm:ss.SSS} [%thread]) %-5level %logger{80} - %msg%n分別為控制臺(tái)的日志輸出格式和文件日志輸出格式
使用logback
在resources下新建logback.xml
<?xml version="1.0" encoding="utf-8" ?> <configuration><appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%-20(%d{yyy-MM-dd HH:mm:ss.SSS} [%thread]) %-5level %logger{80} - %msg%n</pattern></layout></appender><root level="debug"><appender-ref ref="consoleLog" /></root> </configuration>springboot 默認(rèn)支持logback,也就是說(shuō),只需要在classpath下放一個(gè)logback.xml或者logback-spring.xml的文件,即可定制日志的輸出。
如何使用log4j2
現(xiàn)將默認(rèn)的日志排除,并且加入log4j依賴,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency></dependencies> </project>在resources目錄下新建log4j2.xml
<?xml version="1.0" encoding="utf-8" ?> <configuration><appenders><Console name="console" target="SYSTEM_OUT" follow="true"><PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n" /></Console></appenders><loggers><root level="DEBUG"><appender-ref ref="console" /></root></loggers> </configuration>運(yùn)行App.java,輸出結(jié)果為:
2019-05-27 23:28:51.808 [main] DEBUG com.edu.spring.springboot.dao.UserDao - user dao debug log 2019-05-27 23:28:51.808 [main] INFO com.edu.spring.springboot.dao.UserDao - user dao info log 2019-05-27 23:28:51.808 [main] WARN com.edu.spring.springboot.dao.UserDao - user dao warn log 2019-05-27 23:28:51.808 [main] ERROR com.edu.spring.springboot.dao.UserDao - user dao error log 2019-05-27 23:28:51.808 [main] DEBUG com.edu.spring.springboot.service.UserService - user service debug log 2019-05-27 23:28:51.808 [main] INFO com.edu.spring.springboot.service.UserService - user service info log 2019-05-27 23:28:51.808 [main] WARN com.edu.spring.springboot.service.UserService - user service warn log 2019-05-27 23:28:51.808 [main] ERROR com.edu.spring.springboot.service.UserService - user service error log說(shuō)明log4j2配置成功。當(dāng)然了,log4j2.xml 文件名也可以改為log4j2-spring.xml
使用其他的日志組件的步驟1:排除掉默認(rèn)的日志組件spring-boot-starter-logging2:加入新的日志組件依賴3:把相應(yīng)的日志文件加到classpath下?springboot 的相關(guān)日志源碼在org.springframework.boot.logging包下面,其中LogLevel.java定義了日志級(jí)別,LoggingSystemProperties定義了日志配置項(xiàng)。
第九節(jié) springboot監(jiān)控和度量
添加依賴pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId></dependency></dependencies> </project>? ? spring boot2.x中,默認(rèn)只開放了info、health兩個(gè)端點(diǎn),其余的需要自己通過配置management.endpoints.web.exposure.include屬性來(lái)加載(有include自然就有exclude)。如果想單獨(dú)操作某個(gè)端點(diǎn)可以使用management.endpoint.端點(diǎn).enabled屬性進(jìn)行啟用或者禁用。
Endpoints
????Actuator endpoints 允許你去監(jiān)控和操作你的應(yīng)用。SpringBoot包含了許多內(nèi)置的端點(diǎn),當(dāng)然你也可以添加自己的端點(diǎn)。比如 health 端點(diǎn)就提供了基本的應(yīng)用健康信息。
Metrics
????Spring Boot Actuator 提供 dimensional metrics 通過集成 Micrometer.
Audit
????Spring Boot Actuator 有一套靈活的審計(jì)框架會(huì)發(fā)布事件到 AuditEventRepository。
springboot 2.x 默認(rèn)只啟動(dòng)了 health 和 info 端點(diǎn),可以通過 application.properties 配置修改:
management.endpoints.web.exposure.include=health,info,env,metrics項(xiàng)目啟動(dòng)時(shí)可以看到暴露出來(lái)的接口信息:
2019-05-30 18:47:35.162 INFO 9888 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoint(s) beneath base path '/actuator'瀏覽器中輸入http://localhost:8080/actuator/,結(jié)果如下:
{"_links": {"self": {"href": "http://localhost:8080/actuator","templated": false},"health-component": {"href": "http://localhost:8080/actuator/health/{component}","templated": true},"health-component-instance": {"href": "http://localhost:8080/actuator/health/{component}/{instance}","templated": true},"health": {"href": "http://localhost:8080/actuator/health","templated": false},"env": {"href": "http://localhost:8080/actuator/env","templated": false},"env-toMatch": {"href": "http://localhost:8080/actuator/env/{toMatch}","templated": true},"info": {"href": "http://localhost:8080/actuator/info","templated": false},"metrics": {"href": "http://localhost:8080/actuator/metrics","templated": false},"metrics-requiredMetricName": {"href": "http://localhost:8080/actuator/metrics/{requiredMetricName}","templated": true}} }瀏覽器中輸入:http://localhost:8080/actuator/metrics/system.cpu.usage,結(jié)果如下:
{"name": "system.cpu.usage","description": "The \"recent cpu usage\" for the whole system","baseUnit": null,"measurements": [{"statistic": "VALUE","value": 0.24420139608387495}],"availableTags": [] }常用的endpoint:
| GET | /autoconfig | 查看自動(dòng)配置的使用情況 |
| GET | /configprops | 查看配置屬性,包括默認(rèn)配置 |
| GET | /beans | 查看bean及其關(guān)系列表 |
| GET | /dump | 打印線程棧 |
| GET | /env | 查看所有環(huán)境變量 |
| GET | /env/{name} | 查看具體變量值 |
| GET | /health | 查看應(yīng)用健康指標(biāo) |
| GET | /info | 查看應(yīng)用信息 |
| GET | /mappings | 查看所有url映射 |
| GET | /metrics | 查看應(yīng)用基本指標(biāo) |
| GET | /metrics/{name} | 查看具體指標(biāo) |
| POST | /shutdown | 關(guān)閉應(yīng)用 |
| GET | /trace | 查看基本追蹤信息 |
想要查看服務(wù)器的健康狀態(tài)詳細(xì)信息,需要配置application.properties
management.endpoint.health.show-details=always這樣就可以查看健康狀態(tài)的詳細(xì)信息了,例如磁盤利用情況,數(shù)據(jù)庫(kù)情況。
如何自定義健康狀態(tài)檢查?
新建MyHealthIndicator.java
package com.edu.spring.springboot;import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component;@Component public class MyHealthIndicator implements HealthIndicator {@Overridepublic Health health() {return Health.up().withDetail("error","springboot error").build();} }在瀏覽器中輸入http://localhost:8080/actuator/health可以看到自定義的健康狀態(tài)監(jiān)控。
總結(jié):
? ? 自定義健康狀態(tài)監(jiān)測(cè),實(shí)現(xiàn)HealthIndicator接口,并納入spring容器的管理之中。
使用info,
在application.properties中使用info開頭的信息都可以顯示出來(lái),例如:
info.name=myinfo info.datasource.driver-class-name=com.mysql.cj.jdbc.Driver在瀏覽器中輸入,http://localhost:8080/actuator/info?可以查看到這些配置信息。
可以對(duì)git信息進(jìn)行監(jiān)控。
Prometheus Grafana實(shí)現(xiàn)應(yīng)用可視化監(jiān)控
第十節(jié) 打包springboot
pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.1.4.RELEASE</version><configuration><mainClass>com.edu.spring.springboot.App</mainClass></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>一定要指定mainClass才可以
springboot 測(cè)試
添加pom.xml依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.1.4.RELEASE</version><configuration><mainClass>com.edu.spring.springboot.App</mainClass></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>新建UserDao.java
package com.edu.spring.springboot.dao;import org.springframework.stereotype.Repository;@Repository public class UserDao {public Integer addUser(String username) {System.out.println("user dao adduser " + username);if(username == null) {return 0;}return 1;}}在Intellij下Ctrl + Shift + T 新建測(cè)試類UserDaoTest.java
package com.edu.spring.springboot.dao;import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;import static org.junit.Assert.*;@RunWith(SpringRunner.class) @SpringBootTest public class UserDaoTest {@Autowiredprivate UserDao userDao;@Testpublic void addUser() {Assert.assertEquals(Integer.valueOf(1), userDao.addUser("root"));Assert.assertEquals(Integer.valueOf(0), userDao.addUser(null));} }輸出結(jié)果如下:
user dao adduser root user dao adduser nullspringboot測(cè)試步驟,
? ? 直接在測(cè)試類上面添加下面的注解:
????????@RunWith(SpringRunner.class)
????????@SpringBootTest
Test下的包名和java下的包名應(yīng)該一致。
如何測(cè)試bean?
新建User.java并且納入到spring容器管理中去。
package com.edu.spring.springboot.bean;import org.springframework.stereotype.Component;@Component public class User {}新建測(cè)試類ApplicationContextTest.java
package com.edu.spring.springboot.dao;import com.edu.spring.springboot.bean.User; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class) @SpringBootTest public class ApplicationContextTest {@Autowiredprivate ApplicationContext applicationContext;@Testpublic void testNull() {Assert.assertNotNull(applicationContext.getBean(User.class));}}輸出結(jié)果可以顯示,輸出正常。
如何在測(cè)試類中自定義一個(gè)bean?
在src/test/java/com/edu/springboot/dao下新建TestBeanConfiguration.java
package com.edu.spring.springboot.dao;import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean;@TestConfiguration public class TestBeanConfiguration {@Beanpublic Runnable createRunnable() {return () -> {};}}然后在ApplicationContextTest.java 指定classes
package com.edu.spring.springboot.dao;import com.edu.spring.springboot.bean.User; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class) @SpringBootTest(classes = TestBeanConfiguration.class) public class ApplicationContextTest {@Autowiredprivate ApplicationContext applicationContext;@Testpublic void testNull() {Assert.assertNotNull(applicationContext.getBean(User.class));Assert.assertNotNull(applicationContext.getBean(Runnable.class));}}指定classes=TestBeanConfiguration.class就可以使用這個(gè)bean了。
使用@TestConfiguration可以在測(cè)試環(huán)境下指定bean。如果在App.java 中使用這個(gè)bean,那么會(huì)報(bào)錯(cuò),找不到這個(gè)bean。
只有在測(cè)試環(huán)境下有效。
? ? 測(cè)試環(huán)境下,只能用@TestConfiguration,不能用@Configuration
如何環(huán)境測(cè)試?
在test/java/com/edu/spring/springboot/dao下 新建EnvTest.java?
package com.edu.spring.springboot.dao;import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.Environment; import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class) @SpringBootTest public class EnvTest {@Autowiredprivate Environment environment;@Testpublic void testValue() {Assert.assertEquals("myapplication", environment.getProperty("spring.application.name"));}}在main/resources/application.properties內(nèi)容如下:
spring.application.name=myapplication測(cè)試運(yùn)行正常。
如果我們的application.properties在test/resources/下怎么辦?
在test下新建文件夾resources,然后在test/resources/目錄下新建application.properties,內(nèi)容如下:
spring.application.name=myapplication-test然后運(yùn)行EnvTest.java文件,報(bào)錯(cuò):
Expected :myapplication Actual :myapplication-test說(shuō)明這里面的配置文件,優(yōu)先去取test/resources中的application.properties,如果沒有這個(gè)配置文件,則去main/resoures中去取。
在測(cè)試環(huán)境中,springboot會(huì)優(yōu)先加載測(cè)試環(huán)境下的配置文件(application.properties)
測(cè)試環(huán)境下沒有,才會(huì)加載正式環(huán)境下的配置文件。
測(cè)試環(huán)境中自定義指定配置項(xiàng)?
(properties = {"app.version=1.0.0"}):
package com.edu.spring.springboot.dao;import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.Environment; import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class) @SpringBootTest(properties = {"app.version=1.0.0"}) public class EnvTest {@Autowiredprivate Environment environment;@Testpublic void testValue() {Assert.assertEquals("myapplication-test", environment.getProperty("spring.application.name"));Assert.assertEquals("1.0.0", environment.getProperty("app.version"));}}運(yùn)行成功。
Mock如何測(cè)試接口?
新建mapper包,在包下新建UserMapper.java
package com.edu.spring.springboot.mapper;public interface UserMapper {Integer createUser(String username); }在Test下,新建UserDaoTest.java
package com.edu.spring.springboot.mapper;import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) public class UserMapperTest {@MockBeanprivate UserMapper userMapper;@Test(expected = NullPointerException.class)public void createUser() {BDDMockito.given(userMapper.createUser("admin")).willReturn(Integer.valueOf(1));BDDMockito.given(userMapper.createUser("")).willReturn(Integer.valueOf(0));BDDMockito.given(userMapper.createUser(null)).willThrow(NullPointerException.class);Assert.assertEquals(Integer.valueOf(1), userMapper.createUser("admin"));Assert.assertEquals(Integer.valueOf(0), userMapper.createUser(""));Assert.assertEquals(Integer.valueOf(0), userMapper.createUser(null));} }因?yàn)榻涌诓]有實(shí)現(xiàn)類,因此需要做提前預(yù)測(cè),BDDMockito.given就是這個(gè)功能。當(dāng)user.createUser()的輸入時(shí)admin時(shí),返回整型1,當(dāng)user.createUser()的輸入是“”時(shí),返回整型0,當(dāng)user.createUser()的輸入時(shí)null時(shí),返回異常。
mock方法可以卸載init方法中,如下所示:
package com.edu.spring.springboot.mapper;import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) public class UserMapperTest {@MockBeanprivate UserMapper userMapper;@Beforepublic void init() {BDDMockito.given(userMapper.createUser("admin")).willReturn(Integer.valueOf(1));BDDMockito.given(userMapper.createUser("")).willReturn(Integer.valueOf(0));BDDMockito.given(userMapper.createUser(null)).willThrow(NullPointerException.class);}@Test(expected = NullPointerException.class)public void createUser() {Assert.assertEquals(Integer.valueOf(1), userMapper.createUser("admin"));Assert.assertEquals(Integer.valueOf(0), userMapper.createUser(""));Assert.assertEquals(Integer.valueOf(0), userMapper.createUser(null));} }對(duì)Controller進(jìn)行測(cè)試
方法一:
新建BookController.java
package com.edu.spring.springboot.controller;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class BookController {@GetMapping("/book/home")public String home() {System.out.println("/book/home url is invoke");return "book home";}}新建測(cè)試類BookControllerTest.java
package com.edu.spring.springboot.controller;import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.context.junit4.SpringRunner;import static org.junit.Assert.*;@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class BookControllerTest {@Autowiredprivate TestRestTemplate testRestTemplate;@Testpublic void home() {String forObject = testRestTemplate.getForObject("/book/home", String.class);Assert.assertEquals("book home", forObject);} }TestRestTemplate 需要在web環(huán)境中,因此需要SpringBootTest.WebEnvironment.RANDOM_PORT
?在BookController.java中添加方法,測(cè)試有參數(shù)的Controller方法:
@GetMapping("/book/show")public String show(@RequestParam("id") String id) {System.out.println("/book/show url is invoke");return "book" + id;}在測(cè)試方法中:
@Testpublic void show() {String forObject = testRestTemplate.getForObject("/book/show?id=100", String.class);Assert.assertEquals("book100", forObject);}方法二:
新建測(cè)試類BookControllerTest2.java
package com.edu.spring.springboot.controller;import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvcBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers;@RunWith(SpringRunner.class) @WebMvcTest(controllers = BookController.class) public class BookControllerTest2 {@Autowiredprivate MockMvc mockMvc;@Testpublic void home() throws Exception {mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.status().isOk());mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("book home"));}}@WebMvcTest 不需要運(yùn)行在web環(huán)境下,但是,需要指定controllers,表示需要測(cè)試哪些controller
?修改BookController.java,使用UserDao
package com.edu.spring.springboot.controller;import com.edu.spring.springboot.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;@RestController public class BookController {@Autowiredprivate UserDao userDao;@GetMapping("/book/home")public String home() {System.out.println("/book/home url is invoke");return "book home";}@GetMapping("/book/show")public String show(@RequestParam("id") String id) {System.out.println("/book/show url is invoke");userDao.addUser("aaa");return "book" + id;}}報(bào)錯(cuò)信息如下:
java.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125) ~[spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108) ~[spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118) ~[spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) ~[spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44) ~[spring-boot-test-autoconfigure-2.1.4.RELEASE.jar:2.1.4.RELEASE]at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246) ~[spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) [junit-rt.jar:na]at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) [junit-rt.jar:na]at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) [junit-rt.jar:na]at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) [junit-rt.jar:na] Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookController': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.edu.spring.springboot.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]說(shuō)沒有UserDao對(duì)象。因?yàn)槭褂?#64;WebMvcTest方式測(cè)試,不會(huì)加載整個(gè)spring容器,只能測(cè)試controller。Controller里面的一些依賴,需要自己去mock。
但是使用@SpringBootTest方式是可以把整個(gè)spring容器加載進(jìn)來(lái)的。但不能和@WebMvcTest同時(shí)使用
如果要使用@SpringBootTest,則無(wú)法使用MockMVC,如果要是用MockMvc需要添加@AutoConfigureMockMvc,如下所示
package com.edu.spring.springboot.controller;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers;@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class BookControllerTest3 {@Autowiredprivate MockMvc mockMvc;@Testpublic void home() throws Exception {mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.status().isOk());mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("book home"));}@Testpublic void show() throws Exception {mockMvc.perform(MockMvcRequestBuilders.get("/book/show").param("id", "400")).andExpect(MockMvcResultMatchers.status().isOk());mockMvc.perform(MockMvcRequestBuilders.get("/book/show").param("id", "400")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("book400"));}}?
總結(jié)
以上是生活随笔為你收集整理的springboot教程-web(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot教程(一)
- 下一篇: intellij2019.1 破jie不