<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 1\. 了解SpringBoot 在這一部分,我們主要了解以下3個問題: * 什么是SpringBoot * 為什么要學習SpringBoot * SpringBoot的特點 ## 1.1.什么是SpringBoot SpringBoot是Spring項目中的一個子工程,與我們所熟知的Spring-framework 同屬于spring的產品: 我們可以看到下面的一段介紹: > Takes an opinionated view of building production-ready Spring applications. Spring Boot favors convention over configuration and is designed to get you up and running as quickly as possible. 翻譯一下: > 用一些固定的方式來構建生產級別的spring應用。Spring Boot 推崇約定大于配置的方式以便于你能夠盡可能快速的啟動并運行程序。 其實人們把Spring Boot 稱為搭建程序的`腳手架`。其最主要作用就是幫我們快速的構建龐大的spring項目,并且盡可能的減少一切xml配置,做到開箱即用,迅速上手,讓我們關注與業務而非配置。 ## 1.2.為什么要學習SpringBoot java一直被人詬病的一點就是臃腫、麻煩。當我們還在辛苦的搭建項目時,可能Python程序員已經把功能寫好了,究其原因注意是兩點: * 復雜的配置 項目各種配置其實是開發時的損耗, 因為在思考 Spring 特性配置和解決業務問題之間需要進行思維切換,所以寫配置擠占了寫應用程序邏輯的時間。 * 一個是混亂的依賴管理 項目的依賴管理也是件吃力不討好的事情。決定項目里要用哪些庫就已經夠讓人頭痛的了,你還要知道這些庫的哪個版本和其他庫不會有沖突,這難題實在太棘手。并且,依賴管理也是一種損耗,添加依賴不是寫應用程序代碼。一旦選錯了依賴的版本,隨之而來的不兼容問題毫無疑問會是生產力殺手。 而SpringBoot讓這一切成為過去! > Spring Boot 簡化了基于Spring的應用開發,只需要“run”就能創建一個獨立的、生產級別的Spring應用。Spring Boot為Spring平臺及第三方庫提供開箱即用的設置(提供默認設置,存放默認配置的包就是啟動器starter),這樣我們就可以簡單的開始。多數Spring Boot應用只需要很少的Spring配置。 我們可以使用SpringBoot創建java應用,并使用java –jar 啟動它,就能得到一個生產級別的web工程。 ## 1.3.SpringBoot的特點 Spring Boot 主要目標是: * 為所有 Spring 的開發者提供一個非常快速的、廣泛接受的入門體驗 * 開箱即用(啟動器starter-其實就是SpringBoot提供的一個jar包),但通過自己設置參數(.properties),即可快速擺脫這種方式。 * 提供了一些大型項目中常見的非功能性特性,如內嵌服務器、安全、指標,健康檢測、外部化配置等 * 絕對沒有代碼生成,也無需 XML 配置。 更多細節,大家可以到[官網](http://projects.spring.io/spring-boot/)查看。 # 2.快速入門 接下來,我們就來利用SpringBoot搭建一個web工程,體會一下SpringBoot的魅力所在! ## 2.1.創建工程 我們先新建一個空的工程: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1531270595638.png?lastModify=1649173481) 項目信息: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1531283966656.png?lastModify=1649173481) 地址: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1531283988110.png?lastModify=1649173481) ## 2.2.添加依賴 看到這里很多同學會有疑惑,前面說傳統開發的問題之一就是依賴管理混亂,怎么這里我們還需要管理依賴呢?難道SpringBoot不幫我們管理嗎? 別著急,現在我們的項目與SpringBoot還沒有什么關聯。SpringBoot提供了一個名為spring-boot-starter-parent的工程,里面已經對各種常用依賴(并非全部)的版本進行了管理,我們的項目需要以這個項目為父工程,這樣我們就不用操心依賴的版本問題了,需要什么依賴,直接引入坐標即可! ### 2.2.1.添加父工程坐標 ~~~ <parent> ? ?<groupId>org.springframework.boot</groupId> ? ?<artifactId>spring-boot-starter-parent</artifactId> ? ?<version>2.1.3.RELEASE</version> </parent> ~~~ ### 2.2.2.添加web啟動器 為了讓SpringBoot幫我們完成各種自動配置,我們必須引入SpringBoot提供的自動配置依賴,我們稱為`啟動器`。因為我們是web項目,這里我們引入web啟動器: ~~~ <dependencies> ? ?<dependency> ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ?<artifactId>spring-boot-starter-web</artifactId> ? ?</dependency> </dependencies> ~~~ 需要注意的是,我們并沒有在這里指定版本信息。因為SpringBoot的父工程已經對版本進行了管理了。 這個時候,我們會發現項目中多出了大量的依賴: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525486980765.png?lastModify=1649173481) 這些都是SpringBoot根據spring-boot-starter-web這個依賴自動引入的,而且所有的版本都已經管理好,不會出現沖突。 ### 2.2.3.管理jdk版本 如果我們想要修改SpringBoot項目的jdk版本,只需要簡單的添加以下屬性即可,如果沒有需求,則不添加。 ~~~ <properties> ? ?<java.version>1.8</java.version> </properties> ~~~ ### 2.2.4.完整pom ~~~ <?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>com.leyou.demo</groupId> ? ?<artifactId>springboot-demo</artifactId> ? ?<version>1.0-SNAPSHOT</version> ? ? ?<properties> ? ? ? ?<java.version>1.8</java.version> ? ?</properties> ? ? ?<parent> ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ?<artifactId>spring-boot-starter-parent</artifactId> ? ? ? ?<version>2.1.3.RELEASE</version> ? ?</parent> ? ? ?<dependencies> ? ? ? ?<dependency> ? ? ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ? ? ?<artifactId>spring-boot-starter-web</artifactId> ? ? ? ?</dependency> ? ?</dependencies> </project> ~~~ ## 2.3.啟動類 Spring Boot項目通過main函數即可啟動,我們需要創建一個啟動類: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525487293907.png?lastModify=1649173481) 然后編寫main函數: ~~~ @SpringBootApplication public class Application { ? ?public static void main(String[] args) { ? ? ? ?SpringApplication.run(Application.class, args); ? } } ~~~ ## 2.4.編寫controller 接下來,我們就可以像以前那樣開發SpringMVC的項目了! 我們編寫一個controller: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525487465325.png?lastModify=1649173481) 代碼: ~~~ @RestController public class HelloController { ? ? ?@GetMapping("hello") ? ?public String hello(){ ? ? ? ?return "hello, spring boot!"; ? } } ? ~~~ ## 2.5.啟動測試 接下來,我們運行main函數,查看控制臺: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525487613365.png?lastModify=1649173481) 并且可以看到監聽的端口信息: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525487705460.png?lastModify=1649173481) * 1)監聽的端口是8080 * 2)SpringMVC的映射路徑是:/ * 3)`/hello`路徑已經映射到了`HelloController`中的`hello()`方法 打開頁面訪問:[http://localhost:8080/hello](http://localhost:8080/hello) ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525487820824.png?lastModify=1649173481) 測試成功了! ## 2.6. SpringBoot的項目模板 自己搭建SpringBoot項目需要編寫啟動類、pom、測試類等內容,雖然已經很簡潔,但是也會消耗時間。所以Spring提供了一套快速搭建項目的腳手架,非常方便: 1)創建新工程,選擇spring方式: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1545877758683.png?lastModify=1649173481) 2)編寫項目信息 ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1545877779795.png?lastModify=1649173481) 3)勾選所需依賴 ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1545877832623.png?lastModify=1649173481) 4)填寫項目路徑: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1545877864914.png?lastModify=1649173481) 這樣就可以得到完整項目,并且依賴、配置、啟動類、測試類等都已經自動完成: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1545877914333.png?lastModify=1649173481) pom文件: ~~~ <?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> ? ?<parent> ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ?<artifactId>spring-boot-starter-parent</artifactId> ? ? ? ?<version>2.1.3.RELEASE</version> ? ? ? ?<relativePath/> <!-- lookup parent from repository --> ? ?</parent> ? ?<groupId>cn.itcast.demo</groupId> ? ?<artifactId>boot-demo</artifactId> ? ?<version>0.0.1-SNAPSHOT</version> ? ?<name>boot-demo</name> ? ?<description>Demo project for Spring Boot</description> ? ? ?<properties> ? ? ? ?<java.version>1.8</java.version> ? ?</properties> ? ? ?<dependencies> ? ? ? ?<dependency> ? ? ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ? ? ?<artifactId>spring-boot-starter-data-redis</artifactId> ? ? ? ?</dependency> ? ? ? ?<dependency> ? ? ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ? ? ?<artifactId>spring-boot-starter-data-solr</artifactId> ? ? ? ?</dependency> ? ? ? ?<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> ? ? ? ? ? ?</plugin> ? ? ? ?</plugins> ? ?</build> ? </project> ~~~ # 3.Java配置 在入門案例中,我們沒有任何的配置,就可以實現一個SpringMVC的項目了,快速、高效! 但是有同學會有疑問,如果沒有任何的xml,那么我們如果要配置一個Bean該怎么辦?比如我們要配置一個數據庫連接池,以前會這么玩: SpEL ${} ~~~ <!-- 配置連接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" ? ? ?init-method="init" destroy-method="close"> ? ?<property name="url" value="${jdbc.url}" /> ? ?<property name="username" value="${jdbc.username}" /> ? ?<property name="password" value="${jdbc.password}" /> </bean> ~~~ 現在該怎么做呢? ## 3.1.回顧歷史 事實上,在Spring3.0開始,Spring官方就已經開始推薦使用java配置來代替傳統的xml配置了,我們不妨來回顧一下Spring的歷史: * Spring1.0時代 在此時因為jdk1.5剛剛出來,注解開發并未盛行,因此一切Spring配置都是xml格式,想象一下所有的bean都用xml配置,細思極恐啊,心疼那個時候的程序員2秒 * Spring2.0時代 Spring引入了注解開發,但是因為并不完善,因此并未完全替代xml,此時的程序員往往是把xml與注解進行結合,貌似我們之前都是這種方式。 * Spring3.0及以后 3.0以后Spring的注解已經非常完善了,因此Spring推薦大家使用完全的java配置來代替以前的xml,不過似乎在國內并未推廣盛行。然后當SpringBoot來臨,人們才慢慢認識到java配置的優雅。 有句古話說的好:擁抱變化,擁抱未來。所以我們也應該順應時代潮流,做時尚的弄潮兒,一起來學習下java配置的玩法。 ## 3.2.嘗試java配置 java配置主要靠java類和一些注解,比較常用的注解有: * `@Configuration`:聲明一個類作為配置類,代替xml文件 * `@Bean`:聲明在方法上,將方法的返回值加入Bean容器,代替`<bean>`標簽 * `@Value`:屬性注入 * `@PropertySource`:指定外部屬性文件, 我們接下來用java配置來嘗試實現連接池配置: 首先引入Druid連接池依賴: ~~~ <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency> ~~~ 創建一個jdbc.properties文件,編寫jdbc屬性: ~~~ jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/leyou jdbc.username=root jdbc.password=123 ~~~ 然后編寫代碼: ~~~ @Configuration @PropertySource("classpath:jdbc.properties") public class JdbcConfig { @Value("${jdbc.url}") String url; @Value("${jdbc.driverClassName}") String driverClassName; @Value("${jdbc.username}") String username; @Value("${jdbc.password}") String password; @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setDriverClassName(driverClassName); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } } ~~~ 解讀: * `@Configuration`:聲明我們`JdbcConfig`是一個配置類 * `@PropertySource`:指定屬性文件的路徑是:`classpath:jdbc.properties` * 通過`@Value`為屬性注入值 * 通過@Bean將 `dataSource()`方法聲明為一個注冊Bean的方法,Spring會自動調用該方法,將方法的返回值加入Spring容器中。 然后我們就可以在任意位置通過`@Autowired`注入DataSource了! 我們在`HelloController`中測試: ~~~ @RestController public class HelloController { @Autowired private DataSource dataSource; @GetMapping("hello") public String hello() { System.out.println("dataSource: " + dataSource); return "hello, spring boot!"; } } ~~~ 然后打斷點,Debug運行并查看: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525492528558.png?lastModify=1649173481) 屬性注入成功了! ## 3.3.SpringBoot的屬性注入 SpringBoot強調的是約定大于配置,因此遵循約定,我們就能節省很多配置: * 首先,屬性文件的名稱有變化,文件名必須是:application.properties * 其次,要注入的屬性的變量名要和配置文件中的屬性名的最后一部分保持一致 * 最后,要在類上聲明這些屬性在屬性文件中的共同的前綴,并提供getter和setter方法 ### 3.3.1.修改屬性文件名稱 屬性文件的名稱有變化,文件名必須是:application.properties,SpringBoot會自動加載這個文件,無需我們指定加載的文件的名稱。 ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1555551244910.png?lastModify=1649173481) ### 3.3.2.定義屬性注入類 我們新建一個類,用來做屬性注入: ~~~ @ConfigurationProperties(prefix = "jdbc") public class JdbcProperties { private String url; private String driverClassName; private String username; private String password; // ... 略 // getters 和 setters } ~~~ * 在類上通過@ConfigurationProperties注解聲明當前類為屬性讀取類 * `prefix="jdbc"`讀取屬性文件中,前綴為jdbc的值。 * 在類上定義各個屬性,名稱必須與屬性文件中`jdbc.`后面部分一致 ### 3.3.3.使用屬性類 默認情況下,屬性讀取類JdbcProperties是 不會被加入Spring的容器的,需要下面兩種方式來激活: * 方式一:在JdbcProperties中添加@Component注解 ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1555554270542.png?lastModify=1649173481) * 方式二:在配置類上使用@EnableConfigurationProperties(JdbcProperties.class) ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1555554307530.png?lastModify=1649173481) 以上兩種方式都可以,**任選其一**即可。 例如,在JdbcConfig中使用這個屬性: ~~~ @Configuration @EnableConfigurationProperties(JdbcProperties.class) public class JdbcConfig { @Bean public DataSource dataSource(JdbcProperties jdbc) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(jdbc.getUrl()); dataSource.setDriverClassName(jdbc.getDriverClassName()); dataSource.setUsername(jdbc.getUsername()); dataSource.setPassword(jdbc.getPassword()); return dataSource; } } ~~~ * 通過`@EnableConfigurationProperties(JdbcProperties.class)`來聲明要使用`JdbcProperties`這個類的對象 * 然后你可以通過以下方式注入JdbcProperties: * @Autowired注入 ~~~ @Autowired private JdbcProperties prop; ~~~ * 構造函數注入 ~~~ private JdbcProperties prop; public JdbcConfig(Jdbcproperties prop){ this.prop = prop; } ~~~ * 聲明有@Bean的方法參數注入 ~~~ @Bean public Datasource dataSource(JdbcProperties prop){ // ... } ~~~ 本例中,我們采用第三種方式。 測試結果: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525492528558.png?lastModify=1649173481) ## 3.4.更優雅的注入 事實上,@ConfigurationProperties(prefix = "jdbc")的作用機制就是遵循約定: * 注解的前綴,加上 類中的變量名,剛好于屬性文件中的屬性一致: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1555554600504.png?lastModify=1649173481) 因此,只要一個Bean符合這樣的約定,然后加上@ConfigurationProperties(prefix = "jdbc"),Spring就會幫我們注入屬性了。 DataSource中剛好也有jdbc相關的四大參數,符合這個約定,此時,我們只需要把注解加到聲明DataSource的方法上,Spring也會幫我們注入屬性,無需手動注入: ~~~ @Configuration public class JdbcConfig { @Bean // 聲明要注入的屬性前綴,SpringBoot會自動把相關屬性通過set方法注入到DataSource中 @ConfigurationProperties(prefix = "jdbc") public DataSource dataSource() { return new DruidDataSource(); } } ~~~ 我們直接把`@ConfigurationProperties(prefix = "jdbc")`聲明在需要使用的`@Bean`的方法上,然后SpringBoot就會自動調用這個Bean(此處是DataSource)的set方法,然后完成注入。使用的前提是:**該類必須有對應屬性的set方法!** 我們將jdbc的url改成:/heima,再次測試: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525497513206.png?lastModify=1649173481) ## 3.5.Yaml配置文件 ### 3.5.1.基本用法 配置文件除了可以使用application.properties類型,還可以使用后綴名為:.yml或者.yaml的類型,也就是:application.yml或者application.yaml ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1531284020457.png?lastModify=1649173481) 基本格式: ~~~ jdbc: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/heima username: root password: 123 ~~~ 如果兩個配置文件都有,會把兩個文件的配置合并,如果有重復屬性,以Properties中的為準。 ### 3.5.2 高級用法 SpringBoot注入不僅支持基本數據類型的屬性注入,而且支持各種java類型的注入,如List、Map、Set、Object等。 例如我們這樣配置一些屬性: ~~~ cn: itcast: name: jack age: 21 user: name: jack age: 21 girls: - Rose - 柳巖 - 劉亦菲 - 范冰冰 ~~~ 如果我們以cn.itcast為前綴,那么有下列屬性需要注入: * name和age是基本類型屬性 * user是對象類型屬性,可以用Map或者類來接收,又包含下面的子屬性: * name和age:基本類型 * girls:數組或集合類型 我們用來接收的java類: ~~~ @Data @Component @ConfigurationProperties("cn.itcast") public class ItcastProperties { private String name; private int age; private User user = new User();// 用對象介紹cn.itcast.user @Data class User{ // 對應cn.itcast.user類型,包含name、age、girls子屬性 private String name; private int age; private List<String> girls;// 是一個字符串集合 } } ~~~ 最后的結果: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1545877544739.png?lastModify=1649173481) # 4.自動配置原理 使用SpringBoot之后,一個整合了SpringMVC的WEB工程開發,變的無比簡單,那些繁雜的配置都消失不見了,這是如何做到的? 一切魔力的開始,都是從我們的main函數來的,所以我們再次來看下啟動類: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525488044650.png?lastModify=1649173481) 我們發現特別的地方有兩個: * 注解:@SpringBootApplication * run方法:SpringApplication.run() 我們分別來研究這兩個部分。 ## 4.1.了解@SpringBootApplication 點擊進入,查看源碼: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525488226710.png?lastModify=1649173481) 這里重點的注解有3個: * @SpringBootConfiguration * @EnableAutoConfiguration * @ComponentScan ### 4.1.1.@SpringBootConfiguration 我們繼續點擊查看源碼: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525488518514.png?lastModify=1649173481) 通過這段我們可以看出,在這個注解上面,又有一個`@Configuration`注解。通過上面的注釋閱讀我們知道:這個注解的作用就是聲明當前類是一個配置類,然后Spring會自動掃描到添加了`@Configuration`的類,并且讀取其中的配置信息。而`@SpringBootConfiguration`是來聲明當前類是SpringBoot應用的配置類,項目中只能有一個。所以一般我們無需自己添加。 ### 4.1.2.@EnableAutoConfiguration 關于這個注解,官網上有一段說明: > The second class-level annotation is `@EnableAutoConfiguration`. This annotation tells Spring Boot to “guess” how you want to configure Spring, based on the jar dependencies that you have added. Since `spring-boot-starter-web` added Tomcat and Spring MVC, the auto-configuration assumes that you are developing a web application and sets up Spring accordingly. 簡單翻譯以下: > 第二級的注解`@EnableAutoConfiguration`,告訴SpringBoot基于你所添加的依賴,去“猜測”你想要如何配置Spring。比如我們引入了`spring-boot-starter-web`,而這個啟動器中幫我們添加了`tomcat`、`SpringMVC`的依賴。此時自動配置就知道你是要開發一個web應用,所以就幫你完成了web及SpringMVC的默認配置了! 總結,SpringBoot內部對大量的第三方庫或Spring內部庫進行了默認配置,這些配置是否生效,取決于我們是否引入了對應庫所需的依賴,如果有那么默認配置就會生效。 所以,我們使用SpringBoot構建一個項目,只需要引入所需框架的依賴,配置就可以交給SpringBoot處理了。除非你不希望使用SpringBoot的默認配置,它也提供了自定義配置的入口。 ### 4.1.3.@ComponentScan 我們跟進源碼: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525498265579.png?lastModify=1649173481) 并沒有看到什么特殊的地方。我們查看注釋: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525498351385.png?lastModify=1649173481) 大概的意思: > 配置組件掃描的指令。提供了類似與`<context:component-scan>`標簽的作用 > > 通過basePackageClasses或者basePackages屬性來指定要掃描的包。如果沒有指定這些屬性,那么將從聲明這個注解的類所在的包開始,掃描包及子包 而我們的@SpringBootApplication注解聲明的類就是main函數所在的啟動類,因此掃描的包是該類所在包及其子包。因此,**一般啟動類會放在一個比較前的包目錄中。** ## 4.2.默認配置原理 ### 4.2.1.spring.factories 在SpringApplication類構建的時候,有這樣一段初始化代碼: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533684134317.png?lastModify=1649173481) 跟進去: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533684245960.png?lastModify=1649173481) 這里發現會通過loadFactoryNames嘗試加載一些FactoryName,然后利用createSpringFactoriesInstances將這些加載到的類名進行實例化。 繼續跟進loadFactoryNames方法: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533684398745.png?lastModify=1649173481) 發現此處會利用類加載器加載某個文件:`FACTORIES_RESOURCE_LOCATION`,然后解析其內容。我們找到這個變量的聲明: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533684452698.png?lastModify=1649173481) 可以發現,其地址是:`META-INF/spring.factories`,我們知道,ClassLoader默認是從classpath下讀取文件,因此,SpringBoot會在初始化的時候,加載**所有**classpath:META-INF/spring.factories文件,包括jar包當中的。 而在Spring的一個依賴包:spring-boot-autoconfigure中,就有這樣的文件: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533684761902.png?lastModify=1649173481) 以后我們引入的任何第三方啟動器,只要實現自動配置,也都會有類似文件。 ### 4.2.1默認配置類 我們打開剛才的spring.factories文件: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533684789998.png?lastModify=1649173481) 可以發現以EnableAutoConfiguration接口為key的一系列配置,key所對應的值,就是所有的自動配置類,可以在當前的jar包中找到這些自動配置類: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525499397690.png?lastModify=1649173481) 還有: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525499426598.png?lastModify=1649173481) 非常多,幾乎涵蓋了現在主流的開源框架,例如: * redis * jms * amqp * jdbc * jackson * mongodb * jpa * solr * elasticsearch ... 等等 我們來看一個我們熟悉的,例如SpringMVC,查看mvc 的自動配置類: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525499859426.png?lastModify=1649173481) 打開WebMvcAutoConfiguration: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500000816.png?lastModify=1649173481) 我們看到這個類上的4個注解: * `@Configuration`:聲明這個類是一個配置類 * `@ConditionalOnWebApplication(type = Type.SERVLET)` ConditionalOn,翻譯就是在某個條件下,此處就是滿足項目的類是是Type.SERVLET類型,也就是一個普通web工程,顯然我們就是 * `@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })` 這里的條件是OnClass,也就是滿足以下類存在:Servlet、DispatcherServlet、WebMvcConfigurer,其中Servlet只要引入了tomcat依賴自然會有,后兩個需要引入SpringMVC才會有。這里就是判斷你是否引入了相關依賴,引入依賴后該條件成立,當前類的配置才會生效! * `@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)` 這個條件與上面不同,OnMissingBean,是說環境中沒有指定的Bean這個才生效。其實這就是自定義配置的入口,也就是說,如果我們自己配置了一個WebMVCConfigurationSupport的類,那么這個默認配置就會失效! 接著,我們查看該類中定義了什么: 視圖解析器: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500405278.png?lastModify=1649173481) 處理器適配器(HandlerAdapter): ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500452517.png?lastModify=1649173481) 還有很多,這里就不一一截圖了。 ### 4.2.2.默認配置屬性 另外,這些默認配置的屬性來自哪里呢? ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500697391.png?lastModify=1649173481) 我們看到,這里通過@EnableAutoConfiguration注解引入了兩個屬性:WebMvcProperties和ResourceProperties。這不正是SpringBoot的屬性注入玩法嘛。 我們查看這兩個屬性類: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500810914.png?lastModify=1649173481) 找到了內部資源視圖解析器的prefix和suffix屬性。 ResourceProperties中主要定義了靜態資源(.js,.html,.css等)的路徑: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500921773.png?lastModify=1649173481) 如果我們要覆蓋這些默認屬性,只需要在application.properties中定義與其前綴prefix和字段名一致的屬性即可。 ## 4.3.總結 SpringBoot為我們提供了默認配置,而默認配置生效的步驟: * @EnableAutoConfiguration注解會去尋找`META-INF/spring.factories`文件,讀取其中以`EnableAutoConfiguration`為key的所有類的名稱,這些類就是提前寫好的自動配置類 * 這些類都聲明了`@Configuration`注解,并且通過`@Bean`注解提前配置了我們所需要的一切實例。完成自動配置 * 但是,這些配置不一定生效,因為有`@ConditionalOn`注解,滿足一定條件才會生效。比如條件之一:是一些相關的類要存在 * 類要存在,我們只需要引入了相關依賴(啟動器),依賴有了條件成立,自動配置生效。 * 如果我們自己配置了相關Bean,那么會覆蓋默認的自動配置的Bean * 我們還可以通過配置application.properties文件,來覆蓋自動配置中的屬性 1)啟動器 所以,我們如果不想配置,只需要引入依賴即可,而依賴版本我們也不用操心,因為只要引入了SpringBoot提供的stater(啟動器),就會自動管理依賴及版本了。 因此,玩SpringBoot的第一件事情,就是找啟動器,SpringBoot提供了大量的默認啟動器 2)全局配置 另外,SpringBoot的默認配置,都會讀取默認屬性,而這些屬性可以通過自定義`application.properties`文件來進行覆蓋。這樣雖然使用的還是默認配置,但是配置中的值改成了我們自定義的。 因此,玩SpringBoot的第二件事情,就是通過`application.properties`來覆蓋默認屬性值,形成自定義配置。我們需要知道SpringBoot的默認屬性key,非常多,可以再idea中自動提示 # 5.SpringBoot實踐 接下來,我們來看看如何用SpringBoot來玩轉以前的SSM,我們會在數據庫引入一張用戶表tb\_user和實體類User tb\_user: 詳見課前筆記中:《tb\_user.sql》文件。 user實體類: ~~~ public class User{ // id private Long id; // 用戶名 private String userName; // 密碼 private String password; // 姓名 private String name; // 年齡 private Integer age; // 性別,1男性,2女性 private Integer sex; // 出生日期 private Date birthday; // 創建時間 private Date created; // 更新時間 private Date updated; // 備注 private String note; } ~~~ ## 5.0.Lombok 我們編寫pojo時,經常需要編寫構造函數和gettersetter方法,屬性多的時候,就非常浪費時間,使用lombok插件可以解決這個問題: 在idea中安裝lombok插件: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533700386138.png?lastModify=1649173481) 需要在maven中引入依賴: ~~~ <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> ~~~ 然后可以在Bean上使用: @Data :自動提供getter和setter、hashCode、equals、toString等方法 @Getter:自動提供getter方法 @Setter:自動提供setter方法 @Slf4j:自動在bean中提供log變量,其實用的是slf4j的日志功能。 例如,我們在javabean上加@Data,那么就可以getter和setter等方法的編寫,lombok插件會幫我們自動生成: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1533778469361.png?lastModify=1649173481) ## 5.1.整合SpringMVC 雖然默認配置已經可以使用SpringMVC了,不過我們有時候需要進行自定義配置。 ### 5.1.0.日志控制 日志級別控制: ~~~ logging: level: cn.itcast: debug org.springframework: debug ~~~ 其中: * logging.level:是固定寫法,說明下面是日志級別配置,日志相關其它配置也可以使用。 * cn.itcast和org.springframework是指定包名,后面的配置僅對這個包有效。 * debug:日志的級別 ### 5.1.1.修改端口 查看SpringBoot的全局屬性可知,端口通過以下方式配置: ~~~ # 映射端口 server.port=80 ~~~ 重啟服務后測試: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525511878440.png?lastModify=1649173481) ### 5.1.2.訪問靜態資源 現在,我們的項目是一個jar工程,那么就沒有webapp,我們的靜態資源該放哪里呢? 回顧我們上面看的源碼,有一個叫做ResourceProperties的類,里面就定義了靜態資源的默認查找路徑: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525500921773.png?lastModify=1649173481) 默認的靜態資源路徑為: * classpath:/META-INF/resources/ * classpath:/resources/ * classpath:/static/ * classpath:/public 只要靜態資源放在這些目錄中任何一個,SpringMVC都會幫我們處理。 我們習慣會把靜態資源放在`classpath:/static/`目錄下。我們創建目錄,并且添加一些靜態資源: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525512196866.png?lastModify=1649173481) 重啟項目后測試: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525512253682.png?lastModify=1649173481) ### 5.1.3.添加攔截器 攔截器也是我們經常需要使用的,在SpringBoot中該如何配置呢? 攔截器不是一個普通屬性,而是一個類,所以就要用到java配置方式了。在SpringBoot官方文檔中有這么一段說明: > If you want to keep Spring Boot MVC features and you want to add additional [MVC configuration](https://docs.spring.io/spring/docs/5.0.5.RELEASE/spring-framework-reference/web.html#mvc) (interceptors, formatters, view controllers, and other features), you can add your own `@Configuration` class of type `WebMvcConfigurer` but **without** `@EnableWebMvc`. If you wish to provide custom instances of `RequestMappingHandlerMapping`, `RequestMappingHandlerAdapter`, or `ExceptionHandlerExceptionResolver`, you can declare a `WebMvcRegistrationsAdapter` instance to provide such components. > > If you want to take complete control of Spring MVC, you can add your own `@Configuration` annotated with `@EnableWebMvc`. 翻譯: > 如果你想要保持Spring Boot 的一些默認MVC特征,同時又想自定義一些MVC配置(包括:攔截器,格式化器, 視圖控制器、消息轉換器 等等),你應該讓一個類實現`WebMvcConfigurer`,并且添加`@Configuration`注解,但是**千萬不要**加`@EnableWebMvc`注解。如果你想要自定義`HandlerMapping`、`HandlerAdapter`、`ExceptionResolver`等組件,你可以創建一個`WebMvcRegistrationsAdapter`實例 來提供以上組件。 > > 如果你想要完全自定義SpringMVC,不保留SpringBoot提供的一切特征,你可以自己定義類并且添加`@Configuration`注解和`@EnableWebMvc`注解 總結:通過實現`WebMvcConfigurer`并添加`@Configuration`注解來實現自定義部分SpringMvc配置。 首先我們定義一個攔截器: ~~~ @Slf4j public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { log.debug("preHandle method is now running!"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { log.debug("postHandle method is now running!"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { log.debug("afterCompletion method is now running!"); } } ~~~ 然后,我們定義配置類,注冊攔截器: ~~~ @Configuration public class MvcConfig implements WebMvcConfigurer{ /** * 重寫接口中的addInterceptors方法,添加自定義攔截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { // 通過registry來注冊攔截器,通過addPathPatterns來添加攔截路徑 registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**"); } } ~~~ 結構如下: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525513555179.png?lastModify=1649173481) 接下來運行并查看日志: 你會發現日志中什么都沒有,因為我們記錄的log級別是debug,默認是顯示info以上,我們需要進行配置。 SpringBoot通過`logging.level.*=debug`來配置日志級別,\*填寫包名 ~~~ # 設置com.leyou包的日志級別為debug logging: level: com.leyou=debug ~~~ 再次運行查看: ~~~ 2018-05-05 17:50:01.811 DEBUG 4548 --- [p-nio-80-exec-1] com.leyou.interceptor.LoginInterceptor : preHandle method is now running! 2018-05-05 17:50:01.854 DEBUG 4548 --- [p-nio-80-exec-1] com.leyou.interceptor.LoginInterceptor : postHandle method is now running! 2018-05-05 17:50:01.854 DEBUG 4548 --- [p-nio-80-exec-1] com.leyou.interceptor.LoginInterceptor : afterCompletion method is now running! ~~~ ## 5.2.整合jdbc和事務 spring中的jdbc連接和事務是配置中的重要一環,在SpringBoot中該如何處理呢? 答案是不需要處理,我們只要找到SpringBoot提供的啟動器即可: ~~~ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> ~~~ 當然,不要忘了數據庫驅動,SpringBoot并不知道我們用的什么數據庫,這里我們選擇MySQL: ~~~ <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> ~~~ 至于事務,SpringBoot中通過注解來控制。就是我們熟知的`@Transactional` ~~~ @Service public class UserService { public User queryById(Long id){ // 開始查詢 return new User(); } @Transactional public void deleteById(Long id){ // 開始刪除 System.out.println("刪除了: " + id); } } ~~~ ## 5.3.整合連接池 其實,在剛才引入jdbc啟動器的時候,SpringBoot已經自動幫我們引入了一個連接池: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525514424562.png?lastModify=1649173481) HikariCP應該是目前速度最快的連接池了,我們看看它與c3p0的對比: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1525516441005.png?lastModify=1649173481) 因此,我們只需要指定連接池參數即可: ~~~ spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mydb01 username: root password: 123 ~~~ ## 5.4.整合mybatis ### 5.4.1.mybatis SpringBoot官方并沒有提供Mybatis的啟動器,不過Mybatis[官網](https://github.com/mybatis/spring-boot-starter)自己實現了: ~~~ <!--mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> ~~~ 配置,一些文件位置相關的,mybatis知道,需要我們來指定: ~~~ mybatis: # mybatis 別名掃描 type-aliases-package=cn.itcast.pojo # mapper.xml文件位置,如果沒有映射文件,請注釋掉 mapper-locations=classpath:mappers/*.xml ~~~ 另外,Mapper接口的位置在application.yml中并不能配置,Mapper接口的掃描有兩種實現方式: > 方式一 我們需要給每一個Mapper接口添加`@Mapper`注解,由Spring來掃描這些注解,完成Mapper的動態代理。 ~~~ @Mapper public interface UserMapper { } ~~~ > 方式二 在啟動類上添加掃描包注解(**推薦**): ~~~ @SpringBootApplication @MapperScan("cn.itcast.mapper") public class Application { public static void main(String[] args) { // 啟動代碼 SpringApplication.run(Application.class, args); } } ~~~ 這種方式的好處是,不用給每一個Mapper都添加注解。 以下代碼示例中,我們將采用@MapperScan掃描方式進行。 ### 5.4.2.通用mapper 通用Mapper的作者也為自己的插件編寫了啟動器,我們直接引入即可: ~~~ <!-- 通用mapper --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.1.5</version> </dependency> ~~~ **注意**:一旦引入了通用Mapper的啟動器,會覆蓋Mybatis官方啟動器的功能,因此需要移除對官方Mybatis啟動器的依賴。 無需任何配置就可以使用了。如果有特殊需要,可以到通用mapper官網查看:[https://github.com/abel533/Mapper/wiki/3.config](https://github.com/abel533/Mapper/wiki/3.config) 另外,我們需要把啟動類上的@MapperScan注解修改為通用mapper中自帶的: ![](file://G:/BaiduNetdiskDownload/2020java/13.SpringBoot/day01_springboot/%E8%AE%B2%E4%B9%89/assets/1531284085887.png?lastModify=1649173481) 接下來就是通用mapper的使用步驟了: 1)繼承Mapper接口 ~~~ public interface UserMapper extends Mapper<User>{ } ~~~ 2)在實體類上加JPA注解: ~~~ @Data @Table(name = "tb_user") public class User{ @Id @KeySql(useGeneratedKeys = true) // 開啟自增主鍵回顯功能 private Long id; private String userName; private String password; private String name; private Integer age; private Integer sex; private Date birthday; private Date created; private Date updated; private String note; } ~~~ 此時,對UserService的代碼進行簡單改造: ~~~ @Service public class UserService { @Autowired private UserMapper userMapper; public User queryById(Long id){ // 查詢 return userMapper.selectByPrimaryKey(id); } @Transactional public Integer saveUser(User user){ // 新增 return userMapper.insertSelective(user); } } ~~~ ## 5.5.啟動測試 將controller進行簡單改造: ~~~ @RestController public class HelloController { @Autowired private UserService userService; @GetMapping("/user/{id}") public User hello(@PathVariable("id") Long id) { User user = this.userService.queryById(id); return user; } } ~~~ 我們啟動項目,查看:
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看