十分鐘掌握springboot微服務架構的基本使用

 

        前言: SSM的框架的搭建比起傳統的SSH已經優化了不少,但是配置時還是挺繁瑣複雜,特別是各種xml的配置,讓人讓人莫名所以,這也違背了我們程序開發的原則。你說如果你接手一個大型項目,光看配置都要花好久時間,那還咋玩。所以呢,微服務的出現還是很有道理的。因爲每一個大型項目都可以拆分成小的項目,每個小的項目只負責各自單一的功能,每個項目可以使用不同的技術,連接不同的數據庫,部署到不同的服務器,項目之間通過restful接口互相訪問即可。自從用上了微服務架構,感覺程序開發真的是越來越簡便了。厲害厲害。說了一分鐘廢話了,下面用九分鐘說一下springboot的基本使用。

解釋一下概念:springboot不等於微服務,它只是一套開源框架,跟ssm差不多,只是基於springboot來開發微服務相當方便,所以這兩個詞一般都是成對出現的。當我們的服務越來越多時,就可以通過springcloud來統一管理這些服務了,springcloud纔算是真正的微服務框架。可以認爲 springboot ≈ ssm,springcloud = 多個 springboot

項目源碼連接:springboot基本使用項目源碼

一,生成springboot項目並提供接口

1.1 訪問start.spring.io 輸入包名項目名,添加基本依賴web (選Build web, including RESTful, application...) 生成工程。

  • 注1:以上的配圖是比較早版本的樣子,新版有一些改變,但是二者內容還是不變的。
  • 注2:如果使用IDEA開發的,可以在File -> new -> project -> Spring Initializr -> next 輸入包名、項目名及依賴信息後直接在工程中生成springboot項目,很方便。

1.2 導入工程將下載後的項目解壓,使用idea/eclipse的導入 import -> exist maven project. IEDA下是自帶maven插件的,eclipse的話得自己配置一下。自己配置的方法也很簡單,此處不做介紹。

 

1.3 編寫接口。建包controller,寫TestRestController類

 

1.4啓動項目

運行主類:項目名+Application的main方法。

前臺輸入:localhost:8080/test即可顯示:call success!

 

        總結:沒錯,第一個springboot的demo就這麼完成了。是不是很6很簡單很暴力。分分鐘我們就開發了一個項目,提供了一個接口。別人也可以通過訪問這個接口獲取到我們提供的數據了。比起之前的springmvc項目開始前的還需要配置各種xml的是不是爽多了。

 

二,springboot訪問html頁面

2.1 springboot推薦前臺使用thymeleaf模板。pom.xml文件中引入thymeleaf依賴:

(ps通過: mvnrepository.comsearch.maven.org 可以找到所需依賴的dependency)

<!-- thymeleaf 模板相關 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2.2 在自帶的application.properties中對thymeleaf進行配置。

### server config
server.ip=127.0.0.1
server.port=8080
server.servlet.context-path: /sbDemo
​
### themeleaf
spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false

 

2.3 後臺與html交互

    2.3.1建controller類,在Controller寫前往html的接口,並提供數據

 

    2.3.2 寫前臺hello.html,接收並顯示數據。thymeleaf的用法跟jsp差不多。能完成所有jsp能完成的事。

 

    2.3.3訪問接口後前臺顯示:訪問路徑爲 127.0.0.1:8080/sbDemo/hello

    注:路徑前面對應你的配置文件application.properties中server的ip,port,context-path. ip默認是localhost,端口默認是8080,context-path默認爲/,這三個值都可以不配。我只是做個如何配置的示範。不要就按我圖中的ip來配了。都不配時按默認直接訪問 localhost:8080 + 接口名 就可以了

    補充:html中需要引用css與js文件時,如果css和js不是直接在static下,而是在static下的文件夾中。在後臺需要配置對靜態資源的引用,否則訪問不到資源文件。添加配置後,html中就可以用@{...}來引用static下的js/css/img等資源。當然,如果你只提供接口提供數據不涉及頁面的話就不需要這些了。

前臺引用如圖:

 

三,springboot訪問數據庫

3.1 pom.xml中引入相關數據庫如mysql驅動依賴和mybatis依賴:

<!-- mysql 數據庫相關 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- mybatis 依賴相關 -->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>1.1.1</version>
</dependency>

引入數據庫依賴後,在application.properties中配置數據庫連接

3.2 編寫Dao層從數據庫獲取數據,並測試

建庫後,插入表數據sql:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(40) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
​
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', '東邪', '41');
INSERT INTO `users` VALUES ('2', '吸毒', '42');

建立dao層獲取表數據,類中引用Mapper註解即可。(當然,也可以選擇用mapper.xml文件的形式,此處不介紹。)

測試類測試能不能獲取到數據,用自帶的src/test/java即可:

測試成功結果如下。這時候就可以寫controller、service、dao層的調用關係了。此時,前臺的數據就可以顯示爲從數據庫獲取的數據了。

 

        總結:至此,建立springboot工程,連接數據庫,編寫dao層,service層,controller層,提供接口,提供頁面的功能就完成了。都沒花什麼時間,作爲程序猿,除了引入一些必要jar包和簡單的配置一下之外,我們什麼都不用做了。只需要集中精力於我們的業務邏輯就行。整個過程沒花多少時間,感覺還是很不錯的。

 

---------------------------------------------------------------分割線---------------------------------------------------------------------------

分割線後面的內容是一些擴展的內容,有需要的話再看一些。不然只看前面三步就行了。

 

四,springboot自定義異常處理

4.1 springboot默認的異常處理是/error接口。可以通過實現ErrorController來重新定義對錯誤信息的處理。

 

提供兩種返回錯誤。一種是頁面返回、當你是頁面請求的時候就會返回到我們指定的頁面 error.html

另外一種是json請求的時候就會返回json錯誤

 

五,springboot自定義定時任務

同樣可以使用spring框架中的@Scheduled創建定時任務,在定時任務類中加入component註解讓spring找到,然後在需要定時執行的方法上加上@Scheduled就行。

但是springboot中需要在主類中加入@EnableScheduling註解,表示啓用定時任務的配置

 

六,springboot自定義切面(AOP)

與spring中切面的使用一致。都是先定義自己的註解,然後定義自己的切面。使用時在指定相應的PointCut即可。PointCut可以指定到任意地方。某個類中,某個方法,某個包下都可以。此文不做介紹。僅介紹springboot切面的基本使用,如下

6.1 可以定義自己的註解,使以該註解爲切入點

6.2 定義自己的切面

其中獲取註解中的信息的方法如下:

 

6.3 前臺發送請求,後臺接收後執行我們的切面。

 

結果就是在調用接口執行testAsp()方法前,被我們定義的MyAspect先攔截了一波,執行了相應的操作,成功地使用了我們的自定義切面。

常見的切面使用有 log日誌記錄、事務處理、訪問攔截等以實現某些功能。

 

七,springboot導出jar/war包

7.1 導出jar包

由於生成的pom.xml文件中默認指定生成的是jar,

<packing>jar<packing>

所以在pom.xml文件所在的目錄下,打開命令窗口,運行如下命令打包即可。(表示不需要測試類的打包)

mvn clean package -Dmaven.test.skip=true

如果不想打那麼長的後面那串,可以在pom中properties標籤下把skipTests直接設置爲true.即可只用 mvn clean package命令打包

如圖,打包成功Build SUCCESS後

  jar包會生成到target目錄中

移動jar包至任意位置運行,在jar所在位置使用java -jar [jar包名] 即可使用jar包啓動項目。效果與在IDE中運行一致。

 

7.2 導出war包

如果是想部署到外置的tomcat容器中,那就需要導出war包。

    7.21 此時在pom.xml中將打包類型改爲war.

<packaging>war</packaging>

此時可能pom文件會報web.xml文件缺失的錯誤。加上以下語句即可。

<failOnMissWebXml>false</failOnMissWebXml>

    7.22 打包類型改成war包後,此時需要移除springboot的內置tomcat依賴。並添加servlet所需的依賴。

 

    7.23 因爲打war包後就不會運行main方法了,入口改爲了SpringBootServletInitializer。所以我們需要在我們的主類Application.java同級的目錄下,新建一個類繼承SpringBootServletInitializer,並在類中指向我們需要運行的主類Application對象。這樣war包就有入口了。

    7.24 此時,就可以打war包了。還是運行打包的命令:

mvn clean package -Dmaven.test.skip=true

7.25 此時在target目錄中得到的就是war包了,將war包移到tomcat的webApp目錄下,然後在tomcat的bin目錄下運行startup.bat啓動tomcat,就能啓動項目了。前臺訪問地址= ip+端口+war包名+接口地址。

PS:自動生成的war包或者jar包名都會帶有一串數字和英文,可以通過finalName來控制生成的名字。

八,springboot上傳/下載文件

springboot的文件上傳就是spring的文件上傳。

前臺:

<form enctype="multipart/form-data" method="post" action="/sbDemo/uploadImg">
    圖片<input type="file" name="file"/> <input type="submit" value="上傳"/>
</form>

後臺Controller中:

/**
      * 處理文件上傳
    */
    @RequestMapping(value="/uploadImg", method = RequestMethod.POST)
    @ResponseBody
    public String uploadImg(@RequestParam("file") MultipartFile file,
            HttpServletRequest request) {
        String msg = "SUCCESS";
        String fileName = file.getOriginalFilename();
        //該路徑可以從文件中讀取,此處演示略
        String filePath = "C:\\Users\\Administrator\\Desktop\\test\\upload\\img\\";
        System.err.println("上傳路徑:"+filePath);
        try {
            uploadFile(file.getBytes(), filePath, fileName);
        } catch (Exception e) {
            msg = e.getMessage();
        }
        //返回json
        return msg;
    }
​
    /**
     * @Description : 上傳文件
     * @param file : 文件實體
     * @param filePath : 文件目標路徑
     * @param fileName : 文件名
     * @throws Exception
     */
    public void uploadFile(byte[] file, String filePath, String fileName) throws Exception { 
        File targetFile = new File(filePath);  
        if(!targetFile.exists()){    
            targetFile.mkdirs();    
        }       
        FileOutputStream out = new FileOutputStream(filePath+fileName);
        out.write(file);
        out.flush();
        out.close();
    }

     #PS:另外可以在配置文件中設置上傳文件的相關參數

uploadPath=..\\sbDemo-Resource\\upload\\
spring.http.multipart.maxFileSize=100Mb
spring.http.multipart.maxRequestSize=100Mb

文件下載:

前臺:

<a οnclick="window.location.href='/background/url'">下載</a>

後臺:

@RequestMapping(value = "/downUserExcelTemplate.admin")
public void downUserExcelTemplate(HttpServletResponse response){
    try{
        fileDownload(response, "E:\\test.xls", "test.xls");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}


public void fileDownload(final HttpServletResponse response, String filePath, String fileName) throws Exception{

    byte[] data = FileUtil.toByteArray2(filePath);
    fileName = URLEncoder.encode(fileName, "UTF-8");
    response.reset();
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
    response.addHeader("Content-Length", "" + data.length);
    response.setContentType("application/octet-stream;charset=UTF-8");
    OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
    outputStream.write(data);
    outputStream.flush();
    outputStream.close();
    response.flushBuffer();
}

 

 

九、前臺相關功能介紹

 

十、springboot事務

簡單一點的用法就是,直接在controller對應的方法中加入@Transactional註解即可,加入後如果程序出現異常,則已執行的sql將會自動回滾。一般是方法中有多條關聯sql時用。

需要注意的是,如果mysql 事務未提交導致死鎖 Lock wait timeout exceeded; try restarting transaction 解決辦法是:

1.先用這條命令查詢數據庫阻塞的進程

SELECT * FROM information_schema.innodb_trx 

看 trx_tables_in_use, trx_tables_locked, trx_rows_locked 這三個值是否爲0,不爲0的則證明有事務未提交。

此時找到該行對應 trx_mysql_thread_id, 記錄。

2.然後使用kill命令關閉該事務即可

 kill   id  ; (殺死第一步找到的trx_mysql_thread_id的進程)

 

十一、springboot日誌

springboot默認使用Logback來記錄日誌,且默認是隻打印到控制檯中,不會輸出到文件。需要輸出到文件中,只需要在配置文件application.properties中加上一句:loggin.file = xxx 即可。可以使用相對路徑或者絕對路徑。

logging.file=../logs/sbDemo-test.log

默認是輸出INFO以上的內容,可以設置等級如:

  • logging.level.com.jvxb=DEBUG          (執行的sql是以debug的形式輸出的,想在控制檯顯示sql,需要設置成debug)

  • ogging.level.root=WARN:root日誌以WARN級別輸出

有時候我們希望配置複雜一點的日誌記錄,如每天保存一次,每個日誌文件多大,不同level的文件寫入不同的日誌記錄這種,這時候你就要加載自己的日誌配置文件了。 方法是在配置文件application.properties中加上一句:loggin.config=classpath:xxx.xml如下:然後把你自己日誌配置的xml放到resource文件夾下即可。

logging.config=classpath:mylogbackConfig.xml

注:springboot默認去加載classpath下的logback-spring.xml,如果直接把logback-spring.xml文件放到classpath下,則不需要在application.properties中使用logging.config

PS:日誌配置文件 mylogbackConfig.xml實例:

<configuration debug="false" scan="true" scanPeriod="10 seconds">
    <!-- 日誌配置:
    	包名:com.jvxb,將該包下的所有日誌按debug、info、warn、error等級分別保存到[項目同一級]文件夾sbDemo-Log下的logback_debug、logback_info、logback_warn、logback_error文件中。每日凌晨0點將日誌文件歸檔保存爲zip壓縮包,每個壓縮包最大30M,若壓縮包文件大小超過30M,將會被保存爲多個壓縮包。最多保留最近30天的歸檔日誌文件。
	-->

    <contextName>logback</contextName>
    
    <!--輸出sql語句-->
    <logger name="com.jvxb" level="debug"/>
    <property name="path" value="../sbDemoLog"></property>
    <property name="maxHistory" value="30"/>
    <property name="maxFileSize" value="30MB"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="debug_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_debug.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="info_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_info.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="warn_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_warn.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_error.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <root>
        <level value="info"/>
        <appender-ref ref="console"/>
        <appender-ref ref="debug_file"/>
        <appender-ref ref="info_file"/>
        <appender-ref ref="warn_file"/>
        <appender-ref ref="error_file"/>
    </root>

</configuration>

 

十二、springboot過濾器

springboot使用過濾器有兩種方式,

一種是使用 @WebFilter 註解如

@WebFilter(filterName = "LoginFilter", urlPatterns = "/*")
public class LoginFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		
	}
}

然後在Application主類添加上 @ServletComponentScan 註解。加上@ServletComponentScan註解後,Servlet、Filter、Listener可以直接通過@WebServlet、@WebFilter、@WebListener註解自動註冊,無需其他代碼。這個另外說一個註解 @ComponentScan,我們知道,Spring是一個依賴注入框架。所有的內容都是關於bean的定義及其依賴關係。定義Spring Beans的第一步是使用正確的註解:@Component或@Service或@Repository。但是,Spring不知道你定義了某個bean除非它知道從哪裏可以找到這個bean。ComponentScan做的事情就是告訴Spring從哪裏找到bean。使用@ComponentScan自動掃描組件實例包掃描會掃描只要標註了@Controller,@Service,@Repository,@Component 這四個註解都會被掃描到容器中。

另一中是使用配置類如:(注意此時LoginFilter不需要用@WebFilter註解,主類也不需要加上@ServletComponentScan註解),

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean filterRegist() {
        FilterRegistrationBean frBean = new FilterRegistrationBean();
        frBean.setFilter(new LoginFilter());
        frBean.addUrlPatterns("/*");
        frBean.setOrder(1);    //order值越小越先加載
        return frBean;
    }
}

 多個過濾器時推薦使用第二種,便於修改和管理。

如訪問網站時,未登錄者需要先進行登錄操作的登錄過濾器實例:

package com.jvxb.demo.sbDemo.livable.configuration.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.filter.OncePerRequestFilter;

@WebFilter(filterName = "LoginFilter", urlPatterns = "/*")
public class LoginFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// 獲取請求的uri
		String uri = request.getRequestURI();
		// 獲取容器路徑
		String ctxPath = request.getContextPath();

		// 資源文件不過濾: 以.js|css|png|jpg|jpeg|gif|ico|woff結尾的
		if (uri.matches(".+\\.(html|js|css|png|jpg|jpeg|gif|ico|ttf|woff|woff2)$")) {
			filterChain.doFilter(request, response);
			return;
		}

		// 登錄請求、驗證登錄的請求不過濾
		if (uri.startsWith(ctxPath + "/admin/login") || uri.startsWith("/admin/checkLogin")) {
			filterChain.doFilter(request, response);
			return;
		}

		// 否則執行過濾:未登錄者須先前往登錄界面登錄
		Object obj = request.getSession().getAttribute("user");
		if (null == obj) {
			response.sendRedirect(ctxPath + "/admin/login");
		} else {
			filterChain.doFilter(request, response);
		}

	}

}

 

十三、springboot攔截器

springboot的攔截器也是對請求進行的系列驗證或處理,關於攔截器和過濾器的區別此文不做介紹,不清楚的可以參考這裏,下面說一下springboot攔截器的實現。如下:

12.1 寫一個類實現 HandlerInterceptor 接口,然後重寫 裏面的 preHandle()、postHandle()、afterCompletion()方法

public class MyInterceptor implements HandlerInterceptor {
    // JDK8後,三個方法已經有了默認實現,所以不一定要重寫。但我們寫攔截器就是爲了重寫啊,啊哈哈
    // preHandle()、postHandle()、afterCompletion()
}

preHandle

  • 調用時間:Controller方法處理之前
  • 執行順序:鏈式Intercepter情況下,Intercepter按照聲明的順序一個接一個執行
  • 若返回false,則中斷執行,注意:不會進入afterCompletion

postHandle

  • 調用前提:preHandle返回true
  • 調用時間:Controller方法處理完之後,DispatcherServlet進行視圖的渲染之前,也就是說在這個方法中你可以對ModelAndView進行操作
  • 執行順序:鏈式Intercepter情況下,Intercepter按照聲明的順序倒着執行
  • 備註:postHandle雖然post打頭,但post、get方法都能處理

afterCompletion

  • 調用前提:preHandle返回true
  • 調用時間:DispatcherServlet進行視圖的渲染之後
  • 多用於清理資源

12.2 在WebMvcConfigurer的實現類中,重寫addInterceptors()方法將該攔截器註冊即可。

@EnableWebMvc
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
		 //攔截器
		 registry.addInterceptor(new MyInterceptor()).addPathPatterns("/admin/api/**");
	 }

}

下面舉一個攔截器的例子:在系統中會提供對外的數據接口,該攔截器的作用就是是攔截某些訪問數據的請求,使訪問請求不能過快(惡意請求攻擊),或者對請求加一定的驗證(如提供某些參數來驗證該請求是否有效)等。

代碼有點多 - -。就不貼了。 也就是,當 /admin/api/** 的接口被訪問時,就記錄一下訪問者的ip,然後判斷該訪問請求是否有效(1min鍾內超過N次,或者沒有攜帶某些參數,或者參數校驗不合格之類的就攔截住這樣)

注意:攔截器是在過濾器之後才執行的,所以一般攔截器中的攔截地址,過濾器是不用處理的。否則就進不到攔截器中。還有就是可以註冊多個攔截器,會按照註冊順序依次攔截。

 

十四、springboot監聽器

監聽器是一個專門用於對其他對象身上發生的事件或狀態改變進行監聽和相應處理的對象,當被監視的對象發生情況時,立即採取相應的行動。通俗的講,監聽器就比如你盯着一盤好吃的,有人拿你的吃的的時候,你會立馬採取相應的行動。Java對Servlet中的ServletContext(上下文),HttpSession,ServletRequest這三種對象提供了一些監聽的接口,我們可以自定義監聽器來實現這些接口,對這三種對象的一些事件進行監聽。

此處我們以ServletRequestListener來舉例,先說明一下,ServletRequestListener是一個接口,這個接口是用來監聽請求的,裏面有兩個方法,分別是請求創建和請求銷燬。

package com.jvxb.demo.sbDemo.livable.configuration.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;

@WebListener
public class MyRequestListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("請求創建了:");
        //將請求開始時間添加到該請求,用於計算本次請求的用時
        String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        sre.getServletRequest().setAttribute("startTime", startTime);
        try {
            //項目經理說,系統效率需要先慢一點,客戶提出優化時再進行優化
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("請求結束了");
        String uri = ((HttpServletRequest) sre.getServletRequest()).getRequestURL().toString();
        System.out.println("請求路徑:" + uri);
        String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        System.out.println("請求結束時間:" + endTime);
        String startTime = sre.getServletRequest().getAttribute("startTime").toString();
        System.out.println("請求耗時:" + startTime + " - " + endTime);
    }

}

(使用@WebListener註解時APP主類需添加@ServletComponentScan註解) 

此時我們前臺訪問:localhost:8080,查看控制檯。可以看到監聽器與過濾器的信息如下

監聽器與過濾器

其它監聽器的使用可以參考另外一篇:《Servlet中幾個監聽器Listener的使用實例

 

十五、springboot多數據源

1.配置多個數據源(以兩個爲例)

注意 url 不同,多個數據源時改爲 jdbc-url

2.建立多個數據源的配置文件

主數據源配置:

package com.jvxb.test.dbtest.livable.module.test.conf;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

//表示這個類爲一個配置類
@Configuration
// 配置mybatis的接口類放的地方
@MapperScan(basePackages = "com.jvxb.test.dbtest.livable.module.*.mapper", sqlSessionFactoryRef = "testSqlSessionFactory")
public class TestDbConf {
    // 將這個對象放入Spring容器中
    @Bean(name = "testDataSource")
    // 表示這個數據源是默認數據源
    @Primary
    // 讀取application.properties中的配置參數映射成爲一個對象
    // prefix表示參數的前綴
    @ConfigurationProperties(prefix = "spring.datasource.test")
    public DataSource getTestDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "testSqlSessionFactory")
    // 表示這個數據源是默認數據源
    @Primary
    // @Qualifier表示查找Spring容器中名字爲test1DataSource的對象
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                // 設置mybatis的xml所在位置
                new PathMatchingResourcePatternResolver().getResources("com.jvxb.test.dbtest.livable.module.*.mapper"));
        return bean.getObject();
    }
    @Bean("testSqlSessionTemplate")
    // 表示這個數據源是默認數據源
    @Primary
    public SqlSessionTemplate testSqlsessiontemplate(
            @Qualifier("testSqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }
}

其它數據源配置:

package com.jvxb.test.dbtest.livable.module.test.conf;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

//表示這個類爲一個配置類
@Configuration
// 配置mybatis的接口類放的地方
@MapperScan(basePackages = "com.jvxb.test.dbtest.livable.devmodule.*.devmapper", sqlSessionFactoryRef = "devSqlSessionFactory")
public class DevDbConf {

    @Bean(name = "devDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.dev")
    public DataSource getDevDateSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "devSqlSessionFactory")
    public SqlSessionFactory devSqlSessionFactory(@Qualifier("devDataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("com.jvxb.test.dbtest.livable.devmodule.*.devmapper"));
        return bean.getObject();
    }

    @Bean("devSqlSessionTemplate")
    public SqlSessionTemplate devsqlsessiontemplate(
            @Qualifier("devSqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }

}

注意mapper的掃描位置,和設置PathMatchingResourcePatternResolver的位置,改爲自己項目中的。不同數據庫的mapper需要放在不同目錄下。

3.使用多個數據源

並查看結果:

 

十六、springboot其它

resource下放入: banner.txt 文件, 內容如下,會有好運發生喔

${AnsiColor.BRIGHT_YELLOW}
////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            佛祖保佑       永不宕機     永無BUG                 //
////////////////////////////////////////////////////////////////////
${AnsiColor.BRIGHT_BLUE}
::: Project (version:${application.version}) :::             \(^O^)/    Spring-Boot ${spring-boot.version}
${AnsiColor.DEFAULT}

#插入一波: 基於Springboot的簡易後臺框架

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章