Spring Boot 學習筆記

SpringBoot 簡介

  1. 為什麼要使用 Spring Boot

    因為 Spring,SpringMVC 需要使用的大量的配置文件 (xml文件)

    還需要配置各種對象,把使用的對象放入到 spring 容器中才能使用對象

    需要了解其他框架配置規則。

  2. SpringBoot 就相當於 不需要配置文件的 Spring+SpringMVC。 常用的框架和第三方庫都已經配置好了。

    拿來就可以使用了。

  3. SpringBoot開發效率高,使用方便多了

JavaConfig

使用 java 類作為 xml 配置文件的替代, 是配置 spring 容器的純 java 的方式。

在這個 java 類中可以創建 java 對象,把對象放入 spring 容器中(注入到容器)。

對於此JavaConfig相關的示例創建的是普通的Java項目,並非SpringBoot項目!

@Configuration

  • 放在類的上面,表示此類作為配置文件使用,相當於一個配置類

@Bean

  • 放在配置類中的方法上面,用來聲明對象,將對象注入到容器中
  • 方法返回值需要是注入的對象類型
  • 若沒有配置@Bean中的 name 屬性,則從容器中獲取該對象需要用其方法名
  • 若配置了@Bean中的 name 屬性,則從容器中獲取該對象需要用配置的 name 名稱

需要使用到兩個註解:

  • @Configuration:放在一個類的上面,表示這個類是作為配置文件使用的。
  • @Bean:放在配置類中的方法上,聲明對象,把對象注入到容器中。

配置類示例:

/**
 * Configuration:表示當前類是作為配置文件使用的。 就是用來配置容器的
 *       位置:在類的上面
 *
 *  SpringConfig這個類就相當於beans.xml
 */
@Configuration
public class SpringConfig {

    /**
     * 創建方法,方法的返回值是對象。 在方法的上面加入@Bean
     * 方法的返回值對象就注入到容器中。
     *
     * @Bean: 把對象注入到spring容器中。 作用相當於<bean>
     *
     *     位置:方法的上面
     *
     *     說明:@Bean,不指定對象的名稱,默認是方法名是 id
     *
     */
    @Bean
    public Student createStudent(){
        Student s1  = new Student();
        s1.setName("張三");
        s1.setAge(26);
        s1.setSex("男");
        return s1;
    }

    /***
     * 指定對象在容器中的名稱(指定<bean>的id屬性)
     * @Bean的name屬性,指定對象的名稱(id)
     */
    @Bean(name = "lisiStudent")
    public Student makeStudent(){
        Student s2  = new Student();
        s2.setName("李四");
        s2.setAge(22);
        s2.setSex("男");
        return s2;
    }
}

測試:

public class MyTest {

    @Test
    public void test01() {
        // 使用配置文件方式聲明的bean,需要用 ClassPathXmlApplicationContext,參數傳配置文件路徑
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("myStudent");
        System.out.println(student);
    }

    @Test
    public void test02() {
        // 使用配置類方式將對象注入到容器,需要使用 AnnotationConfigApplicationContext,參數傳配置類的class 
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 配置類中方法的Bean設置name屬性,則通過name屬性名獲取對象;否則默認可通過其方法名獲取
        Student s = (Student) ctx.getBean("lisiStudent");
        // Student s = (Student) ctx.getBean("makeStudent");
        // Student s = (Student) ctx.getBean("createStudent");
        System.out.println(s);
    }
}

小結:

  • 使用配置類的方式,需要在配置類上使用@Configuration;需要在配置類中的方法上使用@Bean聲明對象,將對象注入到容器中。
  • 配置類中的方法返回值類型是需要注入的對象類型;若未配置@Bean中的 name 屬性,則獲取該對象時需要使用方法名獲取;若配置了@Bean中的 name 屬性,則通過配置的 name 名稱獲取該對象。
  • 創建容器對象時,不再使用ClassPathXmlApplicationContext介面實現類,不再通過配置文件來注入到容器對象中;而是使用AnnotationConfigApplicationContext介面實現類,通過配置類的 class 來將對象注入到容器。

@ImportResource

  • 使用在配置類的上面
  • 作用是導入其他的 xml 配置文件, 等於在 xml 中使用 <import resources="其他配置文件"/>
  • 需要在 value 屬性中指定配置文件的路徑
@Configuration
@ImportResource(value = "classpath:applicationContext.xml")
public class SpringConfig {
}

@PropertySource

  • 使用在配置類的上面
  • 用來讀取 properties 屬性配置文件
  • 使用屬性配置文件可以實現外部化配置 ,在程式程式碼之外提供數據。

@ComponentScan:使用在配置類上,掃描指定包中註解,來創建對象以及給屬性賦值

示例:

--配置類--
@Configuration
@ImportResource(value = "classpath:applicationContext.xml")  // 導入xml配置文件
@PropertySource(value = "classpath:config.properties")  // 讀取屬性配置文件
@ComponentScan(basePackages = "com.luis.vo")  // 聲明組件掃描器,掃描指定包中的註解
public class SpringConfig {
}

--config.properties--
tiger.name=東北虎
tiger.age=3 

--vo數據類--    
@Component("tiger")
public class Tiger {

    @Value("${tiger.name}")
    private String name;

    @Value("${tiger.age}")
    private Integer age;    
}  

--測試--
@Test
public void test04() {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    Tiger tiger = (Tiger) ctx.getBean("tiger");
    System.out.println(tiger);
}    

SpringBoot 入門

SpringBoot 是 Spring 中的一個成員,可簡化 Spring,SpringMVC 的使用。

其核心還是 IOC 容器。

SpringBoot 特點

  • Create stand-alone Spring applications

    創建 spring 應用

  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)

    有內嵌的 tomcat,jetty,Undertow

  • Provide opinionated ‘starter’ dependencies to simplify your build configuration

    提供了 starter 起步依賴,簡化應用的配置。

    比如使用 MyBatis 框架,需要在 Spring 項目中,配置 MyBatis 的對象 SqlSessionFactory ,Dao 的代理對象

    如今在 SpringBoot 項目中,在 pom.xml 裡面,只需加入一個 mybatis-spring-boot-starter 依賴即可

  • Automatically configure Spring and 3rd party libraries whenever possible

    儘可能去配置 spring 和第三方庫,也叫做自動配置(就是把 spring 中的,第三方庫中的對象都創建好,放到容器中,開發人員可以直接使用)

  • Provide production-ready features such as metrics, health checks, and externalized configuration

    提供了健康檢查,統計,外部化配置

  • Absolutely no code generation and no requirement for XML configuration

    不用生成程式碼,不用使用xml,做配置

創建 SpringBoot 項目的三種方式

創建 SpringBoot 項目有三種方式:

  1. 使用 Spring 提供的初始化器,就是使用嚮導創建 SpringBoot 應用(使用 //start.spring.io 地址)【聯網】

  2. 使用 Spring 提供的初始化器,就是使用嚮導創建 SpringBoot 應用(使用中國的 //start.springboot.io 地址)【聯網】

    還可以選擇使用阿里雲的地址進行創建://start.aliyun.com

  3. 先直接創建 maven 項目,然後補充相關依賴,補全目錄結構即可【不需聯網】

創建簡單 SpringBoot web 項目示例:(以下選擇嚮導方式創建)

  1. 新建 SpringBoot 模組,選擇 Spring Initializr嚮導創建

  2. 配置項目相關資訊,依賴選擇 Spring Web 進行創建

  3. 整理項目結構以及 pom 文件

  4. 在主啟動類所在的包下創建 controller 控制器類

    @Controller
    public class HelloSpringBoot {
    
        @RequestMapping("/hello")
        @ResponseBody
        public String helloSpringBoot() {
            return "歡迎使用SpringBoot";
        }
    }
    
  5. 查看是否配置有 Spring Boot 啟動服務,若沒有則進行配置

  6. 運行主啟動類的 main 方法或 IDEA 主面板運行項目

  7. 瀏覽器輸入地址訪問 //localhost:8080/hello(默認8080埠,無上下文)

@SpringBootApplication

  • 其自動配置在主類之上,每個 SpringBoot 項目都會有一個主類

    @SpringBootApplication
    public class SpringbootTest03Application {
        public static void main(String[] args) {
            SpringApplication.run(SpringbootTest03Application.class, args);
        }
    }
    
@SpringBootApplication
是一個複合註解,包含以下三個註解(以下注解的功能其全具備)
@SpringBootConfiguration  -----> 該註解標註的類可當做配置類使用
@EnableAutoConfiguration  -----> 啟用自動配置,將常用框架以及第三方對象創建好
@ComponentScan  -----> 可掃描到該註解標註的類的包及其子包下其他註解
    
詳解:    
1.@SpringBootConfiguration
    
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
說明:使用了@SpringBootConfiguration註解標註的類,可以作為配置文件使用,
    可以使用@Bean聲明對象,注入到容器,
    故主類可作為一個配置類使用,在其類中可在方法上使用@Bean聲明對象

2.@EnableAutoConfiguration
啟用自動配置,把java對象配置好,注入到spring容器中;例如可以把mybatis的對象創建好,放入到容器中。
    
3.@ComponentScan 
掃描器,找到註解,根據註解的功能創建對象,給屬性賦值等。
默認掃描的包: @ComponentScan所標註的類所在的包和子包。    

SpringBoot 的配置文件

避免中文亂碼,需要在 IDEA 中設置 encoding 為 UTF-8

配置文件名: application

擴展名有:properties( k=v) 以及 yml ( k: v)

可使用 application.properties,application.yml 兩種文件格式

創建 SpringBoot 項目時默認創建的是 application.properties 配置文件格式

以下為配置示例:

例1:application.properties 中設置埠和上下文

# 設置埠
server.port=8888
# 設置訪問應用的上下文路徑
server.servlet.context-path=/myWeb

例2:application.yml 中設置埠和上下文(推薦使用

這種格式的配置文件,結構比較清晰

注意:value 值前有一個空格

server:
  port: 8083
  servlet:
    context-path: /myboot

注意:如果同時存在兩種格式的配置文件,即 application.properties 和 application.yml 兩個配置文件同時存在,則默認使用 application.properties 這種格式中的配置!

多環境配置的應用

有開發環境,測試環境,上線的環境。

每種環境有不同的配置資訊,例如埠,上下文訪問路徑,資料庫 url,用戶名,密碼等,所以需要多種環境配置。

使用多環境配置文件,可以方便的切換不同的配置。

多環境配置方式

  1. 創建多個配置文件,文件命名規則:application-環境名稱.properties(yml)【一般環境名使用 dev/test/online 這幾種】

    創建開發環境的配置文件:application-dev.yml

    #項目開發使用的環境
    server:
      port: 8080
      servlet:
        context-path: /mydev
    

    創建測試環境的配置文件:application-test.yml

    #項目測試使用的環境
    server:
      port: 8081
      servlet:
        context-path: /mytest
    

    創建上線環境的配置文件:application-online.yml

    #項目上線使用的環境
    server:
      port: 8082
      servlet:
        context-path: /myonline
    
  2. 在主配置文件 application.yml 中激活要使用的環境【激活的配置資訊是根據多環境文件的命名來確定】

    主配置文件中激活要使用的環境:application.yml

    #激活使用的環境
    spring:
      profiles:
        active: online
    

使用 @Value 讀取配置文件數據

我們可以在主配置文件中使用 key-value 鍵值對的格式進行數據的配置;

因為主配置文件已經由 Spring 自動管理,所以可以直接在自己寫的控制器類的屬性上通過 @Value("${server.port}")讀取配置文件中數據,並對屬性進行賦值。

@Controller
public class MyController {

    @Value("${server.port}")
    private Integer port;
    @Value("${server.servlet.context-path}")
    private String contextPath;
    @Value("${project.name}")
    private String projectName;
    @Value("${project.dev}")
    private String projectDev;

    @RequestMapping("/data")
    @ResponseBody
    public String queryData() {
        return port + "========> port " + contextPath + "========> contextPath "
                + projectName + "========> projectName " + projectDev + "========> projectDev";
    }
}

@ConfigurationProperties

  • 使用位置:類的上面
  • 作用:把配置文件中符合條件的數據映射為 java 對象
  • 需要配置其 prefix 屬性:即配置文件中的某些 key 開頭的內容

使用示例:@ConfigurationProperties(prefix = "school")

使用解釋:類上添加@ConfigurationProperties註解後,其將從屬性配置文件中找以配置的 prefix 屬性值,即school開頭的配置,將所有 school後面的配置參數與該類中的所有屬性相比較,如果匹配成功,則將配置文件中配置的對應的數據賦值給該類中同名屬性,完成數據映射。

使用示例:

  1. 在配置文件 application.properties 中配置自定義數據

    #配置埠號
    server.port=8888
    #配置上下文訪問路徑
    server.servlet.context-path=/myboot
    
    #自定義數據
    school.name=哈師大
    school.addr=東北
    school.level=2
    
  2. 創建數據對象

    @Component
    @ConfigurationProperties(prefix = "school")
    public class SchoolInfo {
    
        private String name;
        private String addr;
        private Integer level;
        // getter,setter,toString...
    }    
    
  3. 創建控制器類

    @Controller
    public class MyController {
    
        @Resource
        private SchoolInfo info;
    
        @RequestMapping("/info")
        @ResponseBody
        public String querySchoolInfo() {
            return "info = " + info;
        }
    }
    
  4. 運行,訪問測試

    訪問://localhost:8888/myboot/info
    結果:info = SchoolInfo{name='哈師大', addr='東北', level=2}
    

SpringBoot 中使用 JSP(不推薦,了解即可)

SpringBoot 默認不支援 JSP,需要進行相關配置才能使用 JSP。

JSP 技術慢慢被淘汰,有更好的技術替代它。

使用 SpringBoot 框架時不推薦使用 JSP,後面將使用 Thymeleaf 模板技術完全替代 JSP!

使用步驟:

  1. 創建 SpringBoot 項目,整理項目結構和 pom 文件

  2. 添加一個處理 JSP 的依賴,負責編譯 JSP 文件(如果需要使用 servlet jsp jstl 功能則需要另外加對應依賴)

    <!--處理JSP的依賴-->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>jstl</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    </dependency>
    
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    	<artifactId>javax.servlet.jsp-api</artifactId>
    	<version>2.3.1</version>
    </dependency>
    
  3. 在 main 目錄下創建一個名為 webapp 的目錄(名稱固定);在項目工程結構中設置該目錄為 web 資源目錄(進入項目結構設置後,點擊 Web 選項,從 Web Resource Directories 下添加新創建的 webapp 目錄為 web 資源目錄即可)

  4. 在 webapp 目錄下新建 index.jsp 用來顯示 controller 中的數據

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>jsp</title>
    </head>
    <body>
        SpringBoot中使用JSP顯示數據:${data}
    </body>
    </html>
    
  5. 創建 controller 控制器類,指定數據和視圖

    @Controller
    public class JspController {
    
        @RequestMapping("/myjsp")
        public String doJsp(Model model) {
            // 將數據放到request中(與request.setAttribute作用相同)
            model.addAttribute("data", "SpringBoot使用JSP");
            return "index";
        }
    }
    
  6. 配置文件中配置視圖解析器,埠號以及上下文等資訊

    #配置埠號
    server.port=7070
    #配置上下文訪問路徑
    server.servlet.context-path=/myboot
    
    #配置視圖解析器
    # / = src/main/webapp
    spring.mvc.view.prefix=/
    spring.mvc.view.suffix=.jsp
    
  7. 在 pom.xml 中指定 jsp 文件編譯後的存放目錄【在 pom 的 build 標籤下進行指定】

    修改 pom 文件後,記得重新載入下,確保 pom 文件資源完全刷新!

    <!-- 指定jsp編譯後的存放目錄 -->
    <resources>
        <resource>
            <!-- jsp原來的目錄 -->
            <directory>src/main/webapp</directory>
            <!-- 指定編譯後的存放目錄 -->
            <targetPath>META-INF/resources</targetPath>
            <!-- 指定治理後的目錄和文件 -->
            <includes>
                <include>**/*.*</include>
            </includes>
        </resource>
    </resources>
    
  8. 直接運行並啟動主程式,輸入地址訪問測試

    //localhost:7070/myboot/myjsp
    

兩點需要注意下:

  • 必須添加處理 jsp 的依賴
  • 必須指定 jsp 編譯後的存放目錄

原因:因為 SpringBoot 使用的是內嵌的 Tomcat

SpringBoot 使用容器 ApplicationContext

使用場景:不想啟動整個項目,只想測試某個功能,就可直接通過run方法獲取容器對象,通過容器對象進行相關測試。

如果想通過程式碼,從容器中獲取對象:

通過SpringApplication.run(Application.class, args); 其返回值即可獲取容器對象。

SpringApplication.run(SpringbootUseJspApplication.class, args)返回的就是一個 ApplicationContext 容器對象!

解析:SpringApplication.run(SpringbootUseJspApplication.class, args);

點進其run方法:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}   

查看ConfigurableApplicationContext類:
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {...}    

總結:通過run方法獲取的是容器對象ApplicationContext的子類,即也是一個容器對象。

容器使用示例:

  1. 創建 SpringBoot 項目時選擇 Spring Web 依賴,整理項目目錄和 pom 文件

  2. 創建 service 介面及其實現類,定義測試方法

    public interface UserService {
        void sayHello(String name);
    }
    
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        @Override
        public void sayHello(String name) {
            System.out.println("hello " + name);
        }
    }
    
  3. 在主啟動類中直接獲取並使用容器對象進行測試

    @SpringBootApplication
    public class SpringbootUseContainerApplication {
    
        public static void main(String[] args) {
            // 直接通過run方法獲取容器對象
            ApplicationContext ctx = SpringApplication.run(SpringbootUseContainerApplication.class, args);
            // 從容器中獲取指定對象
            UserService userService = (UserService) ctx.getBean("userService");
            // 調用對象的方法進行測試
            userService.sayHello("luis");
        }
    }
    

CommandLineRunner 和 ApplcationRunner 介面

介紹:這兩個介面都有一個 run 方法。

執行時機:容器對象創建好後自動會執行介面中的 run() 方法

使用場景:需要在容器啟動後執行一些內容,比如讀取配置文件,資料庫連接之類的。

使用方式:實現介面,重寫 run 方法,方法中寫需要在容器啟動後待執行的內容。

@FunctionalInterface
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

@FunctionalInterface
public interface ApplicationRunner {
    void run(ApplicationArguments args) throws Exception;
}

使用示例:(以下以實現 CommandLineRunner介面為例)

  1. 創建 SpringBoot 項目,不用選擇依賴,整理項目目錄和 pom 文件

  2. 新建 service 介面以及實現類,定義測試方法

    public interface UserService {
        void sayHello(String name);
    }
    
    @Service("userService")
    public class UserServiceImpl implements UserService {
        @Override
        public void sayHello(String name) {
            System.out.println("你好," + name);
        }
    }
    
  3. 讓啟動類實現 CommandLineRunner介面,重寫 run 方法進行測試

    @SpringBootApplication
    public class SpringbootRunApplication implements CommandLineRunner {
    
        @Resource
        private UserService userService;
    
        public static void main(String[] args) {
            System.out.println("main start");
            // 創建容器對象
            SpringApplication.run(SpringbootRunApplication.class, args);
            System.out.println("main end");
        }
    
        @Override
        public void run(String... args) throws Exception {
            System.out.println("容器對象創建後執行此run方法");
            // 此方法在容器對象創建後自動執行
            // 可在此調用容器對象中的方法
            userService.sayHello("jack");
        }
    }
    
  4. 運行主程式,進行測試

    運行結果:

    main start
    容器對象創建後執行此run方法
    你好,jack
    main end
    

SpringBoot 和 Web 組件

講三個內容:攔截器、Servlet、Filter

SpringBoot 中使用攔截器

攔截器是 SpringMVC 中的一種對象,能攔截對 Controller 的請求。

在框架中有系統實現好的攔截器, 也可以自定義攔截器,實現對請求的預先處理。

回顧 SpringMVC 中使用攔截器

  1. 創建類實現 SpringMVC 框架的 HandlerInterceptor 介面,重寫對應的方法

    public interface HandlerInterceptor {
     default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         return true;
     }
    
     default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
     }
    
     default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
     }
    }
    
  2. 在 SpringMVC 的配置文件中,聲明攔截器

    <mvc:interceptors>
    	<mvc:interceptor>
        	<mvc:path="url" />
            <bean class="攔截器類全限定名稱"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

SpringBoot 中使用攔截器

示例:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理項目目錄和 pom 文件

  2. 創建攔截器類,實現 HandlerInterceptor介面,重寫相關方法

    public class LoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("執行LoginInterceptor攔截器類的preHandle方法");
            return true;
        }
    }
    
  3. 創建自定義配置類實現 WebMvcConfigurer介面,重寫addInterceptors介面方法,在此方法中進行攔截器的相關配置(注入攔截器對象,指定攔截地址等)

    @Configuration // 成為配置類=====
    public class MyAppConfig implements WebMvcConfigurer {
    
        // 重寫addInterceptors方法,添加攔截器對象,注入到容器中
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    
            // 創建攔截器對象
            HandlerInterceptor loginInterceptor = new LoginInterceptor();
    
            // 指定攔截的請求URI地址
            String[] path = {"/user/**"};
            // 指定不攔截的URI地址
            String[] excludePath = {"/user/login"};
    
            // 添加攔截器對象,指定攔截以及不攔截的URI地址
            registry.addInterceptor(loginInterceptor)
                    .addPathPatterns(path)
                    .excludePathPatterns(excludePath);
        }
    }
    
  4. 創建控制器類,進行資源訪問攔截測試

    @Controller
    @RequestMapping("/user")
    public class BootController {
    
        @RequestMapping("/account")
        @ResponseBody
        public String userAccount() {
            return "訪問/user/account地址";
        }
    
        @RequestMapping("/login")
        @ResponseBody
        public String userLogin() {
            return "訪問/user/login地址";
        }
    }
    
  5. 運行主程式,進行訪問測試

    分別訪問下列兩個地址:
    //localhost:8080/user/account
    //localhost:8080/user/login
    訪問的測試結果:
    訪問//localhost:8080/user/account時,後台有攔截器攔截資訊輸出【此請求走攔截器,被攔截器攔截】
    訪問//localhost:8080/user/login時,後台無攔截器攔截資訊輸出【此請求不走攔截器,不會被攔截器攔截】
    

SpringBoot 中使用 Servlet

使用步驟:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理項目目錄和 pom 文件

  2. 創建 servlet 類,繼承 HttpServlet,重寫相關方法

    public class MyServlet extends HttpServlet {
    
        // 直接重寫service方法,則doGet和doPost請求都直接走此方法
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            // 設置字元編碼
            response.setContentType("text/html;charset=UTF-8");
    
            // 使用應答對象HttpServletResponse,輸出應答結果
            PrintWriter out = response.getWriter();
            out.print("SpringBoot中使用Servlet");
            out.flush();
            out.close();
        }
    }
    
  3. 創建自定義配置類,在類中定義指定返回值類型的方法,註冊 Servlet 對象

    @Configuration
    public class WebApplicationConfig {
    
        // ====定義指定返回值類型的方法,註冊servlet對象====
        @Bean
        public ServletRegistrationBean servletRegistrationBean() {
    
            // 兩種方式註冊servlet:
    
            // 方式一:使用ServletRegistrationBean對象的有參構造
            // 下列對象構造方法參數:第一個是需要註冊的servlet對象,第二個是對應servlet的訪問地址
            // ServletRegistrationBean bean = new ServletRegistrationBean(new MyServlet(), "/login1", "/login2");
    
            // 方式二:使用ServletRegistrationBean對象的無參構造
            ServletRegistrationBean bean = new ServletRegistrationBean();
            bean.setServlet(new MyServlet());
            bean.addUrlMappings("/login3", "/login4");
            return bean;
        }
    }
    
  4. 運行主程式,輸入地址訪問測試

    //localhost:8080/login1
    //localhost:8080/login2
    //localhost:8080/login3
    //localhost:8080/login4
    

SpringBoot 中使用過濾器

Filter 是 Servlet 規範中的過濾器,可以處理請求,對請求的參數,屬性進行調整。

常常在過濾器中處理字元編碼。

SpringBoot 中使用過濾器 Filter:

使用步驟:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理項目目錄和 pom 文件,刷新 pom

  2. 創建自定義的過濾器類,實現 javax.servlet.Filter 介面,重寫 doFilter 方法(注意實現的是 servlet 中的 Filter 介面)

    public class MyFilter implements Filter {
        
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("執行自定義過濾器類MyFilter中的doFilter方法");
            // 放行
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
    
  3. 創建自定義配置類,在類中定義指定返回值類型的方法,註冊過濾器對象

    @Configuration
    public class WebApplicationConfig {
    
        @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            
            // 註冊自定義過濾器類,並指定過濾的地址
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setFilter(new MyFilter());
            bean.addUrlPatterns("/user/*");
            return bean;
        }
    }
    
  4. 創建控制器類,進行資源訪問過濾測試

    @Controller
    public class CustomFilterController {
    
        @RequestMapping("/user/login")
        @ResponseBody
        public String userLogin() {
            return "訪問/user/login";
        }
    
        @RequestMapping("/query")
        @ResponseBody
        public String query() {
            return "訪問/query";
        }
    }
    
  5. 運行主啟動類,輸入訪問地址測試

    //localhost:8080/query
    //localhost:8080/user/login
    

SpringBoot 中使用字符集過濾器

一般,都是使用字符集過濾器 CharacterEncodingFilter解決 post 請求中文亂碼的問題。

CharacterEncodingFilter : 解決 post 請求中文亂碼的問題。

在 SpringMVC 框架, 需要在 web.xml 註冊過濾器並配置其相關屬性。

在SpringBoot 中使用字符集過濾器解決 post 請求亂碼有兩種方式:

使用示例:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理項目目錄和 pom 文件,刷新 pom

  2. 創建自定義 servlet,處理請求

    public class MyServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            response.setContentType("text/html"); // 此處並沒有設置字元編碼方式
    
            PrintWriter out = response.getWriter();
            out.write("沒有設置編碼方式,默認使用ISO-8859-1,會出現中文亂碼");
            out.flush();
            out.close();
        }
    }
    
  3. 創建自定義配置類,註冊 servlet,設置訪問路徑

    @Configuration
    public class WebSystemConfig {
    
        // 註冊servlet,設置訪問路徑
        @Bean
        public ServletRegistrationBean servletRegistrationBean() {
    
            ServletRegistrationBean bean = new ServletRegistrationBean();
            bean.setServlet(new MyServlet());
            bean.addUrlMappings("/myservlet");
            return bean;
        }
    }
    
  4. 啟動主程式,輸入訪問地址進行測試

    //localhost:8080/myservlet
    結果:出現中文亂碼
    ?????????????ISO-8859-1????????
    

方式一:配置類中設置字元編碼(不推薦)

不僅需要在配置類中註冊字符集過濾器類,設置字元編碼,還需在配置文件中關閉默認啟用的過濾器。

  1. 在自定義配置類中需創建指定返回值類型的方法,註冊 CharacterEncodingFilter字符集過濾器類,設置字元編碼
@Configuration
public class WebSystemConfig {

    // 註冊servlet,設置訪問路徑
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {

        ServletRegistrationBean bean = new ServletRegistrationBean();
        bean.setServlet(new MyServlet());
        bean.addUrlMappings("/myservlet");
        return bean;
    }

    // ==================================================================
    // 註冊CharacterEncodingFilter,並設置字元編碼方式
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {

        FilterRegistrationBean reg = new FilterRegistrationBean();

        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        // 設置字元編碼方式
        filter.setEncoding("UTF-8");
        // 指定request,response都使用encoding的值
        filter.setForceEncoding(true);

        reg.setFilter(filter); // 註冊字符集過濾器對象CharacterEncodingFilter
        reg.addUrlPatterns("/*"); // 設置過濾地址

        return reg;
    }
    // ==================================================================    
}
  1. 修改 application.properties 文件,讓自定義的過濾器起作用

    #SpringBoot中默認已經配置了CharacterEncodingFilter。 編碼默認ISO-8859-1
    #設置enabled=false 作用是關閉系統中配置好的過濾器, 使用自定義的CharacterEncodingFilter
    server.servlet.encoding.enabled=false
    

方式二:配置文件中設置字元編碼(推薦)

只需要通過幾行配置,即可完成字元編碼的設置。

#讓系統的CharacterEncdoingFilter生效(默認為true)
server.servlet.encoding.enabled=true
#指定使用的編碼方式
server.servlet.encoding.charset=UTF-8
#強制request,response都使用charset屬性的值
server.servlet.encoding.force=true

ORM 操作 MySQL

ORM 是「對象-關係-映射」的簡稱。(Object Relational Mapping,簡稱ORM)

使用 MyBatis 框架操作 MySQL。

使用 MyBatis 框架操作數據,使用 SpringBoot 框架集成 MyBatis。

SpringBoot 集成 Mybatis

以下是集成步驟示例,創建 dao 介面的代理對象有好幾種方式可供選擇,以下方式並不完美,注意後續自己修改!

前期準備
資料庫名:springdb
資料庫表:student
欄位:id (int auto_increment) name(varchar) age(int)

步驟:

  1. 創建 SpringBoot 項目,添加 Spring Web、MyBatis、MySQL 依賴,整理項目目錄和 pom 文件,刷新 pom

  2. 創建實體類 model

    public class Student {
        private Integer id;
        private String name;
        private Integer age;
        // getter,setter,toString...
    }    
    
  3. 寫 dao 介面和 mapper 文件

    此處 dao 介面上使用了 @Mapper註解,告訴MyBatis這是 dao 介面,創建此介面的代理對象

    @Mapper  // 告訴MyBatis這是dao介面,創建此介面的代理對象
    public interface StudentDao {
    
        Student selectById(@Param("stuId") Integer id);
    }
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "//mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.luis.dao.StudentDao">
    
        <select id="selectById" resultType="com.luis.model.Student">
            select id,name,age from student where id=#{stuId}
      </select>
    
    </mapper>
    
  4. 創建 service 介面及其實現類

    // 介面
    public interface StudentService {
    
        Student queryStudentById(Integer id);
    }
    
    // 實現類
    @Service
    public class StudentServiceImpl implements StudentService {
    
        @Resource
        private StudentDao studentDao;
    
        @Override
        public Student queryStudentById(Integer id) {
            return studentDao.selectById(id);
        }
    }
    
  5. 創建控制器類

    @Controller
    @RequestMapping("/student")
    public class StudentController {
        
        @Resource
        private StudentService studentService;
    
        @RequestMapping("/queryById")
        @ResponseBody
        public String queryStudentById(Integer id) {
            Student student = studentService.queryStudentById(id);
            return student.toString();
        }
    }
    
  6. 在 pom 文件的 build 標籤下添加 mapper 文件的 resources 插件

    <!--   resources插件,將mapper文件複製到classes目錄下     -->
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    
  7. 在主配置文件中配置埠,上下文訪問路徑,資料庫連接等資訊

    server.port=8888
    server.servlet.context-path=/orm
    
    #配置資料庫連接資訊 注意mysql新舊版本配置不同
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=luis
    
  8. 運行啟動類,輸入訪問地址測試【注意:填的id必須資料庫中有,否則會出現空指針異常】

    //localhost:8888/orm/student/queryById?id=29
    

日誌配置

在主配置文件中配置 mybatis 日誌:(可選不同的日誌列印,如選 log4j 這種的,則另需添加相關依賴)

# 日誌配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

第一種方式:使用@Mapper(不建議)

  • 使用在類上
  • 作用:告訴 MyBatis 這是 dao 介面,會自動創建此介面的代理對象
  • 缺點:每一個需要創建代理對象的 dao 介面都需要加上該註解,dao 介面多的時候不方便!

示例:

@Mapper  // 告訴MyBatis這是dao介面,創建此介面的代理對象
public interface StudentDao {

    Student selectById(@Param("stuId") Integer id);
}

第二種方式:使用@MapperScan

  • 通常使用在主啟動類上
  • basePackages屬性:通常配置 dao 介面所在包名
  • 作用:找到 Dao 介面以及 Mapper 文件(可直接掃描到該包下所有的 dao 介面和 mapper 文件)
  • 優點:使用該註解直接掃描指定包,不用在每一個 dao 介面上分別加 @Mapper註解

示例:

@SpringBootApplication
@MapperScan(basePackages = "com.luis.dao") // 掃描指定包,找到dao介面和mapper文件,mybatis自動創建介面的代理對象
public class SpringbootMapperApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootMapperApplication.class, args);
    }

}

第三種方式:Mapper文件和Dao介面分開管理(開發常用,推薦使用)

之前我們的 dao 目錄下既有 Java 文件,也有 xml/mapper 文件;如果希望 Java 目錄下只有 Java 文件,將 xml/mapper 文件放到 resources 下分開管理,那麼只使用之前的 @MapperScan註解是掃描不到 resources 下的 Mapper 文件的,此時我們就需要在配置文件中指定 Mapper 文件的位置。

示例:

操作:在 resources 下創建子目錄(自定義的),例如 mapper,用來存放 mapper 映射文件,將之前 dao 下的 mapper 文件全剪切到 mapper 目錄下。

  1. 同樣,我們仍然需要在主啟動類上使用 @MapperScan註解指定 dao 所在的包,掃描 dao 介面:
@SpringBootApplication
@MapperScan(basePackages = "com.luis.dao") // 掃描指定包,找到dao介面和mapper文件,mybatis自動創建介面的代理對象
public class SpringbootMapperApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootMapperApplication.class, args);
    }

}
  1. 在主配置文件中指定 mapper 映射文件的位置:
#指定mapper文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
  1. 配置資源插件,確保資源文件編譯到 classes 目錄下:
<!--   resources插件,將所有資源文件複製到classes目錄下【配置在pom.xml的build標籤下】     -->
<resources>
    <resource>
        <directory>src/main/resources</directory>
        <includes>
            <include>**/*.*</include>
        </includes>
    </resource>
</resources>

注意:如果項目編譯後 .xml/properties/yml 文件沒有出現在 classes 目錄下,則需要在 pom 中添加 resources 資源插件。

事務及mybatis自動程式碼生成器的使用

回顧 Spring 框架中的事務:

  1. 管理事務的對象:事務管理器(介面,介面有很多的實現類)

    例如:使用 Jdbc 或 mybatis 訪問資料庫,使用的事務管理器:DataSourceTransactionManager

  2. 聲明式事務:使用 xml 配置文件或者使用註解說明事務控制的內容

    控制事務:隔離級別,傳播行為,超時時間

  3. 事務處理方式:

    1. Spring 框架中的 @Transactional
    2. Spring 支援 aspectj 框架的事務配置,可在 xml 配置文件中,聲明事務控制的內容

SpringBoot 中使用事務:上面的兩種方式都可以。

使用步驟:(以下以註解方式演示事務的使用:)

  1. 在業務方法的上面加入 @Transactional ,加註解後,方法就有事務功能

  2. 明確的在主啟動類的上面,加入@EnableTransactionManager,啟用事務管理器(可不用添加此註解

/**
     * @Transactional 表示方法有事務支援,拋出運行時異常,則進行回滾
     * 使用MySQL默認的傳播行為、隔離級別、超時時間
     */
@Transactional // 可通過屬性配置傳播行為、隔離級別、超時時間
@Override
public int addUser(User user) {
    System.out.println("業務方法addUser開始執行");
    int rows = userMapper.insert(user);
    System.out.println("業務方法addUser結束執行");

    // 模擬發生運行時異常
    // int num = 10 / 0;
    return rows;
}

SpringBoot 中通過註解使用事務,一句話說明:直接在需要開啟事務的業務方法上添加 @Transactional 註解即可!

示例:(有mybatis自動程式碼生成器的使用示例,可自動生成 model 實體類, dao 介面以及 mapper 映射文件)

準備資料庫表:
資料庫名:springdb 表名:user
欄位:id(int auto_increment)name(varchar)age(int)
  1. 創建 SpringBoot 項目,添加 Spring Web、MyBatis、MySQL 依賴,整理項目目錄和 pom 文件

  2. 在 pom 的 plugins 標籤下添加 mybatis 自動程式碼生成插件

    <!--mybatis自動程式碼生成插件-->
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.6</version>
        <configuration>
            <!--配置文件的位置:在項目的根目錄下,和src平級-->
            <configurationFile>GeneratorMapper.xml</configurationFile>
            <verbose>true</verbose>
            <overwrite>true</overwrite>
        </configuration>
    </plugin>
    
  3. 在 pom 的 build 標籤下添加資源插件,處理資源文件,刷新 pom

    <!--   處理資源文件     -->
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/**.*</include>
            </includes>
        </resource>
    </resources>
    
  4. 在項目根目錄下創建 GeneratorMapper.xml文件,其與 src 平級;將下列內容拷貝到文件中;依照注釋修改配置,修改完後,點開右側 maven 快捷窗口,使用當前項目下 plugins 下 mybatis-genetate 下的 mybatis 自動程式碼生成插件進行生成。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "//mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    
    <generatorConfiguration>
    
        <!-- 指定連接資料庫的JDBC驅動包所在位置,指定到你本機的完整路徑 -->
        <classPathEntry location="D:\Java\tools\mysql-connector-java-8.0.28.jar"/>
    
        <!-- 配置table表資訊內容體,targetRuntime指定採用MyBatis3的版本 -->
        <context id="tables" targetRuntime="MyBatis3">
    
            <!-- 抑制生成注釋,由於生成的注釋都是英文的,可以不讓它生成 -->
            <commentGenerator>
                <property name="suppressAllComments" value="true" />
            </commentGenerator>
    
            <!-- 配置資料庫連接資訊 -->
            <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                            connectionURL="jdbc:mysql://localhost:3306/springdb?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8"
                            userId="root"
                            password="luis">
                <!--MySQL 不支援 schema 或者 catalog 所以需要添加這個-->
                <!--設置原因參考://blog.csdn.net/qq_40233736/article/details/83314596-->
                <property name="nullCatalogMeansCurrent" value="true" />
            </jdbcConnection>
    
            <!-- 生成model類,targetPackage指定model類的包名, targetProject指定生成的model放在eclipse的哪個工程下面-->
            <javaModelGenerator targetPackage="com.luis.model"
                                targetProject="D:\1a-Projects\springboot-projects\springboot-transactional\src\main\java">
                <property name="enableSubPackages" value="false" />
                <property name="trimStrings" value="false" />
            </javaModelGenerator>
    
            <!-- 生成MyBatis的Mapper.xml文件,targetPackage指定mapper.xml文件的包名, targetProject指定生成的mapper.xml放在eclipse的哪個工程下面 -->
            <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
                <property name="enableSubPackages" value="false" />
            </sqlMapGenerator>
    
            <!-- 生成MyBatis的Mapper介面類文件,targetPackage指定Mapper介面類的包名, targetProject指定生成的Mapper介面放在eclipse的哪個工程下面 -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.luis.dao" targetProject="src/main/java">
                <property name="enableSubPackages" value="false" />
            </javaClientGenerator>
    
            <!-- 資料庫表名及對應的Java模型類名 -->
            <table tableName="user" domainObjectName="User"
                   enableCountByExample="false"
                   enableUpdateByExample="false"
                   enableDeleteByExample="false"
                   enableSelectByExample="false"
                   selectByExampleQueryId="false"/>
    
            <table tableName="b_bid_info" domainObjectName="BidInfo"
                   enableCountByExample="false"
                   enableUpdateByExample="false"
                   enableDeleteByExample="false"
                   enableSelectByExample="false"
                   selectByExampleQueryId="false"/>
    
    
            <table tableName="b_income_record" domainObjectName="IncomeRecord"
                   enableCountByExample="false"
                   enableUpdateByExample="false"
                   enableDeleteByExample="false"
                   enableSelectByExample="false"
                   selectByExampleQueryId="false"/>
    
    
            <table tableName="b_recharge_record" domainObjectName="RechargeRecord"
                   enableCountByExample="false"
                   enableUpdateByExample="false"
                   enableDeleteByExample="false"
                   enableSelectByExample="false"
                   selectByExampleQueryId="false"/>
    
    
            <table tableName="u_user" domainObjectName="User"
                   enableCountByExample="false"
                   enableUpdateByExample="false"
                   enableDeleteByExample="false"
                   enableSelectByExample="false"
                   selectByExampleQueryId="false"/>
    
    
            <table tableName="u_finance_account" domainObjectName="FinanceAccount"
                   enableCountByExample="false"
                   enableUpdateByExample="false"
                   enableDeleteByExample="false"
                   enableSelectByExample="false"
                   selectByExampleQueryId="false"/>
        </context>
    
    </generatorConfiguration>
    
  5. 主配置文件配置

    #配置埠
    server.port=9090
    #context-path
    server.servlet.context-path=/mytrans
    
    #資料庫連接配置
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=luis
    
    #配置mapper文件路徑
    mybatis.mapper-locations=classpath:mapper/*.xml
    #開啟日誌
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
  6. 使用 mybatis 生成器生成的 model、dao、mapper 文件

    // model
    public class User {
        private Integer id;
        private String name;
        private Integer age;
        // get,set
    }
    
    // dao
    public interface UserMapper {
        int deleteByPrimaryKey(Integer id);
    
        int insert(User record);
    
        int insertSelective(User record);
    
        User selectByPrimaryKey(Integer id);
    
        int updateByPrimaryKeySelective(User record);
    
        int updateByPrimaryKey(User record);
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "//mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.luis.dao.UserMapper">
        <resultMap id="BaseResultMap" type="com.luis.model.User">
            <id column="id" jdbcType="INTEGER" property="id" />
            <result column="name" jdbcType="VARCHAR" property="name" />
            <result column="age" jdbcType="INTEGER" property="age" />
        </resultMap>
        <sql id="Base_Column_List">
            id, name, age
        </sql>
        <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
            select 
            <include refid="Base_Column_List" />
            from user
            where id = #{id,jdbcType=INTEGER}
        </select>
        <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
            delete from user
            where id = #{id,jdbcType=INTEGER}
        </delete>
        <insert id="insert" parameterType="com.luis.model.User">
            insert into user (id, name, age
            )
            values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}
            )
        </insert>
        <insert id="insertSelective" parameterType="com.luis.model.User">
            insert into user
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="id != null">
                    id,
                </if>
                <if test="name != null">
                    name,
                </if>
                <if test="age != null">
                    age,
                </if>
            </trim>
            <trim prefix="values (" suffix=")" suffixOverrides=",">
                <if test="id != null">
                    #{id,jdbcType=INTEGER},
                </if>
                <if test="name != null">
                    #{name,jdbcType=VARCHAR},
                </if>
                <if test="age != null">
                    #{age,jdbcType=INTEGER},
                </if>
            </trim>
        </insert>
        <update id="updateByPrimaryKeySelective" parameterType="com.luis.model.User">
            update user
            <set>
                <if test="name != null">
                    name = #{name,jdbcType=VARCHAR},
                </if>
                <if test="age != null">
                    age = #{age,jdbcType=INTEGER},
                </if>
            </set>
            where id = #{id,jdbcType=INTEGER}
        </update>
        <update id="updateByPrimaryKey" parameterType="com.luis.model.User">
            update user
            set name = #{name,jdbcType=VARCHAR},
            age = #{age,jdbcType=INTEGER}
            where id = #{id,jdbcType=INTEGER}
        </update>
    </mapper>
    
  7. 創建 service 介面及其實現類,在實現類的業務方法上添加 @Transactional註解,開啟事務支援

    public interface UserService {
        int addUser(User user);
    }
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Resource
        private UserMapper userMapper;
    
        /**
         * @Transactional 表示方法有事務支援,拋出運行時異常,則進行回滾
         * 使用MySQL默認的傳播行為、隔離級別、超時時間
         */
        @Transactional
        @Override
        public int addUser(User user) {
            System.out.println("業務方法addUser開始執行");
            int rows = userMapper.insert(user);
            System.out.println("業務方法addUser結束執行");
    
            // 模擬發生運行時異常
            int num = 10 / 0; // 測試時通過注釋以及放開觀察事務控制
            return rows;
        }
    }
    
  8. 創建控制器類

    @Controller
    public class UserController {
        
        @Resource
        private UserService userService;
    
        @RequestMapping("/addStudent")
        @ResponseBody
        public String addStudent(String name, Integer age) {
    
            User user = new User();
            user.setName(name);
            user.setAge(age);
            int rows = userService.addUser(user);
            return "添加結果:" + rows;
        }
    }
    
  9. 主啟動類上添加@EnableTransactionManagement註解,啟用事務管理器,與業務方法上的@Transactional配套使用;繼續在主啟動類上添加@MapperScan註解,指定dao所在的包,掃描dao包下dao介面和mapper文件。

    @SpringBootApplication
    @EnableTransactionManagement // 啟用事務管理器,與業務方法上的@Transactional一起使用
    @MapperScan(basePackages = "com.luis.dao") // 指定dao所在的包,掃描dao包下dao介面和mapper文件
    public class SpringbootTransactionalApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootTransactionalApplication.class, args);
        }
    }
    
  10. 運行主啟動類,瀏覽器輸入訪問地址;配合業務方法中設置的運行時異常(分別注釋和放開),觀察訪問結果以及資料庫表中數據變化進行測試

    //localhost:9090/mytrans/addUser?name=luis&age=5
    測試結果:發生運行時異常,進行了事務回滾,數據操作撤銷,但主鍵會自增;無運行時異常,數據添加成功
    結論:該業務方法支援了事務
    

介面架構風格——RESTful

介面的概念

  • 介面: API(Application Programming Interface,應用程式介面)是一些預先定義的介面(如函數、HTTP介面),或指軟體系統不同組成部分銜接的約定。 用來提供應用程式與開發人員基於某軟體或硬體得以訪問的一組常式,而又無需訪問源碼,或理解內部工作機制的細節。

此介面不是指 Java 中介面 Interface,而是一種應用程式介面,指可訪問 servlet,controller 的 url,調用其他程式的函數!

前後端分離

前後端分離的思想:前端從後端剝離,形成一個前端工程,前端只利用Json來和後端進行交互,後端不返回頁面,只返回Json數據。前後端之間完全通過public API約定。

前後端分離(解耦)的核心思想是:前端Html頁面通過Ajax調用後端的RestFul API並使用Json數據進行交互。

註: 【在互聯網架構中,web伺服器:一般指像nginx,apache這類的伺服器,他們一般只能解析靜態資源。應用伺服器:一般指像tomcat,jetty,resin這類的伺服器可以解析動態資源也可以解析靜態資源,但解析靜態資源的能力沒有web伺服器好。】

一般只有Web伺服器才能被外網訪問,應用伺服器只能內網訪問。

————————————————
版權聲明:本文為CSDN部落客「山河遠闊」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接://blog.csdn.net/weixin_37539378/article/details/79956760

前後端分離並非僅僅只是一種開發模式,而是一種架構模式(前後端分離架構)。
千萬不要以為只有在擼程式碼的時候把前端和後端分開就是前後端分離了,需要區分前後端項目!
前端項目與後端項目是兩個項目,放在兩個不同的伺服器,需要獨立部署,兩個不同的工程,兩個不同的程式碼庫,不同的開發人員。
前後端工程師需要約定交互介面,實現並行開發,開發結束後需要進行獨立部署,前端通過ajax來調用http請求調用後端的restful api。
前端只需要關注頁面的樣式與動態數據的解析&渲染,而後端專註於具體業務邏輯。
————————————————下面原文很有閱讀價值!
版權聲明:本文為CSDN部落客「山河遠闊」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接://blog.csdn.net/weixin_37539378/article/details/79956760

REST 介紹

  • REST (英文:Representational State Transfer ,簡稱 REST;中文: 表現層狀態轉移 )

  • 一種互聯網軟體架構設計的風格,但它並不是標準,它只是提出了一組客戶端和伺服器交互時的架構理念和設計原則,基於這種理念和原則設計的介面可以更簡潔,更有層次。

  • 任何的技術都可以實現這種理念,如果一個架構符合 REST 原則,就稱它為 RESTFul 架構

比如我們要訪問一個 http 介面://localhost:8080/boot/order?id=1021&status=1
採用 RESTFul 風格則 http 地址為://localhost:8080/boot/order/1021/1

表現層狀態轉移::

  • 表現層就是視圖層,顯示資源的,通過視圖頁面,jsp 等等顯示操作資源的結果。

  • 狀態: 資源變化

  • 轉移:資源是可以變化的。資源能創建,new 狀態,資源創建後可以查詢資源,能看到資源的內容,這個資源內容,可以被修改,修改後,資源和之前的不一樣。

REST中的要素:

  • 用 REST 表示資源和對資源的操作。在互聯網中,表示一個資源或者一個操作。

  • 資源是使用 url 表示的,在互聯網中,使用的圖片,影片,文本,網頁等等都是資源。

  • 資源用名詞表示

操作方式:

一句話說明 REST: 使用 url 表示資源 ,使用 http 動作操作資源。

RESTful 優點

  • 輕量,直接基於 http,不再需要任何別的諸如消息協議
    get/post/put/delete 為 CRUD 操作
  • 面向資源,一目了然,具有自解釋性。
  • 數據描述簡單,一般以 xml,json 做數據交換。
  • 無狀態,在調用一個介面(訪問、操作資源)的時候,可以不用考慮上下文,不用考慮當前狀態,
    極大的降低了複雜度。
  • 簡單、低耦合

REST 操作資源

方式:使用 http 中不同動作(請求方式)表示對資源的不同操作(CURD)

示例:

  • GET:查詢資源 — sql select

​ 處理單個資源:用其單數方式

//localhost:8080/myboot/student/1001

//localhost:8080/myboot/student/1001/1

​ 處理多個資源:使用複數形式

//localhost:8080/myboot/students/1001/1002

  • POST:創建資源 — sql insert

//localhost:8080/myboot/student

​ 在post請求中傳遞數據

<form action="//localhost:8080/myboot/student" method="post">
    姓名:<input type="text" name="name" />
    年齡:<input type="text" name="age" />
</form>
  • PUT: 更新資源 — sql update
<form action="//localhost:8080/myboot/student/1" method="post">
    姓名:<input type="text" name="name" />
    年齡:<input type="text" name="age" />
    <input type="hidden" name="_method" value="PUT" />
</form>
  • DELETE: 刪除資源 — sql delete
<a href="//localhost:8080/myboot/student/1">刪除1的數據</a>

需要的分頁,排序等參數,依然放在 url 的後面,例如

//localhost:8080/myboot/students?page=1&pageSize=20

SpringBoot 開發 RESTful

SpringBoot 開發 RESTful 主要是通過以下幾個註解實現:

  1. @PathVariable :從 url 中獲取簡單類型數據【最重要的一個註解】

  2. @GetMapping:支援的 get 請求方式,等同於@RequestMapping(method=RequestMethod.GET)

  3. @PostMapping :支援 post 請求方式 ,等同於@RequestMapping(method=RequestMethod.POST)

  4. @PutMapping:支援 put 請求方式,等同於@RequestMapping(method=RequestMethod.PUT)

  5. @DeleteMapping:支援 delete 請求方式,等同於@RequestMapping( method=RequestMethod.DELETE)

  6. @RestController:一個複合註解,是@Controller@ResponseBody的組合。

    在類的上面使用@RestController,表示當前類的所有方法都加入了@ResponseBody

使用示例:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理 pom 文件,刷新 pom

  2. 創建控制器類,使用相關註解

    /**
     * @RestController:複合註解,包含@Controller和@ResponseBody
     *      控制器類上加上此註解,則類中所有方法都是返回對象數據
     */
    @RestController
    public class MyRestController {
    
        /**
         * @GetMapping:接收get請求
         * @PathVariable(路徑變數):獲取url中數據
         *      使用位置:控制器方法的形參前
         *      註解的屬性value:路徑變數名
         * 注意:當@PathVariable的路徑變數名value與形參名相同時,屬性value值可以省略
         * 查詢資源:使用GET請求方式
         * //localhost:9999/myboot/student/1002
         */
        @GetMapping("/student/{stuId}") // 接收get請求
        public String queryStudent(@PathVariable("stuId") Integer studentId) {
            return "查詢的學生id:" + studentId;
        }
    }
    
  3. 主配置文件中配置埠和上下文路徑

    server.port=9999
    server.servlet.context-path=/myboot
    
  4. 運行主啟動類,瀏覽器輸入訪問地址進行測試

    //localhost:9999/myboot/student/1002
    

基於以上,進行其他請求測試:

控制器類中添加 post 請求處理方法:

/**
     * 創建資源:使用POST請求方式
     * //localhost:9999/myboot/student/luis/22
     * 注意:當@PathVariable的路徑變數名value與形參名相同時,屬性value值可以省略
     */
@PostMapping("/student/{name}/{age}") // 接收post請求
public String addStudent(@PathVariable String name,
                         @PathVariable Integer age) {
    return "創建student資訊:" + name + " " + age;
}

在 resources/static 下創建 addStudent.html 頁面發送 post 請求:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>post請求創建資源</title>
</head>
<body>
    <p>創建學生資訊</p>
    <form action="student/luis/22" method="post">
        <input type="submit" value="提交post創建學生資訊">
    </form>
</body>
</html>

運行主啟動類,先訪問頁面,在頁面上發送 post 請求:

//localhost:9999/myboot/addStudent.html

可以發現,開發中模擬各種 post 請求,都需要自己創建表單頁面,十分不便!

可以使用 Postman 工具,可模擬發送各種請求,使用王媽媽提供的漢化版本,免安裝,免登陸!

控制器類中分別創建更新資源和刪除資源的處理方法,啟動項目,在 Postman 上進行訪問測試:

/**
     * 修改資源:使用PUT請求方式,瀏覽器使用POST處理PUT請求
     * //localhost:9999/myboot/student/luis/24
     * 注意:當@PathVariable的路徑變數名value與形參名相同時,屬性value值可以省略
     */
@PutMapping("/student/{name}/{age}") // 修改資源使用@PutMapping
public String modifyStudent(@PathVariable String name,
                            @PathVariable Integer age) {
    return "修改資訊:" + name + " " + age;
}

/**
     * 刪除資源:使用DELETE請求方式,瀏覽器使用POST處理DELETE請求
     * //localhost:9999/myboot/student/3
     * 注意:當@PathVariable的路徑變數名value與形參名相同時,屬性value值可以省略
     */
@DeleteMapping("/student/{id}")
public String removeStudentById(@PathVariable Integer id) {
    return "刪除資訊:id = " + id;
}

在頁面或ajax中支援put和delete請求

瀏覽器是不支援 PUT 和 DELETE 請求的,只支援 GET 和 POST;即在表單中只支援 get 和 post!

在 SpringMVC 中有一個過濾器,支援 post 請求轉為 put 和 delete

過濾器:org.springframework.web.filter.HiddenHttpMethodFilter

作用:把請求中的 post 請求轉為 put 或 delete

使用說明:在表單中想發送 put 或 delete 請求,需配置請求方式為 post,然後在表單的隱藏域標籤中指定name=_methodvalue=put/delete;在 SpringBoot 的配置文件中啟用 SpringMVC 的過濾器後,會自動將 post 請求轉為指定的 put 或 delete。

實現步驟:

  1. application.properties(yml) 開啟使用 HiddenHttpMethodFilter 過濾器

    #啟用過濾器,支援put和delete請求
    spring.mvc.hiddenmethod.filter.enabled=true
    
  2. 在請求頁面中,包含 _method 參數,其值是 put 或 delete,發起這個請求需使用 post 方式

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>測試表單提交方式</title>
    </head>
    <body>
        <form action="student/test" method="post">
            <!--    在隱藏域標籤中指定將post轉化為何種請求方式    -->
            <input type="hidden" name="_method" value="put"/>
            <input type="submit" value="表單提交put請求"/>
        </form>
    
        <form action="student/test" method="post">
            <!--    在隱藏域標籤中指定將post轉化為何種請求方式    -->
            <input type="hidden" name="_method" value="delete"/>
            <input type="submit" value="表單提交delete請求"/>
        </form>
    </body>
    </html>
    

附控制器測試方法:

@RestController
public class MyRestController {

    @PutMapping("/student/test")
    public String testPut() {
        return "put請求成功";
    }

    @DeleteMapping("/student/test")
    public String testDelete() {
        return "delete請求成功";
    }
}

使用REST注意

使用 REST 注意,必須保證請求地址 URL 加上請求方式唯一,不然會出現衝突。

如下面兩個請求處理方法,在請求時就會出現衝突,無法確定用哪個方法處理請求!

@GetMapping("/student/{id}")
public String queryId(@PathVariable Integer id) {
    return "查詢的學生id:" + id;
}

@GetMapping("/student/{age}")
public String queryAge(@PathVariable Integer age) {
    return "查詢的學生age:" + age;
}

SpringBoot 集成 Redis

  • Redis:一個NoSQL資料庫,常用作快取使用(cache)

  • Redis 的五種數據類型:string、hash、set、zset、list

  • Redis 是一個中間件,是一個獨立的伺服器。

  • Java 中著名的客戶端:Jedis,lettuce,Redisson

SpringBoot 中有 RedisTemplate 和 StringRedisTemplate,都可以處理和 redis 的數據交互。

PS:其實使用的 RedisTemplate,其底層還是調用的 lettuce 客戶端方法來處理數據。

配置 Windows 版本的 redis

臨時性使用 redis ,可直接在 Windows 上使用解壓後的安裝包,啟動服務端和客戶端即可!(網盤有壓縮包,解壓即可使用)

Redis-x64-3.2.100.rar 解壓縮到一個非中文的目錄

redis-server.exe:服務端,啟動後,不要關閉

redis-cli.exe:客戶端,訪問 redis 中的數據

redisclient-win32.x86_64.2.0.jar:Redis 的一個圖形介面客戶端

執行方式:在這個文件所在的目錄,執行 java -jar redisclient-win32.x86_64.2.0.jar 即可

SpringBoot 集成 Redis

步驟:

  1. 創建 SpringBoot 項目,選擇 Spring Web 和 Redis 依賴,整理 pom 並刷新

  2. application.properties/yml 中配置連接 redis 的相關參數

    server.port=7777
    server.servlet.context-path=/myredis
    
    #配置redis:host ip password
    spring.redis.host=localhost
    spring.redis.port=6379
    #spring.redis.password=123
    
  3. 創建控制器類,創建請求處理方法,獲取操作 redis 數據的對象,處理數據交互

    @RestController
    public class RedisController {
    
        /**
         * 注入RedisTemplate(處理和redis的數據交互)
         * 其默認使用的jdk序列化,可讀性差(查看資料庫中數據有類似亂碼問題)
         * 三種泛型情況:
         *      RedisTemplate<String,String>
         *      RedisTemplate<Object,Object>
         *      RedisTemplate
         */
        @Resource
        private RedisTemplate redisTemplate;
    
        /**
         * 注入StringRedisTemplate(處理和redis的數據交互)
         * 其默認使用的String序列化,可讀性好(資料庫中數據正常顯示)
         */
        @Resource
        private StringRedisTemplate stringRedisTemplate;
    
        @PostMapping("/redis/addstring")
        public String addToRedis(String key, String value) { // 使用傳統風格
            // 添加數據到redis(使用RedisTemplate對象)
            ValueOperations valueOperations = redisTemplate.opsForValue(); // 獲取redis中操作string類型數據的對象
            valueOperations.set(key, value);
            return "成功添加數據到redis";
        }
    
        @GetMapping("/redis/getstring")
        public String getKFromRedis(String key) { // 使用傳統風格
            // 從redis獲取數據(使用RedisTemplate對象)
            ValueOperations valueOperations = redisTemplate.opsForValue(); // 獲取redis中操作string類型數據的對象
            Object o = valueOperations.get(key);
            return key + " = " + key + ", value = " + o;
        }
    
        @PostMapping("/redis/{k}/{v}")
        public String addStringKV(@PathVariable String k,
                                  @PathVariable String v) { // 使用RESTful風格
            // 添加數據到redis(使用StringRedisTemplate對象)
            ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
            stringStringValueOperations.set(k, v);
            return "成功添加數據到redis";
        }
    
        @GetMapping("/redis/{k}")
        public String getK(@PathVariable String k) { // 使用RESTful風格
            // 從redis獲取數據(使用StringRedisTemplate對象)
            ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
            String v = stringStringValueOperations.get(k);
            return k + " = " + k + ", v = " + v;
        }
    }
    
  4. 運行主啟動類,使用 Postman 進行請求測試

對比 RedisTemplate 和 StringRedisTemplate

  • StringRedisTemplate:使用的是 String 的序列化方式,把 k,v 都是作為 String 處理,可讀性好。

    但是有局限性,只能存 String 類型,且只能是 String 的序列化方式,無法修改為其他的序列化方式。

  • RedisTemplate:默認使用的是 JDK 的序列化方式,把 k,v 經過了 JDK 序列化存到 redis;

    經過了 JDK 序列化的 k,v 在資料庫中不能直接識別。

    其默認使用的 jdk 序列化,但可以修改為其他的序列化方式。

    其使用比 StringRedisTemplate 更加廣泛,可修改為其他的序列化方式,不僅僅只能存字元串。

序列化概述

序列化和反序列化:

  • 序列化:把對象轉化為可傳輸的位元組序列的過程稱為序列化。

  • 反序列化:把位元組序列還原為對象的過程稱為反序列化。

為什麼需要序列化?

  • 序列化最終目的是為了對象可以跨平台存儲以及進行網路傳輸。我們進行跨平台存儲和網路傳輸的方式就是 IO,而我們的 IO支援的數據格式就是位元組數組。我們必須在把對象轉成位元組數組的時候就制定一種規則(序列化),以便我們從 IO 流裡面讀出數據的時候以這種規則把對象還原回來(反序列化)。

什麼情況下需要序列化?

  • 凡是需要進行「跨平台存儲」和」網路傳輸」的數據,都需要進行序列化。

本質上存儲和網路傳輸都需要經過把一個對象狀態保存成一種跨平台識別的位元組格式,然後其他的平台才可以通過位元組資訊解析還原對象資訊。

序列化的方式:

序列化只是一種拆裝和組裝對象的規則,這種規則有多種多樣,比如現在常見的序列化方式有:

JDK(不支援跨語言)、JSON、XML、Hessian、Kryo(不支援跨語言)、Thrift、Protofbuff 等

例如:

java 的序列化:把 java 對象轉為 byte[] 二進位數據

json 的序列化:就是將對象轉換為 JSON 字元串格式

json 的反序列化:就是將JSON 字元串格式轉換為對象

Student( name=zs, age=20) —- { “name”:”zs”, “age”:20 }

設置 RedisTemplate 的序列化方式

@PostMapping("/redis/addData")
public String addData(String k, String v) {

    // 設置key,value均使用String序列化方式(在進行存取值之前設置)
    // 注意,key 和 value 的序列化方式可分別設置
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new StringRedisSerializer());

    // 獲取redis中操作string類型數據的對象
    ValueOperations valueOperations = redisTemplate.opsForValue();
    // 向redis中添加數據
    valueOperations.set(k, v);
    return "設置RedisTemplate序列化方式,並向redis中添加數據";
}

設置為 json 序列化方式

設置 json 序列化,存儲時把對象轉為 json 格式字元串進行存儲;獲取時將 json 字元串格式轉為對象。

如果是 String 序列化方式,則只能存儲字元串類型,無法存儲對象。

示例:

  1. 準備 vo 數據對象 Student(需要進行序列化

    public class Student implements Serializable {
        private static final long serialVersionUID = 684647176563341892L; // 添加後就不要再改動!
        private Integer id;
        private String name;
        private Integer age;
        // getter,setter,toString
    }    
    
  2. 寫處理請求的處理器方法

    @PostMapping("/redis/addjson")
    public String addJson() {
    
        // 準備對象數據
        Student student = new Student();
        student.setId(111);
        student.setName("luis");
        student.setAge(22);
    
        // 設置key的序列化方式為String(默認的是jdk方式)
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 設置value的序列化方式為json(默認的是jdk方式)
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
        // 獲取redis中操作string類型數據的對象
        ValueOperations valueOperations = redisTemplate.opsForValue();
        // 向redis中存數據
        // 會依據value設置的json序列化方式自動將Student對象轉換為json格式字元串
        valueOperations.set("mystudent", student);
        return "key-string序列化,value-json序列化";
    }
    
    @GetMapping("/redis/getjson")
    public String getJson() {
    
        // 設置key的序列化方式為String(默認的是jdk方式)
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 設置value的序列化方式為json(默認的是jdk方式)
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
        // 獲取redis中操作string類型數據的對象
        ValueOperations valueOperations = redisTemplate.opsForValue();
        // 從redis中獲取數據
        // 會依據value設置的json序列化方式自動將json格式字元串轉換為Student對象,反序列化回來
        Object o = valueOperations.get("mystudent");
        return "json反序列化:" + o;
    }
    
  3. 使用 Postman 發請求測試

小結

  • 臨時性使用 redis 或小測試,可直接在 Windows 上使用 redis 的安裝包,解壓即可使用
  • SpringBoot 中使用 Redis 主要是合理選擇 RedisTemplate 或 StringRedisTemplate 來處理和 Redis 的交互
  • 處理單純的字元串數據,可直接使用 RedisTemplate 對象處理數據交互,其採用 String 序列化方式。
  • 處理對象類型的數據,需要選擇 StringRedisTemplate 來處理和 Redis 的交互,其默認採用 JDK 序列化方式;需手動設置改變其序列化方式為 json;還可設置成其他的序列化方式。
  • 數據在網路上傳輸,或者跨平台傳輸都需要進行序列化!所以將需要傳輸的數據,如對象類型的,進行實現序列化介面的操作,並設定序列化版本號!序列化版本號設置後不要再改動!
  • 創建資源使用 POST 請求;獲取資源使用 GET 請求;修改資源使用 PUT 請求;刪除資源使用 DELETE 請求。
  • 熟悉 Postman 測試工具:熟練選擇請求方式、根據傳統風格以及 RESTful 風格的快速準確填充數據

SpringBoot 集成 Dubbo

開發中使用 SpringBoot 集成 Dubbo 進行分散式開發!

公共工程負責提供實體 bean 以及業務介面;

服務提供者工程負責實現公共工程中的業務介面,提供並暴露服務,供消費者調用;

服務消費者工程負責調用服務提供者提供的服務。

需要三個工程:介面工程【java】、服務提供者工程【可web】、服務消費者工程【web】
介面工程【java】:負責實體 bean 以及業務介面(提供者和消費者均依賴於它)
服務提供者工程【可web】:負責實現介面工程中的服務介面,提供服務,暴露服務,供消費者調用
服務消費者工程【web】:負責調用提供者提供的服務   

示例:

1.創建介面/公共工程

  1. 創建一個 maven 項目,無需選擇模板,名為 interface-api(公共的介面工程)

  2. 創建 vo 數據類 Student,進行序列化

    public class Student implements Serializable {
        private static final long serialVersionUID = -3498691273686241331L;
        private Integer id;
        private String name;
        private Integer age;
        // getter,setter,toString
    }    
    
  3. 定義服務介面(業務介面)

    public interface StudentService {   
        Student queryStudent(Integer id);
    }
    

2.創建服務提供者工程

  1. 創建 SpringBoot 項目,無需添加其他依賴,項目命名 springboot-service-provider(服務提供者工程)

  2. 在其 pom 文件中加入公共工程依賴、dubbo 起步依賴和 Zookeeper 依賴,整理 pom,刷新

    <!--公共項目(介面工程)的gav-->
    <dependency>
        <groupId>com.luis</groupId>
        <artifactId>interface-api</artifactId>
        <version>1.0.0</version>
    </dependency>
    <!--dubbo起步依賴-->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>2.7.8</version>
    </dependency>
    <!--zookeeper依賴-->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-dependencies-zookeeper</artifactId>
        <version>2.7.8</version>
        <type>pom</type>
        <exclusions>
            <!-- 排除log4j依賴,解決多次引入 -->
            <exclusion>
                <artifactId>slf4j-log4j12</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    

    注意:服務提供者中如果要使用到redis,並涉及json序列化操作,在不添加web起步依賴下,必須加下列jackson依賴!

    <!--單獨添加下面四個Jackson的依賴(或者直接將web起步依賴加上)-->
    <!-- //mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.2.2</version>
    </dependency>
    <!-- //mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jdk8 -->
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jdk8</artifactId>
        <version>2.13.2</version>
    </dependency>
    <!-- //mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>2.13.2</version>
    </dependency>
    <!-- //mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-parameter-names -->
    <dependency>
        <groupId>com.fasterxml.jackson.module</groupId>
        <artifactId>jackson-module-parameter-names</artifactId>
        <version>2.13.2</version>
    </dependency>
    
  3. 實現介面工程中的業務方法,使用 dubbo 中的註解暴露服務

    // @Component 可不用加此註解創建對象!加@DubboService後,該註解可自行創建該類對象。
    //@DubboService(interfaceClass = StudentService.class, version = "1.0", timeout = 5000)//暴露服務
    @DubboService(version = "1.0", timeout = 5000) // 暴露服務(interfaceClass屬性可不加)
    public class StudentServiceImpl implements StudentService {
        @Override
        public Student queryStudent(Integer id) {
    
            Student student = new Student();
            if (1001 == id) {
                student.setId(1001);
                student.setName("1001-luis");
                student.setAge(22);
            } else {
                student.setId(1002);
                student.setName("1002-jack");
                student.setAge(24);
            }
            return student;
        }
    }
    
  4. 配置 application.properties/yml 文件

    #配置服務名稱
    spring.application.name=studentservice-provider
    
    #配置掃描的包,掃描@DubboService
    dubbo.scan.base-packages=com.luis.service
    
    #配置dubbo協議(使用zookeeper註冊中心就不用配置)
    #dubbo.protocol.name=dubbo
    #dubbo.protocol.port=20881
    
    #配置zookeeper註冊中心
    dubbo.registry.address=zookeeper://localhost:2181
    
  5. 在主啟動類上添加註解啟用dubbo

    @SpringBootApplication
    @EnableDubbo // 啟用dubbo,該註解包含@EnableDubboConfig和@DubboComponentScan
    public class SpringbootServiceProviderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootServiceProviderApplication.class, args);
        }
    }
    

3.創建服務消費者工程

  1. 創建 SpringBoot 項目,添加 Spring Web 依賴,項目命令為 springboot-consumer(服務消費者工程)

  2. 在其 pom 文件中加入公共工程依賴、dubbo 起步依賴和 Zookeeper 依賴,整理 pom,刷新。

    其需要的依賴和服務消費者工程所需相同!

    <!--公共項目(介面工程)的gav-->
    <dependency>
        <groupId>com.luis</groupId>
        <artifactId>interface-api</artifactId>
        <version>1.0.0</version>
    </dependency>
    <!--dubbo起步依賴-->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>2.7.8</version>
    </dependency>
    <!--zookeeper依賴-->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-dependencies-zookeeper</artifactId>
        <version>2.7.8</version>
        <type>pom</type>
        <exclusions>
            <!-- 排除log4j依賴 -->
            <exclusion>
                <artifactId>slf4j-log4j12</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    
  3. 創建控制器類,寫請求處理方法,進行遠程介面調用

    @RestController
    public class DubboController {
    
        // 引用遠程服務,將創建好的代理對象,注入給studentService
        //@DubboReference(interfaceClass = StudentService.class, version = "1.0")
        @DubboReference(version = "1.0") // 如果不指定interfaceClass,則默認採用引用類型的class
        private StudentService studentService;
    
        @GetMapping("/query")
        public String queryStudent() {
            // 調用遠程介面,獲取對象
            Student student = studentService.queryStudent(1001);
            return "調用遠程介面獲取的對象:" + student;
        }
    }
    
  4. 在主啟動類上添加啟用dubbo的註解

    @SpringBootApplication
    @EnableDubbo // 啟用Dubbo
    public class SpringbootConsumerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootConsumerApplication.class, args);
        }
    }
    
  5. 配置 application.properties/yml

    #指定服務名稱
    spring.application.name=consumer-application
    #指定註冊中心
    dubbo.registry.address=zookeeper://localhost:2181
    

4.啟動 zookeeper 和服務

  1. 啟動 zookeeper 註冊中心(雙擊 zookeeper/bin 目錄下的 zkServer.cmd)

  2. 運行服務提供者模組的主啟動類

  3. 運行服務消費者模組的主啟動類

  4. 根據服務消費者提供的訪問介面進行服務訪問測試

    //localhost:8080/query
    

SpringBoot 項目打包

打包為 war 文件

示例:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理項目目錄

  2. 此處直接列舉整個 pom,依據注釋的內容進行添加,然後刷新 pom

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.6</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.luis</groupId>
        <artifactId>springboot-war</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <!--指定打包類型-->
        <packaging>war</packaging>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <!--處理jsp的依賴-->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
            </dependency>
            <!--servlet,jsp,jstl需要用再加-->
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <!--打包後的文件名稱-->
            <finalName>myboot</finalName>
            
            <!--resources插件-->
            <resources>
                <!--將jsp編譯到指定的目錄-->
                <resource>
                    <directory>src/main/webapp</directory>
                    <targetPath>META-INF/resources</targetPath>
                    <includes>
                        <include>**/*.*</include>
                    </includes>
                </resource>
                <!--將src/main/java下的文件包含到classes下-->
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.*</include>
                    </includes>
                </resource>
                <!--將src/main/resources下的文件包含到classes下-->
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.*</include>
                    </includes>
                </resource>
            </resources>
    
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  3. 在 main 目錄下,與 java 和 resources 目錄平級,創建名為 webapp 的目錄;在項目工程結構中指定該目錄為 web 資源目錄

  4. 在 webapp 目錄下創建 index.jsp 頁面,用來顯示 controller 中的數據

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>index.jsp</title>
    </head>
    <body>
        index.jsp,顯示controller中的數據:${data}
    </body>
    </html>
    
  5. 創建 controller 控制器類,處理請求

    @Controller
    public class JspController {
    
        @RequestMapping("/main")
        public String main(Model model) {
            model.addAttribute("data", "SpringBoot打包為war文件");
            return "index";
        }
    }
    
  6. 配置 application.properties/yml

    server.port=9001
    server.servlet.context-path=/myjsp
    
    #指定視圖解析器
    spring.mvc.view.prefix=/
    spring.mvc.view.suffix=.jsp
    
  7. 讓主啟動類繼承 SpringBootServletInitializer並重寫 configure 方法,修改返回內容。

    /**
     * 讓主啟動類繼承 SpringBootServletInitializer 並重寫重寫 configure 方法才可以使用外部 Tomcat!
     * SpringBootServletInitializer 就是原有 web.xml 的替代。
     */
    @SpringBootApplication
    public class SpringbootWarApplication extends SpringBootServletInitializer {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootWarApplication.class, args);
        }
    
        // 重寫此configure方法,修改返回內容!
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(SpringbootWarApplication.class);
        }
    }
    
  8. 運行主啟動類,瀏覽器輸入訪問地址,測試

    //localhost:9001/myjsp/main
    
  9. 測試正常後,使用 maven 的 lifecycle 中 package 打包命令進行打包(右側 maven 窗口中);【建議先clean再package】

    在 target 目錄下即可看到打包完成的 war 包。

  10. 將打包好的 war 包放到 Tomcat 的 webapps 目錄下,啟動 Tomcat;

    若是本地 Tomcat,則通過//localhost:8080/myboot/main可訪問該項目。

重點小結

  • 指定打包類型為 war
  • 指定打包後名稱
  • 添加資源插件
  • 主啟動類繼承 SpringBootServletInitializer並重寫 configure 方法,修改返回內容。
  • 使用 package 命令打包

打包為 jar 文件

示例:

  1. 創建 SpringBoot 項目,選擇 Spring Web 依賴,整理項目目錄

  2. 此處直接列舉整個 pom,依據注釋的內容進行添加,然後刷新 pom

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.6</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.luis</groupId>
        <artifactId>springboot-jar</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
    
            <!--處理jsp的依賴-->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
            </dependency>
            <!--servlet,jsp,jstl需要用再加-->
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <!--打包後的文件名稱-->
            <finalName>myboot</finalName>
    
            <!--resources插件-->
            <resources>
                <!--將jsp編譯到指定的目錄-->
                <resource>
                    <directory>src/main/webapp</directory>
                    <targetPath>META-INF/resources</targetPath>
                    <includes>
                        <include>**/*.*</include>
                    </includes>
                </resource>
                <!--將src/main/java下的mapper文件包含到classes下-->
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
                <!--將src/main/resources下的文件包含到classes下-->
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.*</include>
                    </includes>
                </resource>
            </resources>
    
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <!--  打包jar,有jsp文件時,必須指定maven-plugin插件版本為 1.4.2.RELEASE  -->
                    <version>1.4.2.RELEASE</version>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  3. 在 main 目錄下,與 java 和 resources 目錄平級,創建名為 webapp 的目錄;在項目工程結構中指定該目錄為 web 資源目錄

  4. 在 webapp 目錄下創建 main.jsp 頁面,用來顯示 controller 中的數據

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
      main.jsp,顯示數據:${data}
    </body>
    </html>
    
  5. 創建控制器類,處理請求

    @Controller
    public class HelloController {
    
        @RequestMapping("/hello")
        public ModelAndView hello() {
            ModelAndView mv = new ModelAndView();
            mv.addObject("data", "SpringBoot打包為jar文件");
            mv.setViewName("main");
            return mv;
        }
    }
    
  6. 配置 application.properties/yml

    server.port=9002
    server.servlet.context-path=/myboot
    
    #視圖解析器
    spring.mvc.view.prefix=/
    spring.mvc.view.suffix=.jsp
    
  7. 運行主啟動類,瀏覽器輸入地址訪問測試

    //localhost:9002/myboot/hello
    
  8. 測試正常後,使用 maven 的 lifecycle 中 package 打包命令進行打包(右側 maven 窗口中);【建議先clean再package】

    在 target 目錄下即可看到打包完成的 jar 包。

  9. 在 jar 包所在目錄,開啟 cmd 窗口,輸入 java -jar myboot.jar,回車,即可獨立運行項目;

    (可以將java -jar myboot.jar這段命令寫進一個文件,改後綴為 .bat,放在 jar 包同級下,雙擊即可運行項目)

    後台資訊列印可看到埠和項目訪問上下文路徑;

    此時使用的是內嵌的 tomcat,輸入//localhost:9002/myboot/hello即可訪問。

重點小結

  • 指定打包名稱
  • 添加資源插件
  • 指定 maven-plugin 插件版本
  • 使用 package 命令打包

Thymeleaf 模板引擎

Thymeleaf 介紹

  • Thymeleaf 是一個流行的模板引擎,該模板引擎採用 Java 語言開發
  • 模板引擎是一個技術名詞,是跨領域跨平台的概念,在 Java 語言體系下有模板引擎,在
    C#、PHP 語言體系下也有模板引擎,甚至在 JavaScript 中也會用到模板引擎技術,Java 生態下
    的模板引擎有 Thymeleaf 、Freemaker、Velocity、Beetl(國產) 等
  • Thymeleaf 對網路環境不存在嚴格的要求,既能用於 Web 環境下,也能用於非 Web 環境
    下。在非 Web 環境下,他能直接顯示模板上的靜態數據;在 Web 環境下,它能像 Jsp 一樣從
    後台接收數據並替換掉模板上的靜態數據。它是基於 HTML 的,以 HTML 標籤為載體,
    Thymeleaf 要寄托在 HTML 標籤下實現
  • Spring Boot 集成了 Thymeleaf 模板技術,並且 Spring Boot 官方也推薦使用 Thymeleaf 來
    替代 JSP 技術
    ,Thymeleaf 是另外的一種模板技術,它本身並不屬於 Spring Boot,Spring Boot
    只是很好地集成這種模板技術,作為前端頁面的數據展示,在過去的 Java Web 開發中,我們
    往往會選擇使用 Jsp 去完成頁面的動態渲染,但是 jsp 需要翻譯編譯運行,效率低。

Thymeleaf 的官方網站://www.thymeleaf.org
Thymeleaf 官方手冊://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

完善 Thymeleaf 語法提示

  • 添加了 Thymeleaf 依賴後,在創建的 html 標籤中使用其相關語法,若想使用 IDEA 的語法提示,還需進行以下設置:

    在默認創建的 html 的 <html lang="en">中補充xmlns:th="//www.thymeleaf.org",如下:

    <html lang="en" xmlns:th="//www.thymeleaf.org">
    

使用示例

  1. 創建 SpringBoot 項目,選擇 Spring Web 和 Thymeleaf 依賴,整理 pom,並刷新

  2. 寫控制器類,處理請求,轉發頁面到指定的 Thymeleaf 頁面,顯示數據

    @Controller
    public class MyController {
    
        @GetMapping("/hello")
        public String helloThymeleaf(HttpServletRequest request, Model model) {
    
            // 將數據放到request作用域
            request.setAttribute("data1", "歡迎使用Thymeleaf模板引擎");
            // 將數據放到request作用域
            model.addAttribute("data2", "我是後台的數據");
    
            // 指定轉發的邏輯視圖(使用Thymeleaf模板引擎,其通常使用的html)
            // 模板引擎默認配置了resources/tmeplates下的以.html結尾文件的視圖解析
            return "hello";
        }
    }
    
  3. 在 resources/templates 下,創建 hello.html 頁面顯示數據(可在 html 標籤中使用 Thymeleaf)

    <!DOCTYPE html>
    <html lang="en" xmlns:th="//www.thymeleaf.org">
    <!-- xmlns:th="//www.thymeleaf.org" 是手動加的,加上後idea使用Thymeleaf會有相關提示 -->
    <head>
        <meta charset="UTF-8">
        <title>hello.html</title>
    </head>
    <body>
        <h3>Thymeleaf使用示例</h3>
        <!--  後台有數據填充,則替換p標籤的value內容;沒有,則不替換  -->
        <p th:text="${data1}">想顯示數據</p>
        <p th:text="${data2}">靜態展示數據</p>
    </body>
    </html>
    
  4. 配置 application.properties/yml

    #關閉模板快取,開發階段配置,讓修改立即生效(項目上線後需修改為false,效率更高)
    spring.thymeleaf.cache=false
    # 去掉html5的語法驗證(thymeleaf對html的標籤約束非常嚴格,所有的標籤必須有開有閉,比如<br></br>或者<br/>是可以的,但是<br>會報錯,
    # 配置spring.thymeleaf.mode=LEGACYHTML5 目的就是為了解決這個問題,可以使頁面松校驗。)
    spring.thymeleaf.mode=LEGACYHTML5
    
    
    #以下是一些可能用到的配置,需要改變時配置即可,一般不需要配置,使用默認即可
    #-------------------------------------------------
    #編碼格式(默認是UTF-8)
    spring.thymeleaf.encoding=UTF-8
    #模板的類型(默認是HTML)
    spring.thymeleaf.mode=HTML
    #模板的前綴(默認是classpath:/templates/,其下html文件不用配置視圖解析器,直接可用邏輯名稱)
    spring.thymeleaf.prefix=classpath:/templates/
    #模板的後綴(默認是.html)
    spring.thymeleaf.suffix=.html
    #-------------------------------------------------
    
  5. 運行主啟動類,瀏覽器輸入地址訪問測試;

    然後不通過地址,直接在 idea 中點擊右上角瀏覽器標識,打開 hello.html,查看效果

    //localhost:8080/hello
    

    結果:後台沒有數據傳過來時,顯示的是頁面原有數據;後台有數據傳過來,則覆蓋原有數據進行顯示!

    使用 Thymeleaf 可實現前後端同時有效開發,互不影響;前端可用自己的示例數據,後端只需進行數據替換!

表達式

1.標準變數表達式

  • 語法:${key}

  • 作用:從 request 作用域中獲取 key 對應的文本數據;

    可使用request.setAttribute()model.addAttribute()向 request 中存數據

  • 使用:在頁面的 html 標籤中,使用th:text="${key}"表達式

  • 示例:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="//www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>expression1</title>
    </head>
    <body>
        <p3>標準變數表達式:${key}</p3>
        <div>
            <p>獲取相關數據:</p>
            <p th:text="${msg}">msg</p>
            <p th:text="${myuser.id}">id</p>
            <p th:text="${myuser.name}">name</p>
            <p th:text="${myuser.sex}">sex</p>
            <p th:text="${myuser.age}">age</p>
        </div>
    </body>
    </html>
    

2.選擇/星號變數表達式

  • 語法: *{key}

  • 作用:獲取這個 key 對應的數據;*{key}需要和th:object這個屬性一起使用,簡化對象屬性值的獲取。

  • 示例:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="//www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>expression2</title>
    </head>
    <body>
        <p3>選擇變數表達式:*{key}</p3>
        <p>獲取對象相關數據:</p>
        <div th:object="${myuser}">
            <p th:text="*{id}">id</p>
            <p th:text="*{name}">name</p>
            <p th:text="*{sex}">sex</p>
            <p th:text="*{age}">age</p>
        </div>
        <p>使用*{}完整表示對象的屬性值,此用法其實和${key}作用相同,了解即可</p>
        <p th:text="*{myuser.name}">name</p>
    </body>
    </html>
    

3.鏈接表達式

  • 語法:@{url}

  • 作用:表示鏈接,可使用在如下等情境中

    <script src="...">,<link href="...">,<a href="..">,<form action="...">,<img src="...">
    
  • 示例:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="//www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>鏈接表達式</title>
    </head>
    <body>
        <p3>鏈接絕對路徑</p3><br/>
        <a th:href="@{//www.baidu.com}">鏈接到百度</a><br/>
    
        <p3>鏈接的是相對路徑</p3><br/>
        <a th:href="@{/tpl/queryAccount}">相對地址,沒有參數</a><br/>
        <p3>鏈接的相對地址,使用字元串鏈接傳遞參數</p3><br/>
        <a th:href="@{'/tpl/queryAccount?id=' + ${userId}}">獲取model中的數據添加到鏈接地址中(不推薦此種拼接方式)</a><br/>
    
        <p3>推薦使用的傳參方式:</p3><br/>
        <a th:href="@{/tpl/queryAccount(id=${userId})}">傳遞一個參數(參數值可自己設置,也可從作用域中獲取)</a><br/>
        <a th:href="@{/tpl/queryUser(name='李四',age=20)}">傳遞多個參數(參數值可自己設置)</a><br/>
        <a th:href="@{/tpl/queryUser(name=${name},age=${age})}">傳遞多個參數(參數值可自己設置,也可從作用域中獲取)</a><br/>
    </body>
    </html>
    

Thymeleaf 屬性

  • 大部分屬性和 html 的一樣,只不過前面加了一個 th 前綴;加了 th 前綴,表示經過了模板引擎處理;經過了模板引擎處理的屬性,就可以使用其相關表達式,可獲取動態變數。

  • th:actionth:methodth:hrefth:srcth:textth:styleth:nameth:value

  • 基本屬性示例:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="//www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>模板屬性</title>
        <!--  引入jQuery  -->
        <script th:src="@{/js/jquery-3.4.1.js}" type="text/javascript"></script>
    
        <script type="text/javascript">
            $(function() {
                $("#btn").click(function(){
                    alert("按鈕單擊綁定======");
                })
            })
            function btnClick() {
                alert("按鈕單擊回調");
            }
        </script>
    
    </head>
    <body>
        <p3>使用模板屬性可獲取動態變數</p3>
        
        <form th:action="@{/login}" th:method="${methodAttr}">
            <input th:type="text" th:name="${paramname}" th:value="${paramvalue}" />
            <input type="button" value="按鈕" id="btn" th:onclick="btnClick()" />
        </form>
    
        <p th:style="${textcolor}">獲取動態變數,改變字體顏色</p>
        <p th:style="'color:skyblue'">此處經過模板處理,若仍手動設置,需要使用字元串</p>
        <p style="color: red">原版的改變字體顏色</p>
    
    </body>
    </html>
    

th:each

  • each 循環,可以循環 List 集合,Array 數組,Map 集合
  • 其實整體循環方式差不多,只不過數據存儲方式不一樣,存取方式有一些差異。

th:each循環List/Array

each 循環集合 List 和循環數組 Array 語法相同!

語法:在一個 html 標籤中,如下列方式使用 th:each(以下以 div 為例,實際多循環表格標籤)

<div th:each="集合循環成員,循環狀態變數:${key}">
    <p th:text="${集合循環成員}"></p>
</div>

集合循環成員,循環狀態變數:兩個名稱都是自定義的。 
「循環狀態變數」這個名稱可不定義,默認名稱是"集合循環成員Stat"

如果不定義「循環狀態變數」,則直接可這樣寫:(但要使用其時,直接使用"集合循環成員Stat"這個默認的名稱即可)
<div th:each="集合循環成員:${key}">
    <p th:text="${集合循環成員}"></p>
</div>

具體語法說明:

th:each="user,iterStat:${userlist}"中的${userList}是後台傳過來的集合
◼  user
定義變數,去接收遍歷${userList}集合中的一個數據
◼  iterStat
${userList} 循環體的資訊
◼  其中 user 及 iterStat 自己可以隨便取名
◼  interStat 是循環體的資訊,通過該變數可以獲取如下資訊
index: 當前迭代對象的 index(從0開始計算)
count: 當前迭代對象的個數(從1開始計算)這兩個用的較多
size: 被迭代對象的大小
current: 當前迭代變數
even/odd: 布爾值,當前循環是否是偶數/奇數(從0開始計算)
first: 布爾值,當前循環是否是第一個
last: 布爾值,當前循環是否是最後一個
注意:循環體資訊 interStat 也可以不定義,則默認採用迭代變數加上 Stat 後綴,即 userStat

示例:

<!DOCTYPE html>
<html lang="en" xmlns:th="//www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>eachList.html</title>
</head>
<body>
    <p3>each循環List</p3>
    <table border="1">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>sex</th>
                <th>age</th>
                <th>userStat</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="user,userStat:${myusers}">
                <td th:text="${user.id}"></td>
                <td th:text="${user.name}"></td>
                <td th:text="${user.sex}"></td>
                <td th:text="${user.age}"></td>
                <td th:text="${userStat}"></td>
            </tr>
        </tbody>
    </table>

    <br/>

    <table border="1">
        <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>sex</th>
            <th>age</th>
        </tr>
        </thead>
        <tbody>
        <!-- 可不寫循環體資訊userStat,默認會自動加上去 -->
        <tr th:each="user:${myusers}">
            <td th:text="${user.id}"></td>
            <td th:text="${user.name}"></td>
            <td th:text="${user.sex}"></td>
            <td th:text="${user.age}"></td>
        </tr>
        </tbody>
    </table>

</body>
</html>

th:each循環Map

語法:在一個 html 標籤中,如下列方式使用 th:each(以下以 div 為例,實際多循環表格標籤)

<div th:each="集合循環成員,循環的狀態變數:${key}">
    <p th:text="${集合循環成員.key}" ></p>
    <p th:text="${集合循環成員.value}"></p>
</div>

集合循環成員,循環的狀態變數:兩個名稱都是自定義的。 
「循環的狀態變數」這個名稱可以不定義,默認是"集合循環成員Stat"

key:map集合中的key
value:map集合key對應的value值

循環 List-Map 示例

示例循環一個稍複雜點的數據類型如:List<Map<String,Student>>

第一次循環,取出每一個 Map;

第二次循環,取出每一個 Map 中的 key 和 value。

<div th:each="lm:${listMap}">
    <div th:each="map:${lm}">
        <p th:text="${map.key}"></p>
        <p th:text="${map.value}"></p>        
    </div>
</div>

th:ifth:unless

th:if:判斷語句,當條件為 true,顯示 html 標籤體內容,反之不顯示;沒有 else 語句

示例:

<div th:if="10 > 0">條件為true才顯示此內容</div>

<p th:if="${sex=='m'}">性別是男才顯示</p>
<p th:if="${isLogin}">isLogin為true才顯示</p>
<p th:if="${age>18}">年齡大於18才顯示</p>
<!--""空字元是true-->
<p th:if="${str}">當str為空串表示true,會顯示</p>
<!--null是false-->
<p th:if="${str}">當str為null表示false,不會顯示</p>

th:unless:判斷語句,當條件為 false,顯示 html 標籤體內容,反之不顯示;沒有 else 語句

示例:

<div th:unless="10 < 0">條件為false才顯示此內容</div>
動態變數控制的示例此處省略,和if的使用相同,就是顯示條件相反!

th:swithth:case

th:switch 和 Java 中的 switch 是一樣的;

進行條件匹配,按上下順序匹配,一旦匹配成功則結束;否則使用默認的;無論如何,最終只有一個匹配上!

  • 語法:
<div th:switch="要比對的值">
    <p th:case="值1">
        結果1
    </p>
    <p th:case="值2">
        結果2
    </p>
    <p th:case="*">
        默認結果
    </p>
    以上的case只有一個語句執行
    
</div>

示例:

<div th:switch="${sex}">
    <p th:case="m">男</p>
    <p th:case="f">女</p>
    <p th:case="*">性別未知</p>
</div>

th:inline

  • 作用:不依賴於 html 標籤,直接通過內聯表達式[[${key}]]來獲取動態數據

  • th:inline有三個取值類型 (text, javascript 和 none),通常使用 text 和 javascript 這兩個

  • 語法:th:inline="text/javascript"

  • 內聯表達式:[[${key}]]

內聯 text

  • 作用:在 html 標籤外,獲取動態數據。

  • 語法:th:line="text",不指定則默認使用內聯 text!

  • 特點:可顯示的指定使用內聯 text,不指定則默認使用內聯 text!

  • 示例:

    <!-- 可顯示的指定使用內聯text -->
    <div th:inline="text">
        <p>性別:[[${sex}]],年齡[[${age}]]</p>
    </div>
    
    <!-- 沒有指定內聯類型,默認使用內聯text -->
    <div>
        <p>性別:[[${sex}]],年齡[[${age}]]</p>
    </div>
    

內聯 javascript

  • 作用:可以在 js 中,獲取模版中的數據

  • 語法:th:inline="javascript",必須指定!

  • 示例:

    <button onclick="fun()">單擊按鈕</button>
    
    <script type="text/javascript" th:inline="javascript">
        var name = [[${myuser.name}]];
        var id = [[${myuser.id}]];
        
        function fun() {
            alert("click 用戶是"+name+",他的 id 是"+id);
        }
    </script>
    

自面量

  • 指在模板文件的表達式中使用的一些數據,如文本、數字、boolean、null
 <div style="margin-left: 400px">
     <h3>文本字面量: 使用單引號括起來的字元串</h3>
     <p th:text="'我是'+${name}+',我所在的城市是'+${city}">數據顯示</p>

     <h3>數字字面量</h3>
     <p th:if="${20>5}"> 20大於 5</p>

     <h3>boolean字面量</h3>
     <p th:if="${isLogin == true}">用戶已經登錄系統</p>

     <h3>null字面量</h3>
     <p th:if="${myuser != null}">有myuser數據</p>
</div>

字元串連接

在模板文件的表達式中連接字元串有兩種語法:

  1. 將字元串使用單引號括起來,使用加號連接其他的字元串或者表達式:
<p th:text="'我是'+${name}+',我所在的城市是'+${city}">數據顯示</p>
  1. 使用雙豎線,將需要拼接的字元串和表達時候寫在雙豎線中即可:|字元串和表達式|
<p th:text="|我是${name},我所在城市是${city}|">
    顯示數據
</p>

運算符

  • 算術運算:+ , – , * , / , %
  • 關係比較:> , < , >= , <= ( gt , lt , ge , le )
  • 相等判斷:== , != ( eq , ne )

使用示例:

<div style="margin-left: 400px">
    <h3>使用運算符</h3>
    <p th:text="${age > 10}">年齡大於 10 </p>
    <p th:text="${20 + 30}">顯示運算結果</p>
    <p th:if="${myuser == null}">myuser是null</p>
    <p th:if="${myuser eq null}">myuser是null</p>
    <p th:if="${myuser ne null}">myuser不是null</p>

    <p th:text="${isLogin == true ? '用戶已經登錄' : '用戶需要登錄'}"></p>
    <p th:text="${isLogin == true ? (age > 10 ? '年齡大於10' : '年齡小於10') : '用戶需要登錄'}"></p>
</div>

三元運算符:
	表達式  ? true的結果 : false的結果

三元運算符可以嵌套

Thymeleaf 基本對象

常用的內置對象有以下幾個:

  1. #request:表示 HttpServletRequest 對象

  2. #session:表示 HttpSession 對象

  3. session:表示一個 Map 對象,是 #session的簡單表示方式,用來便捷獲取 session 中指定 key 的值。

    #session.getAttribute("loginname") 等同 session.loginname
    

使用示例:

 <div style="margin-left: 350px">
     <h3>內置對象 #request,#session,session 的使用</h3>
     <p>獲取作用域中的數據</p>
     <p th:text="${#request.getAttribute('requestData')}"></p>
     <p th:text="${#session.getAttribute('sessionData')}"></p>
     <p th:text="${session.loginname}"></p>

     <br/>
     <br/>
     <h3>使用內置對象的方法</h3>
     getRequestURL=<span th:text="${#request.getRequestURL()}"></span><br/>
     getRequestURI=<span th:text="${#request.getRequestURI()}"></span><br/>
     getQueryString=<span th:text="${#request.getQueryString()}"></span><br/>
     getContextPath=<span th:text="${#request.getContextPath()}"></span><br/>
     getServerName=<span th:text="${#request.getServerName()}"></span><br/>
     getServerPort=<span th:text="${#request.getServerPort()}"></span><br/>
</div>

Thymeleaf 內置工具類對象

內置工具類:Thymeleaf 自己的一些類,提供對日期、數字、字元串、list集合等的一些處理方法。

下面是一些常用的內置工具類對象:

  • #dates:處理日器的工具類

  • #numbers:處理數字的工具類

  • #strings:處理字元串的工具類

  • #lists:處理 list 集合的工具類

使用示例:

<div style="margin-left: 350px">
    <h3>日期類對象 #dates</h3>
    <p th:text="${#dates.format(mydate )}"></p>
    <p th:text="${#dates.format(mydate,'yyyy-MM-dd')}"></p>
    <p th:text="${#dates.format(mydate,'yyyy-MM-dd HH:mm:ss')}"></p>
    <p th:text="${#dates.year(mydate)}"></p>
    <p th:text="${#dates.month(mydate)}"></p>
    <p th:text="${#dates.monthName(mydate)}"></p>
    <p th:text="${#dates.createNow()}"></p>
    <br/>

    <h3>內置工具類 #numbers,操作數字的</h3>
    <p th:text="${#numbers.formatCurrency(mynum)}"></p>
    <p th:text="${#numbers.formatDecimal(mynum,5,2)}"></p>

    <br/>
    <h3>內置工具類 #strings, 操作字元串</h3>
    <p th:text="${#strings.toUpperCase(mystr)}"></p>
    <p th:text="${#strings.indexOf(mystr,'power')}"></p>
    <p th:text="${#strings.substring(mystr,2,5)}"></p>
    <p th:text="${#strings.substring(mystr,2)}"></p>
    <p th:text="${#strings.concat(mystr,'---java開發的黃埔軍校---')}"></p>
    <p th:text="${#strings.length(mystr)}"></p>
    <p th:text="${#strings.length('hello')}"></p>
    <p th:unless="${#strings.isEmpty(mystr)}"> mystring 不是 空字元串  </p>

    <br/>
    <h3>內置工具類 #lists, 操作list集合</h3>
    <p th:text="${#lists.size(mylist)}"></p>
    <p th:if="${#lists.contains(mylist,'a')}">有成員a</p>
    <p th:if="!${#lists.isEmpty(mylist)}"> list 集合有多個成員</p>

    <br/>
    <h3>處理null</h3>
    <!-- 對象前加問號,會自動查詢其是否為null,不是null則繼續獲取;是null則不獲取。 -->
    <p th:text="${zoo?.dog?.name}"></p>

</div>

自定義模板

  • 作用:內容復用,定義一次,在其他的模板文件中可多次使用。

1.自定義模板

  • 模板定義語法:th:fragment="模板自定義名稱"

  • 示例:(在 head.html 中創建下列模板)

    <div th:fragment="top">
        <p>百度地址</p>
        <p>www.baidu.com</p>
    </div>
    

2.使用自定義模板

有兩種使用的語法格式:

  1. ~{文件名稱 :: 自定義的模板名}
  2. 文件名稱 :: 自定義的模板名

PS:文件名稱指的自定義的模板所在的 html 文件名,不含後綴

對於模板,有兩種常用的使用方式:包含模板(th:include),插入模板(th:insert)

a.插入模板
  • 解釋:是在原有標籤中,將自定義的模板添加進來,不會失去原有標籤,但原有標籤下的內容將失去。

  • 語法

    • 格式1:th:insert="~{文件名稱 :: 自定義的模板名}"
    • 格式2:th:insert="文件名稱 :: 自定義的模板名"
  • 示例:(在 test.html 中,將 head.html 中名為 top 的模板插入到指定標籤中)

    使用格式1:

    <div th:insert="~{head :: top}">
        此div標籤下內容將會丟失,但div標籤仍然存在!
    </div>
    

    使用格式2:

    <div th:insert="head :: top">
        此div標籤下內容將會丟失,但div標籤仍然存在!
    </div>
    
b.包含模板
  • 解釋:用自定義的模板替換原有標籤,原有標籤將不存在

  • 語法

    • 格式1:th:include="~{文件名稱 :: 自定義的模板名}"
    • 格式2:th:include="文件名稱 :: 自定義的模板名"
  • 示例:(在 test.html 中,將 head.html 中名為 top 的模板包含(替換)到指定的標籤)

    使用格式1:

    <div th:include="~{head :: top}">
        整個div標籤將被完全替換為自定義的模板!
    </div>
    

    使用格式2:

    <div th:include="head :: top">
        整個div標籤將被完全替換為自定義的模板!
    </div>
    

3.整個 html 作為模板

如果要將整個 html 的內容作為模板插入(insert)或包含(include)到指定的標籤中,可使用下列幾種方式:

示例:將整個 head.html 作為模板,插入到指定標籤處

a.插入整個 html 模板
方式一:
<div th:insert="head :: html">
    將整個 head.html 作為模板,插入到此標籤處!
</div>

方式二:
<div th:insert="head">
    將整個 head.html 作為模板,插入到此標籤處!
</div>

示例:將整個 head.html 作為模板,包含(替換)到指定標籤處

b.包含整個 html 模板
方式一:
<div th:include="head :: html">
    將整個 head.html 作為模板,包含(替換)到此標籤處!
</div>

方式二:
<div th:include="head">
    將整個 head.html 作為模板,包含(替換)到此標籤處!
</div>

4.使用其他目錄中的模板

說明:如果要使用的自定義模板或要使用的整個 html 模板與當前頁面不在同一目錄下,則在指定使用模板的文件名時需要使用相對路徑即可!

例如:在 test.html 某標籤中要將同級 common 目錄下 left.html 作為整個模板來使用:

方式一:插入
<div th:insert="common/left :: html"></div>
方式二:插入
<div th:insert="common/left"></div>

方式一:包含
<div th:include="common/left :: html"></div>
方式二:包含
<div th:include="common/left"></div>

以上是使用其他目錄中的整個 html 作為模板示例;使用其他目錄中指定 html 頁面中的某個自定義模板用法和其相同。

註解總結

Spring + SpringMVC + SpringBoot

創建對象的:
@Controller:放在類的上面,創建控制器對象,注入到容器中
@RestController:放在類的上面,創建控制器對象,注入到容器中。
             作用:複合註解,含@Controller,@ResponseBody,使用這個註解類的,裡面的控制器方法的返回值都是數據。

@Service:放在業務層的實現類上面,創建service對象,注入到容器
@Repository:放在dao層的實現類上面,創建dao對象,放入到容器。沒有使用這個註解,是因為現在使用MyBatis框架,dao對象				是MyBatis通過代理生成的。不需要使用@Repository,所以沒有使用。
@Component:放在類的上面,創建此類的對象,放入到容器中。 

賦值的:
@Value:簡單類型的賦值,例如在屬性的上面使用@Value("李四") private String name
          還可以使用@Value獲取配置文件者的數據(properties或yml)。 
          @Value("${server.port}") private Integer port

@Autowired:引用類型賦值自動注入的,支援byName, byType. 默認是byType 。 放在屬性的上面,也可以放在構造         		    方法的上面。 推薦是放在構造方法的上面
@Qualifer:給引用類型賦值,使用byName方式。   
            @Autowird, @Qualifer都是Spring框架提供的。

@Resource:來自jdk中的註解,javax.annotation。 實現引用類型的自動注入, 支援byName, byType.
             默認是byName, 如果byName失敗,再使用byType注入;在屬性上面使用。

其他:
@Configuration:放在類的上面,表示這是個配置類,相當於xml配置文件

@Bean:放在方法的上面,把方法的返回值對象,注入到spring容器中。

@ImportResource:載入其他的xml配置文件,把文件中的對象注入到spring容器中。

@PropertySource:讀取其他的properties屬性配置文件

@ComponentScan:掃描器,指定包名,掃描註解的

@ResponseBody:放在方法的上面,表示方法的返回值是數據,不是視圖
@RequestBody:把請求體中的數據,讀取出來,轉為java對象使用。

@ControllerAdvice:控制器增強,放在類的上面,表示此類提供了方法,可以對controller增強功能。

@ExceptionHandler:處理異常的,放在方法的上面

@Transactional:處理事務的, 放在service實現類的public方法上面,表示此方法有事務

SpringBoot中使用的註解:
@SpringBootApplication:放在啟動類上面,包含了@SpringBootConfiguration
                          @EnableAutoConfiguration,@ComponentScan
    
MyBatis相關的註解:
@Mapper:放在類的上面,讓MyBatis找到介面,創建他的代理對象    
@MapperScan:放在主類的上面,指定掃描的包,把這個包中的所有介面都創建代理對象,將對象注入到容器中
@Param:放在dao介面的方法的形參前面,作為命名參數使用的。
    
Dubbo註解:
@DubboService:在提供者端使用的,暴露服務的,放在介面的實現類上面
@DubboReference:在消費者端使用的,引用遠程服務,放在屬性上面使用。
@EnableDubbo:放在主類上面,表示當前引用啟用Dubbo功能。