300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Springboot-自定义starter

Springboot-自定义starter

时间:2021-06-22 18:34:45

相关推荐

Springboot-自定义starter

Springboot在引用其他组件时通常只需要增加对应的spring-boot-starter-xxxx。例如:增加web server的能力只需要引用spring-boot-starter-web。那么如何自定义我们自己的starter呢?其实非常简单,示例如下:

自定义hello-starter

假设我们需要新增一个hello-starter,这个组件具有打印hello的能力。

首先创建一个maven工程,名称为hello-starter

<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"><parent><artifactId>starter-test</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>hello-starter</artifactId><properties><piler.source>17</piler.source><piler.target>17</piler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></dependency></dependencies></project>

HelloService

package org.example;public class HelloService {public void print(){System.out.println("hello");}}

创建自动配置类

package org.example;import org.springframework.context.annotation.Bean;public class HelloServiceAutoConfiguration {@BeanHelloService hello(){return new HelloService();}}

创建文件resources/META-INFO/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.example.HelloServiceAutoConfiguration

到此为止自定义的stater已经全部完成。

验证hello-starter

新建一个spingboot的web应用,并且引用hello-starter,pom.xml如下

<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"><parent><artifactId>starter-test</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>web</artifactId><properties><piler.source>17</piler.source><piler.target>17</piler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.example</groupId><artifactId>hello-starter</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>

启动引导类

package org.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplicationpublic class Main {public static void main(String[] args) {ConfigurableApplicationContext applicationContext = SpringApplication.run(Main.class,args);HelloService helloService = applicationContext.getBean("hello",HelloService.class);helloService.print();}}

输出

/Users/xiaosa/dev_tools/jdk-17.0.4.1.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=55116:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/xiaosa/idea_project/starter-test/web/target/classes:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.7.0/spring-boot-starter-web-2.7.0.jar:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot-starter/2.7.0/spring-boot-starter-2.7.0.jar:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot/2.7.0/spring-boot-2.7.0.jar:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.7.0/spring-boot-autoconfigure-2.7.0.jar:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.7.0/spring-boot-starter-logging-2.7.0.jar:/Users/xiaosa/.m2/repository/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar:/Users/xiaosa/.m2/repository/ch/qos/logback/logback-core/1.2.11/logback-core-1.2.11.jar:/Users/xiaosa/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar:/Users/xiaosa/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.17.2/log4j-to-slf4j-2.17.2.jar:/Users/xiaosa/.m2/repository/org/apache/logging/log4j/log4j-api/2.17.2/log4j-api-2.17.2.jar:/Users/xiaosa/.m2/repository/org/slf4j/jul-to-slf4j/1.7.36/jul-to-slf4j-1.7.36.jar:/Users/xiaosa/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-core/5.3.20/spring-core-5.3.20.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-jcl/5.3.20/spring-jcl-5.3.20.jar:/Users/xiaosa/.m2/repository/org/yaml/snakeyaml/1.30/snakeyaml-1.30.jar:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.7.0/spring-boot-starter-json-2.7.0.jar:/Users/xiaosa/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.13.3/jackson-databind-2.13.3.jar:/Users/xiaosa/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.13.3/jackson-annotations-2.13.3.jar:/Users/xiaosa/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.13.3/jackson-core-2.13.3.jar:/Users/xiaosa/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.13.3/jackson-datatype-jdk8-2.13.3.jar:/Users/xiaosa/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.13.3/jackson-datatype-jsr310-2.13.3.jar:/Users/xiaosa/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.13.3/jackson-module-parameter-names-2.13.3.jar:/Users/xiaosa/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.7.0/spring-boot-starter-tomcat-2.7.0.jar:/Users/xiaosa/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.63/tomcat-embed-core-9.0.63.jar:/Users/xiaosa/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.63/tomcat-embed-el-9.0.63.jar:/Users/xiaosa/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.63/tomcat-embed-websocket-9.0.63.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-web/5.3.20/spring-web-5.3.20.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-beans/5.3.20/spring-beans-5.3.20.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-webmvc/5.3.20/spring-webmvc-5.3.20.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-aop/5.3.20/spring-aop-5.3.20.jar:/Users/xiaosa/.m2/repository/org/springframework/spring-expression/5.3.20/spring-expression-5.3.20.jar:/Users/xiaosa/idea_project/starter-test/hello-starter/target/classes:/Users/xiaosa/.m2/repository/org/springframework/spring-context/5.3.20/spring-context-5.3.20.jar org.example.Main. _____ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::(v2.7.0)-03-24 23:59:46.469 INFO 15295 --- [ main] org.example.Main: Starting Main using Java 17.0.4.1 on XIAOSAdeMacBook-Pro.local with PID 15295 (/Users/xiaosa/idea_project/starter-test/web/target/classes started by xiaosa in /Users/xiaosa/idea_project/starter-test)-03-24 23:59:46.470 INFO 15295 --- [ main] org.example.Main: No active profile set, falling back to 1 default profile: "default"-03-24 23:59:46.872 INFO 15295 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)-03-24 23:59:46.878 INFO 15295 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]-03-24 23:59:46.878 INFO 15295 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.63]-03-24 23:59:46.917 INFO 15295 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext-03-24 23:59:46.917 INFO 15295 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 425 ms-03-24 23:59:47.066 INFO 15295 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''-03-24 23:59:47.071 INFO 15295 --- [ main] org.example.Main: Started Main in 0.959 seconds (JVM running for 1.104)hello

控制台输出hello,证明自定义的hello-starter已经生效。

原理

Springboot在启动时会加载一个SpringFactoriesLoader类,该类的作用就是加载classpath下所有的jar文件中的META-INF/spring.factories文件,并读取对应的配置。将相关的Bean加载到Spring容器中。

/** Copyright 2002- the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.core.io.support;import java.io.IOException;import .URL;import java.util.ArrayList;import java.util.Collections;import java.util.Enumeration;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import java.util.stream.Collectors;import mons.logging.Log;import mons.logging.LogFactory;import org.springframework.core.annotation.AnnotationAwareOrderComparator;import org.springframework.core.io.UrlResource;import org.springframework.lang.Nullable;import org.springframework.util.Assert;import org.springframework.util.ClassUtils;import org.springframework.util.ConcurrentReferenceHashMap;import org.springframework.util.ReflectionUtils;import org.springframework.util.StringUtils;/*** General purpose factory loading mechanism for internal use within the framework.** <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates* factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which* may be present in multiple JAR files in the classpath. The {@code spring.factories}* file must be in {@link Properties} format, where the key is the fully qualified* name of the interface or abstract class, and the value is a comma-separated list of* implementation class names. For example:** <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>** where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}* and {@code MyServiceImpl2} are two implementations.** @author Arjen Poutsma* @author Juergen Hoeller* @author Sam Brannen* @since 3.2*/public final class SpringFactoriesLoader {/*** The location to look for factories.* <p>Can be present in multiple JAR files.*/public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();private SpringFactoriesLoader() {}/*** Load and instantiate the factory implementations of the given type from* {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.* <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}.* <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}* to obtain all registered factory names.* <p>As of Spring Framework 5.3, if duplicate implementation class names are* discovered for a given factory type, only one instance of the duplicated* implementation type will be instantiated.* @param factoryType the interface or abstract class representing the factory* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)* @throws IllegalArgumentException if any factory implementation class cannot* be loaded or if an error occurs while instantiating any factory* @see #loadFactoryNames*/public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {Assert.notNull(factoryType, "'factoryType' must not be null");ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);}List<T> result = new ArrayList<>(factoryImplementationNames.size());for (String factoryImplementationName : factoryImplementationNames) {result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result;}/*** Load the fully qualified class names of factory implementations of the* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given* class loader.* <p>As of Spring Framework 5.3, if a particular implementation class name* is discovered more than once for the given factory type, duplicates will* be ignored.* @param factoryType the interface or abstract class representing the factory* @param classLoader the ClassLoader to use for loading resources; can be* {@code null} to use the default* @throws IllegalArgumentException if an error occurs while loading factory names* @see #loadFactories*/public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = cache.get(classLoader);if (result != null) {return result;}result = new HashMap<>();try {Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();String[] factoryImplementationNames =maDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {puteIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}// Replace all lists with unmodifiable lists containing unique elementsresult.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));cache.put(classLoader, result);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;}@SuppressWarnings("unchecked")private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {try {Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);if (!factoryType.isAssignableFrom(factoryImplementationClass)) {throw new IllegalArgumentException("Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");}return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();}catch (Throwable ex) {throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",ex);}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。