日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

springboot教程-web(二)

發(fā)布時(shí)間:2024/9/16 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot教程-web(二) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

擼了今年阿里、頭條和美團(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
?

spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp

在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=123456

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);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=false

spring.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 log

service啟用了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:

HTTP方法路徑描述
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 null

springboot測(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

久久久久国产精品一区二区 | 亚洲性少妇性猛交wwww乱大交 | 国产一区二区视频在线 | 色综合久久五月天 | 国产免费小视频 | 五月激情片 | 国产视频在线观看一区 | 婷婷在线色 | 国产伦理久久 | 九九99靖品 | 99精品免费 | 亚洲精品永久免费视频 | 日韩中文在线观看 | 丁香花中文在线免费观看 | 日韩一区二区三区高清在线观看 | 免费一级特黄录像 | 成年人免费电影 | 成人禁用看黄a在线 | 国产一级片播放 | 久久久久久久影院 | 色先锋av资源中文字幕 | 国产极品尤物在线 | 久久国产精品电影 | 中文字幕一区二区在线观看 | 欧美日韩aa | a黄在线观看 | 欧美韩日精品 | 国产精品va在线观看入 | 9色在线视频 | 在线 国产 亚洲 欧美 | 在线观看岛国片 | 婷婷在线网站 | 一区二区三区国 | 在线免费观看国产精品 | 久久国产精品视频免费看 | 国产亚洲精品综合一区91 | 在线精品视频在线观看高清 | 在线观看视频一区二区三区 | 97色婷婷成人综合在线观看 | av超碰在线 | 在线亚洲免费视频 | av中文在线| 久久久久久不卡 | 亚洲女人av| 久久久久久久久久电影 | 新版资源中文在线观看 | 午夜电影av | 中文字幕日韩在线播放 | 亚洲视频在线观看免费 | 美女网色| 国产99区| 五月天堂色 | 精品在线视频观看 | 天天激情天天干 | 亚洲欧洲成人精品av97 | 国产精品久久久久久久久久久免费看 | 日日草天天草 | se婷婷| 国产精品国产三级国产aⅴ无密码 | 91九色视频网站 | 欧美一二三区在线观看 | 国产精品久久久久免费a∨ 欧美一级性生活片 | 久久99国产精品久久 | 久久人人爽人人片av | 人人操日日干 | 亚洲精品国产综合99久久夜夜嗨 | 国产精品涩涩屋www在线观看 | 欧美一区二区视频97 | 免费看麻豆| 国产在线欧美在线 | 日日夜日日干 | 午夜精品久久久久久久99婷婷 | 久久免费在线视频 | 午夜影院一级 | 久久国产一区二区三区 | 日韩最新在线视频 | 日p视频在线观看 | 91精品国自产在线观看 | 国产日韩欧美在线观看视频 | 精选久久 | 欧美色图视频一区 | 一区二区三区韩国免费中文网站 | 91视频中文字幕 | 亚洲欧美经典 | 91精彩视频在线观看 | 精品国产一区二区三区久久 | 99热最新在线 | 国产精品免费久久久久久 | 中文av日韩 | 久久黄色免费视频 | 日韩欧美高清一区二区 | 激情综合啪 | 成人理论电影 | 中文字幕国产精品 | 手机看片国产日韩 | 久久亚洲欧美日韩精品专区 | 精品国产观看 | 中文字幕在线成人 | 91人人澡人人爽 | 在线韩国电影免费观影完整版 | 国产高清不卡在线 | 久久精品亚洲国产 | 欧美网址在线观看 | 91精品伦理 | 在线免费观看视频一区 | 超碰在线98 | 亚洲国产美女精品久久久久∴ | 夜夜骑天天操 | 亚洲精品午夜久久久久久久久久久 | 中文字幕免费看 | 亚洲成a人片综合在线 | 中文字幕在线观看免费 | 欧美日韩破处 | 最近久乱中文字幕 | 免费三级影片 | 国产精品久久中文字幕 | 一区二区三区免费 | 天天操综合网站 | 国产99在线播放 | 福利视频第一页 | 麻豆手机在线 | 欧美日本一区 | 黄色三级免费看 | 久久久免费看视频 | 亚洲国产视频a | 国产色网| 久久综合婷婷国产二区高清 | aaa日本高清在线播放免费观看 | 狠狠干电影| 干狠狠| 亚洲精品视频在线观看网站 | 911精品视频 | 亚洲欧美日韩一二三区 | 国产精品福利无圣光在线一区 | 日韩.com| 日韩久久精品一区二区三区 | 中文在线8资源库 | 最近日本mv字幕免费观看 | 黄在线免费看 | 色综合天天综合网国产成人网 | 国产在线传媒 | 在线中文视频 | 狠狠干网| 亚洲日本欧美 | 免费在线激情电影 | 99精品视频在线观看 | 超碰电影在线观看 | av 在线观看 | 国产99久久久精品 | 天天色天天操综合网 | 在线看国产视频 | 亚洲国产高清在线观看视频 | 日日操夜夜操狠狠操 | 免费观看高清 | 亚洲激情一区二区三区 | 国产精品一区二区三区在线播放 | 亚洲精品在线观看中文字幕 | 国产精品自产拍在线观看中文 | 久久免费视频这里只有精品 | 国产亚洲精品中文字幕 | 亚洲人成人天堂h久久 | 一区在线电影 | 色姑娘综合 | 国产亚洲片 | 999久久国精品免费观看网站 | 欧洲一区二区在线观看 | 精品亚洲一区二区 | 欧美午夜精品久久久久久孕妇 | 中文字幕在线播放一区二区 | 国产精品成人国产乱 | 国产精品一区二区三区在线免费观看 | 欧美亚洲免费在线一区 | 国产精品一区二区麻豆 | 国产精品嫩草影视久久久 | 久久精品视频在线免费观看 | 2023年中文无字幕文字 | 亚洲一级黄色片 | 色综合国产 | 国产乱老熟视频网88av | 欧美日韩在线观看视频 | 天天操天天透 | 国产精品99久久99久久久二8 | 成人国产精品一区二区 | 九九热免费在线视频 | 免费观看视频的网站 | 日韩欧美高清在线 | 啪啪肉肉污av国网站 | 成人国产精品免费观看 | 亚洲电影图片小说 | av在线看网站 | 97成人在线视频 | 午夜三级理论 | 日韩欧美电影在线观看 | 波多野结衣视频一区二区 | 国产真实精品久久二三区 | www色综合 | 欧美亚洲成人免费 | 日本在线观看一区二区 | 91视频链接 | 日韩精品视频第一页 | 国产专区在线看 | 亚洲女人天堂成人av在线 | 国产中文字幕三区 | 99在线视频观看 | 91精品在线免费观看 | 最新国产在线观看 | 久久婷婷一区二区三区 | 国产亚洲欧美日韩高清 | 婷婷伊人五月天 | 狠狠色噜噜狠狠狠狠2021天天 | 久久99精品久久久久久 | 日韩在线 一区二区 | 国产精品少妇 | 欧美了一区在线观看 | 在线a视频免费观看 | 91在线视频网址 | 国产99视频在线观看 | 国产99久久久国产 | 91亚色在线观看 | 国产v在线观看 | 天天操天天干天天综合网 | 亚洲专区视频在线观看 | 天堂av网址| 99视| 亚洲黄色av网址 | 日韩欧美有码在线 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 日韩在线免费看 | 国产精品99精品 | 欧美日韩精品免费观看 | 成片视频免费观看 | 黄色大片日本免费大片 | 国产91精品看黄网站在线观看动漫 | 国产成人在线看 | 国产美腿白丝袜足在线av | 日本在线观看中文字幕无线观看 | av丁香| 在线观看你懂的网址 | 日韩精品一区二区在线视频 | 久久在线免费观看视频 | 四虎伊人 | 91视频成人免费 | 成人欧美一区二区三区在线观看 | 青青草在久久免费久久免费 | 97超碰人人澡 | 亚洲精品免费看 | 久久97久久 | 亚洲男男gⅴgay双龙 | 人人澡人人澡人人 | 国产午夜精品一区二区三区四区 | 成人试看120秒 | 亚洲三级影院 | 亚洲婷婷伊人 | 一区二区三区免费在线观看视频 | 精品久久在线 | 日韩免费中文 | 久久精品艹 | japanesefreesex中国少妇 | 伊人小视频 | 97国产精品免费 | 一区二区三区四区五区在线视频 | 亚洲一区在线看 | 国产精品麻| 欧美成人tv | 日韩中文字幕国产 | 在线视频欧美日韩 | 日色在线视频 | 免费看黄网站在线 | www91在线 | 欧美日本日韩aⅴ在线视频 插插插色综合 | 五月婷婷中文网 | 成人毛片a| 人人爽爽人人 | 在线天堂亚洲 | av电影一区 | 国内综合精品午夜久久资源 | 久久精品视频国产 | 九九九在线观看视频 | 婷婷久月 | 一区 在线 影院 | 国产日产精品一区二区三区四区的观看方式 | 国产黑丝袜在线 | 免费午夜网站 | 日本成人黄色片 | 天天综合人人 | 久久99亚洲网美利坚合众国 | 亚洲美女免费视频 | 伊人在线视频 | 2020天天干夜夜爽 | 日韩成年视频 | 日韩r级电影在线观看 | 美女黄频免费 | 国产一级片毛片 | 欧美日韩高清在线观看 | 欧美日韩视频精品 | 国产不卡一区二区视频 | aⅴ视频在线 | 亚洲黄色av网址 | 久久精品专区 | 国产99在线免费 | 久久免费观看视频 | 国产色网站 | 日韩专区一区二区 | 精品亚洲va在线va天堂资源站 | 一本—道久久a久久精品蜜桃 | www.久久久.com| 久久综合五月婷婷 | www.大网伊人| 国产精彩视频一区 | 一区二区三区四区五区六区 | 久久蜜臀av| 一区免费观看 | 99精品在这里 | 99久久99久国产黄毛片 | 国产成人在线免费观看 | 日韩午夜在线播放 | 激情深爱.com | 中文字幕999| 日韩精品一区二区三区水蜜桃 | 日韩精品一区二区三区三炮视频 | 9999毛片 | 久久成熟 | 欧美三级在线播放 | 最新成人在线 | 国产91影院 | 国产精品乱码一区二区视频 | 久久久久久久久久久久国产精品 | 国产精品久久电影观看 | 97在线观看免费高清完整版在线观看 | 日日夜夜精品网站 | 中文字幕av最新 | 久免费视频| 99精品黄色片免费大全 | 一区二区三区精品在线视频 | 91香蕉视频在线下载 | 久草在线中文视频 | 日韩欧美视频一区二区三区 | 99色婷婷 | 九九国产精品视频 | 欧美国产精品一区二区 | 中文av免费| 欧美一二三区在线观看 | 婷婷视频 | 亚洲精品综合久久 | 超碰97国产精品人人cao | 日韩xxx视频 | 久久精品一区二区 | 亚洲精品综合一区二区 | 超碰久热 | 狠狠躁天天躁 | 九9热这里真品2 | 欧美日韩免费一区 | 蜜臀av一区二区 | 偷拍精偷拍精品欧洲亚洲网站 | 久久婷亚洲五月一区天天躁 | 激情欧美日韩一区二区 | 精品国产一区二区三区日日嗨 | 日本激情视频中文字幕 | 狠狠夜夜 | 天天色天天综合网 | 亚洲国产av精品毛片鲁大师 | 一级黄色片网站 | 欧美片一区二区三区 | 在线观看成人毛片 | 日韩在线视频一区二区三区 | 亚洲乱码一区 | 91在线看片| 手机在线中文字幕 | 在线免费色 | 激情综合久久 | 欧美精品中文字幕亚洲专区 | www·22com天天操 | 99爱视频在线观看 | 男女全黄一级一级高潮免费看 | 黄色1级毛片 | 色94色欧美 | 久草在线免费看视频 | 人人干免费 | av黄色免费在线观看 | 久久精品黄色 | 久久久久免费精品国产小说色大师 | 国产一区二区三区高清播放 | 国产精品国产精品 | 99爱国产精品 | 天天躁天天狠天天透 | 成人在线视频在线观看 | 日韩aⅴ视频| 亚洲人成免费 | 国产区 在线 | 国产看片 色 | 97色狠狠 | 国产高清视频在线免费观看 | 操操操av | 中文字幕乱码一区二区 | 又黄又爽的免费高潮视频 | 黄色网址在线播放 | 最近更新中文字幕 | a成人v在线 | 色福利网站 | 国产大片免费久久 | 蜜桃视频精品 | 日本黄色免费播放 | 又黄又爽又无遮挡的视频 | 在线有码中文 | 久久久精品99 | 91成人精品一区在线播放69 | 亚洲精品在线观看不卡 | 欧美亚洲久久 | 久草热久草视频 | 91天堂影院| 美女网站视频久久 | 日韩在线国产 | 久精品视频免费观看2 | 欧美一级在线看 | 免费a级毛片在线看 | 国产高清视频在线 | 91精品久久香蕉国产线看观看 | 波多野结衣在线播放一区 | 欧美日韩国产免费视频 | 国产字幕在线看 | 国产国产人免费人成免费视频 | 日韩女同一区二区三区在线观看 | 免费看高清毛片 | 亚洲 精品在线视频 | 国产精品一区二区久久精品爱涩 | 免费三级骚| 黄a在线| 久久理论电影网 | 永久免费精品视频 | 欧美一区二区精品在线 | 午夜免费视频网站 | 在线黄网站 | 久久久久视| 精品国产成人 | 美女网站在线观看 | 国产999在线观看 | 国产精品久久久久高潮 | 97色视频在线 | 国产一级久久久 | 园产精品久久久久久久7电影 | 在线视频91| 欧美在线观看视频免费 | 日日干天天爽 | 91在线看视频 | 久久国产午夜精品理论片最新版本 | 五月婷婷激情综合 | 欧美伊人网 | 男女啪啪视屏 | 国产成人av在线影院 | 精品国产一区在线观看 | 免费在线播放视频 | 毛片网在线播放 | 亚洲美女在线一区 | 久久久久免费精品国产小说色大师 | 国产精品久久久久久久久岛 | 伊香蕉大综综综合久久啪 | 九九热中文字幕 | 一级片黄色片网站 | 亚洲精品色 | 午夜精品久久久久99热app | 国产第一页在线播放 | 麻豆视频国产 | 日韩欧美视频一区二区 | 成人欧美日韩国产 | 九色精品免费永久在线 | 狠狠色噜噜狠狠狠狠2022 | 黄污在线看 | 一区二区三区高清在线 | www.天天射| 精品国产激情 | 天堂在线成人 | 国产精品无av码在线观看 | 欧美日韩在线网站 | 一区二区视频欧美 | 国产色综合天天综合网 | 亚洲精品乱码久久久久久久久久 | 亚洲国产精品久久 | 亚洲欧美怡红院 | 97精品超碰一区二区三区 | 91精品国产高清自在线观看 | 国产精品99久久久久久宅男 | 视频高清 | 中文字幕在线视频网站 | 99视频在线免费看 | av再线观看 | 99视频在线| 国产视频色 | 欧洲精品久久久久毛片完整版 | 91自拍视频在线 | 999亚洲国产996395 | 黄色影院在线播放 | 久久久久久不卡 | 亚洲精品视频在线观看网站 | 久久精品视频在线播放 | 99精品国产高清在线观看 | 黄色毛片网站在线观看 | 亚洲天堂精品视频 | 99久久综合精品五月天 | 日韩av午夜| 丁香六月婷婷综合 | 国产成人综合图片 | 91精品人成在线观看 | 久久久精品久久 | 高清不卡一区二区三区 | 500部大龄熟乱视频使用方法 | 天天干.com | 国产视频在线观看一区 | 国产 欧美 日产久久 | 99色国产| 日韩av高潮 | 久久理伦片 | 日日干干| 操老逼免费视频 | 国内久久精品 | 免费观看的av网站 | 国产1区2区3区在线 亚洲自拍偷拍色图 | 国内精品久久久久久久久久 | 亚洲视频六区 | 正在播放国产精品 | 综合久久精品 | 欧美va在线观看 | 久久国产免费看 | 少妇av片| 欧美另类xxx| 三级黄色片在线观看 | 97视频在线播放 | 黄色小说在线观看视频 | 免费在线看成人av | 国产高潮久久 | 九月婷婷色 | 国产精品成人国产乱 | 9ⅰ精品久久久久久久久中文字幕 | 欧美极品xxxx | 天天操天天干天天摸 | 九色视频网 | 亚洲激情免费 | 国产香蕉av| 久草在线91 | 免费看特级毛片 | 日韩国产欧美在线视频 | www好男人 | 欧美久久久久久久久久 | av一级一片 | 国产精品18久久久久久久 | 欧美激情视频免费看 | 国产视频在线观看一区二区 | 91在线免费观看网站 | 91精品中文字幕 | 蜜桃久久久 | 日韩精品观看 | 国产免费人成xvideos视频 | 在线 高清 中文字幕 | avlulu久久精品 | 天天色中文 | 久久精品成人热国产成 | 色婷婷久久 | 日韩av综合网站 | 色欧美综合 | 黄色一级动作片 | 日韩三级视频 | 天天干天天做天天爱 | 亚洲毛片久久 | 亚洲,播放 | 亚洲成a人片77777kkkk1在线观看 | 超级碰碰碰免费视频 | 婷婷爱五月天 | 亚洲区视频在线观看 | 亚洲精品视频播放 | 欧美一级日韩免费不卡 | 91中文字幕永久在线 | 人人插人人搞 | 狠狠88综合久久久久综合网 | 久久久午夜剧场 | 爱爱av网站| av直接看| 最近中文字幕国语免费av | 亚洲v欧美v国产v在线观看 | 久久久久女人精品毛片 | 国产精品99久久久久久久久久久久 | 国产视频综合在线 | 国产成人一区二区三区 | 精品久久久久久亚洲综合网站 | 91精品国产九九九久久久亚洲 | 免费在线观看av电影 | 81国产精品久久久久久久久久 | 久艹视频免费观看 | 国内毛片毛片 | 久久综合色一综合色88 | 亚洲成人精品在线观看 | 91一区二区三区久久久久国产乱 | 国产高清成人在线 | 天天干干| 久久久久中文字幕 | 国产区av在线 | 午夜男人影院 | 免费在线国产 | 日本在线观看中文字幕无线观看 | 91亚洲在线| 欧美91精品国产自产 | 国产91精品一区二区麻豆亚洲 | 中文一二区 | 99在线热播精品免费99热 | 亚洲日韩精品欧美一区二区 | 在线精品观看 | 中文字幕在线观看视频免费 | 热99在线视频 | 久久久免费少妇 | 国产精品18videosex性欧美 | 日韩一区二区三区免费视频 | 日日麻批40分钟视频免费观看 | 日韩高清不卡一区二区三区 | 久久久精品网站 | 欧美一区二区三区免费看 | 丝袜av网站 | 综合久色 | 视频在线精品 | 日韩成人xxxx| www.五月激情.com | 天天射天天色天天干 | 久久免费国产电影 | 国产又黄又爽无遮挡 | 久草网免费 | 91丨精品丨蝌蚪丨白丝jk | 亚州欧美精品 | 韩国av免费| 久久伦理影院 | 久草网在线观看 | www免费看片com | 久久在现视频 | 久久久久久久久久久久久久av | 亚洲一级电影视频 | 99日精品 | 欧美爽爽爽 | 免费看短| 国产91大片| 国产粉嫩在线 | 91精品国产成人 | 国产精品久久艹 | 久久精品资源 | 99国产成+人+综合+亚洲 欧美 | 国产福利精品在线观看 | 精品黄色片| 97超碰在线资源 | 免费在线播放av电影 | 欧美天天综合网 | 97超碰人人模人人人爽人人爱 | 久久久不卡影院 | 久久精品国产亚洲精品2020 | 婷婷综合在线 | 国产资源免费在线观看 | 亚洲精品在线免费播放 | 久久看片 | 国产一区观看 | 久久成人人人人精品欧 | 激情在线免费视频 | 欧美另类一二三四区 | 人人舔人人射 | 久久精品99国产精品 | 一区二区三区在线不卡 | 97成人精品区在线播放 | 精品久久久久一区二区国产 | 天天射色综合 | 91视视频在线直接观看在线看网页在线看 | 中文字幕在线观看视频一区 | 欧美综合在线视频 | 日本中文字幕在线看 | 偷拍久久久 | 91爱爱中文字幕 | av资源免费观看 | 中文在线字幕免费观看 | 天堂va在线高清一区 | 欧美一级片免费播放 | 国产中文伊人 | 欧美色图88 | www.色爱| 成在线播放 | 国产高清久久久久 | 日本中文字幕网址 | 国产中文字幕免费 | 91大神电影 | 国产精品99久久免费观看 | 日韩欧美一区二区三区在线 | 毛片美女网站 | 亚洲精品美女久久17c | 免费人做人爱www的视 | 精品国产一区二区三区久久久蜜臀 | 亚洲成年人免费网站 | 国产美女免费 | 婷婷99| 久久精品中文字幕 | av手机在线播放 | 99视频在线 | 少妇av片| 色999五月色 | 四虎成人免费影院 | 日韩av影视 | 久草在线久草在线2 | 中国一级片在线播放 | 日韩一级黄色大片 | 国产午夜精品一区二区三区嫩草 | 91最新地址永久入口 | 久久精品精品电影网 | 在线你懂的视频 | 亚洲精品动漫久久久久 | 夜夜干天天操 | 久久人视频 | 久草在线视频免赞 | 精品一区二区在线观看 | 欧美日性视频 | 久久99国产精品自在自在app | 激情av网址| 久久精品99国产精品 | 亚洲1区 在线 | 最近日本韩国中文字幕 | 成人永久视频 | 中文字幕第一页在线vr | 亚洲精品在线视频网站 | 一级黄色av| 欧美天堂视频在线 | 久久久久久国产精品亚洲78 | 欧美日韩一区二区视频在线观看 | 久久久在线免费观看 | 美女精品国产 | www..com毛片| 99久久99久久精品国产片果冰 | 免费男女羞羞的视频网站中文字幕 | 亚洲成人黄色网址 | 精品一区二区视频 | 精品国产精品久久 | 伊人亚洲综合网 | 91传媒91久久久 | 国内精品美女在线观看 | 91精品91| 天天操天天操天天 | 一区二区久久 | 久久久精品电影 | 色综合中文字幕 | 亚洲精品国产精品国自产 | 色婷婷综合久色 | 天天操天天艹 | 亚洲第一av在线播放 | 黄色成人在线观看 | 亚洲视频在线免费看 | 精品久久久久久一区二区里番 | 另类老妇性bbwbbw高清 | 少妇av片 | 欧美日韩精品在线观看视频 | 亚洲一区二区视频 | 国产99一区视频免费 | 激情五月六月婷婷 | www.天天综合 | 国产精品亚洲片夜色在线 | 四虎国产精品永久在线国在线 | 国产999在线| 狠狠操夜夜 | 久久新| 欧美a级免费视频 | av片一区二区 | 黄色aaa毛片| 婷婷精品视频 | 综合久久久久久久 | 美女视频黄,久久 | 国产原创中文在线 | 久久久久久久久久久影视 | www日韩精品 | 亚洲日韩精品欧美一区二区 | 激情五月婷婷综合 | 国产手机在线观看视频 | 嫩草91影院| 成人在线播放网站 | 国产一区二区视频在线播放 | 又污又黄网站 | 97成人超碰| 国产黄在线播放 | 人人艹视频| 国产剧在线观看片 | 色婷婷综合久久久 | 久久久国产精品成人免费 | 国产又黄又爽又猛视频日本 | 久久久久一区二区三区 | 成人午夜片av在线看 | 91资源在线视频 | 日韩av电影免费在线观看 | 国产在线不卡视频 | 五月婷婷久久综合 | 五月婷婷av | 国产精品久久久久久久7电影 | 精品久久久久久亚洲综合网站 | 蜜臀av一区二区 | 日韩激情三级 | 中文字幕一区二区三区在线视频 | 91丨九色丨蝌蚪丨对白 | 亚洲欧洲日韩在线观看 | 天天色天天爱天天射综合 | 日韩免费b | 国产夫妻性生活自拍 | 国产一二区视频 | 免费成人黄色av | 国内精品久久久久影院男同志 | 成人一区二区在线 | 亚洲情感电影大片 | 精品视频久久久 | 在线观看精品黄av片免费 | 欧美巨大荫蒂茸毛毛人妖 | 亚洲91中文字幕无线码三区 | 香蕉在线播放 | 丁香婷婷久久久综合精品国产 | 国产又粗又猛又爽又黄的视频免费 | 97看片网| 奇米四色影狠狠爱7777 | 国产在线播放一区二区三区 | 亚洲狠狠婷婷综合久久久 | 一区二区三区久久精品 | 欧美 日韩 国产 成人 在线 | 欧美日韩国产色综合一二三四 | 18性欧美xxxⅹ性满足 | 久草视频在线免费播放 | 久久国产区 | 久久国产午夜精品理论片最新版本 | 国产精品久久嫩一区二区免费 | 久久综合免费视频 | 久操视频在线免费看 | 亚洲永久精品国产 | 国产性天天综合网 | 国产午夜精品一区二区三区四区 | 日本精品中文字幕 | 青青草国产免费 | www.五月天激情 | 久久精品中文字幕少妇 | 免费视频资源 | 亚洲国产精品va在线 | 超碰在线亚洲 | 亚洲成a人片77777kkkk1在线观看 | 成人午夜电影在线播放 | 亚洲精品在线二区 | 精品国产乱码一区二 | 在线观看免费国产小视频 | 在线观看深夜视频 | 99re久久资源最新地址 | 在线导航av | 欧美精品国产精品 | 欧美亚洲国产一卡 | 亚洲精品tv久久久久久久久久 | 久草精品国产 | 亚洲一二三区精品 | 欧美日韩一区二区三区在线免费观看 | 国产精品久久久久久久久久久免费看 | 国产欧美精品在线观看 | 中文字幕在线观看2018 | 久久精品精品 | 国产色影院 | 国产亚洲成人网 | 久久久久国产一区二区三区四区 | 久久午夜羞羞影院 | 久久私人影院 | 五月激情丁香图片 | 色姑娘综合| 日日爽视频| 中文字幕乱码日本亚洲一区二区 | 96精品视频 | 九草视频在线观看 | 亚洲国产视频直播 | 亚洲国产精品成人va在线观看 | 国产资源站 | 欧美专区国产专区 | 亚洲国产精品500在线观看 | 中文字幕日韩高清 | 国产录像在线观看 | 日韩一级成人av | 在线观看国产福利片 | 操操综合| 成人97视频一区二区 | 久久国产精品网站 | 五月婷婷狠狠 | 久久久久亚洲国产精品 | 国产又粗又猛又爽 | 国产一区二区三区网站 | 欧美在线观看小视频 | 天天干亚洲| 四虎4hu永久免费 | 激情丁香综合 | 国产一区二区在线免费播放 | 五月婷亚洲 | av成人黄色 | 久久亚洲在线 | 在线免费高清视频 | 日韩欧美一区二区三区在线 | 久久国产经典 | 久久国产精品第一页 | 日韩免费电影在线观看 | 日本中文字幕电影在线免费观看 | 天天干天天操人体 | 欧美久久久一区二区三区 | 天堂在线免费视频 | 日本午夜在线观看 | 国产无遮挡又黄又爽在线观看 | 天天爱天天干天天爽 | 久久久久久久久久免费视频 | 久久久久久免费毛片精品 | 久久久高清免费视频 | 免费看国产视频 | 亚洲人人爱| 欧美精品久久久久 | 五月综合激情网 | 色婷婷导航 | 国产裸体视频bbbbb | 亚洲精选在线观看 | 国产日韩在线看 | 在线观看福利网站 | 日本xxxxav | 亚洲一区二区三区精品在线观看 | www.午夜视频 | 97超碰免费在线观看 | av在线直接看 | 久久夜色精品国产欧美一区麻豆 | av免费试看 | 中文字幕日韩免费视频 | 欧美成人在线网站 | 91精品办公室少妇高潮对白 | 国产亚洲欧美精品久久久久久 | 中文字幕在线观看网 | 日日夜夜亚洲 | 国产一区二区三区四区在线 | 国产黄色网 | 国产在线欧美在线 | 国产精品嫩草在线 | 亚洲色综合 | 人人澡人人草 | 久久久久国产精品www | 成人免费毛片aaaaaa片 | 丝袜美女视频网站 | 久久视频精品在线 | 国产精品乱码在线 | 亚洲欧美日韩国产精品一区午夜 | 日本久久99 | 国产经典 欧美精品 | 久久中文字幕在线视频 | 在线观看中文字幕dvd播放 | 日韩精品视频免费 | 亚洲国产精品久久久 | 国产又黄又爽无遮挡 | 伊人色**天天综合婷婷 | 日韩a级黄色片 | 欧美中文字幕久久 | 国产精品96久久久久久吹潮 | 久久99国产精品自在自在app | 91视频久久 | 久久一区二区三区日韩 | 国产手机视频在线观看 | 亚洲一级电影视频 | 国产一区二区久久精品 | 中文字幕av在线 | 久久夜夜夜 | 欧美精品一区二区性色 | 国产资源av| 国产美女网站在线观看 | 午夜精品一区二区三区在线 | 国产中文字幕在线看 | 中文有码在线 | www免费| 亚洲黄色app| 黄色大片日本 | 在线观看亚洲专区 | 人人爽人人av | 日韩成人黄色av | 国产精品第一 | 超碰99在线 | 国产一区二区影院 | 17videosex性欧美 | 中文成人字幕 | 在线电影a| 日韩一区二区三区观看 | 亚洲精品在线视频 | 91精品久久久久久久久久入口 | 国产69精品久久久久久 | 成片免费观看视频999 | 欧美成人tv | 视频在线91| 国产尤物在线观看 | 香蕉色综合 | 日韩黄色在线观看 | 四虎国产精品永久在线国在线 | 国语麻豆 | 国产精品第十页 | 精品久久五月天 | 一级做a爱片性色毛片www | 视频一区二区在线观看 | 久久国产精品久久国产精品 | 天天操天天干天天 | 日韩免费一区二区 | 日韩一级成人av | 视频在线观看日韩 | 视频成人免费 | 国外调教视频网站 | av电影一区二区三区 | 中文字幕av电影下载 | 亚洲理论片 | 久久综合九色综合久久久精品综合 |