El principio de la configuración de carga automática de arranque por resorte está relacionado

    El principio de carga automática de arranque por resorte.

Visión general:

En el proyecto spring-boot, solo necesitamos crear una clase de inicio y marcar la anotación @SpringBootApplication para completar la configuración automatizada. El principio de esto es principalmente el rol de la anotación @SpringBootApplication. Analicemos esta anotación en detalle.

 Uno, anotación SpringBootApplication

@SpringBootApplication comentario empaque primario tres subanotados de la siguiente manera:
@ SpringBootConfiguration, @ EnableAutoConfiguration, @ ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {
   
   

Entre ellos, @ ComponentScan se usa para escanear el rol del paquete, @ SpringBootConfiguration es un contenedor de @Configuration y @Configuration es un contenedor de @Component, lo que significa que esta clase es una clase de configuración.
        
La anotación @EnableAutoConfiguration es nuestro principal objeto de análisis.

	@Target(ElementType.TYPE)
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@Inherited
	@AutoConfigurationPackage
	@Import(EnableAutoConfigurationImportSelector.class)
	public @interface EnableAutoConfiguration 

Hay dos funciones principales de esta anotación, una es @AutoConfigurationPackage y la otra es @Import (análisis de claves)

Sabemos que @Import también puede especificar la subclase de implementación de ImportSelector además de cargar la clase de la clase especificada. El método de interfaz selectImports, la lista devuelta es la clase que esta anotación de @Import necesita cargar.

La realización concreta de EnableAutoConfigurationImportSelector.

public String[] selectImports(AnnotationMetadata metadata) {
			try {
				AnnotationAttributes attributes = getAttributes(metadata);
				List<String> configurations = getCandidateConfigurations(metadata,
						attributes); // 加载候选的配置类
				// ....略
				return configurations.toArray(new String[configurations.size()]);
			}
			catch (IOException ex) {
				throw new IllegalStateException(ex);
			}
		}

El método selectImports llama al método getCandidateConfigurations para cargar clases candidatas, y este método realmente usa el método loadFactoryNames de SpringFactoriesLoader para cargar varias clases de configuración automática.

		protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
			List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
					getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
			
			return configurations;
		}

El método loadFactoryNames de SpringFactoriesLoader pasa el nombre de clase completo de una clase EnableAutoConfiguration y luego carga principalmente el archivo de recursos de la ruta META-INF / spring.factories, recorre y luego encuentra el valor que coincide con la clave del nombre de clase completo para análisis y procesamiento.

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
			String factoryClassName = factoryClass.getName();
			try {
				Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
						ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // 加载资源文件
				List<String> result = new ArrayList<String>();
				while (urls.hasMoreElements()) {
					URL url = urls.nextElement();
					Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); // 解析成prop
					String factoryClassNames = properties.getProperty(factoryClassName); // 得到factoryClass的属性
					result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
				}
				return result;
			}
			catch (IOException ex) {
			}
		}

        A continuación, encontramos la configuración en este archivo de recursos de META-INF en el paquete jar de spring-boot-autoconfigure.

  org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
        org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
        org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

    2. Explicación de la configuración de algunas clases de configuración predeterminadas

1. Análisis de la configuración automática de la fuente de datos

1) Configuración de anotaciones en la clase

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })

Esta anotación incluye un total de 4 subanotaciones. @Configuration es una clase de configuración de anotación, @ConditionalOnClass indica que debe existir una clase DataSource y @EnableConfigurationProperties es para cargar archivos de configuración.
 Las dos clases introducidas por @Import utilizan el método de especificar directamente la carga de la clase. Uno de los registradores se utiliza para generar un BeanPostProcessor para realizar el propósito de cargar el DataSourceInitializer después de que se inicializa el bean de la fuente de datos. DataSourcePoolMetadataProvidersConfiguration es para proporcionar la configuración de la clase de proveedor de fuente de datos. Como DBCP, TOMCAT, etc. fuente de datos que proporciona clases.

2) Configuración de método y subclase

Método 1) La configuración de dataSourceInitializer es principalmente para inicializar un bean público de DataSourceInitializer La función principal de este bean es asignar la URL
            y la contraseña de la configuración de la clase de configuración a la fuente de datos creada por defecto.

        @Bean
            @ConditionalOnMissingBean // 表示
            public DataSourceInitializer dataSourceInitializer() {
                return new DataSourceInitializer();
            }

Clase interna 2) PooledDataSourceConfiguration Esta clase interna proporciona principalmente clases para varias fuentes de datos, que es proporcionada directamente por @Import.

        @Configuration
            @Conditional(PooledDataSourceCondition.class)
            @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
            @Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
                    DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class,
                    DataSourceConfiguration.Generic.class })
            protected static class PooledDataSourceConfiguration {
            }

     Esta clase interna debe tener la clase de fuente de datos dbcp2, y el spring.datasource.type debe especificarse correctamente o no proporcionarse (matchIfMissing = true) ¿Cómo crear una fuente de datos? La ejecución final de su método de creación es DataSourceBuilder.create () ..... type (). Build (),

   @ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class) 
            @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource", matchIfMissing = true)
            static class Dbcp2 extends DataSourceConfiguration {
        
                @Bean
                @ConfigurationProperties("spring.datasource.dbcp2")
                public org.apache.commons.dbcp2.BasicDataSource dataSource(
                        DataSourceProperties properties) {
                    return createDataSource(properties,
                            org.apache.commons.dbcp2.BasicDataSource.class);
                }
            }

 Finalmente, enumeremos la fuente de datos que creamos nosotros mismos, creada por DataSourceBuilder, y luego usemos el archivo de configuración para dar propiedades

       @ConfigurationProperties(prefix = "spring.datasource.xxx")
            @Bean(destroyMethod = "close", name = "xxx")
            public DataSource dataSource1() {
                return DataSourceBuilder.create().type(dataSourceType).build();
            }

    3. Análisis del código fuente de series condicionales de anotaciones.

    Usamos varias condiciones ConditionalOnClass en la clase de configuración anterior A continuación analizamos el principio de este tipo de anotación a partir de una anotación ConditionalOnClass. En primer lugar, el valor del valor de la anotación condicional es una interfaz que implementa la condición. Si la implementación del método de coincidencias de esta interfaz devuelve verdadero, el bean se cargará y la anotación de ConditionalOnClass realmente envuelve Conditional y la clase que implementa la interfaz de condición es OnClassCondition

1. OnClassCondition
            OnClassCondition hereda SpringBootCondition, SpringBootCondition implementa el método de coincidencia, pero de hecho coincide con llamadas al método de plantilla de subclase  getMatchOutcome . El método es el siguiente (tenga en cuenta que ConditionOutcome es una clase po que envuelve un tipo booleano de marca e información, el método de coincidencia es verdadero y noMatch es falso)

  public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
            ConditionMessage matchMessage = ConditionMessage.empty();
            MultiValueMap<String, Object> onClasses = getAttributes(metadata,ConditionalOnClass.class); // 得到注解元素
            if (onClasses != null) {
                List<String> missing = getMatchingClasses(onClasses, MatchType.MISSING,context);  // 判断缺少的类
                if (!missing.isEmpty()) {
                    return ConditionOutcome
                            .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
                                    .didNotFind("required class", "required classes")
                                    .items(Style.QUOTE, missing));
                }
            }
            //....
            return ConditionOutcome.match(matchMessage);
        }
private List<String> getMatchingClasses(MultiValueMap<String, Object> attributes,
				MatchType matchType, ConditionContext context) {
			List<String> matches = new LinkedList<String>();
			addAll(matches, attributes.get("value"));
			addAll(matches, attributes.get("name"));
			Iterator<String> iterator = matches.iterator();
			while (iterator.hasNext()) {
				if (!matchType.matches(iterator.next(), context)) {
					iterator.remove(); // matchType传入的枚举是不符合,这里!就是符合的移除,剩余的就是没有提供的。
				}
			}
			return matches;
		}
private enum MatchType {
			PRESENT { // 提供了class
				public boolean matches(String className, ConditionContext context) {
					return ClassUtils.isPresent(className, context.getClassLoader());
				}
			},
			MISSING {  // 没有提供calss
				public boolean matches(String className, ConditionContext context) {
					return !ClassUtils.isPresent(className, context.getClassLoader());
				}
			};
			public abstract boolean matches(String className, ConditionContext context);
		}

A partir del código anterior, podemos ver que el juicio de OnClassCondition es muy simple, se basa en el tipo de clase del valor proporcionado por la anotación, y luego el reflejo de class.forName es verdadero, es decir, se proporciona la clase , si no, no se proporciona. Class, y finalmente el método getMatchOutcome devolverá una coincidencia.

    ¡completo!

 

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/113276641
Recomendado
Clasificación