SpringBoot的主配置
这里先看自动装配,extends SpringBootServletInitializer这块下一步再探讨
@SpringBootApplication注解标注的类为SpringBoot的主配置类,SpringBoot就会运行这个类的main方法来启动SpringBoot项目 main方法运行了一个run()方法,run方法传入自己
@SpringBootApplication
public class StarrySpringBootApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(StarrySpringBootApplication.class, args);
}
@Override//为了打包springboot项目
protected SpringApplicationBuilder configure(
SpringApplicationBuilder builder) {
return builder.sources(this.getClass());
}
}
spring的run方法源码
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
是一个组合注解,核心是三个Annotation组成
SpringBootApplication源码
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*/
@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 {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
这个注解实际上就是代表了一个配置类,表名该类是一个Spring的配置类,相当于一个beans.xml文件 具体可查看网上的文献:基于注解开发初探 https://www.jianshu.com/p/81880251a700
SpringBootConfiguration源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@ComponentScan的功能其实就是自动扫描并加载符合条件的组件或bean定义,最终将这些bean定义加载到容器中
用法 // 1、只包含有Controller注解bean @ComponentScan(value="com.qiu",includeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),} ,useDefaultFilters = false) // 2、排除含有controller注解的bean @ComponentScan(value="com.qiu",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) })
ComponentScan源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//可以重复使用
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
//使用包名
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
//使用具体类型名称
Class<?>[] basePackageClasses() default {};
....其他属性
//指定扫描的时候只需要包含哪些组件
Filter[] includeFilters() default {};
//指定扫描的时候按照什么规则排除那些组件
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
//过滤规则类
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
//FilterType定义按什么过滤类型来进行过滤,
/**
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则
*/
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
在spring中有关于@Enablexxx的注解是开启某一项功能的注解。其原理是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到Ioc容器
@EnableAsync 开启异步 @EnableScheduling 表示开启spring的定时任务
@EnableAutoConfiguration==代表开启springboot的自动装配==
EnableAutoConfiguration源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 按类型排序不需要自动装配的类
Class<?>[] exclude() default {};
// 按名称排除不需要自动装配的类
String[] excludeName() default {};
}
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)
先来看看@AutoConfigurationPackage
这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class
AutoConfigurationPackage源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
再来看Registrar类,这是一个内部静态类
==就是通过这个方法获取扫描的包路径==,这里就需要我们做一个小小的debug来验证下,请接着往下看
首先是Registrar类源码
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* 存储注册导入过来的基本包
* configuration.
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
在第10行打个断点,开始debug
如果不能调试 File->Settings-> Build, Execution, Deployment ->Debugger->Stepping 把
Do not step into the classes
下的java.*,javac.*
取消勾选,然后保存即可
从下图可以看出metadata就是我们的springboot的主启动类,getPackageNames获取到这个类的包名,
==说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意==
接着我们看@Import(AutoConfigurationImportSelector.class)
@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector
AutoConfigurationImportSelector源码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 这个地方才可以断点,上面的selectImports设置断点是没用的
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
...
}
再次debug看看:
断点需要打在==getAutoConfigurationEntry方法上==,selectImports方法断点不会生效
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件:
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。
那他是如何获取到这些配置类的呢,看看上面这个方法:
会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:
看看
getSpringFactoriesLoaderFactoryClass()是啥
getBeanClassLoader()是啥
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了
// 1、原型案例
@Scope("prototype")
@Bean()
public User user() {
return new User();
}
// 2、单例 默认就是单例
@Scope("singleton")
@Lazy注解用于标识bean是否需要延迟加载,默认是true。 没加注解主要容器启动就会实例化bean 加上@Lazy注解则第一次调用的时候才会加载
Conditional源码
//作用于方法和类
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
//参数是:继承于Condition的类数组
Class<? extends Condition>[] value();
}
//condition接口,自定义的condition类需要实现该接口
public interface Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
使用案例
1、判断是否是linux系统
public class LinuxCondition implements Condition{
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
{
//1.能获取到ioc使用的Beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//3.获取到当前环境信息
Environment environment = context.getEnvironment();
//4.获取到bean定义的注册类信息
BeanDefinitionRegistry registry = context.getRegistry();
//=============这里主要是为了获取当前系统的环境变脸
String property=environment.getProperty("os.name");
if (property.contains("linux")) {
return true;//放行
}
return false;
}
}
2、判断是否是windows系统
public class WindowsCondition implements Condition{
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
{
Environment environment = context.getEnvironment();
String property=environment.getProperty("os.name");
if (property.contains("Windows")) {
return true;//放行
}
return false;
}
}
3、@Conditional:按照一定的逻辑进行判断,满足条件给容器注入bean
public class ConditionalConfig {
//注入windows
@Conditional(value= {WindowsCondition.class})
@Bean
public User user1() {
User user=new User();
user.setUserName("bill");
return user;
}
//注入linux
@Conditional(value= {LinuxCondition.class})
@Bean
public User user2() {
User user=new User();
user.setUserName("linus");
return user;
}
4、idea中更换操作系统方法:-Dos.name=linux
介绍完了条件注解在spring中的使用,在Springboot中条件注解的分类:
Class conditions:@ConditionalOnClass和@ConditionalOnMissingClass,表示类是否在类路径下的条件注解 Bean conditions:@ConditionalOnBean和@ConditionalOnMissingBean,表示Bean是否被定义的条件注解 Property conditions:@ConditionalOnProperty,使用prefix和name属性用来表示是否有值,默认的话,只要该属性存在值,且不为false,即可匹配 Resource conditions:@ConditionalOnResource表示是否存在指定的resouce的条件注解 Web application conditions:@ConditionalOnWebApplication和@ConditionalOnNotWebApplication,当项目是web项目,或者不是web项目的条件注解 SpEL expression conditions:@ConditionalOnExpression,根据SPEL表达式执行结果作为条件
版权属于:dingzhenhua
本文链接:https://cloud.tencent.com/developer/article/2019361
转载时须注明出处及本声明
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有