关于Springboot 工具及其源码实现

SprintBoot 为我们实现了很多的工具集:
常见如下:

spring-boot-maven-plugin

maven项目的pom.xml中,添加了org.springframework.boot:spring-boot-maven-plugin
插件,当运行“mvn package”进行打包时,会打包成一个可以直接运行的 JAR 文件,使用“Java -jar”命令就可以直接运行。

一般的maven项目的打包命令,不会把依赖的jar包也打包进去的,只是会放在jar包的同目录下,能够引用就可以了,但是spring-boot-maven-plugin插件,会将依赖的jar包全部打包进去。比如下面这个jar包的BOOT/INF/lib目录下面就包含了所有依赖的jar包

spring-boot-maven-plugin插件,在很大程度上简化了应用的部署,只需要安装了 JRE 就可以运行。

只要在源码中实现:

<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>2.0.4.RELEASE</version>
				<configuration>
					<mainClass>com.zhengbangnet.app.Startup</mainClass>
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

spring-boot-maven-plugin插件默认在父工程sprint-boot-starter-parent中被指定为repackage,
执行以上命令时会自动触发spring-boot-maven-plugin插件的repackage目标,完后可以在target目录下看到生成的jar,如下图:

关于Springboot 工具及其源码实现_第1张图片
包后生成的可执行jar,即可以通过java -jar common.jar命令启动。original这个则是mvn package打包的原始jar,在spring-boot-maven-plugin插件repackage命令操作时重命名为xxx.original,这个是一个普通的jar,可以被引用在其他服务中。

内部结构:
关于Springboot 工具及其源码实现_第2张图片
其中BOOT-INF主要是一些启动信息,包含classes和lib文件,classes文件放的是项目里生成的字节文件class和配置文件,lib文件是项目所需要的jar依赖。
META-INF目录下主要是maven的一些元数据信息,MANIFEST.MF文件内容如下:

其中Start-Class是项目的主程序入口,即main方法。Springboot-Boot-Classes和Spring-Boot-Lib指向的是生成的BOOT-INF下的对应位置。
思考一下:问题为什么有时候:报红色呢?
关于Springboot 工具及其源码实现_第3张图片

源码如下:

public abstract class AbstractAotMojo extends AbstractDependencyFilterMojo {

	/**
	 * The current Maven session. This is used for toolchain manager API calls.
	 */
	@Parameter(defaultValue = "${session}", readonly = true)
	private MavenSession session;

	/**
	 * The toolchain manager to use to locate a custom JDK.
	 */
	@Component
	private ToolchainManager toolchainManager;

	/**
	 * Skip the execution.
	 */
	@Parameter(property = "spring-boot.aot.skip", defaultValue = "false")
	private boolean skip;

	/**
	 * List of JVM system properties to pass to the AOT process.
	 */
	@Parameter
	private Map<String, String> systemPropertyVariables;

	/**
	 * JVM arguments that should be associated with the AOT process. On command line, make
	 * sure to wrap multiple values between quotes.
	 */
	@Parameter(property = "spring-boot.aot.jvmArguments")
	private String jvmArguments;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		if (this.skip) {
			getLog().debug("Skipping AOT execution as per configuration");
			return;
		}
		try {
			executeAot();
		}
		catch (Exception ex) {
			throw new MojoExecutionException(ex.getMessage(), ex);
		}
	}

	protected abstract void executeAot() throws Exception;

	protected void generateAotAssets(URL[] classPath, String processorClassName, String... arguments) throws Exception {
		List<String> command = CommandLineBuilder.forMainClass(processorClassName)
				.withSystemProperties(this.systemPropertyVariables)
				.withJvmArguments(new RunArguments(this.jvmArguments).asArray()).withClasspath(classPath)
				.withArguments(arguments).build();
		if (getLog().isDebugEnabled()) {
			getLog().debug("Generating AOT assets using command: " + command);
		}
		JavaProcessExecutor processExecutor = new JavaProcessExecutor(this.session, this.toolchainManager);
		processExecutor.run(this.project.getBasedir(), command, Collections.emptyMap());
	}

	protected final void compileSourceFiles(URL[] classPath, File sourcesDirectory, File outputDirectory)
			throws Exception {
		List<Path> sourceFiles = Files.walk(sourcesDirectory.toPath()).filter(Files::isRegularFile).toList();
		if (sourceFiles.isEmpty()) {
			return;
		}
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
			List<String> options = new ArrayList<>();
			options.add("-cp");
			options.add(ClasspathBuilder.build(Arrays.asList(classPath)));
			options.add("-d");
			options.add(outputDirectory.toPath().toAbsolutePath().toString());
			Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromPaths(sourceFiles);
			Errors errors = new Errors();
			CompilationTask task = compiler.getTask(null, fileManager, errors, options, null, compilationUnits);
			boolean result = task.call();
			if (!result || errors.hasReportedErrors()) {
				throw new IllegalStateException("Unable to compile generated source" + errors);
			}
		}
	}

Springboot的ant工具:

使用如下:

public class FindMainClass extends Task {

	private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";

	private String mainClass;

	private File classesRoot;

	private String property;

	public FindMainClass(Project project) {
		setProject(project);
	}

	@Override
	public void execute() throws BuildException {
		String mainClass = this.mainClass;
		if (!StringUtils.hasText(mainClass)) {
			mainClass = findMainClass();
			if (!StringUtils.hasText(mainClass)) {
				throw new BuildException("Could not determine main class given @classesRoot " + this.classesRoot);
			}
		}
		handle(mainClass);
	}

	private String findMainClass() {
		if (this.classesRoot == null) {
			throw new BuildException("one of @mainClass or @classesRoot must be specified");
		}
		if (!this.classesRoot.exists()) {
			throw new BuildException("@classesRoot " + this.classesRoot + " does not exist");
		}
		try {
			if (this.classesRoot.isDirectory()) {
				return MainClassFinder.findSingleMainClass(this.classesRoot, SPRING_BOOT_APPLICATION_CLASS_NAME);
			}
			return MainClassFinder.findSingleMainClass(new JarFile(this.classesRoot), "/",
					SPRING_BOOT_APPLICATION_CLASS_NAME);
		}
		catch (IOException ex) {
			throw new BuildException(ex);
		}
	}

	private void handle(String mainClass) {
		if (StringUtils.hasText(this.property)) {
			getProject().setProperty(this.property, mainClass);
		}
		else {
			log("Found main class " + mainClass);
		}
	}

	/**
	 * Set the main class, which will cause the search to be bypassed.
	 * @param mainClass the main class name
	 */
	public void setMainClass(String mainClass) {
		this.mainClass = mainClass;
	}

	/**
	 * Set the root location of classes to be searched.
	 * @param classesRoot the root location
	 */
	public void setClassesRoot(File classesRoot) {
		this.classesRoot = classesRoot;
	}

	/**
	 * Set the ANT property to set (if left unset, result will be printed to the log).
	 * @param property the ANT property to set
	 */
	public void setProperty(String property) {
		this.property = property;
	}

}

这样我们就可以从ant 中找到main 类了。

Springboot spring-boot-autoconfigure-processor

这个是Springboot的自动配置处理器:
使用方法如下:

org.springframework.boot.autoconfigureprocessor.AutoConfigureAnnotationProcessor
org.springframework.boot.autoconfigureprocessor.AutoConfigurationImportsAnnotationProcessor
org.springframework.boot.autoconfigureprocessor.ManagementContextConfigurationImportsAnnotationProcessor

实现类就有:

/*
 * Copyright 2012-2022 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
 *
 *      https://www.apache.org/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.boot.autoconfigureprocessor;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

/**
 * An {@link AbstractProcessor} to scan for annotated classes and write the class names to
 * a file using the Spring Boot {@code .imports} format.
 *
 * @author Scott Frederick
 */
abstract class AbstractImportsAnnotationProcessor extends AbstractProcessor {

	private final List<String> qualifiedClassNames = new ArrayList<>();

	abstract String getImportsFilePath();

	@Override
	public SourceVersion getSupportedSourceVersion() {
		return SourceVersion.latestSupported();
	}

	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		for (TypeElement annotation : annotations) {
			Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
			for (Element element : elements) {
				this.qualifiedClassNames.add(element.asType().toString());
			}
		}
		if (roundEnv.processingOver()) {
			try {
				writeImportsFile();
			}
			catch (IOException ex) {
				throw new IllegalStateException("Failed to write imports file '" + getImportsFilePath() + "'", ex);
			}
		}
		return false;
	}

	private void writeImportsFile() throws IOException {
		if (!this.qualifiedClassNames.isEmpty()) {
			Filer filer = this.processingEnv.getFiler();
			FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "", getImportsFilePath());
			try (Writer writer = new OutputStreamWriter(file.openOutputStream(), StandardCharsets.UTF_8)) {
				Collections.sort(this.qualifiedClassNames);
				for (String className : this.qualifiedClassNames) {
					writer.append(className);
					writer.append(System.lineSeparator());
				}
			}
		}
	}

}




```java

public class DockerConnectionException extends RuntimeException {

	private static final String JNA_EXCEPTION_CLASS_NAME = "com.sun.jna.LastErrorException";

	public DockerConnectionException(String host, Exception cause) {
		super(buildMessage(host, cause), cause);
	}

	private static String buildMessage(String host, Exception cause) {
		Assert.notNull(host, "Host must not be null");
		Assert.notNull(cause, "Cause must not be null");
		StringBuilder message = new StringBuilder("Connection to the Docker daemon at '" + host + "' failed");
		String causeMessage = getCauseMessage(cause);
		if (StringUtils.hasText(causeMessage)) {
			message.append(" with error \"").append(causeMessage).append("\"");
		}
		message.append("; ensure the Docker daemon is running and accessible");
		return message.toString();
	}

	private static String getCauseMessage(Exception cause) {
		if (cause.getCause() != null && cause.getCause().getClass().getName().equals(JNA_EXCEPTION_CLASS_NAME)) {
			return cause.getCause().getMessage();
		}
		return cause.getMessage();
	}

}

```java

import com.fasterxml.jackson.annotation.JsonCreator;

/**
 * A {@link ProgressUpdateEvent} fired as an image is loaded.
 *
 * @author Phillip Webb
 * @since 2.3.0
 */
public class LoadImageUpdateEvent extends ProgressUpdateEvent {

	private final String stream;

	@JsonCreator
	public LoadImageUpdateEvent(String stream, String status, ProgressDetail progressDetail, String progress) {
		super(status, progressDetail, progress);
		this.stream = stream;
	}

	/**
	 * Return the stream response or {@code null} if no response is available.
	 * @return the stream response.
	 */
	public String getStream() {
		return this.stream;
	}

}

spring-boot-configuration-metadata

Springboot 配置原信息这块:主要是Springboot 配置原信息的插件,源码如下:

public class ConfigurationMetadataGroup implements Serializable {

	private final String id;

	private final Map<String, ConfigurationMetadataSource> sources = new HashMap<>();

	private final Map<String, ConfigurationMetadataProperty> properties = new HashMap<>();

	public ConfigurationMetadataGroup(String id) {
		this.id = id;
	}

	/**
	 * Return the id of the group, used as a common prefix for all properties associated
	 * to it.
	 * @return the id of the group
	 */
	public String getId() {
		return this.id;
	}

	/**
	 * Return the {@link ConfigurationMetadataSource sources} defining the properties of
	 * this group.
	 * @return the sources of the group
	 */
	public Map<String, ConfigurationMetadataSource> getSources() {
		return this.sources;
	}

	/**
	 * Return the {@link ConfigurationMetadataProperty properties} defined in this group.
	 * 

* A property may appear more than once for a given source, potentially with * conflicting type or documentation. This is a "merged" view of the properties of * this group. * @return the properties of the group * @see ConfigurationMetadataSource#getProperties() */ public Map<String, ConfigurationMetadataProperty> getProperties() { return this.properties; } }

 */
class ConfigurationMetadataItem extends ConfigurationMetadataProperty {

	private String sourceType;

	private String sourceMethod;

	/**
	 * The class name of the source that contributed this property. For example, if the
	 * property was from a class annotated with {@code @ConfigurationProperties} this
	 * attribute would contain the fully qualified name of that class.
	 * @return the source type
	 */
	String getSourceType() {
		return this.sourceType;
	}

	void setSourceType(String sourceType) {
		this.sourceType = sourceType;
	}

	/**
	 * The full name of the method (including parenthesis and argument types) that
	 * contributed this property. For example, the name of a getter in a
	 * {@code @ConfigurationProperties} annotated class.
	 * @return the source method
	 */
	String getSourceMethod() {
		return this.sourceMethod;
	}

	void setSourceMethod(String sourceMethod) {
		this.sourceMethod = sourceMethod;
	}

}
class RawConfigurationMetadata {

	private final List<ConfigurationMetadataSource> sources;

	private final List<ConfigurationMetadataItem> items;

	private final List<ConfigurationMetadataHint> hints;

	RawConfigurationMetadata(List<ConfigurationMetadataSource> sources, List<ConfigurationMetadataItem> items,
			List<ConfigurationMetadataHint> hints) {
		this.sources = new ArrayList<>(sources);
		this.items = new ArrayList<>(items);
		this.hints = new ArrayList<>(hints);
		for (ConfigurationMetadataItem item : this.items) {
			resolveName(item);
		}
	}

	List<ConfigurationMetadataSource> getSources() {
		return this.sources;
	}

	ConfigurationMetadataSource getSource(ConfigurationMetadataItem item) {
		if (item.getSourceType() == null) {
			return null;
		}
		return this.sources.stream()
				.filter((candidate) -> item.getSourceType().equals(candidate.getType())
						&& item.getId().startsWith(candidate.getGroupId()))
				.max(Comparator.comparingInt((candidate) -> candidate.getGroupId().length())).orElse(null);
	}

	List<ConfigurationMetadataItem> getItems() {
		return this.items;
	}

	List<ConfigurationMetadataHint> getHints() {
		return this.hints;
	}

	/**
	 * Resolve the name of an item against this instance.
	 * @param item the item to resolve
	 * @see ConfigurationMetadataProperty#setName(String)
	 */
	private void resolveName(ConfigurationMetadataItem item) {
		item.setName(item.getId()); // fallback
		ConfigurationMetadataSource source = getSource(item);
		if (source != null) {
			String groupId = source.getGroupId();
			String dottedPrefix = groupId + ".";
			String id = item.getId();
			if (hasLength(groupId) && id.startsWith(dottedPrefix)) {
				String name = id.substring(dottedPrefix.length());
				item.setName(name);
			}
		}
	}

	private static boolean hasLength(String string) {
		return (string != null && !string.isEmpty());
	}

}

spring-boot-configuration-processor:

Springboot 配置处理器的:
源码如下:

private final TypeUtils typeUtils;

	private final Elements elements;

	private final Messager messager;

	private final FieldValuesParser fieldValuesParser;

	private final Map<TypeElement, Map<String, Object>> defaultValues = new HashMap<>();

	private final String configurationPropertiesAnnotation;

	private final String nestedConfigurationPropertyAnnotation;

	private final String deprecatedConfigurationPropertyAnnotation;

	private final String constructorBindingAnnotation;

	private final String defaultValueAnnotation;

	private final Set<String> endpointAnnotations;

	private final String readOperationAnnotation;

	private final String nameAnnotation;

	private final String autowiredAnnotation;

	MetadataGenerationEnvironment(ProcessingEnvironment environment, String configurationPropertiesAnnotation,
			String nestedConfigurationPropertyAnnotation, String deprecatedConfigurationPropertyAnnotation,
			String constructorBindingAnnotation, String autowiredAnnotation, String defaultValueAnnotation,
			Set<String> endpointAnnotations, String readOperationAnnotation, String nameAnnotation) {
		this.typeUtils = new TypeUtils(environment);
		this.elements = environment.getElementUtils();
		this.messager = environment.getMessager();
		this.fieldValuesParser = resolveFieldValuesParser(environment);
		this.configurationPropertiesAnnotation = configurationPropertiesAnnotation;
		this.nestedConfigurationPropertyAnnotation = nestedConfigurationPropertyAnnotation;
		this.deprecatedConfigurationPropertyAnnotation = deprecatedConfigurationPropertyAnnotation;
		this.constructorBindingAnnotation = constructorBindingAnnotation;
		this.autowiredAnnotation = autowiredAnnotation;
		this.defaultValueAnnotation = defaultValueAnnotation;
		this.endpointAnnotations = endpointAnnotations;
		this.readOperationAnnotation = readOperationAnnotation;
		this.nameAnnotation = nameAnnotation;
	}

	private static FieldValuesParser resolveFieldValuesParser(ProcessingEnvironment env) {
		try {
			return new JavaCompilerFieldValuesParser(env);
		}
		catch (Throwable ex) {
			return FieldValuesParser.NONE;
		}
	}

	TypeUtils getTypeUtils() {
		return this.typeUtils;
	}

	Messager getMessager() {
		return this.messager;
	}

	/**
	 * Return the default value of the field with the specified {@code name}.
	 * @param type the type to consider
	 * @param name the name of the field
	 * @return the default value or {@code null} if the field does not exist or no default
	 * value has been detected
	 */
	Object getFieldDefaultValue(TypeElement type, String name) {
		return this.defaultValues.computeIfAbsent(type, this::resolveFieldValues).get(name);
	}

	boolean isExcluded(TypeMirror type) {
		if (type == null) {
			return false;
		}
		String typeName = type.toString();
		if (typeName.endsWith("[]")) {
			typeName = typeName.substring(0, typeName.length() - 2);
		}
		return TYPE_EXCLUDES.contains(typeName);
	}

	boolean isDeprecated(Element element) {
		if (isElementDeprecated(element)) {
			return true;
		}
		if (element instanceof VariableElement || element instanceof ExecutableElement) {
			return isElementDeprecated(element.getEnclosingElement());
		}
		return false;
	}

	ItemDeprecation resolveItemDeprecation(Element element) {
		AnnotationMirror annotation = getAnnotation(element, this.deprecatedConfigurationPropertyAnnotation);
		String reason = null;
		String replacement = null;
		if (annotation != null) {
			Map<String, Object> elementValues = getAnnotationElementValues(annotation);
			reason = (String) elementValues.get("reason");
			replacement = (String) elementValues.get("replacement");
		}
		reason = (reason == null || reason.isEmpty()) ? null : reason;
		replacement = (replacement == null || replacement.isEmpty()) ? null : replacement;
		return new ItemDeprecation(reason, replacement);
	}

	boolean hasConstructorBindingAnnotation(ExecutableElement element) {
		return hasAnnotation(element, this.constructorBindingAnnotation);
	}

	boolean hasAutowiredAnnotation(ExecutableElement element) {
		return hasAnnotation(element, this.autowiredAnnotation);
	}

	boolean hasAnnotation(Element element, String type) {
		return getAnnotation(element, type) != null;
	}

Springboot gradle plugin:
使用gragle 编译插件:

final class ApplicationPluginAction implements PluginApplicationAction {

	@Override
	public void execute(Project project) {
		JavaApplication javaApplication = project.getExtensions().getByType(JavaApplication.class);
		DistributionContainer distributions = project.getExtensions().getByType(DistributionContainer.class);
		Distribution distribution = distributions.create("boot");
		distribution.getDistributionBaseName()
				.convention((project.provider(() -> javaApplication.getApplicationName() + "-boot")));
		TaskProvider<CreateStartScripts> bootStartScripts = project.getTasks().register("bootStartScripts",
				CreateStartScripts.class,
				(task) -> configureCreateStartScripts(project, javaApplication, distribution, task));
		CopySpec binCopySpec = project.copySpec().into("bin").from(bootStartScripts);
		binCopySpec.setFileMode(0755);
		distribution.getContents().with(binCopySpec);
	}

	private void configureCreateStartScripts(Project project, JavaApplication javaApplication,
			Distribution distribution, CreateStartScripts createStartScripts) {
		createStartScripts
				.setDescription("Generates OS-specific start scripts to run the project as a Spring Boot application.");
		((TemplateBasedScriptGenerator) createStartScripts.getUnixStartScriptGenerator())
				.setTemplate(project.getResources().getText().fromString(loadResource("/unixStartScript.txt")));
		((TemplateBasedScriptGenerator) createStartScripts.getWindowsStartScriptGenerator())
				.setTemplate(project.getResources().getText().fromString(loadResource("/windowsStartScript.txt")));
		project.getConfigurations().all((configuration) -> {
			if ("bootArchives".equals(configuration.getName())) {
				distribution.getContents().with(artifactFilesToLibCopySpec(project, configuration));
				createStartScripts.setClasspath(configuration.getArtifacts().getFiles());
			}
		});
		createStartScripts.getConventionMapping().map("outputDir",
				() -> new File(project.getBuildDir(), "bootScripts"));
		createStartScripts.getConventionMapping().map("applicationName", javaApplication::getApplicationName);
		createStartScripts.getConventionMapping().map("defaultJvmOpts", javaApplication::getApplicationDefaultJvmArgs);
	}

	private CopySpec artifactFilesToLibCopySpec(Project project, Configuration configuration) {
		CopySpec copySpec = project.copySpec().into("lib").from(artifactFiles(configuration));
		copySpec.setFileMode(0644);
		return copySpec;
	}

	private Callable<FileCollection> artifactFiles(Configuration configuration) {
		return () -> configuration.getArtifacts().getFiles();
	}

	@Override
	public Class<? extends Plugin<Project>> getPluginClass() {
		return ApplicationPlugin.class;
	}

	private String loadResource(String name) {
		try (InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream(name))) {
			char[] buffer = new char[4096];
			int read;
			StringWriter writer = new StringWriter();
			while ((read = reader.read(buffer)) > 0) {
				writer.write(buffer, 0, read);
			}
			return writer.toString();
		}
		catch (IOException ex) {
			throw new GradleException("Failed to read '" + name + "'", ex);
		}
	}

}

Springboot grade test-support 插件:

public class GradleBuild {

	private final Dsl dsl;

	private File projectDir;

	private String script;

	private String settings;

	private String gradleVersion;

	private String springBootVersion = "TEST-SNAPSHOT";

	private GradleVersion expectDeprecationWarnings;

	private List<String> expectedDeprecationMessages = new ArrayList<>();

	private boolean configurationCache = false;

	private Map<String, String> scriptProperties = new HashMap<>();

	public GradleBuild() {
		this(Dsl.GROOVY);
	}

	public GradleBuild(Dsl dsl) {
		this.dsl = dsl;
	}

	public Dsl getDsl() {
		return this.dsl;
	}

	void before() throws IOException {
		this.projectDir = Files.createTempDirectory("gradle-").toFile();
	}

	void after() {
		this.script = null;
		FileSystemUtils.deleteRecursively(this.projectDir);
	}

	private List<File> pluginClasspath() {
		return Arrays.asList(new File("bin/main"), new File("build/classes/java/main"),
				new File("build/resources/main"), new File(pathOfJarContaining(LaunchScript.class)),
				new File(pathOfJarContaining(ClassVisitor.class)),
				new File(pathOfJarContaining(DependencyManagementPlugin.class)),
				new File(pathOfJarContaining("org.jetbrains.kotlin.cli.common.PropertiesKt")),
				new File(pathOfJarContaining("org.jetbrains.kotlin.compilerRunner.KotlinLogger")),
				new File(pathOfJarContaining(KotlinPlugin.class)), new File(pathOfJarContaining(KotlinProject.class)),
				new File(pathOfJarContaining("org.jetbrains.kotlin.daemon.client.KotlinCompilerClient")),
				new File(pathOfJarContaining(KotlinCompilerPluginSupportPlugin.class)),
				new File(pathOfJarContaining(LanguageSettings.class)),
				new File(pathOfJarContaining(ArchiveEntry.class)), new File(pathOfJarContaining(BuildRequest.class)),
				new File(pathOfJarContaining(HttpClientConnectionManager.class)),
				new File(pathOfJarContaining(HttpRequest.class)), new File(pathOfJarContaining(Module.class)),
				new File(pathOfJarContaining(Versioned.class)),
				new File(pathOfJarContaining(ParameterNamesModule.class)),
				new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)),
				new File(pathOfJarContaining(Toml.class)), new File(pathOfJarContaining(Lexer.class)),
				new File(pathOfJarContaining("org.graalvm.buildtools.gradle.NativeImagePlugin")),
				new File(pathOfJarContaining("org.graalvm.reachability.GraalVMReachabilityMetadataRepository")),
				new File(pathOfJarContaining("org.graalvm.buildtools.utils.SharedConstants")));
	}

	private String pathOfJarContaining(String className) {
		try {
			return pathOfJarContaining(Class.forName(className));
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalArgumentException(ex);
		}
	}

	private String pathOfJarContaining(Class<?> type) {
		return type.getProtectionDomain().getCodeSource().getLocation().getPath();
	}

	public GradleBuild script(String script) {
		this.script = script.endsWith(this.dsl.getExtension()) ? script : script + this.dsl.getExtension();
		return this;
	}

	public void settings(String settings) {
		this.settings = settings;
	}

	public GradleBuild expectDeprecationWarningsWithAtLeastVersion(String gradleVersion) {
		this.expectDeprecationWarnings = GradleVersion.version(gradleVersion);
		return this;
	}

	public GradleBuild expectDeprecationMessages(String... messages) {
		this.expectedDeprecationMessages.addAll(Arrays.asList(messages));
		return this;
	}

	public GradleBuild configurationCache() {
		this.configurationCache = true;
		return this;
	}

	public boolean isConfigurationCache() {
		return this.configurationCache;
	}

参考资料和推荐阅读:

https://www.jb51.net/article/197968.htm
http://t.zoukankan.com/kingsonfu-p-11805455.html

你可能感兴趣的:(#,spring,boot,java,jvm)