SpringBoot(注解)

1.解析主类—注解

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = "com.wangxing.springboot")
public class Springbootdemo1Application {
    
    
    public static void main(String[] args) {
    
    
  	SpringApplication.run(Springbootdemo1Application.class, args);
    }
}

@SpringBootApplication
源码:

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

1.@SpringBootApplication实际上它是一个复合注解。
虽然它的定义使用了多个注解,但实际上对于 SpringBoot 应用来说,重要的只有三个 注解,这三个注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

所以,如果我们使用上面三个注解作用在主类上整个 SpringBoot 应用依然可以与之前的启动类功能对等。

package com.wangxing.springboot.springbootdemo2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    
     @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public class Springbootdemo2Application {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springbootdemo2Application.class, args);
    }
}

但每次都写三个注解显然过于繁琐,所以写一个 @SpringBootApplication 这样的一站式复合注解显然更方便些。
@SpringBootConfiguration注解 [创建配置类从而代替配置文件]
@SpringBootConfiguration注解中包含了@Configuration。
@Configuration–是 Spring JavaConfig 形式的 Spring IoC 容器的配置类使用的注解。既然 SpringBoot 应用骨子里就是一个 Spring 应用,那么,自然也需要加载某个 IoC 容器的配置,而 SpringBoot 社区推荐使用基于 JavaConfig 的配置形式,所以,很明显,这里的启动类标注了 @Configuration 之后,本身其实也是一个 IoC 容器的配置类。
Spring JavaConfig 形式的 Spring IoC 容器的配置类
配置方式
1.基于 XML 的配置方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->

</beans>

2.基于 JavaConfig 的配置方式

@Configuration
public class MyConfiguration{
// bean定义
} 

任何一个标注了 @Configuration 的 Java 类定义都是一个 JavaConfig 配置类。
注册 bean 定义
1.基于 XML 的注册 bean 定义方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->
<bean id=””  class=””></bean>
</beans>

2.基于JavaConfig 的注册 bean 定义方式。

@Configuration
public class MyConfiguration{
    
    
// bean定义
 	@Bean
    public StudentService  studentService() {
    
    
        return new StudentServiceImpl();
    }
} 

任何一个标注了 @Bean 的方法,其返回值将作为一个 bean 定义注册到 Spring 的 IoC 容器,方法名将默认成为该 bean 定义的 id。
依赖注入的配置方式
1.基于 XML 的依赖注入方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->
<bean id=””  class=””>
<property name="studentDao" ref="studentDao" />
</bean>
</beans>

2.基于JavaConfig 的依赖注入方式。

@Configuration
public class MyConfiguration{
    
    
// bean定义
 	@Bean
    public StudentService  studentService() {
    
    
        return new StudentServiceImpl(studentDao() );
}

	@Bean
    public  StudentDao  studentDao() {
    
    
        return new StudentDaoImpl();
	}
} 

如果一个 bean 的定义依赖其他 bean,则直接调用对应 JavaConfig 类中依赖 bean 的创建方法就可以了。

@SpringBootApplication中包含了@SpringBootConfiguration注解,@SpringBootConfiguration注解中包含了@Configuration注解,@Configuration注解标注在哪一个java类上,那么这个java类就是一个 JavaConfig 配置类,这个JavaConfig 配置类可以代替掉Spring配置文件【applicationContext.xml】,当我们在主类上标注@SpringBootApplication时,意味着主类是一个JavaConfig 配置类,因此我们在创建SptingBoot项目的时候才不用去编写Spring配置文件【applicationContext.xml】。

@EnableAutoConfiguration [“智能”的完成自动配置]
1.@EnableAutoConfiguration 中包含了@Import注解
@Import注解将bean对象注册到javaConfig配置类中。
@EnableAutoConfiguration注解利用@Import注解收集和注册特定模块相关的 bean 定义
源码:

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

@Import(AutoConfigurationImportSelector.class).借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。
在这里插入图片描述

借助于 Spring 框架原有的一个工具类:SpringFactoriesLoader 的支持,@EnableAutoConfiguration 可以“智能”地自动配置功效才得以大功告成!

在**@Import(AutoConfigurationImportSelector.class)**的AutoConfigurationImportSelector.class中的

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in METAINF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

SpringFactoriesLoader类

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;
}
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 =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
    
    
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }
      // Replace all lists with unmodifiable lists containing unique elements
      result.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;
}


public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
spring-boot-autoconfigure-2.4.0.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
...........

SpringBoot核心自动配置原理
1).SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
主类添加了@SpringBootConfiguration注解,@SpringBootConfiguration注解包含了@EnableAutoConfiguration注解
2).@EnableAutoConfiguration注解利用@Import(AutoConfigurationImportSelector.class)中的参数EnableAutoConfigurationImporttSelector类给SpringIOC容器中导入一些组件;
通过EnableAutoConfigurationImporttSelector类中的selectImports()这个方法中找到AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);找到getAutoConfigurationEntry()方法,
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
getCandidateConfigurations()得到
List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());得到SpringFactoriesLoader类,通过SpringFactoriesLoader类中的loadFactoryNames方法找到loadSpringFactories()有一句话Properties properties = PropertiesLoaderUtils.loadProperties(resource);
通过这句得到public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;
扫描所有jar包类路径下,META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中,将类路径下META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中
3).每一个自动配置类进行自动配置功能;
4).根据当前不同的条件判断,决定这个配置类是否生效?一旦这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的.
以HttpEncodingAutoConfiguration (Http编码自动配置)为例解释自动配置原理;
//表示这是一个配置类没以前编写的配置文件一样,也可以给容器中添加组件

@Configuration(proxyBeanMethods = false)
//启动指定类的ConfigurationProperties功能,将配置文件中对应的值和HttpEncodingAutoConfigurationProperties绑定起来;
@EnableConfigurationProperties(ServerProperties.class)
//Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效(即判断当前应用是否是web应用)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码处理的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

   private final Encoding properties;

   public HttpEncodingAutoConfiguration(ServerProperties properties) {
      this.properties = properties.getServlet().getEncoding();
   }

   @Bean
   @ConditionalOnMissingBean
   public CharacterEncodingFilter characterEncodingFilter() {
      CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
      filter.setEncoding(this.properties.getCharset().name());
      filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
      filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
      return filter;
   }

   @Bean
   public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
      return new LocaleCharsetMappingsCustomizer(this.properties);
   }

   static class LocaleCharsetMappingsCustomizer
         implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

      private final Encoding properties;

      LocaleCharsetMappingsCustomizer(Encoding properties) {
         this.properties = properties;
      }

      @Override
      public void customize(ConfigurableServletWebServerFactory factory) {
         if (this.properties.getMapping() != null) {
            factory.setLocaleCharsetMappings(this.properties.getMapping());
         }
      }

      @Override
      public int getOrder() {
         return 0;
      }
   }
}

5.所有在配置文件中能配置的属性都是在xxxxPropertites类中封装着,配置文件能配置什么就可以参照某个功能对应的这个属性类;
@ComponentScan
【配置自动扫描包,就可以让类上带有@Component 和 @Repository,@Service注解的java类创建出对象】
@ComponentScan 对应 XML 配置形式中的 <context:component-scan> 元素,用于配合一些元信息注解,比如 @Component 和 @Repository 等,将标注了这些元信息注解的 bean 定义类批量采集到 Spring 的 IoC 容器中。我们可以通过 basePackages 等属性来细粒度地定制 @ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现会从声明 @ComponentScan 所在类的 package 进行扫描。
如果我们当前应用没有任何 bean 定义需要通过 @ComponentScan 加载到当前 SpringBoot 应用对应使用的 IoC 容器,那么,除去 @ComponentScan 的声明,当前 SpringBoot 应用依然可以照常运行,功能对等。

SpringBoot自动化配置关键组件
在这里插入图片描述
mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean

猜你喜欢

转载自blog.csdn.net/guoguo0717/article/details/110622303
今日推荐