自定义一个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中加上如下配置:
在这里插入图片描述
再次测试,发现拦截器又生效了。

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