Java >> Java Tutorial >  >> Tag >> Spring

Details zur Spring Boot AutoConfiguration

Was ist der Springboot? Wie funktioniert die automatische Konfiguration von Spring Boot? Dieser Beitrag befasst sich mit den Details der Spring Boot Autoconfiguration.

Was ist der Spring Boot?

Auf der Spring Boot-Website heißt es:„Wir nehmen die Spring-Plattform und Bibliotheken von Drittanbietern kritisch wahr, damit Sie mit minimalem Aufwand loslegen können. Die meisten Spring Boot-Anwendungen benötigen eine minimale Spring-Konfiguration.

Spring Boot ist ein Framework zum Erstellen von Anwendungen.

Spring Boot bietet verschiedene Funktionen, und wenn Sie es zum Erstellen Ihrer Anwendung verwenden, benötigen Sie unterschiedliche Beans. Die automatische Konfiguration konfiguriert also automatisch die Spring Boot-Anwendung, indem diese Beans erstellt werden.

Warum verwenden Sie die automatische Konfiguration?

Effizienz und Zeit. Mit der automatischen Konfiguration wird Spring Entwicklern viel Arbeit abnehmen und Zeit beim Erstellen der Beans sparen.

Hinter den Kulissen ist es im Grunde ein Haufen @Configuration Klassen. Diese Klassen verwenden nicht die Anmerkung @Configuration .

Einige der Anmerkungen, die diese Klassen verwenden, sind:

  • @ConditionalOnClass – Die Anwendung verwendet dies nur, wenn sich die angegebene Klasse im Klassenpfad befindet.
  • @Conditional – Nur wenn eine Bedingung erfüllt ist
  • @ConditionalOnMissingBean – Die Anwendung verwendet dies, wenn eine Bean fehlt oder nicht erstellt wurde.

Kurz gesagt, @Conditional Anmerkung ist die Basis aller Anmerkungen.

Wie verstehen Sie das wirklich?

Sie oder Ihr Team arbeiten an mehreren Projekten und diese Projekte haben einen gemeinsamen Code. Wenn Sie diesen gemeinsamen Code in eine eigene Bibliothek oder Shared Beans extrahieren möchten, können alle Projekte diese verwenden.


@Configuration
public class SharedObjects
{
   @Bean
   public CommonObject commonObject()
   {
      return new CommonObject();
   }
}

Einmal diese CommonObject wird über eine JAR-Datei geteilt, andere Projekte können sie importieren.

Der Nachteil bei diesem Ansatz ist, wenn das andere Projekt CommonObject verwenden möchte , aber keine anderen Beans aus diesem gemeinsamen Code verwenden möchten. Das Importieren dieser Beans würde während des Starts unnötigen Overhead im Projekt bedeuten. Daher müssen Sie Spring mitteilen, dass wir nur CommonObject benötigen Bohnen und nicht andere Bohnen, erschaffe nicht einmal andere Bohnen. Dann können wir @Conditional verwenden Anmerkung.

Um diesen @Conditional zu verwenden Anmerkung, es gibt ein paar Möglichkeiten. Spring Boot stellt Condition bereit Schnittstelle, die eine Klasse implementieren kann.


public class IsBrowserOnCondition implements Condition
{
   @Override
   public boolean matches(ConditionContext context, AnotatedTypeMetadata metadata)
   {
      return isMozillaFirefoxEnabled(context);
   }
   
   public boolean isMozillaFirefoxEnabled(ConditionContext context)
   {
      return context.getEnvironment().containsProperty("spring.preferredbrowser");
   }
}

In dieser Klasse IsBrowserOnCondition sehen wir die Implementierung der Schnittstelle Condition.

  • Diese Implementierung beinhaltet die Methode matches .
  • Diese Methode ruft eine andere Methode auf, um zu überprüfen, ob der Mozilla Firefox-Browser aktiviert wurde.
  • Dabei prüft es auf eine Eigenschaft spring.preferredbrowser Zustand.
  • Wenn wir nun neue Beans für die Bedingung erstellen wollen, verwenden wir die Anmerkung @Conditional als @Conditional(IsBrowserOnCondition.class) .

Kurz gesagt, Spring Boot ist eine Shared-Context-Konfiguration mit einer Reihe von Beans, die mit der Anmerkung @Conditional erstellt wurden .

AutoConfiguration mit Spring Boot

Um mehr über die Autokonfiguration zu erfahren, verwenden wir eine einfache Spring Boot-Anwendung. Wir möchten wissen, was passiert, wenn wir diese Anwendung starten.

Die Hauptklasse dieser Anwendung sieht also wie folgt aus:


package com.betterjavacode.abccompany

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.web.filter.CommonsRequestLoggingFilter;

@SpringBootApplication
@EnableJdbcHttpSession
public class HomeApplication extends SpringBootServletInitializer
{
	public static void main(String[] args)
	{
		SpringApplication.run(HomeApplication.class, args);
	}
}

Wenn ich diese Hauptklasse ausführe, startet Spring Boot den Tomcat-Webserver.



.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.6.RELEASE)

2020-06-20 16:38:20.317  INFO 19632 --- [           main] c.rentersfeedback.home.HomeApplication   : Starting HomeApplication on YMALI2019 with PID 19632 (C:\projects\rentersfeedback\out\production\classes started by Yogesh Mali in C:\projects\rentersfeedback)
2020-06-20 16:38:20.320  INFO 19632 --- [           main] c.rentersfeedback.home.HomeApplication   : No active profile set, falling back to default profiles: default
2020-06-20 16:38:21.483  INFO 19632 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2020-06-20 16:38:21.589  INFO 19632 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 96ms. Found 5 repository interfaces.
2020-06-20 16:38:22.052  INFO 19632 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$fdb646fa] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-06-20 16:38:22.674  INFO 19632 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https)
2020-06-20 16:38:22.700  INFO 19632 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-06-20 16:38:22.700  INFO 19632 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.21]
2020-06-20 16:38:22.906  INFO 19632 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext


Hinter den Kulissen führt Spring Boot einige Arbeiten aus, wenn die Anwendung auf Tomcat gestartet wird. Es gibt verschiedene 17 Quellen von Eigenschaften, die Spring Boot hier verwendet. Die offizielle Spring Boot-Dokumentation enthält Einzelheiten zu diesen 17 Quellen. Ein Entwickler kann diese Eigenschaften auslagern und oft tun wir das für application.properties . Wenn Sie also eine dieser Eigenschaften konfiguriert haben, liest Spring Boot diese Eigenschaften anstelle der Standardquellen.

Wenn wir nun die JAR-Datei erweitern spring-boot-autoconfigure-2.1.6.RELEASE.jar , sehen Sie die Anzahl der Verzeichnisse unter Paket org.springframework.boot.autoconfigure . Alle diese Unterpakete sind die Beans, die Spring Boot hochzieht, aber nur basierend auf @Conditional verwendet Anmerkung. Daher lädt Spring Boot während des Starts einige dieser Pakete basierend auf den Abhängigkeiten, die Sie in Ihrer Maven- oder Gradle-Build-Datei konfiguriert haben.

Aus diesem JAR, wenn wir die Quelldatei für ThymeleafAutoConfiguration öffnen , sehen wir Folgendes:


/*
 * Copyright 2012-2019 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.autoconfigure.thymeleaf;

import java.util.Collection;
import java.util.LinkedHashMap;

import javax.annotation.PostConstruct;
import javax.servlet.DispatcherType;

import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect;
import nz.net.ultraq.thymeleaf.LayoutDialect;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.thymeleaf.dialect.IDialect;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;
import org.thymeleaf.spring5.ISpringWebFluxTemplateEngine;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.template.TemplateLocation;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties.Reactive;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ConditionalOnMissingFilterBean;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.util.MimeType;
import org.springframework.util.unit.DataSize;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 * @author Stephane Nicoll
 * @author Brian Clozel
 * @author Eddú Meléndez
 * @author Daniel Fernández
 * @author Kazuki Shimizu
 * @author Artsiom Yudovin
 */
@Configuration
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration {

	@Configuration
	@ConditionalOnMissingBean(name = "defaultTemplateResolver")
	static class DefaultTemplateResolverConfiguration {

		private static final Log logger = LogFactory.getLog(DefaultTemplateResolverConfiguration.class);

		private final ThymeleafProperties properties;

		private final ApplicationContext applicationContext;

		DefaultTemplateResolverConfiguration(ThymeleafProperties properties, ApplicationContext applicationContext) {
			this.properties = properties;
			this.applicationContext = applicationContext;
		}

		@PostConstruct
		public void checkTemplateLocationExists() {
			boolean checkTemplateLocation = this.properties.isCheckTemplateLocation();
			if (checkTemplateLocation) {
				TemplateLocation location = new TemplateLocation(this.properties.getPrefix());
				if (!location.exists(this.applicationContext)) {
					logger.warn("Cannot find template location: " + location + " (please add some templates or check "
							+ "your Thymeleaf configuration)");
				}
			}
		}

		@Bean
		public SpringResourceTemplateResolver defaultTemplateResolver() {
			SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
			resolver.setApplicationContext(this.applicationContext);
			resolver.setPrefix(this.properties.getPrefix());
			resolver.setSuffix(this.properties.getSuffix());
			resolver.setTemplateMode(this.properties.getMode());
			if (this.properties.getEncoding() != null) {
				resolver.setCharacterEncoding(this.properties.getEncoding().name());
			}
			resolver.setCacheable(this.properties.isCache());
			Integer order = this.properties.getTemplateResolverOrder();
			if (order != null) {
				resolver.setOrder(order);
			}
			resolver.setCheckExistence(this.properties.isCheckTemplate());
			return resolver;
		}

	}

	@Configuration
	protected static class ThymeleafDefaultConfiguration {

		private final ThymeleafProperties properties;

		private final Collection templateResolvers;

		private final ObjectProvider dialects;

		public ThymeleafDefaultConfiguration(ThymeleafProperties properties,
				Collection templateResolvers, ObjectProvider dialectsProvider) {
			this.properties = properties;
			this.templateResolvers = templateResolvers;
			this.dialects = dialectsProvider;
		}

		@Bean
		@ConditionalOnMissingBean
		public SpringTemplateEngine templateEngine() {
			SpringTemplateEngine engine = new SpringTemplateEngine();
			engine.setEnableSpringELCompiler(this.properties.isEnableSpringElCompiler());
			engine.setRenderHiddenMarkersBeforeCheckboxes(this.properties.isRenderHiddenMarkersBeforeCheckboxes());
			this.templateResolvers.forEach(engine::addTemplateResolver);
			this.dialects.orderedStream().forEach(engine::addDialect);
			return engine;
		}

	}

	@Configuration
	@ConditionalOnWebApplication(type = Type.SERVLET)
	@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
	static class ThymeleafWebMvcConfiguration {

		@Bean
		@ConditionalOnEnabledResourceChain
		@ConditionalOnMissingFilterBean(ResourceUrlEncodingFilter.class)
		public FilterRegistrationBean resourceUrlEncodingFilter() {
			FilterRegistrationBean registration = new FilterRegistrationBean<>(
					new ResourceUrlEncodingFilter());
			registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
			return registration;
		}

		@Configuration
		static class ThymeleafViewResolverConfiguration {

			private final ThymeleafProperties properties;

			private final SpringTemplateEngine templateEngine;

			ThymeleafViewResolverConfiguration(ThymeleafProperties properties, SpringTemplateEngine templateEngine) {
				this.properties = properties;
				this.templateEngine = templateEngine;
			}

			@Bean
			@ConditionalOnMissingBean(name = "thymeleafViewResolver")
			public ThymeleafViewResolver thymeleafViewResolver() {
				ThymeleafViewResolver resolver = new ThymeleafViewResolver();
				resolver.setTemplateEngine(this.templateEngine);
				resolver.setCharacterEncoding(this.properties.getEncoding().name());
				resolver.setContentType(
						appendCharset(this.properties.getServlet().getContentType(), resolver.getCharacterEncoding()));
				resolver.setProducePartialOutputWhileProcessing(
						this.properties.getServlet().isProducePartialOutputWhileProcessing());
				resolver.setExcludedViewNames(this.properties.getExcludedViewNames());
				resolver.setViewNames(this.properties.getViewNames());
				// This resolver acts as a fallback resolver (e.g. like a
				// InternalResourceViewResolver) so it needs to have low precedence
				resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);
				resolver.setCache(this.properties.isCache());
				return resolver;
			}

			private String appendCharset(MimeType type, String charset) {
				if (type.getCharset() != null) {
					return type.toString();
				}
				LinkedHashMap parameters = new LinkedHashMap<>();
				parameters.put("charset", charset);
				parameters.putAll(type.getParameters());
				return new MimeType(type, parameters).toString();
			}

		}

	}

	@Configuration
	@ConditionalOnWebApplication(type = Type.REACTIVE)
	@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
	static class ThymeleafReactiveConfiguration {

		private final ThymeleafProperties properties;

		private final Collection templateResolvers;

		private final ObjectProvider dialects;

		ThymeleafReactiveConfiguration(ThymeleafProperties properties, Collection templateResolvers,
				ObjectProvider dialectsProvider) {
			this.properties = properties;
			this.templateResolvers = templateResolvers;
			this.dialects = dialectsProvider;
		}

		@Bean
		@ConditionalOnMissingBean(ISpringWebFluxTemplateEngine.class)
		public SpringWebFluxTemplateEngine templateEngine() {
			SpringWebFluxTemplateEngine engine = new SpringWebFluxTemplateEngine();
			engine.setEnableSpringELCompiler(this.properties.isEnableSpringElCompiler());
			engine.setRenderHiddenMarkersBeforeCheckboxes(this.properties.isRenderHiddenMarkersBeforeCheckboxes());
			this.templateResolvers.forEach(engine::addTemplateResolver);
			this.dialects.orderedStream().forEach(engine::addDialect);
			return engine;
		}

	}

	@Configuration
	@ConditionalOnWebApplication(type = Type.REACTIVE)
	@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
	static class ThymeleafWebFluxConfiguration {

		private final ThymeleafProperties properties;

		ThymeleafWebFluxConfiguration(ThymeleafProperties properties) {
			this.properties = properties;
		}

		@Bean
		@ConditionalOnMissingBean(name = "thymeleafReactiveViewResolver")
		public ThymeleafReactiveViewResolver thymeleafViewResolver(ISpringWebFluxTemplateEngine templateEngine) {
			ThymeleafReactiveViewResolver resolver = new ThymeleafReactiveViewResolver();
			resolver.setTemplateEngine(templateEngine);
			mapProperties(this.properties, resolver);
			mapReactiveProperties(this.properties.getReactive(), resolver);
			// This resolver acts as a fallback resolver (e.g. like a
			// InternalResourceViewResolver) so it needs to have low precedence
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);
			return resolver;
		}

		private void mapProperties(ThymeleafProperties properties, ThymeleafReactiveViewResolver resolver) {
			PropertyMapper map = PropertyMapper.get();
			map.from(properties::getEncoding).to(resolver::setDefaultCharset);
			resolver.setExcludedViewNames(properties.getExcludedViewNames());
			resolver.setViewNames(properties.getViewNames());
		}

		private void mapReactiveProperties(Reactive properties, ThymeleafReactiveViewResolver resolver) {
			PropertyMapper map = PropertyMapper.get();
			map.from(properties::getMediaTypes).whenNonNull().to(resolver::setSupportedMediaTypes);
			map.from(properties::getMaxChunkSize).asInt(DataSize::toBytes).when((size) -> size > 0)
					.to(resolver::setResponseMaxChunkSizeBytes);
			map.from(properties::getFullModeViewNames).to(resolver::setFullModeViewNames);
			map.from(properties::getChunkedModeViewNames).to(resolver::setChunkedModeViewNames);
		}

	}

	@Configuration
	@ConditionalOnClass(LayoutDialect.class)
	protected static class ThymeleafWebLayoutConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public LayoutDialect layoutDialect() {
			return new LayoutDialect();
		}

	}

	@Configuration
	@ConditionalOnClass(DataAttributeDialect.class)
	protected static class DataAttributeDialectConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public DataAttributeDialect dialect() {
			return new DataAttributeDialect();
		}

	}

	@Configuration
	@ConditionalOnClass({ SpringSecurityDialect.class })
	protected static class ThymeleafSecurityDialectConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public SpringSecurityDialect securityDialect() {
			return new SpringSecurityDialect();
		}

	}

	@Configuration
	@ConditionalOnClass(Java8TimeDialect.class)
	protected static class ThymeleafJava8TimeDialect {

		@Bean
		@ConditionalOnMissingBean
		public Java8TimeDialect java8TimeDialect() {
			return new Java8TimeDialect();
		}

	}

}

Wenn Sie eine Webanwendung erstellen, ist die Thymeleaf-Vorlage Ihre Standard-Vorlagen-Engine. Spring Boot lädt diese Klasse, wenn TemplateMode und SpringBootEngine geladen sind. Wir können die Verwendung von @Conditional sehen Anmerkung.

Wie schließt man Spring Boot AutoConfiguration aus?

Spring Boot bietet eine Option zum Ausschließen von Autokonfigurationen, die Sie nicht in Ihr Projekt aufnehmen möchten.


@SpringBootApplication(exclude = {BatchAutoConfiguration.class)
public class HomeApplication 
{
    public static void main(String[] args) {
        SpringApplication.run(HomeApplication.class, args);
    }
}

Eine Sache, an die Sie sich hier erinnern sollten, ist, dass Sie wissen müssen, warum Sie eine bestimmte Bean ausschließen, und wenn Sie damit einverstanden sind, dass möglicherweise einige abhängige Konfigurationen ausgeschlossen werden.

Schlussfolgerung

In diesem Beitrag habe ich

gezeigt
  • Wie Spring Boot funktioniert und wie Sie die Spring Boot-Anwendung mit einigen der Abhängigkeiten erstellen können.
  • Was die automatische Konfiguration ist und was sie beinhaltet.

Wenn Ihnen dieser Beitrag gefallen hat oder Sie weitere Fragen haben, abonnieren Sie meinen Blog.

Referenzen

  1. Spring Boot-Leitfaden
  2. Offizielle Spring Boot-Dokumentation
  3. Was ist Spring Boot?


Java-Tag