springboot项目创建笔记16 之《@SpringBootApplication注解解析》

一、注解的使用
1、使用非常简单,加在自定义的类上面,右键 - Run As - Java Application启动springboot

@SpringBootApplication
public class MybootApplication {

	public static void main(String[] args) {
		SpringApplication.run(MybootApplication.class, args);
	}

}

二、注解的组成
1、@SpringBootApplication是一个复合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration、@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 {
...
}

三、注解的作用
1、说明
1)@Target(ElementType.TYPE):注解的目标位置,接口、类、枚举
2)@Retention(RetentionPolicy.RUNTIME):注解会在class字节码文件中存在,在运行时可以通过反射获取
3)@Documented:用于生成javadoc,默认情况下,javadoc是不包括注解的。但如果声明注解时指定了@Documented,则它会被javadoc之类的工具处理,所以注解类型信息也会被包含在生成的文档中
4)@Inherited注解:在类继承关系中,如果子类要继承父类的注解,那么该注解必须被@Inherited修饰

2、核心注解
这三个注解作用就是把bean注册到spring ioc容器
1)@SpringBootConfiguration注解:通过@Configuration与@Bean结合,注册到spring ioc容器
2)@EnableAutoConfiguration注解:通过spring.factories的配置,来实现bean注册到spring ioc容器
3)@ComponentScan注解:通过范围扫描的方式,扫描特定注解类,将其注册到spring ioc容器

四、@SpringBootConfiguration注解
1、@SpringBootConfiguration实际就等同于@Configuration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

2、什么是@Configuration注解
从spring3.0开始,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含一个或多个@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化spring容器

3、@Configuration使用
标注在类上,@Configuration等价于spring的xml配置文件中的<Beans></Beans>

4、我们可以用
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
代替
@SpringBootConfiguration
加在主类上,也可以启动springboot

5、@Bean注解
@Bean注解在方法上,如果未通过@Bean指定Bean的名称,则方法名作为默认的Bean对象名

6、AnnotationConfigApplicationContext
这个类和ClassPathXmlApplicationContext功能一样,用来初始化spring容器。一个是从xml文件,一个是从配置类

五、@ComponentScan注解

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

1、excludeFilters:过滤不需要的扫描类型

2、@Filter:过滤注解
1)FilterType.CUSTOM:过滤类型为自定义规则,即指定特定的class
2)class:过滤指定的class,即剔除TypeExcludeFilter.class,AutoConfigurationExcludeFilter.class

3、默认到@SpringBootApplication注解的所在包及其下级包,都会将class扫描到并装入spring ioc容器

4、如果你自定义一个bean,不在@SpringBootApplication注解的所在包及其下级包,都必须手动加上@ComponentScan注解并指定那个bean所在的包

5、作用
告诉spring去扫描@ComponentScan指定包下所有的注解类(@Service、@Controller、@Component),然后将扫描到的类加入spring ioc容器

六、@EnableAutoConfiguration注解
1、源码

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

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

2、@Import(AutoConfigurationImportSelector.class)说明
@Import注解:将指定的类实例注入到spring ioc容器中

3、@Import的例子
采用@Import来,将UserBean注入到spring ioc容器中
1)建立包test.imports
2)添加UserBean.java

package test.imports;

public class UserBean {

}

3)添加UserService.java

package test.imports;

import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;

@Component
@Import({UserBean.class})
public class UserService {

}

4)添加TestImport.java

package test.imports;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 这里只初始化spring ioc容器,并没有启动springboot
 * @author User
 *
 */
public class TestImport {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserService.class);
		UserService userService = context.getBean(UserService.class);
		UserBean userBean = context.getBean(UserBean.class);
		
		System.out.println(userService.toString());
		System.out.println(userBean.toString());
		
		context.close();
	}
}

5)执行结果
可以看到取到了这两个bean

test.imports.UserService@11a9e7c8
test.imports.UserBean@3901d134

4、ImportSelector接口有什么作用
AutoConfigurationImportSelector类继承了6个核心接口
1)代码

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...		
}

2)DeferredImportSelector继承ImportSelector接口
3)ImportSelector接口定义了一个selectImports方法,作用是收集需要的class,将class注册到spring ioc容器
4)ImportSelector接口一般和@Import一起使用
5)@Import引入ImportSelector接口的实现类后,会把实现类中返回的String[]数组里的class注入到spring ioc容器中

七、模仿@EnableAutoConfiguration,写一个@Enable*开关注解类
在springboot里有大量的开关注解类,底层是用ImportSelector接口来实现,@Enable*的作用就是开启一项功能,起到了开关的作用
@Enable*开关注解类是帮你引入相关的类,然后可以在加了注解的类里面使用它的功能
1、建立包test.selector

2、添加实体类
建立类RoleBean.java

package test.selector;

public class RoleBean {

}

建立类UserBean.java

package test.selector;

public class UserBean {

}

3、建立UserImportSelector.java(实现了ImportSelector接口的类)

package test.selector;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class UserImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[] {"test.selector.RoleBean","test.selector.UserBean"};
	}

}

4、建立注解,EnableUserConfig.java

package test.selector;


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.Import;

/**
 * enable注解
 * @author User
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(UserImportSelector.class)
public @interface EnableUserConfig {

}

5、建立配置类,UserConfig.java,添加了@EnableUserConfig注解

package test.selector;

/**
 * 配置类
 * @author User
 *
 */
@EnableUserConfig
public class UserConfig {

}

6、建立测试类TestSelector.java

package test.selector;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestSelector {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
		UserBean userBean = context.getBean(UserBean.class);
		RoleBean roleBean = context.getBean(RoleBean.class);
		
		System.out.println(userBean.toString());
		System.out.println(roleBean.toString());
		
		context.close();
	}
}

7、执行结果
取到了这两个类

test.selector.UserBean@7c0c77c7
test.selector.RoleBean@7adda9cc

八、EnableAutoConfiguration注解分析
1、@Import(AutoConfigurationImportSelector.class)
会加载一个配置文件

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

这个配置文件是springboot的解耦扩展机制,这种机制实际上仿照了java的SPI扩展机制来实现的
常用于加载第三方非spring管理的类,非扫描路径的类

2、应用
在这个文件里面,配置你自己的实现类名称,spring会读取META-INF/spring.factories文件内容,并实例化进IOC容器
在spring-boot-autoconfigure-2.1.8.RELEASE.jar/META-INF/spring.factories文件内找到EnableAutoConfiguration注解名

# 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,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
...

包括了好几十个类,以上目的就是把spring.factories里面的class加载到spring IOC容器中

3、AutoConfigurationImportSelector分析
1)找到public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)
2)进入getAutoConfigurationEntry方法,再进入getCandidateConfigurations方法
3)核心方法
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
4)加载配置文件
Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

九、自定义配置spring.factories文件
1、新建一个@Configuration配置类和实体类
包名放在,不在springboot的扫描路径里面,建立包test.factories
FactoriesTestBean.java

package test.factories;

public class FactoriesTestBean {

	public static String Info() {
		return "hello";
	}
}

FactoriesTestConfig.java

package test.factories;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FactoriesTestConfig {

	@Bean
	public FactoriesTestBean factoriesTestBean() {
		return new FactoriesTestBean();
	}
}

2、在src/main/resource目录下的META-INF创建spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=test.factories.FactoriesTestConfig

3、建立测试文件FactoriesTestApplication.java

package test.factories;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ServletComponentScan(basePackages = {"com.example"})  // 指定扫描路径
@ComponentScan(basePackages = {"com.example"})  // 指定扫描路径
@SpringBootApplication
public class FactoriesTestApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext ctx = SpringApplication.run(FactoriesTestApplication.class, args);
		FactoriesTestBean test = ctx.getBean(FactoriesTestBean.class);
		System.out.println(test.Info());
	}

}

4、执行结果
正常执行,打印hello
如果没有spring.factroies则报错,Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'test.factories.FactoriesTestBean' available

参考资料:
https://blog.csdn.net/truelove12358/article/details/107697845

注:最新代码上传至https://github.com/csj50/myboot
 

猜你喜欢

转载自blog.csdn.net/csj50/article/details/108576572