Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 天堂网2014av,99色视频在线观看,亚洲精品高清中文字幕完整版

          整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢(xún)熱線:

          SpringBoot萬(wàn)字長(zhǎng)文介紹

          pring Boot是Pivotal團(tuán)隊(duì)在Spring的基礎(chǔ)上提供的一套全新的開(kāi)源框架,其目的是為了簡(jiǎn)化Spring應(yīng)用的搭建和開(kāi)發(fā)過(guò)程。Spring Boot去除了大量的XML配置文件,簡(jiǎn)化了復(fù)雜的依賴(lài)管理。

          官網(wǎng)地址:spring.io/projects/sp…

          Spring Boot入門(mén)

          簡(jiǎn)介

          Spring Boot是簡(jiǎn)化Spring應(yīng)用開(kāi)發(fā)的一個(gè)框架、整個(gè)Spring技術(shù)棧的一個(gè)大整合(Spring全家桶時(shí)代)、J2EE開(kāi)發(fā)的一站式解決方案(Spring Cloud是分布式整體解決方案)。 優(yōu)點(diǎn): – 快速創(chuàng)建獨(dú)立運(yùn)行的Spring項(xiàng)目以及與主流框架集成 – 使用嵌入式的Servlet容器,應(yīng)用無(wú)需打成WAR包 – starters自動(dòng)依賴(lài)與版本控制 – 大量的自動(dòng)配置,簡(jiǎn)化開(kāi)發(fā),也可修改默認(rèn)值 – 無(wú)需配置XML,無(wú)代碼生成,開(kāi)箱即用 – 準(zhǔn)生產(chǎn)環(huán)境的運(yùn)行時(shí)應(yīng)用監(jiān)控 – 與云計(jì)算的天然集成

          單體應(yīng)用與微服務(wù)

          – 單體應(yīng)用:ALL IN ONE(所有內(nèi)容都在一個(gè)應(yīng)用里面) – 微服務(wù):每一個(gè)功能元素最終都是一個(gè)可獨(dú)立替換和獨(dú)立升級(jí)的軟件單元 微服務(wù)是一種架構(gòu)風(fēng)格(服務(wù)微化),一個(gè)應(yīng)用應(yīng)該是一組小型服務(wù),可以通過(guò)HTTP的方式進(jìn)行互通

          HelloWorld案例

          工程創(chuàng)建及案例可以參考文章進(jìn)行操作:在IDEA中創(chuàng)建SpringBoot項(xiàng)目

          POM文件

          父項(xiàng)目是Spring Boot的版本仲裁中心(他來(lái)真正管理Spring Boot應(yīng)用里面的所有依賴(lài)版本),以后我們導(dǎo)入依賴(lài)默認(rèn)是不需要寫(xiě)版本(沒(méi)有在dependencies里面管理的依賴(lài)自然需要聲明版本號(hào))

          xml 代碼解讀復(fù)制代碼<parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.7.11</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>

          啟動(dòng)器 spring-boot-starter(spring-boot場(chǎng)景啟動(dòng)器),spring-boot-starter-web 幫我們導(dǎo)入了web模塊正常運(yùn)行所依賴(lài)的組件。

          xml 代碼解讀復(fù)制代碼<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>

          Spring Boot將所有的功能場(chǎng)景都抽取出來(lái),做成一個(gè)個(gè)的starters(啟動(dòng)器),只需要在項(xiàng)目里面引入這些starter相關(guān)場(chǎng)景的所有依賴(lài)都會(huì)導(dǎo)入進(jìn)來(lái)。要用什么功能就導(dǎo)入什么場(chǎng)景的啟動(dòng)器。

          主程序類(lèi)

          // 自動(dòng)生成的
          @SpringBootApplication
          public class SpringBootDemo0Application {
              public static void main(String[] args) {
                  SpringApplication.run(SpringBootDemo0Application.class, args);
              }
          }

          @SpringBootApplication: Spring Boot應(yīng)用標(biāo)注在某個(gè)類(lèi)上說(shuō)明這個(gè)類(lèi)是SpringBoot的主配置類(lèi),SpringBoot 就應(yīng)該運(yùn)行這個(gè)類(lèi)的main方法來(lái)啟動(dòng)SpringBoot應(yīng)用。

          Spring Boot配置

          配置文件

          SpringBoot使用一個(gè)全局的配置文件,配置文件名固定:application.properties 或者 application.yml。配置文件放在 src/main/resources目錄 或者 類(lèi)路徑/config 下。作用是修改SpringBoot自動(dòng)配置的默認(rèn)值。

          YAML

          YAML(YAML Ain't Markup Language),.yml為結(jié)尾,以數(shù)據(jù)為中心,比json、xml等更適合做配置文件。

          YAML配置例子
          server:
              port: 8081
          
          等價(jià)于XML配置:
          <server>
              <port>8081</port>
          </server>

          【語(yǔ)法】 key: value(注意冒號(hào)后面有個(gè)空格) 以空格的縮進(jìn)來(lái)控制層級(jí)關(guān)系,只要是左對(duì)齊的一列數(shù)據(jù),都是同一個(gè)層級(jí)

          【值寫(xiě)法】

          (1)字面量:普通的值(數(shù)字,字符串,布爾)

          • k: v,字面量直接寫(xiě)
          • 字符串默認(rèn)不用加上單引號(hào)或者雙引號(hào)
          • ""(雙引號(hào)),name: "zhangsan \n lisi" 會(huì)輸出 zhangsan 換行 lisi
          • ''(單引號(hào)),name: 'zhangsan \n lisi' 會(huì)輸出 zhangsan \n lisi

          (2)對(duì)象、Map

          • k: v,在下一行來(lái)寫(xiě)對(duì)象的屬性和值
          friends:
              lastName: zhangsan
              age: 20
          或者:
          friends: {lastName:zhangsan,age:18}

          (3)數(shù)組(List、Set)

          • 用- 值表示數(shù)組中的一個(gè)元素
          pets:
              ‐ cat
              ‐ dog
              ‐ pig
          pets: [cat,dog,pig]

          配置文件值注入

          @ConfigurationProperties

          1)導(dǎo)入配置文件處理器

          <!‐‐導(dǎo)入配置文件處理器,配置文件進(jìn)行綁定就會(huì)有提示‐‐>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-configuration-processor</artifactId>
              <optional>true</optional>
          </dependency>

          2)javaBean對(duì)象 @ConfigurationProperties(prefix = "person") 會(huì)將配置文件和類(lèi)進(jìn)行綁定:

          /**
           * 將配置文件中配置的每一個(gè)屬性的值,映射到這個(gè)組件中
           * @ConfigurationProperties:告訴SpringBoot將本類(lèi)中的所有屬性和配置文件中相關(guān)的配置進(jìn)行綁定;
           *      prefix = "person":配置文件中哪個(gè)下面的所有屬性進(jìn)行一一映射
           * 只有這個(gè)組件是容器中的組件,才能容器提供的@ConfigurationProperties功能;
           */
          @Component
          @ConfigurationProperties(prefix = "person")
          public class Person {
              private String lastName;
              private Integer age;
              private Boolean boss;
              private Date birth;
              private Map<String, Object> maps;
              private List<Object> lists;
              private Dog dog;
              ....
          }

          3)配置文件 application.yml

          person:
            lastName: haha
            age: 18
            boss: false
            birth: 2022/01/01
            maps: {k1: v1,k2: v2}
            lists:
              - lisi
              - wangwu
            dog:
              name: 芒果
              age: 1

          或者配置文件application.properties

          properties 代碼解讀復(fù)制代碼#解決亂碼問(wèn)題
          spring.message.encoding=UTF-8
          #person
          person.last-name=haha
          person.age=20
          person.birth=2022/01/02
          person.boss=true
          person.maps.k1=v1
          person.maps.k2=v2
          person.lists=a,b,c
          person.dog.name=丸子
          person.dog.age=5

          亂碼問(wèn)題還需要配置:

          4)單元測(cè)試,先將內(nèi)容注入(@Autowired)然后使用

          與@Value的區(qū)別

          @ConfigurationProperties@Value 的區(qū)別:

          • @ConfigurationProperties 是批量注入配置文件中的屬性,@Value 是一個(gè)個(gè)指定
          • @ConfigurationProperties 支持松散綁定(松散語(yǔ)法) 、不支持SpEL(表達(dá)式如#{2*4})、支持JSR303數(shù)據(jù)校驗(yàn) 、支持復(fù)雜類(lèi)型封裝(如map)
          • @Value 不支持松散綁定(松散語(yǔ)法) 、支持SpEL、不支持JSR303數(shù)據(jù)校驗(yàn) 、不支持復(fù)雜類(lèi)型封裝
          @Component
          // @ConfigurationProperties(prefix = "person")
          public class Person {
              @Value("${person.last-name}")
              private String lastName;
              @Value("#{2*4}")
              private Integer age;
              @Value("true")
              private Boolean boss;
              @Value("${person.birth}")
              private Date birth;
              ...

          松散綁定: – person.firstName:使用標(biāo)準(zhǔn)方式 – person.first-name:大寫(xiě)用- – person.first_name:大寫(xiě)用_ – PERSON_FIRST_NAME:推薦系統(tǒng)屬性使用這種寫(xiě)法 JSR303數(shù)據(jù)校驗(yàn):

          使用規(guī)則:

          • 如果說(shuō),我們只是在某個(gè)業(yè)務(wù)邏輯中需要獲取一下配置文件中的某項(xiàng)值,使用@Value
          @Value("${febase.api.host}")
          private String febaseHost;
          • 如果說(shuō),我們專(zhuān)門(mén)編寫(xiě)了一個(gè)javaBean來(lái)和配置文件進(jìn)行映射,我們就直接使用@ConfigurationProperties

          讀取外部配置文件

          @PropertySource:加載指定的配置文件

          @PropertySource("classpath:person.properties")
          @Component
          @ConfigurationProperties(prefix = "person")
          public class Person {
              private String lastName;
              private Integer age;
              private Boolean boss;
              ...
          }

          @ImportResource:導(dǎo)入Spring的配置文件,讓配置文件里面的內(nèi)容生效--標(biāo)注在一個(gè)配置類(lèi)上 如下我們自己編寫(xiě)的配置文件:

          xml 代碼解讀復(fù)制代碼<?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          
            <bean id="helloService" class="com.stydyspring.spring_boot_demo0.service.HelloService"></bean>
          </beans>

          我們可以標(biāo)注在主配置類(lèi)上:

          @SpringBootApplication
          // 導(dǎo)入Spring的配置文件讓其生效
          @ImportResource(locations = {"classpath:beans.xml"})
          public class SpringBootDemo0Application {
              public static void main(String[] args) {
                  SpringApplication.run(SpringBootDemo0Application.class, args);
              }
          }

          測(cè)試:

          @SpringBootTest
          class SpringBootDemo0ApplicationTests {
              @Autowired
              ApplicationContext ioc;
          
              @Test
              public void testHelloService(){
                  boolean containsBean = ioc.containsBean("helloService");
                  System.out.println(containsBean); 
                  // 上一步?jīng)]加@ImportResource之前返回false
                  // 添加@ImportResource之后返回true
              }
          }

          SpringBoot推薦給容器中添加組件的方式,推薦使用全注解的方式 @Configuration

          /**
           * @Configuration:指明當(dāng)前類(lèi)是一個(gè)配置類(lèi),就是來(lái)替代之前的Spring配置文件
           * 
           * 在配置文件中用<bean><bean/>標(biāo)簽添加組件。在配置類(lèi)中使用@Bean注解
           */
          @Configuration
          public class MyAppConfig {
              // 將方法的返回值添加到容器中;容器中這個(gè)組件默認(rèn)的id就是方法名
              @Bean
              public HelloService helloService(){
                  System.out.println("配置類(lèi)@Bean給容器中添加組件了");
                  return new HelloService();
              }
          }

          配置文件占位符

          隨機(jī)數(shù)

          ${random.value}、${random.int}、${random.long}、${random.uuid}
          ${random.int(10)}、${random.int[1024,65536]}

          占位符獲取之前配置的值,如果沒(méi)有可以是用:指定默認(rèn)值

          properties 代碼解讀復(fù)制代碼#person
          person.last-name=haha${random.uuid}
          person.age=${random.int}
          person.birth=2222/02/02
          person.boss=false
          person.maps.k1=v11111
          person.maps.k2=v22222
          person.lists=a,b,c,d,e,f
          person.dog.name=${person.hello:hello}_dog
          person.dog.age=1

          Profile

          Profile是Spring對(duì)不同環(huán)境提供不同配置功能的支持,可以通過(guò)激活、指定參數(shù)等方式快速切換環(huán)境。 多profile文件形式格式如:application-{profile}.properties/yml,如 application-dev.properties、application-prod.properties

          默認(rèn)使用application.properties的配置

          激活方式:

          • 命令行
          • --spring.profiles.active=dev
            • java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
          • 配置文件 spring.profiles.active=dev
          • jvm參數(shù) –Dspring.profiles.active=dev


          yml支持多文檔塊方式:

          properties 代碼解讀復(fù)制代碼server:
          	port: 8081
          spring:
          	profiles:
          	active: prod
          
          ‐‐‐
          server:
          	port: 8083
          spring:
          	profiles: dev
          
          ‐‐‐
          server:
          	port: 8084
          spring:
          	profiles: prod #指定屬于哪個(gè)環(huán)境

          配置文件加載位置

          spring boot 啟動(dòng)會(huì)掃描以下位置的application.properties或者application.yml文件作為Spring boot的默認(rèn)配置文件

          1. file:./config/
          2. file:./
          3. classpath:/config/
          4. classpath:/

          以上是按照優(yōu)先級(jí)從高到低的順序,所有位置的文件都會(huì)被加載,高優(yōu)先級(jí)配置內(nèi)容會(huì)覆蓋低優(yōu)先級(jí)配置內(nèi)容。 可以通過(guò)配置spring.config.location來(lái)改變默認(rèn)配置。項(xiàng)目打包好以后,可以使用命令行參數(shù)的形式,啟動(dòng)項(xiàng)目的時(shí)候來(lái)指定配置文件的新位置;指定配置文件和默認(rèn)加載的這些配置文件共同起作用形成互補(bǔ)配置: java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties

          外部配置加載順序

          Spring Boot支持多種外部配置方式,優(yōu)先級(jí)從高到低。高優(yōu)先級(jí)的配置覆蓋低優(yōu)先級(jí)的配置,所有的配置會(huì)形成互補(bǔ)配置:

          1. 命令行參數(shù)
            1. 所有的配置都可以在命令行上進(jìn)行指定:java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
            2. 多個(gè)配置用空格分開(kāi); --配置項(xiàng)=值
          2. 來(lái)自java:comp/env的JNDI屬性
          3. Java系統(tǒng)屬性(System.getProperties())
          4. 操作系統(tǒng)環(huán)境變量
          5. RandomValuePropertySource配置的random.*屬性值

          由jar包外向jar包內(nèi)進(jìn)行尋找。優(yōu)先加載帶profile

          1. jar包外部的application-{profile}.properties或application.yml(帶spring.profile)配置文件
          2. jar包內(nèi)部的application-{profile}.properties或application.yml(帶spring.profile)配置文件

          再來(lái)加載不帶profile

          1. jar包外部的application.properties或application.yml(不帶spring.profile)配置文件
          2. jar包內(nèi)部的application.properties或application.yml(不帶spring.profile)配置文件
          3. @Configuration注解類(lèi)上的@PropertySource
          4. 通過(guò)SpringApplication.setDefaultProperties指定的默認(rèn)屬性

          自動(dòng)配置原理

          配置文件可以配置的屬性:docs.spring.io/spring-boot…

          自動(dòng)配置原理: 1)Spring Boot啟動(dòng)時(shí)加載主配置類(lèi)(帶有@SpringBootApplication),其里面開(kāi)啟了自動(dòng)配置功能@EnableAutoConfiguration 2)@EnableAutoConfiguration利用@Import(AutoConfigurationImportSelector.class)給容器導(dǎo)入一些組件。導(dǎo)入的組件是通過(guò)List configurations = getCandidateConfigurations(annotationMetadata, attributes);獲取到的。里面通過(guò)SpringFactoriesLoader.loadFactoryNames 掃描所有jar包類(lèi)路徑下"META-INF/spring.factories",把掃描到的這些文件的內(nèi)容包裝成properties對(duì)象,從properties中獲取到EnableAutoConfiguration.class類(lèi)(類(lèi)名)對(duì)應(yīng)的值,然后把他們添加在容器中。其實(shí)就是將類(lèi)路徑下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中。每一個(gè)這樣的 xxxAutoConfiguration 類(lèi)都是容器中的一個(gè)組件,都加入到容器中;用他們來(lái)做自動(dòng)配置; 3)每一個(gè)自動(dòng)配置類(lèi)進(jìn)行自動(dòng)配置功能 4)以HttpEncodingAutoConfiguration配置類(lèi)進(jìn)行分析:

          // 表示這是一個(gè)配置類(lèi),以前編寫(xiě)的配置文件一樣,也可以給容器中添加組件
          @AutoConfiguration
          // 啟動(dòng)指定類(lèi)的ConfigurationProperties功能,將配置文件中對(duì)應(yīng)的值和xxxProperties綁定起來(lái),???把xxxProperties加入到ioc容器中
          @EnableConfigurationProperties(ServerProperties.class)
          // Spring底層@Conditional注解(Spring注解版),根據(jù)不同的條件,如果滿(mǎn)足指定的條件,整個(gè)配置類(lèi)里面的配置就會(huì)生效;
          @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
          // 判斷當(dāng)前項(xiàng)目有沒(méi)有這個(gè)類(lèi)CharacterEncodingFilter-SpringMVC中進(jìn)行亂碼解決的過(guò)濾器
          @ConditionalOnClass(CharacterEncodingFilter.class)
          // 判斷配置文件中是否存在某個(gè)配置 spring.servlet.encoding.enabled;如果不存在,判斷也是成立的
          // 即使我們配置文件中不配置spring.servlet.encoding.enabled=true,也是默認(rèn)生效的;
          @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
          public class HttpEncodingAutoConfiguration {
          
              // 他已經(jīng)和SpringBoot的配置文件映射了
              private final Encoding properties;
          
              // 只有一個(gè)有參構(gòu)造器的情況下,參數(shù)的值就會(huì)從容器中拿
              public HttpEncodingAutoConfiguration(ServerProperties properties) {
                  this.properties = properties.getServlet().getEncoding();
              }
          
              @Bean  // 給容器中添加一個(gè)組件,這個(gè)組件的某些值需要從properties中獲取
              @ConditionalOnMissingBean // 判斷容器沒(méi)有這個(gè)組件,就給配置一個(gè)
              public CharacterEncodingFilter characterEncodingFilter() {
                  CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
                  filter.setEncoding(this.properties.getCharset().name());
                  filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
                  filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
                  return filter;
              }
          	...
          }

          根據(jù)當(dāng)前不同的條件判斷,決定這個(gè)配置類(lèi)是否生效 一但這個(gè)配置類(lèi)生效,這個(gè)配置類(lèi)就會(huì)給容器中添加各種組件,這些組件的屬性是從對(duì)應(yīng)的properties類(lèi)中獲取的,這些類(lèi)里面的每一個(gè)屬性又是和配置文件綁定的 5)、所有在配置文件中能配置的屬性都是在xxxxProperties類(lèi)中封裝著,配置文件能配置什么就可以參照某個(gè)功能對(duì)應(yīng)的這個(gè)屬性類(lèi)

          使用精髓: 1)、SpringBoot啟動(dòng)會(huì)加載大量的自動(dòng)配置類(lèi) ; 2)、我們看我們需要的功能有沒(méi)有SpringBoot默認(rèn)寫(xiě)好的自動(dòng)配置類(lèi); 3)、我們?cè)賮?lái)看這個(gè)自動(dòng)配置類(lèi)中到底配置了哪些組件(只要我們要用的組件有,我們就不需要再來(lái)配置了) 4)、給容器中自動(dòng)配置類(lèi)添加組件的時(shí)候,會(huì)從properties類(lèi)中獲取某些屬性。我們就可以在配置文件中指定這些屬性的值; xxxxAutoConfigurartion:自動(dòng)配置類(lèi); 給容器中添加組件 xxxxProperties:封裝配置文件中相關(guān)屬性

          @Conditional注解

          作用:必須是@Conditional指定的條件成立,才給容器中添加組件,配置配里面的所有內(nèi)容才生效 也就是說(shuō),自動(dòng)配置類(lèi)必須在一定的條件下才能生效

          @Conditional擴(kuò)展注解

          作用(判斷是否滿(mǎn)足當(dāng)前指定條件)

          @ConditionalOnJava

          系統(tǒng)的java版本是否符合要求

          @ConditionalOnBean

          容器中存在指定Bean

          @ConditionalOnMissingBean

          容器中不存在指定Bean

          @ConditionalOnExpression

          滿(mǎn)足SpEL表達(dá)式指定

          @ConditionalOnClass

          系統(tǒng)中有指定的類(lèi)

          @ConditionalOnMissingClass

          系統(tǒng)中沒(méi)有指定的類(lèi)

          @ConditionalOnSingleCandidate

          容器中只有一個(gè)指定的Bean,或者這個(gè)Bean是首選Bean

          @ConditionalOnProperty

          系統(tǒng)中指定的屬性是否有指定的值

          @ConditionalOnResource

          類(lèi)路徑下是否存在指定資源文件

          @ConditionalOnWebApplication

          當(dāng)前是web環(huán)境

          @ConditionalOnNotWebApplication

          當(dāng)前不是web環(huán)境

          @ConditionalOnJndi

          JNDI存在指定項(xiàng)

          想要查看生效的自動(dòng)配置類(lèi),可以在配置文件中配置debug=true,positive為啟動(dòng)的,negative沒(méi)啟用的

          Spring Boot與日志

          日志框架

          市場(chǎng)上存在非常多的日志框架:JUL(java.util.logging),JCL(Apache Commons Logging),Log4j,Log4j2,Logback、SLF4j、jboss-logging等。 Spring Boot在框架內(nèi)容部使用JCL,spring-boot-starter-logging采用了 slf4j+logback的形式,Spring Boot也能自動(dòng)適配(jul、log4j2、logback) 并簡(jiǎn)化配置 SpringBoot底層是Spring框架,Spring框架默認(rèn)是用JCL。SpringBoot選用SLF4j(日志抽象層)和logback(日志實(shí)現(xiàn))

          SLF4j

          開(kāi)發(fā)時(shí)日志記錄方法的調(diào)用,不應(yīng)該來(lái)直接調(diào)用日志的實(shí)現(xiàn)類(lèi),而是調(diào)用日志抽象層里面的方法:

          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          
          public class HelloWorld {
          
            public static void main(String[] args) {
              Logger logger = LoggerFactory.getLogger(HelloWorld.class);
              logger.info("Hello World");
            }
          }

          每一個(gè)日志的實(shí)現(xiàn)框架都有自己的配置文件。使用slf4j以后,配置文件還是做成日志實(shí)現(xiàn)框架自己本身的配置文件。 如何讓系統(tǒng)中所有的日志都統(tǒng)一到slf4j:

          1. 將系統(tǒng)中其他日志框架先排除出去
          2. 用中間包來(lái)替換原有的日志框架
          3. 我們導(dǎo)入slf4j其他的實(shí)現(xiàn)

          SpringBoot日志關(guān)系

          添加依賴(lài):

          xml 代碼解讀復(fù)制代碼<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
          </dependency>

          SpringBoot能自動(dòng)適配所有的日志,而且底層使用slf4j+logback的方式記錄日志,引入其他框架的時(shí)候,只需要把這個(gè)框架依賴(lài)的日志框架排除掉即可

          xml 代碼解讀復(fù)制代碼<dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring‐core</artifactId>
              <exclusions>
                  <exclusion>
                      <groupId>commons‐logging</groupId>
                      <artifactId>commons‐logging</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>

          默認(rèn)日志配置

          日志級(jí)別由低到高:trace<debug<info<warn<error SpringBoot默認(rèn)給我們使用的是info級(jí)別的(日志就只會(huì)在這個(gè)級(jí)別及以后的高級(jí)別生效),沒(méi)有指定級(jí)別的就用SpringBoot默認(rèn)規(guī)定的級(jí)別。 日志輸出格式:

          • %d --表示日期時(shí)間
          • %thread --表示線程名
          • %‐5level --級(jí)別從左顯示5個(gè)字符寬度
          • %logger{50} --表示logger名字最長(zhǎng)50個(gè)字符,否則按照句點(diǎn)分割
          • %msg --日志消息
          • %n --是換行符
          public class Hello {
              // 記錄器
              Logger logger = LoggerFactory.getLogger(getClass());
          
              @RequestMapping("/world")
              public String hello() {
                  logger.trace("trace日志");
                  logger.debug("debug日志");
                  // 默認(rèn)
                  logger.info("info日志");
                  logger.warn("warn日志");
                  logger.error("error日志");
                  return "Hello World~";
              }
          }
          
          默認(rèn)是info,所以只會(huì)輸出:
          2023-05-23 11:44:27.419 INFO 98527 --- [nio-8080-exec-2] c.s.spring_boot_demo3.controller.Hello   : info日志
          2023-05-23 11:44:27.419 WARN 98527 --- [nio-8080-exec-2] c.s.spring_boot_demo3.controller.Hello   : warn日志
          2023-05-23 11:44:27.419 ERROR 98527 --- [nio-8080-exec-2] c.s.spring_boot_demo3.controller.Hello   : error日志

          修改默認(rèn)級(jí)別:

          logging.level.com.study=trace
          
          2023-05-23 11:50:00.774 TRACE 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : trace日志
          2023-05-23 11:50:00.774 DEBUG 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : debug日志
          2023-05-23 11:50:00.774  INFO 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : info日志
          2023-05-23 11:50:00.774  WARN 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : warn日志
          2023-05-23 11:50:00.774 ERROR 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : error日志

          日志配置:

          logging.file.name(建議)

          logging.file.path

          例子

          備注


          只在控制臺(tái)輸出

          指定文件名

          my.log

          輸出日志到my.log文件

          指定目錄

          /var/log

          輸出到指定目錄的 spring.log 文件中

          xml 代碼解讀復(fù)制代碼# 日志
          # logging.file.name=my.log
          # 配置日志路徑,默認(rèn)在此目錄下生成一個(gè)名為:spring.log的日志文件
          logging.file.path=/test/log
          # 在控制臺(tái)輸出的日志的格式
          logging.pattern.console=%d{yyyy‐MM‐dd}[%thread]%‐5level%logger{50}‐%msg%n
          # 指定文件中日志輸出的格式
          logging.pattern.file=%d{yyyy‐MM‐dd}===[%thread]===%‐5level===%logger{50}====%msg%n

          指定日志配置

          給類(lèi)路徑下放上每個(gè)日志框架自己的配置文件即可,SpringBoot就不使用他默認(rèn)配置的了

          日志系統(tǒng)

          自定義配置文件

          Logback

          logback-spring.xml , logback-spring.groovy, logback.xml or logback.groovy

          Log4j2

          log4j2-spring.xml or log4j2.xml

          JDK (Java Util Logging)

          logging.properties

          logback.xml:直接就被日志框架識(shí)別了; logback-spring.xml:日志框架就不直接加載日志的配置項(xiàng),由SpringBoot解析日志配置,可以使用SpringBoot的高級(jí)Profile功能(激活對(duì)應(yīng)環(huán)境下生效)

          xml 代碼解讀復(fù)制代碼<springProfilename="staging">
            <!‐‐ configuration to be enabled when the "staging" profile is active ‐‐>
            可以指定某段配置只在某個(gè)環(huán)境下生效
          </springProfile>
          xml 代碼解讀復(fù)制代碼<appendername="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <layout class="ch.qos.logback.classic.PatternLayout">
              <springProfile name="dev">
                <pattern>%d{yyyy‐MM‐dd HH:mm:ss.SSS} ‐‐‐‐> [%thread] ‐‐‐> %‐5level
                  %logger{50} ‐ %msg%n</pattern>
              </springProfile>
              <springProfile name="!dev">
                <pattern>%d{yyyy‐MM‐dd HH:mm:ss.SSS} ==== [%thread] ==== %‐5level
                  %logger{50} ‐ %msg%n</pattern>
              </springProfile>
            </layout>
          </appender>

          Spring Boot與Web開(kāi)發(fā)

          靜態(tài)資源映射規(guī)則

          1)所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找資源

          webjars:是以jar包的方式引入靜態(tài)資源(網(wǎng)址:www.webjars.org/)


          引入后訪問(wèn):http://localhost:8080/webjars/jquery/3.3.1/src/jquery.js,就可以找到資源:

          2) /** 訪問(wèn)當(dāng)前項(xiàng)目的任何資源,都去「靜態(tài)資源的文件夾」找映射

          • "classpath:/META‐INF/resources/"
          • "classpath:/resources/"
          • "classpath:/static/"
          • "classpath:/public/"
          • "/":當(dāng)前項(xiàng)目的根路徑

          如,localhost:8080/abc,會(huì)去靜態(tài)資源文件夾里面找abc

          3)首頁(yè)映射,靜態(tài)資源文件夾下的所有index.html頁(yè)面,被"/**"映射 localhost:8080/ ,會(huì)找index頁(yè)面

          4)所有的 **/favicon.ico 都是在靜態(tài)資源文件下找

          Thymeleaf模板引擎

          thymeleaf使用

          默認(rèn)規(guī)則:只要我們把HTML頁(yè)面放在classpath:/templates/,thymeleaf就能自動(dòng)渲染

          // 源碼
          @ConfigurationProperties(prefix="spring.thymeleaf")
          public class ThymeleafProperties{
          
          	private static final Charset DEFAULT_ENCODING = Charset.forName("UTF‐8");
          	private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
          	public static final String DEFAULT_PREFIX = "classpath:/templates/";
          	public static final String DEFAULT_SUFFIX = ".html";

          第一步)添加依賴(lài)

          xml 代碼解讀復(fù)制代碼 <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>

          第二步)屬性配置

          xml 代碼解讀復(fù)制代碼# 將緩存關(guān)閉
          spring.thymeleaf.cache=false

          第三步)創(chuàng)建thymeleaf模板文件 創(chuàng)建success.html,放入classpath:/templates/文件夾下

          xml 代碼解讀復(fù)制代碼<!DOCTYPE html>
          <!-- 導(dǎo)入thymeleaf的名稱(chēng)空間 -->
          <html lang="en" xmlns:th="http://www.thymeleaf.org">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
          </head>
          <body>
              <h2>成功頁(yè)面!</h2>
          		<!-- th:text將div里面的文本內(nèi)容設(shè)置為name代表的數(shù)據(jù) -->
              <div th:text="${name}"></div>
          </body>
          </html>

          第四步)編寫(xiě)控制器

          // 這里需要使用@Controller,而不是@RestController
          @Controller
          @RequestMapping("/api")
          public class Hello {
              @ResponseBody
              @RequestMapping("/hello")
              public String hello() {
                  return "hello";
              }
          
              @RequestMapping("/success")
              public String success(Model model) {
                  // classpath:/templates/success.html
                  model.addAttribute("name","alice");
                  return "success";
              }
          }

          第五步)訪問(wèn)頁(yè)面 訪問(wèn)http://localhost:8080/api/success,可以看到html頁(yè)面內(nèi)容

          thymeleaf語(yǔ)法規(guī)則

          1)th:text:改變當(dāng)前元素里面的文本內(nèi)容(th:任意html屬性:來(lái)替換原生屬性的值)

          2)表達(dá)式

          properties 代碼解讀復(fù)制代碼【 Simpleexpressions:(表達(dá)式語(yǔ)法) 】
              1、Variable Expressions: ${...}:獲取變量值(OGNL)
                  1)、獲取對(duì)象的屬性、調(diào)用方法
                  2)、使用內(nèi)置的基本對(duì)象:
                      #ctx : the context object.
                      #vars: the context variables.
                      #locale : the context locale.
                      #request : (only in Web Contexts) the HttpServletRequest object.
                      #response : (only in Web Contexts) the HttpServletResponse object.
                      #session : (only in Web Contexts) the HttpSession object.
                      #servletContext : (only in Web Contexts) the ServletContext object.
                  3)、內(nèi)置的一些工具對(duì)象:
                      #execInfo : information about the template being processed.
                      #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{...} syntax.
                      #uris : methods for escaping parts of URLs/URIs
                      #conversions : methods for executing the configured conversion service (if any).
                      #dates : methods for java.util.Date objects: formatting, component extraction, etc.
                      #calendars : analogous to #dates , but for java.util.Calendar objects.
                      #numbers : methods for formatting numeric objects.
                      #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
                      #objects : methods for objects in general.
                      #bools : methods for boolean evaluation.
                      #arrays : methods for arrays.
                      #lists : methods for lists.
                      #sets : methods for sets.
                      #maps : methods for maps.
                      #aggregates : methods for creating aggregates on arrays or collections.
                      #ids : methods for dealing with id attributes that might be repeated (for example, as aresult of an iteration).
          
                  2、Selection Variable Expressions: *{...}:選擇表達(dá)式。和${}在功能上是一樣(補(bǔ)充:配合th:object="${session.user})
                 	例子:
                  <div th:object="${session.user}">
                      <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
                      <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
                      <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
                  </div>
          
                  3、Message Expressions: #{...}:獲取國(guó)際化內(nèi)容 
          		
                  4、Link URL Expressions: @{...}:定義URL;
                  例子:@{/order/process(execId=${execId},execType='FAST')}
          
                  5、Fragment Expressions: ~{...}:片段引用表達(dá)式
                  例子:<div th:insert="~{commons :: main}">...</div>
          
          【 Literals(字面量) 】
              Text literals: 'one text' , 'Another one!' ,... 
              Number literals: 0 , 34 , 3.0 , 12.3 ,... 
              Boolean literals: true , false
              Null literal: null
              Literal tokens: one , sometext , main ,...
          
          【Text operations:(文本操作)】
              String concatenation: +
              Literal substitutions: |The name is ${name}|
          
          【Arithmetic operations:(數(shù)學(xué)運(yùn)算)】
              Binary operators: + , ‐ , * , / , %
              Minus sign (unary operator): ‐
          
          【Booleanoperations:(布爾運(yùn)算)】
              Binary operators: and , or
              Boolean negation (unary operator): ! , not
          
          【Comparisonsandequality:(比較運(yùn)算)】
              Comparators: > , < , >= , <= ( gt , lt , ge , le )
              Equality operators: == , != ( eq , ne )
          
          【Conditionaloperators:條件運(yùn)算(三元運(yùn)算符)】
              If‐then: (if) ? (then)
              If‐then‐else: (if) ? (then) : (else)
              Default: (value) ?: (defaultvalue)
          
          【Specialtokens:(特殊操作) 】
              No‐Operation: _  代表空操作,如在三元運(yùn)算符的冒號(hào)后面使用

          SpringMVC自動(dòng)配置

          SpringMVC自動(dòng)配置

          Spring Boot 自動(dòng)配置好了SpringMVC。以下是SpringBoot對(duì)SpringMVC的默認(rèn)配置(WebMvcAutoConfiguration):

          • Inclusion of
          • ContentNegotiatingViewResolver
          • and
          • BeanNameViewResolver
          • beans
            • 自動(dòng)配置了ViewResolver(視圖解析器:根據(jù)方法的返回值得到視圖對(duì)象(View),視圖對(duì)象決定如何渲染(轉(zhuǎn)發(fā)或重定向))
            • ContentNegotiatingViewResolver:組合所有的視圖解析器的
            • 如果需要定制視圖解析器:我們可以自己給容器中添加一個(gè)視圖解析器,ContentNegotiatingViewResolver會(huì)自動(dòng)的將其組合進(jìn)來(lái);
          • Support for serving static resources, including support for WebJars
            • 靜態(tài)資源文件夾路徑、webjars
          • Static
          • index.html
          • support
            • 靜態(tài)首頁(yè)訪問(wèn)
          • Custom Favicon support
            • favicon.ico
          • 自動(dòng)注冊(cè)了
          • Converter
          • ,
          • GenericConverter
          • ,
          • Formatter
          • beans.
            • Converter:轉(zhuǎn)換器,public String hello(User user):類(lèi)型轉(zhuǎn)換使用Converter
            • Formatter:格式化器,2017.12.17===Date;
            • 自己添加的格式化器轉(zhuǎn)換器,我們只需要放在容器中即可(使用@Bean)
          • Support for
          • HttpMessageConverters
            • HttpMessageConverter:SpringMVC用來(lái)轉(zhuǎn)換Http請(qǐng)求和響應(yīng)的;User---Json;
            • HttpMessageConverters是從容器中確定,獲取所有的HttpMessageConverter;
            • 自己給容器中添加HttpMessageConverter,只需要將自己的組件注冊(cè)容器中(**@Bean 或 @Component**)
          • Automatic registration of MessageCodesResolver:定義錯(cuò)誤代碼生成規(guī)則
          • Automatic use of a
          • ConfigurableWebBindingInitializer
          • bean
            • 初始化web數(shù)據(jù)綁定器的,WebDataBinder。他的作用是把請(qǐng)求數(shù)據(jù)轉(zhuǎn)換為JavaBean
            • 我們可以配置一個(gè)ConfigurableWebBindingInitializer來(lái)替換默認(rèn)的;(添加到容器)

          擴(kuò)展SpringMVC

          擴(kuò)展方式:編寫(xiě)一個(gè)配置類(lèi)(@Configuration),是WebMvcConfigurerAdapter類(lèi)型,不能標(biāo)注@EnableWebMvc。 既保留了所有的自動(dòng)配置,也能用我們擴(kuò)展的配置(SpringMVC的自動(dòng)配置和我們的擴(kuò)展配置都會(huì)起作用)

          // 使用WebMvcConfigurerAdapter可以來(lái)擴(kuò)展SpringMVC的功能
          @Configuration
          public class MyMvcConfig extends WebMvcConfigurerAdapter {
              // 添加視圖映射
              @Override
              public void addViewControllers(ViewControllerRegistry registry) {
                  // super.addViewControllers(registry);
                  // 瀏覽器發(fā)送 /other 請(qǐng)求來(lái)到 success
                  registry.addViewController("/other").setViewName("success");
              }
          }

          瀏覽器訪問(wèn) http://localhost:8080/other , 可以看到成功映射到了success頁(yè)面

          全面接管SpringMVC

          @EnableWebMvc(不推薦使用) SpringBoot對(duì)SpringMVC的自動(dòng)配置不需要了,所有都是我們自己配置。所有的SpringMVC的自動(dòng)配置都失效了。我們需要在配置類(lèi)中添加@EnableWebMvc即可

          // 使用WebMvcConfigurerAdapter可以來(lái)擴(kuò)展SpringMVC的功能
          @Configuration
          // 全面接管SpringMVC
          @EnableWebMvc
          public class MyMvcConfig extends WebMvcConfigurerAdapter {
              // 添加視圖映射
              @Override
              public void addViewControllers(ViewControllerRegistry registry) {
                  // super.addViewControllers(registry);
                  // 瀏覽器發(fā)送 /other 請(qǐng)求來(lái)到 success
                  registry.addViewController("/other").setViewName("success");
              }
          }

          接管前通過(guò)http://localhost:8080/static.html可以訪問(wèn)靜態(tài)頁(yè)面,全面接管后靜態(tài)頁(yè)面的規(guī)則就失效了,我們就無(wú)法直接訪問(wèn)了

          修改SpringBoot的默認(rèn)配置

          模式: 1)、SpringBoot在自動(dòng)配置很多組件的時(shí)候,先看容器中有沒(méi)有用戶(hù)自己配置的 (@Bean、@Component)。如果有就用用戶(hù)配置的,如果沒(méi)有才自動(dòng)配置。如果有些組件可以有多個(gè)(如ViewResolver),則將用戶(hù)配置的和自己默認(rèn)的組合起來(lái); 2)、在SpringBoot中會(huì)有非常多的xxxConfigurer幫助我們進(jìn)行擴(kuò)展配置 3)、在SpringBoot中會(huì)有很多的xxxCustomizer幫助我們進(jìn)行定制配置

          CRUD案例

          1)默認(rèn)訪問(wèn)首頁(yè)

          方法1:在controller中添加訪問(wèn)路徑的匹配規(guī)則

          @RequestMapping({"/", "/index.html"})
          public String index() {
              return "index";
          }

          方法2:在配置類(lèi)中注冊(cè)組件到容器

          // 使用WebMvcConfigurerAdapter可以來(lái)擴(kuò)展SpringMVC的功能
          @Configuration
          public class MyMvcConfig extends WebMvcConfigurerAdapter {
              // 添加視圖映射
              @Override
              public void addViewControllers(ViewControllerRegistry registry) {
                  registry.addViewController("/other").setViewName("success");
              }
          
              // 所有的WebMvcConfigurerAdapter組件都會(huì)一起起作用
              @Bean //將組件注冊(cè)在容器
              public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
                  WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
                      @Override
                      public void addViewControllers(ViewControllerRegistry registry) {
                          registry.addViewController("/").setViewName("login");
                          registry.addViewController("/index.html").setViewName("login");
                      }
                  };
                  return adapter;
              }
          }

          2)國(guó)際化

          第一步:編寫(xiě)國(guó)際化配置文件,抽取頁(yè)面需要顯示的國(guó)際化消息

          第二步:SpringBoot自動(dòng)配置好了管理國(guó)際化資源文件的組件 我們的配置文件可以直接放在類(lèi)路徑下叫messages.properties,或者在application.properties里配置路徑

          # 國(guó)際化配置的路徑
          spring.messages.basename=i18n.login

          第三步:使用#{}可以在頁(yè)面上獲取國(guó)際化的值

          <!DOCTYPE html>
          <html lang="en"  xmlns:th="http://www.thymeleaf.org">
          	<head>
          		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
          		<meta name="description" content="">
          		<meta name="author" content="">
          		<title>Signin Template for Bootstrap</title>
          		<!-- Bootstrap core CSS -->
          		<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/3.3.5/css/bootstrap.css}" rel="stylesheet">
          		<!-- Custom styles for this template -->
          		<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
          	</head>
          	<body class="text-center">
          		<form class="form-signin" action="dashboard.html" th:action="@{/user/login}" method="post">
          			<img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
          			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
          			<!--判斷-->
          			<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
          			<label class="sr-only" th:text="#{login.username}">Username</label>
          			<input type="text"  name="username" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
          			<label class="sr-only" th:text="#{login.password}">Password</label>
          			<input type="password" name="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
          			<div class="checkbox mb-3">
          				<label>
                    			<input type="checkbox" value="remember-me"/> [[#{login.remember}]]
                  		</label>
          			</div>
          			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
          			<p class="mt-5 mb-3 text-muted">? 2017-2018</p>
          			<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
          			<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
          		</form>
          	</body>
          
          </html>

          第四步:點(diǎn)擊鏈接切換國(guó)際化

          自己實(shí)現(xiàn)一個(gè)LocaleResolver,然后在配置類(lèi)中注冊(cè)組件到容器

          @Bean
          public LocaleResolver localeResolver(){
              return new MyLocaleResolver();
          }

          實(shí)現(xiàn)效果:

          3)登錄

          @Controller
          public class Login {
              // @RequestMapping(value = "/user/login", method = RequestMethod.POST)
              @PostMapping(value = "/user/login")
              public String login(@RequestParam("username") String username,
                                  @RequestParam("password") String password,
                                  Map<String, Object> map, HttpSession session) {
                  if (!StringUtils.isEmpty(username) && "1234".equals(password)) {
                      session.setAttribute("loginUser", username);
                      return "redirect:/main.html";
                  } else {
                      map.put("msg", "用戶(hù)名密碼錯(cuò)誤");
                      return "login";
                  }
              }
          }

          配置類(lèi)中添加一個(gè)試圖映射

          java
          
           代碼解讀
          復(fù)制代碼registry.addViewController("/main.html").setViewName("dashboard");

          錯(cuò)誤消息顯示:

          java
          
           代碼解讀
          復(fù)制代碼<pstyle="color:red"th:text="${msg}"th:if="${not#strings.isEmpty(msg)}"></p>

          4)攔截器進(jìn)行登錄檢查

          攔截器

          // 登陸檢查
          public class LoginHandlerInterceptor implements HandlerInterceptor {
              //目標(biāo)方法執(zhí)行之前
              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                  Object user = request.getSession().getAttribute("loginUser");
                  if(user == null){
                      //未登陸,返回登陸頁(yè)面
                      request.setAttribute("msg","沒(méi)有權(quán)限請(qǐng)先登陸");
                      request.getRequestDispatcher("/index.html").forward(request,response);
                      return false;
                  }else{
                      //已登陸,放行請(qǐng)求
                      return true;
                  }
              }
          
              @Override
              public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
          
              @Override
              public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
          }

          注冊(cè)攔截器

          // 所有的WebMvcConfigurerAdapter組件都會(huì)一起起作用
          @Bean //將組件注冊(cè)在容器
          public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
              WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
                  @Override
                  public void addViewControllers(ViewControllerRegistry registry) {
                      registry.addViewController("/").setViewName("login");
                      registry.addViewController("/index.html").setViewName("login");
                      registry.addViewController("/main.html").setViewName("dashboard");
                  }
          
                  //注冊(cè)攔截器
                  @Override
                  public void addInterceptors(InterceptorRegistry registry) {
                      //super.addInterceptors(registry);
                      // 靜態(tài)資源; *.css , *.js
                      // SpringBoot已經(jīng)做好了靜態(tài)資源映射
                      registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                              .excludePathPatterns("/index.html","/","/user/login");
                  }
              };
              return adapter;
          }

          5)員工列表

          三種引入公共片段的th屬性:

          • th:insert:將公共片段整個(gè)插入到聲明引入的元素中
          • th:replace:將聲明引入的元素替換為公共片段
          • th:include:將被引入的片段的內(nèi)容包含進(jìn)這個(gè)標(biāo)簽中
          html 代碼解讀復(fù)制代碼<body>
            <!--引入抽取的topbar-->
            <!--模板名:會(huì)使用thymeleaf的前后綴配置規(guī)則進(jìn)行解析-->
            <div th:replace="commons/bar::topbar"></div>
          
            <div class="container-fluid">
              <div class="row">
                <!--引入側(cè)邊欄-->
                <div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>
          
                <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
                  <h2><a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">員工添加</a></h2>
                  <div class="table-responsive">
                    <table class="table table-striped table-sm">
                      <thead>
                        <tr>
                          <th>#</th>
                          <th>lastName</th>
                          <th>email</th>
                          <th>gender</th>
                          <th>department</th>
                          <th>birth</th>
                          <th>操作</th>
                        </tr>
                      </thead>
                      <tbody>
                        <tr th:each="emp:${emps}">
                          <td th:text="${emp.id}"></td>
                          <td>[[${emp.lastName}]]</td>
                          <td th:text="${emp.email}"></td>
                          <td th:text="${emp.gender}==0?'女':'男'"></td>
                          <td th:text="${emp.department.departmentName}"></td>
                          <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
                          <td>
                            <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">編輯</a>
                            <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">刪除</button>
                          </td>
                        </tr>
                      </tbody>
                    </table>
                  </div>
                </main>
                <form id="deleteEmpForm"  method="post">
                  <input type="hidden" name="_method" value="delete"/>
                </form>
              </div>
            </div>
          
            <!-- Bootstrap core JavaScript
            ================================================== -->
            <!-- Placed at the end of the document so the pages load faster -->
            <script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js" th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
            <script type="text/javascript" src="asserts/js/popper.min.js" th:src="@{/webjars/popper.js/1.11.1/dist/popper.js}"></script>
            <script type="text/javascript" src="asserts/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/4.0.0/js/bootstrap.js}"></script>
          
            <!-- Icons -->
            <script type="text/javascript" src="asserts/js/feather.min.js" th:src="@{/asserts/js/feather.min.js}"></script>
            <script>
              feather.replace()
            </script>
            <script>
              $(".deleteBtn").click(function(){
                  //刪除當(dāng)前員工的
                  $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
                  return false;
                    });
            </script>
          </body>

          6)員工添加

          請(qǐng)求添加頁(yè)面

          //來(lái)到員工添加頁(yè)面
          @GetMapping("/emp")
          public String toAddPage(Model model){
              //來(lái)到添加頁(yè)面,查出所有的部門(mén),在頁(yè)面顯示
              Collection<Department> departments = departmentDao.getDepartments();
              model.addAttribute("depts",departments);
              return "emp/add";
          }

          添加頁(yè)面的表單(見(jiàn)下方修改) 員工添加功能

          //員工添加
          //SpringMVC自動(dòng)將請(qǐng)求參數(shù)和入?yún)?duì)象的屬性進(jìn)行一一綁定;要求請(qǐng)求參數(shù)的名字和javaBean入?yún)⒌膶?duì)象里面的屬性名是一樣的
          @PostMapping("/emp")
          public String addEmp(Employee employee){
              //來(lái)到員工列表頁(yè)面
              System.out.println("保存的員工信息:"+employee);
              //保存員工
              employeeDao.save(employee);
              // redirect: 表示重定向到一個(gè)地址  /代表當(dāng)前項(xiàng)目路徑
              // forward: 表示轉(zhuǎn)發(fā)到一個(gè)地址
              return "redirect:/emps";
          }

          7)員工修改

          查詢(xún)員工信息并回顯

          //來(lái)到修改頁(yè)面,查出當(dāng)前員工,在頁(yè)面回顯
          @GetMapping("/emp/{id}")
          public String toEditPage(@PathVariable("id") Integer id,Model model){
              Employee employee = employeeDao.get(id);
              model.addAttribute("emp",employee);
          
              //頁(yè)面要顯示所有的部門(mén)列表
              Collection<Department> departments = departmentDao.getDepartments();
              model.addAttribute("depts",departments);
              //回到修改頁(yè)面(add是一個(gè)修改添加二合一的頁(yè)面);
              return "emp/add";
          }

          修改和添加是同一個(gè)頁(yè)面:

          <!--需要區(qū)分是員工修改還是添加;-->
          <form th:action="@{/emp}" method="post">
              <!--發(fā)送put請(qǐng)求修改員工數(shù)據(jù)-->
              <!--
              1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自動(dòng)配置好的)
              2、頁(yè)面創(chuàng)建一個(gè)post表單
              3、創(chuàng)建一個(gè)input項(xiàng),name="_method";值就是我們指定的請(qǐng)求方式
              -->
              <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
              <input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">
              <div class="form-group">
                  <label>LastName</label>
                  <input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}">
              </div>
              <div class="form-group">
                  <label>Email</label>
                  <input name="email" type="email" class="form-control" placeholder="zhangsan@atguigu.com" th:value="${emp!=null}?${emp.email}">
              </div>
              <div class="form-group">
                  <label>Gender</label><br/>
                  <div class="form-check form-check-inline">
                      <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">
                      <label class="form-check-label">男</label>
                  </div>
                  <div class="form-check form-check-inline">
                      <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}">
                      <label class="form-check-label">女</label>
                  </div>
              </div>
              <div class="form-group">
                  <label>department</label>
                  <!--提交的是部門(mén)的id-->
                  <select class="form-control" name="department.id">
                      <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
                  </select>
              </div>
              <div class="form-group">
                  <label>Birth</label>
                  <input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">
              </div>
              <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'">添加</button>
          </form>

          修改功能

          //員工修改;需要提交員工id;
          @PutMapping("/emp")
          public String updateEmployee(Employee employee){
              System.out.println("修改的員工數(shù)據(jù):"+employee);
              employeeDao.save(employee);
              return "redirect:/emps";
          }

          8)員工刪除

          <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">刪除</button>
          
          <script>
          	$(".deleteBtn").click(function(){ //刪除當(dāng)前員工。先修改action地址再提交
                  $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
                  return false;
              });
          </script>

          刪除功能方法:

          //員工刪除
          @DeleteMapping("/emp/{id}")
          public String deleteEmployee(@PathVariable("id") Integer id){
              employeeDao.delete(id);
              return "redirect:/emps";
          }

          錯(cuò)誤處理機(jī)制

          默認(rèn)處理

          瀏覽器返回一個(gè)默認(rèn)的錯(cuò)誤頁(yè)面

          Whitelabel Error Page
          This application has no explicit mapping for /error, so you are seeing this as a fallback.
          
          Sun Jun 11 10:32:29 CST 2023
          There was an unexpected error (type=Not Found, status=404).

          客戶(hù)端請(qǐng)求默認(rèn)返回JSON數(shù)據(jù)提示錯(cuò)誤

          {
              "timestamp": "2023-06-11T02:37:03.631+00:00",
              "status": 404,
              "error": "Not Found",
              "path": "/hello1"
          }

          一但系統(tǒng)出現(xiàn)4xx或者5xx之類(lèi)的錯(cuò)誤,ErrorPageCustomizer就會(huì)生效(定制錯(cuò)誤的響應(yīng)規(guī)則),就會(huì)來(lái)到/error請(qǐng)求,就會(huì)被BasicErrorController處理

          @Value("${error.path:/error}")
          private String path = "/error";
          // 系統(tǒng)出現(xiàn)錯(cuò)誤以后來(lái)到error請(qǐng)求進(jìn)行處理;(web.xml注冊(cè)的錯(cuò)誤頁(yè)面規(guī)則)
          private ModelAndView resolve(String viewName, Map<String, Object> model) {
              //默認(rèn)SpringBoot可以去找到一個(gè)頁(yè)面? error/404
          	String errorViewName = "error/" + viewName;
          	//模板引擎可以解析這個(gè)頁(yè)面地址就用模板引擎解析
          	TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
                      .getProvider(errorViewName, this.applicationContext);
              if (provider != null) {
          		//模板引擎可用的情況下返回到errorViewName指定的視圖地址
                  return new ModelAndView(errorViewName, model);
              }
          	//模板引擎不可用,就在靜態(tài)資源文件夾下找errorViewName對(duì)應(yīng)的頁(yè)面 error/404.html
              return resolveResource(errorViewName, model);
          }

          定制錯(cuò)誤頁(yè)面

          1)有模板引擎的情況下:error/狀態(tài)碼。將錯(cuò)誤頁(yè)面命???為 錯(cuò)誤狀態(tài)碼.html 放在模板引擎文件夾里面的error文件夾下,發(fā)生此狀態(tài)碼的錯(cuò)誤就會(huì)來(lái)到對(duì)應(yīng)的頁(yè)面; 我們可以使用 4xx5xx 作為錯(cuò)誤頁(yè)面的文件名來(lái)匹配這種類(lèi)型的所有錯(cuò)誤。精確優(yōu)先(優(yōu)先尋找精確的狀態(tài)碼.html) 頁(yè)面能獲取的信息:timestamp:時(shí)間戳、status:狀態(tài)碼、error:錯(cuò)誤提示、exception:異常對(duì)象、message:異常消息、errors:JSR303數(shù)據(jù)校驗(yàn)的錯(cuò)誤都在這里 2)沒(méi)有模板引擎(模板引擎找不到這個(gè)錯(cuò)誤頁(yè)面),靜態(tài)資源文件夾下找; 3)以上都沒(méi)有錯(cuò)誤頁(yè)面,就是默認(rèn)來(lái)到SpringBoot默認(rèn)的錯(cuò)誤提示頁(yè)面;

          定制錯(cuò)誤JSON數(shù)據(jù)

          1、方式1:自定義異常處理&返回定制json數(shù)據(jù)

          @ControllerAdvice
          public class MyExceptionHandler {
              @ResponseBody
              @ExceptionHandler({UserNotExistException.class})
              public Map<String, Object> handleException(Exception e) {
                  Map<String, Object> map = new HashMap<>();
                  map.put("code", "user.notexist");
                  map.put("message", e.getMessage());
                  return map;
              }
          }

          缺點(diǎn):沒(méi)有自適應(yīng)效果(瀏覽器和客戶(hù)端請(qǐng)求返回的都是JSON數(shù)據(jù))

          2、轉(zhuǎn)發(fā)到/error進(jìn)行自適應(yīng)響應(yīng)效果處理

          @ControllerAdvice
          public class MyExceptionHandler {
              @ExceptionHandler({UserNotExistException.class})
              public String handleException(Exception e, HttpServletRequest request) {
                  Map<String, Object> map = new HashMap<>();
                  request.setAttribute("javax.servlet.error.status_code", 500);
                  map.put("code", "user.notexist");
                  map.put("message", e.getMessage());
                  // 轉(zhuǎn)發(fā)到 /error
                  return "forward:/error";
              }
          }

          配置嵌入式Servlet容器

          SpringBoot默認(rèn)使用Tomcat作為嵌入式的Servlet容器

          定制修改Servlet容器的相關(guān)配置

          方式1:修改和server有關(guān)的配置(ServerProperties【本質(zhì)也是EmbeddedServletContainerCustomizer】)

          properties 代碼解讀復(fù)制代碼server.port=8081
          server.context‐path=/crud
          
          server.tomcat.uri‐encoding=UTF‐8
          //通用的Servlet容器設(shè)置
          server.xxx
          //Tomcat的設(shè)置
          server.tomcat.xxx

          方式2:編寫(xiě)一個(gè)EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器,來(lái)修改Servlet容器的配置

          @Bean //一定要將這個(gè)定制器加入到容器中
          publicEmbeddedServletContainerCustomizerembeddedServletContainerCustomizer(){
              return new EmbeddedServletContainerCustomizer() {
                  //定制嵌入式的Servlet容器相關(guān)的規(guī)則
                  @Override
                  public void customize(ConfigurableEmbeddedServletContainer container) {
                      container.setPort(8083);
                  }
              };
          }

          注冊(cè)Servlet三大組件

          由于SpringBoot默認(rèn)是以jar包的方式啟動(dòng)嵌入式的Servlet容器來(lái)啟動(dòng)SpringBoot的web應(yīng)用,沒(méi)有web.xml文件。 注冊(cè)三大組件(Servlet、Filter、Listener)用以下方式: ServletRegistrationBean

          @Configuration
          public class MyServerConfig {
              // 注冊(cè)三大組件
              @Bean
              public ServletRegistrationBean myServlet(){
                  ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
                  return registrationBean;
              }
          }
          
          // 請(qǐng)求 http://localhost:8080/myServlet  就會(huì)出現(xiàn)MyServlet中返回的內(nèi)容

          FilterRegistrationBean

          @Bean
          public FilterRegistrationBean myFilter(){
              FilterRegistrationBean registrationBean = new FilterRegistrationBean();
              registrationBean.setFilter(new MyFilter());
              registrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet"));
              return registrationBean;
          }
          
          // 請(qǐng)求 http://localhost:8080/myServlet  就會(huì)出現(xiàn)MyFilter的doFilter()中輸出的內(nèi)容

          ServletListenerRegistrationBean

          @Bean
          public ServletListenerRegistrationBean myListener(){
              ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
              return registrationBean;
          }
          
          // 輸出結(jié)果:
          contextInitialized---web應(yīng)用啟動(dòng)
          contextDestroyed---web應(yīng)用關(guān)閉  // 這里是在點(diǎn)擊暫停的時(shí)候

          其他嵌入式Servlet容器

          Tomcat(默認(rèn)使用)

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring‐boot‐starter‐web</artifactId>
              // 引入web模塊默認(rèn)就是使用嵌入式的Tomcat作為Servlet容器;
          </dependency>

          Jetty(開(kāi)發(fā)長(zhǎng)連接應(yīng)用)

          <dependency>
              <artifactId>spring‐boot‐starter‐jetty</artifactId>
              <groupId>org.springframework.boot</groupId>
           </dependency>

          Undertow(不支持JSP)

          <dependency>
             <artifactId>spring‐boot‐starter‐undertow</artifactId>
             <groupId>org.springframework.boot</groupId>
          </dependency>

          Spring Boot與Docker

          Docker簡(jiǎn)介

          Docker是一個(gè)開(kāi)源的應(yīng)用容器引擎,是一個(gè)輕量級(jí)容器技術(shù)。Docker支持將軟件編譯成一個(gè)鏡像,然后在鏡像中各種軟件做好配置,將鏡像發(fā)布出去,其他使用者可以直接使用這個(gè)鏡像;運(yùn)行中的這個(gè)鏡像稱(chēng)為容器,容器啟動(dòng)是非常快速的。

          • docker鏡像(Images):Docker鏡像是用于創(chuàng)建Docker容器的模板
          • docker容器(Container):容器是獨(dú)立運(yùn)行的一個(gè)或一組應(yīng)用
          • docker客戶(hù)端(Client):客戶(hù)端通過(guò)命令行或者其他工具使用Docker API(docs.docker.com/reference/a…) 與 Docker 的守護(hù)進(jìn)程通信,即:連接docker主機(jī)進(jìn)行操作
          • docker主機(jī)(Host):一個(gè)物理或者虛擬的機(jī)器用于執(zhí)行Docker守護(hù)進(jìn)程和容器。即:安裝了Docker程序的機(jī)器(Docker直接安裝在操作系統(tǒng)之上)
          • docker倉(cāng)庫(kù)(Registry):Docker倉(cāng)庫(kù)用來(lái)保存鏡像,可以理解為代碼控制中的代碼倉(cāng)庫(kù)。Docker Hub(hub.docker.com) 提供了龐大的鏡像集合供使用。

          使用Docker的步驟: 1)安裝Docker 2)去Docker倉(cāng)庫(kù)找到這個(gè)軟件對(duì)應(yīng)的鏡像 3)使用Docker運(yùn)行這個(gè)鏡像,這個(gè)鏡像就會(huì)生成一個(gè)Docker容器 4)對(duì)容器的啟動(dòng)停止就是對(duì)軟件的啟動(dòng)停止

          安裝Docker

          安裝教程可參考:www.runoob.com/docker/maco…

          shell
          
           代碼解讀
          復(fù)制代碼$ docker info

          Docker常用操作

          鏡像操作

          操作

          命令

          說(shuō)明

          檢索

          docker search 關(guān)鍵字 eg:docker search redis

          我們經(jīng)常去docker hub上檢索鏡像的詳細(xì)信息,如鏡像的TAG

          拉取

          docker pull 鏡像名:tag

          :tag是可選的,tag表示標(biāo)簽,多為軟件的版本,默認(rèn)是latest

          列表

          docker images

          查看所有本地鏡像

          刪除

          docker rmi image-id

          刪除指定的本地鏡像

          容器操作

          流程:軟件鏡像(QQ安裝程序)-->運(yùn)行鏡像-->產(chǎn)生一個(gè)容器(正在運(yùn)行的軟件,運(yùn)行的QQ)

          操作

          命令

          說(shuō)明

          運(yùn)行

          docker run --name container-name -d image-name eg:docker run –name myredis –d redis

          -name:自定義容器名 -d:后臺(tái)運(yùn)行 image-name:指定鏡像模板

          列表

          docker ps(查看運(yùn)行中的容器)

          加上-a可以查看所有容器

          停止

          docker stop container-name/container-id

          停止當(dāng)前運(yùn)行的容器

          啟動(dòng)

          docker start container-name/container-id

          啟動(dòng)容器

          刪除

          docker rm container-id

          刪除指定容器

          端口映射

          -p 6379:6379 eg:docker run -d -p 6379:6379 --name myredis docker.io/redis

          -p: 主機(jī)端口(映射到)容器內(nèi)部的端口 ‐d:后臺(tái)運(yùn)行

          容器日志

          docker logs container-name/container-id


          更多命令可查看:docs.docker.com/engine/refe…

          示例(tomcat):

          shell 代碼解讀復(fù)制代碼% docker images //查看鏡像列表
          % docker search tomcat //搜索鏡像
          % docker pull tomcat //拉取鏡像
          % docker run --name myTomcat -d tomcat:latest //根據(jù)鏡像啟動(dòng)容器
          % docker ps //查看運(yùn)行中的容器
              ------輸出------
              CONTAINER ID   IMAGE           COMMAND             CREATED          STATUS          PORTS      NAMES
              700a4fa11db6   tomcat:latest   "catalina.sh run"   25 seconds ago   Up 24 seconds   8080/tcp   myTomcat
          % docker stop 700a4fa11db6[容器ID] //停止運(yùn)行中的容器
          % docker ps -a //查看所有的容器
              ------輸出------
              CONTAINER ID   IMAGE           COMMAND             CREATED         STATUS                            PORTS     NAMES
              700a4fa11db6   tomcat:latest   "catalina.sh run"   5 minutes ago   Exited (143) About a minute ago             myTomcat
          % docker start 700a4fa11db6[容器ID] //啟動(dòng)容器
          % docker rm 700a4fa11db6[容器ID] //刪除一個(gè)容器
          % docker run -d -p 8888:8080 tomcat //啟動(dòng)一個(gè)做了端口映射的tomcat
              ‐d:后臺(tái)運(yùn)行
              ‐p: 將主機(jī)的端口映射到容器的一個(gè)端口 主機(jī)端口:容器內(nèi)部的端口
              ------docker ps 輸出------
              CONTAINER ID   IMAGE     COMMAND             CREATED          STATUS          PORTS                    NAMES
          	8dbc9df132b4   tomcat    "catalina.sh run"   19 seconds ago   Up 19 seconds   0.0.0.0:8888->8080/tcp   eloquent_moore
          % dockerlogscontainer‐name/container‐id //查看容器的日志

          示例(mysql):

          shell 代碼解讀復(fù)制代碼% docker pull mysql
          % docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql //啟動(dòng)mysql
              ------輸出------
          	c9c10a720ba86f440737503396019c80ad0de88b8ae659e19214d8eda3253481
          
          
          幾個(gè)其他的高級(jí)操作:
              
          docker run --name mysql03 ‐v /conf/mysql:/etc/mysql/conf.d ‐e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
          把主機(jī)的/conf/mysql文件夾掛載到mysql docker容器的/etc/mysql/conf.d文件夾里面
          改mysql的配置文件就只需要把mysql配置文件放在自定義的文件夾下(/conf/mysql)
          
          docker run --name some‐mysql ‐e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character‐set‐server=utf8mb4 ‐‐collation‐server=utf8mb4 --collation -server=utf8mb4_unicode_ci
          指定mysql的一些配置參數(shù)

          Spring Boot與數(shù)據(jù)訪問(wèn)

          對(duì)于數(shù)據(jù)訪問(wèn)層,無(wú)論是SQL還是NOSQL,Spring Boot默認(rèn)采用整合Spring Data的方式進(jìn)行統(tǒng)一處理,添加大量自動(dòng)配置,屏蔽了很多設(shè)置。引入各種xxxTemplate、xxxRepository來(lái)簡(jiǎn)化我們對(duì)數(shù)據(jù)訪問(wèn)層的操作。對(duì)我們來(lái)說(shuō)只需要進(jìn)行簡(jiǎn)單的設(shè)置即可。 JDBC、MyBatis、JPA

          整合JDBC

          配置:

          properties 代碼解讀復(fù)制代碼spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jdbc
          spring.datasource.username=root
          spring.datasource.password=root
          spring.datasource.driver-class-name=com.mysql.jdbc.Driver

          測(cè)試代碼:

          @SpringBootTest
          class SpringDemo08JdbcApplicationTests {
              @Autowired
              DataSource dataSource;
          
              @Test
              void contextLoads() {
                  // 默認(rèn)使用的是 class com.zaxxer.hikari.HikariDataSource 數(shù)據(jù)源
                  System.out.println(dataSource.getClass());
              }
          }

          數(shù)據(jù)源的相關(guān)配置都在DataSourceProperties源代碼里面

          // 源碼
          @ConfigurationProperties(
              prefix = "spring.datasource"
          )

          SpringBoot默認(rèn)可以支持:org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource、自定義數(shù)據(jù)源類(lèi)型。

          驗(yàn)證JDBC

          配置文件里增加如下配置:

          properties 代碼解讀復(fù)制代碼#spring.datasource.initialization-mode=always  此行已失效,使用下面的
          spring.sql.init.mode=always

          編寫(xiě)SQL并放在resources文件夾下面

          啟動(dòng)springboot工程,刷新數(shù)據(jù)庫(kù),可以看到表成功創(chuàng)建(下次啟動(dòng)還是會(huì)創(chuàng)建,所以最好創(chuàng)建完畢后刪除sql文件

          編寫(xiě)測(cè)試查詢(xún)代碼

          @SpringBootTest
          class SpringDemo08JdbcApplicationTests {
          
              @Autowired
              JdbcTemplate jdbcTemplate;
          
              @Test
              void contextLoads() {
                  List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from Skynet_test");
                  System.out.println(list);
                  // [{id=46, date=2023-06-01, APPversion=1.2.3, uv=123456, tag=gray, platform=Android, 
                  // create_time=2023-06-01, last_modify=2023-06-01, version=1.2}]
              }
          }

          整合Druid數(shù)據(jù)源

          引入依賴(lài)

          xml 代碼解讀復(fù)制代碼<!--引入自定義數(shù)據(jù)源druid-->
          <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.8</version>
          </dependency>

          修改配置文件

          properties 代碼解讀復(fù)制代碼spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jdbc
          spring.datasource.username=root
          spring.datasource.password=root1234
          spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
          spring.datasource.type=com.alibaba.druid.pool.DruidDataSource // 切換數(shù)據(jù)源

          測(cè)試代碼

          @Test
          void contextLoads() {
              System.out.println(dataSource.getClass()); // class com.alibaba.druid.pool.DruidDataSource
          }

          配置生效:

          @Configuration
          public class DruidConfig {
              @ConfigurationProperties(prefix = "spring.datasource")
              @Bean
              public DataSource druid(){
                  return new DruidDataSource();
              }
              // 這樣在配置文件中配置druid的一些屬性就可以生效了
          }

          整合Mybatis

          驗(yàn)證Mybatis

          引入上方的druid數(shù)據(jù)源 配置文件:

          properties 代碼解讀復(fù)制代碼spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jdbc
          spring.datasource.username=root
          spring.datasource.password=root1234
          spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
          spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

          建表語(yǔ)句:

          sql 代碼解讀復(fù)制代碼CREATE TABLE `department` (
            `id` int NOT NULL AUTO_INCREMENT,
            `departmentName` varchar(255) DEFAULT NULL,
            PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
          
          CREATE TABLE `employee` (
            `id` int NOT NULL AUTO_INCREMENT,
            `lastName` varchar(255) DEFAULT NULL,
            `email` varchar(255) DEFAULT NULL,
            `gender` int DEFAULT NULL,
            `d_id` int DEFAULT NULL,
            PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

          創(chuàng)建JavaBean:Employee & Department

          注解版

          @Mapper //指定這是一個(gè)操作數(shù)據(jù)庫(kù)的mapper
          public interface DepartmentMapper {
          
              @Select("select * from department where id=#{id}")
              public Department getDeptById(Integer id);
          
              @Delete("delete from department where id=#{id}")
              public int deleteDeptById(Integer id);
          
              @Options(useGeneratedKeys = true, keyProperty = "id")
              @Insert("insert into department(departmentName) values (#{departmentName})")
              public int insertDept(Department department);
          
              @Update("update department set departmentName=#{departmentName} where id=#{id}")
              public int updateDept(Department department);
          }

          測(cè)試驗(yàn)證:

          @RestController
          public class DepartmentController {
              @Autowired
              DepartmentMapper departmentMapper;
          
              @GetMapping("/dept/{id}")
              public Department getDepartment(@PathVariable("id") Integer id) {
                  return departmentMapper.getDeptById(id);
                  // 測(cè)試鏈接:http://localhost:8080/dept/1
                  // 返回:{"id":1,"departmentName":"開(kāi)發(fā)部"}
              }
          
              @GetMapping("/dept")
              public Department insertDepartment(Department department) {
                  departmentMapper.insertDept(department);
                  return department;
                  // 測(cè)試鏈接:http://localhost:8080/dept?departmentName=開(kāi)發(fā)部
                  // 返回:{"id":1,"departmentName":"開(kāi)發(fā)部"}
              }
          }

          如果此時(shí)數(shù)據(jù)庫(kù)里字段是(department_name),查詢(xún)結(jié)果就展示不出來(lái)名字了:{"id":1,"departmentName":null}。如何開(kāi)啟駝峰命名法配置?方法是自定義MyBatis的配置規(guī)則,給容器中添加一個(gè)ConfigurationCustomizer:

          @org.springframework.context.annotation.Configuration
          public class MyBatisConfig {
              @Bean
              public ConfigurationCustomizer configurationCustomizer(){
                  return new ConfigurationCustomizer() {
                      @Override
                      public void customize(Configuration configuration) {
                          configuration.setMapUnderscoreToCamelCase(true); // 開(kāi)啟駝峰命名
                      }
                  };
              }
          }

          另一個(gè)問(wèn)題是,每個(gè)mapper上都需要標(biāo)注@Mapper注解,自動(dòng)掃描配置呢?

          @MapperScan(value = "com.example.spring_demo09_mybatis.mapper") // 批量掃描所有的Mapper接口
          @SpringBootApplication
          public class SpringDemo09MybatisApplication {
          
              public static void main(String[] args) {
                  SpringApplication.run(SpringDemo09MybatisApplication.class, args);
              }
          
          }

          配置文件版

          @Mapper
          public interface EmployeeMapper {
              public Employee getEmpById(Integer id);
              public void insertEmp(Employee employee);
          }

          mybatis配置文件:

          java 代碼解讀復(fù)制代碼<?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE configuration
                  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                  "http://mybatis.org/dtd/mybatis-3-config.dtd">
          <configuration>
          
              <settings>
                  <setting name="mapUnderscoreToCamelCase" value="true"/>
              </settings>
          </configuration>
          java 代碼解讀復(fù)制代碼<?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE mapper
                  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="com.example.spring_demo09_mybatis.mapper.EmployeeMapper">
             <!--    public Employee getEmpById(Integer id);-->
              <select id="getEmpById" resultType="com.example.spring_demo09_mybatis.bean.Employee">
                  SELECT * FROM employee WHERE id=#{id}
              </select>
          
              <!--public void insertEmp(Employee employee);-->
              <insert id="insertEmp">
                  INSERT INTO employee(lastName,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})
              </insert>
          </mapper>

          修改Spring配置文件增加如下內(nèi)容:

          java 代碼解讀復(fù)制代碼#mybatis
          #指定全局配置文件的位置
          mybatis.config-location=classpath:mybatis/mybatis-config.xml
          #指定sql映射文件的位置
          mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

          測(cè)試方法:

          java 代碼解讀復(fù)制代碼@GetMapping("/emp/{id}")
          public Employee getEmp(@PathVariable("id") Integer id) {
              return employeeMapper.getEmpById(id);
              // 測(cè)試鏈接:http://localhost:8080/emp/1
              // 返回:{"id":1,"lastName":"Wang","gender":1,"email":"1111@qq.com","dId":1}
          }

          使用參考:mybatis.org/spring-boot…

          整合JPA

          Spring Data 項(xiàng)目的目的是為了簡(jiǎn)化構(gòu)建基于 Spring 框架應(yīng)用的數(shù)據(jù)訪問(wèn)技術(shù),包括非關(guān)系數(shù)據(jù)庫(kù)、 Map-Reduce 框架、云數(shù)據(jù)服務(wù)等等,另外也包含對(duì)關(guān)系數(shù)據(jù)庫(kù)的訪問(wèn)支持。 SpringData 為我們提供使用統(tǒng)一的API來(lái)對(duì)數(shù)據(jù)訪問(wèn)層進(jìn)行操作,這主要是Spring Data Commons項(xiàng)目來(lái)實(shí)現(xiàn)的。Spring Data Commons讓我們?cè)谑褂藐P(guān)系型或者非關(guān)系型數(shù)據(jù)訪問(wèn)技術(shù)時(shí)都基于Spring提供的統(tǒng)一標(biāo)準(zhǔn),標(biāo)準(zhǔn)包含了CRUD(創(chuàng)建、獲取、更新、刪除)、查詢(xún)、 排序和分頁(yè)的相關(guān)操作。

          統(tǒng)一的Repository接口:

          • Repository<T, ID extends Serializable>:統(tǒng)一接口
          • RevisionRepository<T, ID extends Serializable, N extends Number & Comparable>:基于樂(lè)觀鎖機(jī)制
          • CrudRepository<T, ID extends Serializable>:基本CRUD操作
          • PagingAndSortingRepository<T, ID extends Serializable>:基本CRUD及分頁(yè)

          提供數(shù)據(jù)訪問(wèn)模板類(lèi) xxxTemplate,如:MongoTemplate、RedisTemplate等

          驗(yàn)證JPA

          1)、編寫(xiě)一個(gè)bean實(shí)體類(lèi)和數(shù)據(jù)表進(jìn)行映射,并且配置好映射關(guān)系;

          package com.example.spring_demo10_jpa.entity;
          
          import javax.persistence.*;
          
          // 使用JPA注解配置映射關(guān)系
          @Entity //告訴JPA這是一個(gè)實(shí)體類(lèi)(和數(shù)據(jù)表映射的類(lèi))
          @Table(name = "tbl_user") // @Table來(lái)指定和哪個(gè)數(shù)據(jù)表對(duì)應(yīng),如果省略默認(rèn)表名就是user
          public class User {
              @Id // 代表這是一個(gè)主鍵
              @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主鍵
              private Integer id;
          
              @Column(name = "name", length = 50) // 這是和數(shù)據(jù)表對(duì)應(yīng)的一個(gè)列
              private String name;
          
              @Column // 省略默認(rèn)列名就是屬性名
              private String email;
          }

          2)、編寫(xiě)一個(gè)Dao接口來(lái)操作實(shí)體類(lèi)對(duì)應(yīng)的數(shù)據(jù)表(Repository)

          // 繼承JpaRepository來(lái)完成對(duì)數(shù)據(jù)庫(kù)的操作
          public interface UserRepository extends JpaRepository<User, Integer> {
          }

          3)、基本的配置

          properties 代碼解讀復(fù)制代碼#jpa
          #更新或者創(chuàng)建數(shù)據(jù)表結(jié)構(gòu)
          spring.jpa.hibernate.ddl-auto=update
          #控制臺(tái)新鮮事SQL
          spring.jpa.show-sql=true

          4)、啟動(dòng)工程,自動(dòng)生成數(shù)據(jù)表:

          5)、測(cè)試

          @RestController
          public class UserController {
              @Autowired
              UserRepository userRepository;
          
              //    @GetMapping("/user/{id}")
              //    public User getUser(@PathVariable("id") Integer id){
              //        User user = userRepository.findOne(id);
              //        return user;
              //    }
          
              @GetMapping("/user")
              public User insertUser(User user){
                  User save = userRepository.save(user);
                  return save;
              }
          }

          請(qǐng)求http://localhost:8080/user?name=haha&email=qqqq@qq.com會(huì)進(jìn)行日志輸出: Hibernate: insert into tbl_user (email, name) values (?, ?)

          Spring Boot啟動(dòng)配置原理

          啟動(dòng)流程

          SpringApplication.run(主程序類(lèi)) 1、 創(chuàng)建SpringApplication對(duì)象; 這一步主要是加載并保存所有的 ApplicationContextInitializer 和 ApplicationListener,并獲取到主程序類(lèi) 2、運(yùn)行run()方法; 回調(diào)所有的SpringApplicationRunListener的starting、準(zhǔn)備環(huán)境、創(chuàng)建ioc容器對(duì)象(web環(huán)境容器和普通環(huán)境容器)

          事件監(jiān)聽(tīng)機(jī)制

          1、準(zhǔn)備環(huán)境

          • 執(zhí)行ApplicationContextInitializer. initialize()
          • 監(jiān)聽(tīng)器SpringApplicationRunListener回調(diào)contextPrepared
          • 加載主配置類(lèi)定義信息
          • 監(jiān)聽(tīng)器SpringApplicationRunListener回調(diào)contextLoaded

          2、刷新啟動(dòng)IOC容器

          • 掃描加載所有容器中的組件
          • 包括從META-INF/spring.factories中獲取的所有EnableAutoConfiguration組件

          3、回調(diào)容器中所有的ApplicationRunner、CommandLineRunner的run方法

          4、監(jiān)聽(tīng)器SpringApplicationRunListener回調(diào)finished

          Spring Boot自定義starters

          編寫(xiě)自動(dòng)配置:

          @Configuration //指定這個(gè)類(lèi)是一個(gè)配置類(lèi)
          @ConditionalOnXXX //在指定條件成立的情況下自動(dòng)配置類(lèi)生效
          @AutoConfigureAfter //指定自動(dòng)配置類(lèi)的順序
          @Bean //給容器中添加組件
          
          @ConfigurationPropertie結(jié)合相關(guān)xxxProperties類(lèi)來(lái)綁定相關(guān)的配置
          @EnableConfigurationProperties//讓xxxProperties生效加入到容器中
          
          自動(dòng)配置類(lèi)要能加載,將需要啟動(dòng)就加載的自動(dòng)配置類(lèi),配置在META‐INF/spring.factories
          org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
          org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
          org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

          設(shè)計(jì)模式: 啟動(dòng)器starter只用來(lái)做依賴(lài)導(dǎo)入;專(zhuān)門(mén)寫(xiě)一個(gè)自動(dòng)配置模塊,啟動(dòng)器依賴(lài)這個(gè)自動(dòng)配置模塊;自定義啟動(dòng)器名-spring-boot-starter

          作者:島雨QA 鏈接:https://juejin.cn/post/7254384256280035388

          近項(xiàng)目需求,寫(xiě)了一個(gè)類(lèi)似百度搜索框的功能。

          把代碼整理了一遍,然后分享出來(lái)給大家看看,如果有不對(duì)的地方請(qǐng)多指教。

          實(shí)現(xiàn)效果

          使用的語(yǔ)言:html,css,JavaScript,jQuery

          代碼部分

          html部分:


          1. <div style="color: #323232; ">
          2. 查找:
          3. <input id="input"/>
          4. <button>搜索</button>
          5. <!--輸入框下方的列表-->
          6. <ul id="ul" style="display:none;z-index: 2;">
          7. <li>1</li>
          8. <li>2</li>
          9. <li>3</li>
          10. </ul>
          11. <!--輸入框內(nèi)的箭頭-->
          12. <span id="jiantou" style="padding:10px 20px 10px 20px;margin-left: -200px; background-image: url();background-repeat: no-repeat; background-position: center;"></span>
          13. </div>

          js部分:


          1. $(document).ready(function(){
          2. //先監(jiān)聽(tīng)input的值變化
          3. document.getElementById("input").oninput=function(){
          4. var val = $(this).val()
          5. if(val!=""){
          6. //當(dāng)input中有值 的時(shí)候顯示ul列表
          7. $("#ul").css("display","block")
          8. var a = -1 //設(shè)置一個(gè)變量 用來(lái)控制鍵盤(pán)上下鍵選中的li標(biāo)簽
          9. //開(kāi)始監(jiān)聽(tīng)鍵盤(pán)事件
          10. document.onkeydown = function (e) {
          11. e = e || window.event;
          12. var code = e.keyCode //用來(lái)儲(chǔ)存 e.keycode
          13. if(code==40){
          14. console.log("下")
          15. //獲取ul中的li個(gè)數(shù),使選擇不會(huì)超出
          16. if(a<$("#ul").children("li").length){
          17. a++;
          18. //開(kāi)始獲取到選擇中的li標(biāo)簽
          19. $("#ul").children("li").eq(a)
          20. }
          21. }else if(code==38){
          22. console.log("上")
          23. //獲取ul中的li個(gè)數(shù),使選擇不會(huì)超出
          24. if(0<a){
          25. a--;//開(kāi)始獲取到選擇中的li標(biāo)簽
          26. $("#ul").children("li").eq(a)
          27. }
          28. }else if(code==13){
          29. console.log("回車(chē)")
          30. }
          31. }
          32. }else{
          33. //當(dāng)input中沒(méi)有值的時(shí)候隱藏ul列表
          34. $("#ul").css("display","none")
          35. }
          36. }
          37. //給body添加一個(gè)點(diǎn)擊事件來(lái)隱藏ul,使用戶(hù)點(diǎn)擊頁(yè)面ul可以隱藏,不用input失去焦點(diǎn)是因?yàn)樵谶x擇li的時(shí)候很容易出bug
          38. $("body").click(function(){
          39. $("#ul").css("display","none")
          40. })
          41. //好啦最后別忘了失去鍵盤(pán)焦點(diǎn)的時(shí)候要把鍵盤(pán)監(jiān)聽(tīng)事件關(guān)閉,不然很容易出BUG
          42. $("#input").blur(function(){
          43. document.onkeydown =null
          44. })
          45. })

          全部代碼:


          1. <!DOCTYPE html>
          2. <html>
          3. <head>
          4. <meta charset="UTF-8">
          5. <title>jQuery實(shí)現(xiàn)百度搜索輸入框</title>
          6. </head>
          7. <body>
          8. <!--
          9. 作者:380012546@qq.com
          10. 時(shí)間:2018-08-02
          11. 描述:實(shí)現(xiàn)百度搜索輸入框的功能
          12. -->
          13. <div style="color: #323232; ">
          14. 查找:
          15. <input id="input"/>
          16. <button>搜索</button>
          17. <!--輸入框下方的列表-->
          18. <ul id="ul" style="display:none;z-index: 2;">
          19. <li>1</li>
          20. <li>2</li>
          21. <li>3</li>
          22. </ul>
          23. <!--輸入框內(nèi)的箭頭-->
          24. <span id="jiantou" style="padding:10px 20px 10px 20px;margin-left: -200px; background-image: url();background-repeat: no-repeat; background-position: center;"></span>
          25. </div>
          26. </body>
          27. <!--引入jQuery方便遍歷-->
          28. <script src="js/jquery-3.3.1.js"></script>
          29. <!--開(kāi)始js-->
          30. <script>
          31. $(document).ready(function(){
          32. //先監(jiān)聽(tīng)input的值變化
          33. document.getElementById("input").oninput=function(){
          34. var val = $(this).val()
          35. if(val!=""){
          36. //當(dāng)input中有值 的時(shí)候顯示ul列表
          37. $("#ul").css("display","block")
          38. var a = -1 //設(shè)置一個(gè)變量 用來(lái)控制鍵盤(pán)上下鍵選中的li標(biāo)簽
          39. //開(kāi)始監(jiān)聽(tīng)鍵盤(pán)事件
          40. document.onkeydown = function (e) {
          41. e = e || window.event;
          42. var code = e.keyCode //用來(lái)儲(chǔ)存 e.keycode
          43. if(code==40){
          44. console.log("下")
          45. //獲取ul中的li個(gè)數(shù),使選擇不會(huì)超出
          46. if(a<$("#ul").children("li").length){
          47. a++;
          48. //開(kāi)始獲取到選擇中的li標(biāo)簽
          49. $("#ul").children("li").eq(a)
          50. }
          51. }else if(code==38){
          52. console.log("上")
          53. //獲取ul中的li個(gè)數(shù),使選擇不會(huì)超出
          54. if(0<a){
          55. a--;//開(kāi)始獲取到選擇中的li標(biāo)簽
          56. $("#ul").children("li").eq(a)
          57. }
          58. }else if(code==13){
          59. console.log("回車(chē)")
          60. }
          61. }
          62. }else{
          63. //當(dāng)input中沒(méi)有值的時(shí)候隱藏ul列表
          64. $("#ul").css("display","none")
          65. }
          66. }
          67. //給body添加一個(gè)點(diǎn)擊事件來(lái)隱藏ul,使用戶(hù)點(diǎn)擊頁(yè)面ul可以隱藏,不用input失去焦點(diǎn)是因?yàn)樵谶x擇li的時(shí)候很容易出bug
          68. $("body").click(function(){
          69. $("#ul").css("display","none")
          70. })
          71. //好啦最后別忘了失去鍵盤(pán)焦點(diǎn)的時(shí)候要把鍵盤(pán)監(jiān)聽(tīng)事件關(guān)閉,不然很容易出BUG
          72. $("#input").blur(function(){
          73. document.onkeydown =null
          74. })
          75. })
          76. </script>
          77. </html>

          1 wang編輯器效果圖

          1、npm安裝

          安裝過(guò)程比較簡(jiǎn)單,不做重復(fù),說(shuō)一下使用過(guò)程遇到的問(wèn)題

          1. 如果編輯器放到了table td內(nèi),會(huì)發(fā)現(xiàn)插入分隔線(也就是插入hr)不好用,沒(méi)有找到在哪里改,換一個(gè)方式去實(shí)現(xiàn):直接使用editor.config.menus = []加載我們使用的菜單,主要是為了去掉splitLine(分隔線),然后使用自定義擴(kuò)展菜單創(chuàng)建新的分隔線菜單;
          2. 添加查看源碼擴(kuò)展;
          3. 彈出的窗口關(guān)閉的時(shí)候,只是切換到了后邊的菜單不能關(guān)閉菜單。

          插入hr

          import E from 'wangeditor'
          
          mounted () {
              const editor = new E('#div1')
              const menuKey = 'hrMenuKey'
              const { BtnMenu } = E
          
              // 第一,菜單 class ,Button 菜單繼承 BtnMenu class
          
              class HrMenu extends BtnMenu {
                constructor (editor) {
                  // data-title屬性表示當(dāng)鼠標(biāo)懸停在該按鈕上時(shí)提示該按鈕的功能簡(jiǎn)述
                  const $elem = E.$(
                    `<div class="w-e-menu" data-title="分割線">
                      <i class='w-e-icon-split-line'></i>
                    </div>`
                  )
                  super($elem, editor)
                }
                // 菜單點(diǎn)擊事件
                clickHandler () {
                  editor.cmd.do('insertHtml', '<hr>')
                }
                tryChangeActive () {
                  // 激活菜單
                  // 1. 菜單 DOM 節(jié)點(diǎn)會(huì)增加一個(gè) .w-e-active 的 css class
                  // 2. this.this.isActive === true
                  this.active()
                  // // 取消激活菜單
                  // // 1. 菜單 DOM 節(jié)點(diǎn)會(huì)刪掉 .w-e-active
                  // // 2. this.this.isActive === false
                  // this.unActive()
                }
              }
              // 注冊(cè)菜單
              E.registerMenu(menuKey, HrMenu)
          
              editor.config.placeholder = ''
              editor.config.uploadImgServer = '/public/sss/admin.php/ajaxweb/uppic.html'
              editor.config.uploadImgMaxSize = 1024 * 1024
              editor.config.uploadImgAccept = ['jpg', 'jpeg', 'png', 'gif']
              editor.config.height = 300
              editor.config.focus = true
              editor.config.menus = [
                'source',
                'head',
                'bold',
                'fontSize',
                'fontName',
                'italic',
                'underline',
                'strikeThrough',
                'indent',
                'lineHeight',
                'foreColor',
                'backColor',
                'link',
                'list',
                'justify',
                'quote',
                'image',
                'video',
                'table',
                'undo',
                'redo']
              editor.create()
          }

          查看源碼

          圖2 查看源碼效果圖

          實(shí)現(xiàn)目標(biāo):

          點(diǎn)擊查看的時(shí)候,遮蓋其它的按鈕,防止查看源碼的時(shí)候,點(diǎn)擊了別的按鈕進(jìn)行了誤操作。

          新加的菜單默認(rèn)都是在最后全屏前邊,分割線還可以,但是查看源碼我個(gè)人還是習(xí)慣在最前邊,使用的是jquery prepend感覺(jué)更加簡(jiǎn)單一些,代碼如下:

          import $ from 'jquery'
          mounted () {
            $(document).ready(function () {
                $('#div1 .w-e-toolbar').prepend('<div class=\'w-e-menu\' style=\'z-index:991;\' data-title=\'查看源碼\'><a style=\' display:block;width:100%;height:100%;\' ct=1 id=\'viewsource\'><i class=\'fa fa-file-text-o\'></i></a></div>')
                $(document).delegate('#viewsource', 'click', function () {
                  var editorHtml = editor.txt.html()
                  // console.log($(this).attr('ct'))
                  if (parseInt($(this).attr('ct')) === 1) {
                    $('#div1 .w-e-toolbar').prepend('<div id=\'zzc\' style=\'position:absolute;left:0;top:0;z-index:99;background-color:rgba(0,0,0,0.5);width:100%;height:40px;\'></div>')
                    $(this).parent().parent().parent().find('.w-e-text').css('width', $('.w-e-text').width() + 'px')
                    editorHtml = editorHtml.replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ')
                    $(this).attr('ct', '2')
                    $(this).css({'background-color': '#EEE'})
                  } else {
                    editorHtml = editor.txt.text().replace(/</ig, '<').replace(/>/ig, '>').replace(/ /ig, ' ')
                    $(this).attr('ct', '1')
                    $(this).parent().parent().parent().find('.w-e-text').css('width', '100%')
                    $(this).parent().parent().find('#zzc').remove()
                    $(this).css({'background-color': '#FFF'})
                  }
                  editor.txt.html(editorHtml)
                  // editor.change && editor.change()
                })
              })
          }

          說(shuō)明:

          1. 使用jquery prepend向頭部插入查看源碼按鈕;
          2. ct用來(lái)判斷是查看源碼,還是切換回原來(lái)的格式:1 查看源碼 2切換回原來(lái)的;
          3. <div id="zzc"></div>是遮罩層,用于點(diǎn)擊查看的時(shí)候遮蓋其它按鈕,防止誤操作,查看的時(shí)候添加,切換的時(shí)候移除;
          4. 查看源碼的時(shí)候,如果全是英文有可能出現(xiàn)不換行,頁(yè)面被撐開(kāi)的情況(因?yàn)閷挾仁?00%),所以這里要獲取編輯器所在div w-e-text的寬度(像素),通過(guò)jquery重新設(shè)置寬度;

          彈出的窗口,點(diǎn)擊關(guān)閉無(wú)效不能關(guān)閉

          圖3 彈出菜單無(wú)法關(guān)閉

          如圖,點(diǎn)擊關(guān)閉的時(shí)候會(huì)切換到了網(wǎng)絡(luò)圖片的表單,這應(yīng)該是菜單同級(jí)別,遮蓋了關(guān)閉的按鈕,所以我們要寫(xiě)css樣式加上z-index使關(guān)閉的菜單在其他的上層;

          css代碼

          .w-e-icon-close{
             display:block;
             z-index:999999 !important;
          }

          2、cdn引用js

          我是下載到本地的

          圖4 多圖上傳F12查看效果

          圖片上傳,如果選擇多個(gè)圖片,可能會(huì)出現(xiàn)以上圖片的情況style="font-size: 14px; font-family: "Helvetica Neue", Helvetica, "PingFang SC", Tahoma, Arial, sans-serif; max-width: 100%;"

          復(fù)制html發(fā)現(xiàn)是如下代碼:

          <img src="/public/upload/image/20211201/111.jpg" style="font-size: 14px; font-family: & quot;Helvetica Neue& quot;, Helvetica, & quot;PingFang SC& quot;, Tahoma, Arial, sans-serif; max-width: 100%;">

          很明顯style內(nèi)的 css樣式,不是應(yīng)該出現(xiàn)的,沒(méi)有找到在哪里修改。

          雙引號(hào)換成了& quot;如果不用查看源碼,頁(yè)面直接顯示沒(méi)有問(wèn)題,但是查看源碼,切換回來(lái)以后,會(huì)發(fā)現(xiàn)圖片亂了,所以查看源碼的時(shí)候,需要把& quot;替換成空。

          以下使用jquery實(shí)現(xiàn)自定義菜單(查看源碼、插入分割線):

          import $ from 'jquery'
            mounted () {
                $(document).ready(function () {
                //  查看源碼菜單
                $('#div1 .w-e-toolbar').prepend('<div class=\'w-e-menu\' style=\'z-index:991;\' data-title=\'查看源碼\'><a style=\' display:block;width:100%;height:100%;\' ct=1 id=\'viewsource\'><i class=\'fa fa-file-text-o\'></i></a></div>')
                //  分割線菜單
                var menl=$('#div1 .w-e-toolbar .w-e-menu').length;
          		  $("#div1  .w-e-toolbar .w-e-menu").eq(menl-1).before('<div class=\'w-e-menu\'  data-title=\'分割線\'><a  style=\'display:block;width:100%;height:100%;\' id=\'splitline\'><i class=\'w-e-icon-split-line\'></i></a></div>')
                //  查看源碼點(diǎn)擊
                $(document).delegate('#viewsource', 'click', function () {
                  var editorHtml = editor.txt.html()
                  // console.log($(this).attr('ct'))
                  if (parseInt($(this).attr('ct')) === 1) {
                    $('#div1 .w-e-toolbar').prepend('<div id=\'zzc\' style=\'position:absolute;left:0;top:0;z-index:99;background-color:rgba(0,0,0,0.5);width:100%;height:40px;\'></div>')
                    $(this).parent().parent().parent().find('.w-e-text').css('width', $('.w-e-text').width() + 'px')
                    editorHtml = editorHtml.replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ').replace(/& quot;/g, '')
                    $(this).attr('ct', '2')
                    $(this).css({'background-color': '#EEE'})
                  } else {
                    editorHtml = editor.txt.text().replace(/</ig, '<').replace(/>/ig, '>').replace(/ /ig, ' ')
                    $(this).attr('ct', '1')
                    $(this).css('border', '0px solid #DDD')
                    $(this).parent().parent().parent().find('.w-e-text').css('width', '100%')
                    $(this).parent().parent().find('#zzc').remove()
                    $(this).css({'background-color': '#FFF'})
                  }
                  editor.txt.html(editorHtml)
                  // editor.change && editor.change()
                })
                //分割線插入hr點(diǎn)擊
          	    $(document).delegate('#splitline', 'click', function () {
          		    editor.cmd.do('insertHtml', '<hr>');
          	    });
              })
            
            }

          如果我們把插入分割線的代碼單獨(dú)拿出來(lái),只是下面幾行,相對(duì)使用官方提供的方法會(huì)簡(jiǎn)單很多

                //   插入分割線菜單  
                var menl=$('#div1 .w-e-toolbar .w-e-menu').length;
          		  $('#div1  .w-e-toolbar .w-e-menu').eq(menl-1).before('<div class=\'w-e-menu\'  data-title=\'分割線\'><a  style=\'display:block;width:100%;height:100%;\' id=\'splitline\'><i class=\'w-e-icon-split-line\'></i></a></div>')
                // 分割線插入 hr點(diǎn)擊事件
          	    $(document).delegate('#splitline', 'click', function () {
          		    editor.cmd.do('insertHtml', '<hr>');
          	    });

          說(shuō)明:

          1. menl是菜單的個(gè)數(shù);
          2. $("#div1 .w-e-toolbar .w-e-menu").eq(menl-1).before 使用jquery before向最后一個(gè)菜單(也就是全屏菜單)之前插入分割線按鈕,使用after也可以;
          3. 查看源碼的替換多加了.replace(/& quot;/g, ‘’) ;
          4. 如果是cdn引入的js可能出現(xiàn)菜單一些功能不好用的情況,點(diǎn)擊了沒(méi)反應(yīng),可以嘗試把const editor = new E('#div1') 換成window.editor = new E('#div1')
          5. 注意:因?yàn)? quot;連起來(lái)會(huì)被轉(zhuǎn)換成雙引號(hào),這里沒(méi)法顯示,實(shí)際替換的時(shí)候要連起來(lái),不要有空格;


          editorHtml = editorHtml.replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ').replace(/& quot;/g, '') 

          附錄一下jquery after,before,append,prepend用法:

          向class=w-e-toolbar的div頭部插入html

          $(" .w-e-toolbar").prepend("<div >頭部插入html</div>")

          向class=w-e-toolbar的div底部插入html

          $(" .w-e-toolbar").append("<div >底部插入html</div>")

          向class=w-e-toolbar的div之前插入html

          $(" .w-e-toolbar").before("<div >之前插入html</div>")

          向class=w-e-toolbar的div之后插入html

          $(" .w-e-toolbar").after("<div >后面插入html</div>")

          可以做一下延伸:像我們上邊用到的向第幾個(gè)元素之后或者之前插入代碼。


          主站蜘蛛池模板: 国产伦精品一区二区三区视频猫咪 | 午夜性色一区二区三区不卡视频| 国产精品无码一区二区三区毛片| 爆乳熟妇一区二区三区霸乳| 极品少妇一区二区三区四区| 日韩一区二区三区四区不卡| 蜜桃传媒视频麻豆第一区| 国产伦一区二区三区高清| 国产精品亚洲产品一区二区三区 | 91福利一区二区| 久久一区二区精品| 国产一区二区三区免费| 日韩视频一区二区三区| 寂寞一区在线观看| 性色av一区二区三区夜夜嗨| 国内国外日产一区二区| 不卡一区二区在线| 日韩人妻无码一区二区三区综合部 | 能在线观看的一区二区三区| 激情综合丝袜美女一区二区| 国产嫖妓一区二区三区无码| 国产一区二区三区亚洲综合| 日韩AV在线不卡一区二区三区 | 成人免费一区二区三区| 日本大香伊一区二区三区| 鲁丝片一区二区三区免费| 亚洲一区中文字幕久久| 日韩免费视频一区二区| 亚洲午夜精品一区二区麻豆| 亚洲AV无码国产精品永久一区| 中文字幕一区二区三区5566| 中文字幕人妻AV一区二区| 国产成人av一区二区三区在线观看| 无码精品人妻一区二区三区免费| 国产精品无码一区二区在线观一| 久久精品一区二区东京热| 亚洲色偷精品一区二区三区| 鲁大师成人一区二区三区| 中文字幕一区二区三区日韩精品| 无码精品人妻一区二区三区中| 无码精品尤物一区二区三区|