自定義一個SpringBoot Starter項目

前言

我們在用SpringBoot的時候,只需要依賴一個starter項目,無需配置,就能使用這個starter項目的Bean。使用過早期Spring的人都知道,我們要想使用一個Spring bean,必須在xml配置文件裏定義這個Bean。後來有了註解,只需在類上加註解,然後配置Spring掃描包的範圍,就能夠創建這些Bean。那麼SpringBoot是如何做到自動創建starter項目裏的Bean的呢?下面來一探究竟。

第一個starter項目

新建一個普通的maven項目,注意,不是springboot項目。在這個項目裏我們自定義一個springmvc的攔截器,模擬web接口鑑權。pom.xml配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.starter.demo</groupId>
  <artifactId>auth-interceptor-starter</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>auth-interceptor</name>
  <properties>
		<java.version>1.8</java.version>
  </properties>
  <dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-autoconfigure</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-web</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	</dependency>
	<dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    	<scope>provided</scope>
	</dependency>
  </dependencies> 
  <dependencyManagement>
  	<dependencies>
  		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>2.2.7.RELEASE</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency> 
		
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-web</artifactId>
		    <version>5.2.6.RELEASE</version>
		</dependency>
		
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-webmvc</artifactId>
		    <version>5.2.6.RELEASE</version>
		</dependency>
		
		<dependency>
		    <groupId>javax.servlet</groupId>
		    <artifactId>javax.servlet-api</artifactId>
		    <version>4.0.1</version>
		    <scope>provided</scope>
		</dependency>
  	</dependencies>
  </dependencyManagement>
</project>

然後創建一個攔截器類,代碼如下:

package com.auth.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class AuthInterceptor extends HandlerInterceptorAdapter {
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("----------攔截器鑑權-------------");
		return true;
	}
	
}

編寫攔截器配置類:

package com.auth.interceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@ComponentScan({"com.auth.interceptor"})
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor())
                .addPathPatterns("/**");
    }

    @Bean
    public AuthInterceptor authInterceptor() {
        return new AuthInterceptor();
    }
}

這個配置類除了配置攔截器之外,還有一個重要作用,就是定義spring掃描包的範圍,所以加上了這個@ComponentScan註解。到此,starter項目就編寫完了,其實還差一步,先賣個關子,後面再說。最後,給大家看下包結構:
在這裏插入圖片描述

創建一個用於測試的SpringBoot項目

就是一個常規的spring-boot-starter-web項目,pom.xml添加依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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.2.7.RELEASE</version>
		<relativePath/>
	</parent>
	<groupId>com.example</groupId>
	<artifactId>starter-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>starter-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<dependency>
			<groupId>com.starter.demo</groupId>
			<artifactId>auth-interceptor-starter</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

這裏依賴了我們剛纔編寫的starter項目。然後編寫一個controller用於測試,代碼如下:

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
	
	@GetMapping("/1")
	public String test() {
		return "OK";
	}

}

最後web項目的包結構如下:
在這裏插入圖片描述
啓動web項目,請求接口,發現starter項目裏的攔截器根本沒起作用。這很正常,因爲啓動類在com.example.demo目錄下面,所以springboot只會掃描com.example.demo及其子包,而攔截器在com.auth.interceptor這個包下面,所以根本就不會掃描它啊。我們在開發的時候,自己定義的包名跟第三方starter也是不一樣的,那怎麼讓springboot掃描到starter項目裏的bean啊?

掃描starter項目的入口

在starter項目的類加載路徑下面添加META-INF/spring.factories配置文件。對應maven項目的這個路徑:
在這裏插入圖片描述
spring.factories配置文件的內容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.auth.interceptor.InterceptorConfig

springboot在啓動時會掃描並解析所有jar包中的META-INF/spring.factories.這裏我們配置InterceptorConfig作爲入口,而InterceptorConfig中加了@ComponentScan註解,這樣,springboot就知道該掃描starter項目的哪些包了。
重新編譯,再次運行,發現控制檯打印瞭如下內容:
----------攔截器鑑權-------------
說明攔截器生效了!

Conditional控制bean的創建

spring提供了一個更通用的基於條件去創建bean的機制——使用@Conditional註解。@Conditional
根據滿足的某一個特定條件創建一個特定 bean.比方說,當某一 JAR 包在一個類路徑下
的時候,會自動配置一個或多個 bean ;或者只有當某個 bean 被創建後纔會創建另外的bean.
總的來說,就是根據特定條件來控制 bean 的創建行爲,這樣我們可以利用這個特性進行一些
自動的配置。當然, @Conditional 註解有非常多的使用方式,這裏我們僅僅示範@ConditionalOnProperty.在InterceptorConfig類上加上@ConditionalOnProperty註解:
在這裏插入圖片描述
該註解的意思是當配置屬性auth.enable=true時纔會創建InterceptorConfig bean.重新編譯,測試發現攔截器沒起作用。然後,我們在application.yml中加上如下配置:
在這裏插入圖片描述
再次測試,發現攔截器又生效了。

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