文章目录
一、java注解的使用
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的 一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方 法参数等的前面,用来对这些元素进行说明,注释。
作用:本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解 来替代,这样就使得编程更加简洁,代码更加清晰。
原理:本质上就是一个继承自java.lang.annotation.Annotation
的接口。
**接口设置属性:**简单来说,就是java通过动态代理的方式生成了一个实现了注解的实例(对于当前的实体来说,例如类、方法、属性域等,这个代理对象是单例的),然后对该代理实例的属 性赋值,这样就可以在程序运行时(如果将注解设置为运行时可见的话)通过反射获取到注解的配 置信息
1.1注解分类
1)按照运行机制划分:
【源码注解→编译时注解→运行时注解】
源码注解:只在源码中存在,编译成. c lass文件就不存在了。
编译时注解:在源码和. class文件中都存在。像前面的@Override、@Deprecated、 @SuppressWarnings,他们都属于编译时注解。
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的这样 一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。
2)按照来源划分:
【来自JDK的注解——来自第三方的注解——自定义注解】
3)元注解: 元注解是给注解进行注解,可以理解为注解的注解就是元注解
1.2注解详解
首先我们要明确这不是一个接口,它是使用@intterface关键字定义的一个注解。
然后我们看下面的几个方法,String desc();虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量 (成员以无参无异常的方式声明),int age() default 18 ;(成员变量可以用deault指定一个默认值的)。
最后我们要知道:
①.成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation ,Enumeration等。
②.如果注解只有一个成员,则成员名必须取名为v a l u e (),在使用时可以忽略成员名和赋值号(=)。
③.注解类可以没有成员,没有成员的注解称为标识注解
1.3元注解(负责注解其它注解)
1) @Target是这个注解的作用域,ElementType.METHOD是这个注解的作用域的列表, METHOD是方法声明,除此之外,还有:CONSTRUCTOR(构造方法声明),FIELD(字段声 明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声 明),PARAMETER(参数声明),TYPE(类接口)
2)@Retention是它的生命周期,前面不是说注解按照运行机制有一个分类嘛,RUNTIME就是在 运行时存在,可以通过反射读取。除此之外,还有:SOURCE(只在源码显示,编译时丢 弃),CLASS(编译时记录到class中,运行时忽略),RUNTIME(运行时存在,可以通过反射读取)
3) @Inherited是一个标识性的元注解,它允许子注解继承它。
4) @Documented,生成javadoc时会包含注解
二、Spring注解
2.1Spring常用注入注解
注解注入就是通过注解来实现注入。Spring和注入相关的常见注解有@Autowired、@Resource、 @Qualifier、@Service、@Control ler、@Repositor y、@Component。
1)@Autowired是自动注入,默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的 Bean时,Spring将其注入@Autowired标注的变量中
2)@Resour ce用来指定名称注入
3)@Qualifier和Autowired配合使用,指定bean的名称。如果容器中有一个以上匹配的Bean,则可以通过 @Qualifier注解限定Bean的名称
4)@Service,@Control ler,@Repositor y分别标记类是Service层类,Control ler层类,数据访问层的类, spring扫描注解配置时,会标记这些类要生成bean
5)@Component是一种泛指,标记类是组件,spring扫描注解配置时,会标记这些类要生成bean。
注:上面的@Autowired和@Resource是用来修饰字段,构造函数,或者设置方法,并做注入的。 而@Service,@Control ler,@Repositor y,@Component则是所有受Spring 管理组件的通用形式,在组 件不明确具体类别时使用
2.2spring常用配置注解
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一 个或多个被@Bean注解的方法
- @EnableXXX注解: 配合@Configuration使用,包括 @EnableAsync, @EnableScheduling, @EnableTransac tionManagement, @EnableAspectJAutoProxy, @EnableWebMvc。
2)@PropertySources注解,配合@Configuration加载外部配置文件,使用@Value加载配置项。
3)@Import注解,可以组合多个配置类。
4)@Lazy懒加载注解,与@Configuration和@Bean配合使用。Spring默认不是懒加载。
5)@Profile逻辑组配置,用于指定配置类加载的逻辑组(开发/生产环境),只有在组内的配置类才会被加载
2.3spring重要注解
Spring Boot的强大之处在于使用了Spring 4框架的新特性:@Conditional注释,此注释使得只 有在特定条件满足时才启用一些配置。
除此之外,拓展注解还有:
@ConditionalOnBean(仅仅在当前一个Bean上下文中存在某个对象时,才会实例化)
@ConditionalOnClass(某个c las s位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个c lass类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnWebAppl ication(是web应用时)
三、springboot
3.1Starter启动器
SpringBoot的一大优势就是Starter,由于SpringBoot有很多开箱即用的Starter依赖,使得我们 开发变得简单,我们不需要过多的关注框架的配置。
可以认为starter是一种服务——使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要 具体的配置信息,由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入bean
3.2SpringBootApplication
实际是@SpringBootConfiguration,@EnableAutoConfiguration以及@ComponentScan的 组合。声明是配置类、自动注入配置、自动扫描包
- @SpringBootConfiguration和@Configuration有相同的作用,配置该注解的类能够以 JavaConfig的方式完成配置,不再使用XML配置
- @EnableAutoConfiguration注释告诉由@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)组成功能是基于已添加的依赖项,猜测你要如何使用Spring的相关配置。
- @ComponentScan注解的功能是自动扫描,默认扫描所在类的同级类和同级目录下的所有类。 相当于Spring XML配置文件中的 @Target、@Retention、@Documented、@Inherited是java元注解(注解其他注解) 表明注解的使用位置、保留时间(编译策略)、文档化使用和允许子类继承父类等
3.3启动过程
构造一个SpringApplication的实例,然后运行它的run方法,而run方法会返回一个 ConfigurableAppl icationContext 创建SpringAppl ication对象的过程(initialize方法)
- 保存主配置类、确定web应用类型
- 从类路径下找到META-INF/ spring.factories配置的所有ApplicationContextInitializer和 Appl icationListener
- 从多个配置类中找到有main方法的主配置类
run方法的执行过程
1. 封装命令行参数、准备环境,创建ApplicationContext
2. ioc容器初始化,扫描创建组件,回调后返回容器
执行过程逻辑
//创建了SpringApplication实例 执行run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
//创建实例的方法
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//加载主配置 确定web应用类型等等
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从SpringFactoriesLoader类中的loadSpringFactories方法
//加载文件META-INF/spring.factories
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//查找主程序的main函数
this.mainApplicationClass = deduceMainApplicationClass();
}
//run方法的执行过程
//加载一系列的配置 准备一系列的环境 打印一系列的日志 开始创建容器启动监听
public ConfigurableApplicationContext run(String... args) {
//计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建应用上下文/容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//加载配置
configureHeadlessProperty();
//获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//创建默认参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建容器
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
//计时结束 启动完成
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
...
try {
//启动监听器
listeners.running(context);
}
...
//返回容器
return context;
}
3.4自动配置原理
@EnableAutoConfiguration
//注入自动配置//有两大注解组成
@AutoConfigurationPackage 查找并注册自动配置包
@Import(AutoConfigurationImportSelector.class) 引入资源,自动配置的包选择器相关的逻辑都在spring-boot-antoconfigure.jar里
a)@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class)
//引入资源 自动配置的包注册器
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
//注册bean定义器 包名是主程序入口的所在目录(默认只扫描入口类的当前目录和子目录)
b) @Import(AutoConfigurationImportSelector.class)
-1- AutoConfigurationImportSelector类有一个selectImports方法
-2- selectImports方法执行的逻辑: 加载元数据和具体的配置条目
AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
-3- getAutoConfigurationEntry调用了getCandidateConfigurations方法,去加载条件配置
-4- getCandidateConfigurations方法的逻辑是通过spring工厂加载器 获取名称
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
-5- loadFactoryNames方法的逻辑
加载文件 META-INF/spring.factories 里面的内容
XXXAutoConfiguration命名的一系列配置类 如ThymeleafAutoConfiguration
c) 以ThymeleafAutoConfiguration为例 查看选择注解的机制
四大注解
@Configuration
//声明这是配置类 xml
@EnableConfigurationProperties(ThymeleafProperties.class)
//加载ThymeleafProperties这个配置文件类
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
//控制条件注解 当存在TemplateMode和SpringTemplateEngine的时候 才会执行类的逻辑
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
//控制执行顺序 在WebMvcAutoConfiguration和WebFluxAutoConfiguration执行之后再执行
ThymeleafAutoConfiguration注解的执行逻辑:
1 先执行AutoConfigureAfter里面的属性类
2 判断是否存在ConditionalOnClass里面的class文件 (取决于pom是否引入依赖)
<!--首先引入starter (封装的启动器)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 启动器引入具体的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<!--具体的依赖就存在需要的class -->
<!--thymeleaf-spring5 jar包中存在SpringTemplateEngine.class -->
3 判断需要执行后 ,开始加载ThymeleafProperties配置文件类
@ConfigurationProperties(prefix = "spring.thymeleaf")
//这是一个配置文件类 并且寻找前缀为spring.thymeleaf开头的配置
spring.thymeleaf.prefix 默认值classpath:/templates/
spring.thymeleaf.suffix 默认值html
spring.thymeleaf.mode 默认值HTML
spring.thymeleaf.encoding 默认值UTF-8
4 执行类里的逻辑
//注入bean 是spring资源模板解析器
@Bean
public SpringResourceTemplateResolver defaultTemplateResolver() {
//是通过spring-thymeleaf这个粘合器,调用thymeleaf里面的核心逻辑
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;
}