后端——框架——容器框架——spring_core——注解

  注解分为三类,容器相关,bean相关,null相关。

1、容器

采用注解方式配置IOC容器,有三种方式

纯注解形式,提供至少一个配置类(@Configuration标注的类)

纯注解形式,提供至少一个Bean(@Component等标注的类)

混合方式,提供配置类,使用@ImportResource引入Spring的配置文件。

1.1   创建

容器创建,实质是创建applicationContext对象,使用注解时,创建的对象为AnnotationConfigApplicationContext对象,它的创建方式有以下几种。

单个配置类:创建AnnotationConfig对象,参数为XXApp.class

public ApplicationContext singleConfig() {
	ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
	return ac;
}

  多个配置类:有三种方式,第一种使用@Import注解,第二种方式多个XXApp.class参数,第三种方式调用register方法。

// 方式一
@Configuration
@Import({AppConfig1.class,AppConfig2.class})
public class AppConfig {}
// 方式二
ApplicationContext ac = new  AnnotationConfigApplicationContext(AppConfig.class,AppConfig2.class);
//  方式三,此时必须是AnnotationConfigApplicationContext对象,
//  ApplicationContext接口中无这些方法
ac.register(AppConfig1.class);
ac.register(AppConfig.class);

  纯注解形式:多个Component与Configuration注解相同,只不过IOC容器中只存在初始化时传入的几个Bean。

  混合方式:使用@ImportResource注解

@Configuration
@ImportResource({"classpath:spring/application.xml"})
public class AppConfig {}

1.2     扫描

1.2.1    路径

指定扫描路径的方式有两种

使用@ComponentScan注解,通过设置basePackages指定bean所在包的名称,多个包名时使用逗号分隔,包名可以是正则表达,SpringEL表达式。

调用context.scan方法,参数为包名。使用频率很低。

1.2.2  过滤条件

1.2.2.1     概念

添加过滤条件有两个含义,第一类是给配置类上添加过滤条件,第二类是给具体的某个bean上添加过滤条件。

第一类是给@Component-scan设置include-filter和exclude-filter属性,它们的值可以是以下五类:

  1. Annotation:根据类上的注解进行过滤,默认值
  2. Assignable:根据类的class或类名进行过滤
  3. Aspectj:根据AspectJ的类型表达式对类名进行过滤
  4. Regex:根据正则表达式对类型进行过滤
  5. Custom:根据TypeFilter的实现类进行过滤。

  第二类是给具体的bean上添加过滤条件,当满足条件时注入bean,不满足时跳过。

  它是给具体的bean上添加@Conditional注解,并设置value属性,每一个value属性的值都是Condition接口的实现类。Condition接口只有一个方法matches,当满足条件时,该方法返回true,不满足条件时,返回false。

  要添加自定义过滤条件,至少需要两个步骤

  1. 第一步,编写XXConditionImpl,实现Condition接口,
  2. 第二步,在类上添加@Conditional注解,并设置value的值为XXConditionImpl.class

springBoot中存在大量该注解,@Conditional和Condition都有自己的类结构。例如@ConditionalOnClass表示某个类是否在IOC容器中,当存在时满足条件,不存在时不满足条件。

想要深入了解,可以查看DataSourceAutoConfig的源码。

1.2.2.2     componentScan

描述

它用于指定容器注入bean时的一些配置。

属性

basePackage

说明:指定扫描包的根路径,存在多个时,使用逗号分隔

类型:字符串,包名或者包名的正则表达式

是否必填:否,不指定时默认为classpath

属性

Include-filter

说明:添加包含的过滤条件,过滤条件有五种类型,

  1. Annotation:根据类上的注解进行过滤,默认值
  2. Assignable:根据类或者类名进行过滤
  3. Aspectj:根据AspectJ的类型表达式对类名进行过滤
  4. Regex:根据正则表达式对类型进行过滤
  5. Custom:根据TypeFilter的实现类进行过滤。

类型:不同类型对应不同的数值类型

是否必填:否,不指定时无过滤器

属性

enclude-filter

说明:添加排除的过滤条件,过滤条件有五种类型,

  1. Annotation:根据类上的注解进行过滤,默认值
  2. Assignable:根据类或者类名进行过滤
  3. Aspectj:根据AspectJ的类型表达式对类名进行过滤
  4. Regex:根据正则表达式对类型进行过滤
  5. Custom:根据TypeFilter的实现类进行过滤。

类型:不同类型对应不同的数值类型

是否必填:否,不指定时无过滤器

属性

lazyInit

说明:指定bean加载时是否延迟加载

类型:布尔值

是否必填:否,默认值为false。

属性

nameGenerator

说明:指定bean注入时,名称的生成

类型:NameGenerator的实现类

是否必填:否。

位置

配置类上


1.2.3   添加索引

添加索引是指在注入bean时,给bean添加索引,在获取bean时会变快,提高性能。

添加索引的步骤有两步:

  1. 添加spring-context-indexer依赖。
  2. 在IOC容器中注入CandidateComponentsIndexer类。

如果想禁用此功能,又不想移除依赖,可以设置spring.index.ignore属性值为true。

1.2.4      profile

Profile本质也是一种过滤条件,过滤条件是基于”环境”的,例如生产环境,测试环境。

使用@Profile注解的步骤有以下两步:

第一步:在bean上添加@profile注解,当激活的环境与设置的环境相等时,注入bean。方式有以下三种

  1. 在类上添加,在方法上添加,类上标注有@Component等,方法上有@Bean。
  2. 创建@Profile的元注解,在类上,方法上添加元注解。例如创建@Production注解
  3. 在配置文件中,给beans标签设置profile属性,相当于全局;或给单个bean标签设置profile属性。

第二步:激活profile。方式有四种

  1. 设置spring.profiles.active属性。Java argument形式,配置文件形式等等
  2. 调用context.getEnvironment().setActiveProfiles方法,参数为激活的环境。
  3. 指定默认的profile。
  4. 如果存在spring-test依赖,在配置类上添加@ActiveProfiles

设置默认profile的方式也有三种。

  1. 在配置类上添加@Profile注解
  2. 调用context.getEnvironment().setDefaultProfiles方法
  3. 设置spring.profiles.default属性。

Profile的值可以是单个环境,多个环境,或者是环境的逻辑表达式。

单个环境,@Profile(“envName”);

多个环境,@Profile({“envName1”,”envName2”}),它们是逻辑或关系,只要其中一个环境激活即加载。

逻辑表达式,与(&),或(|),非(!)。例如@Profile(“!envName”)。

2、bean

  bean的注解分为三类,注入bean。获取bean。配置bean,例如属性,作用域,生命周期等。

2.1     注入

当存在配置文件,且没有配置类时(@Application标注的类),注解生效需要配置context:annotation-config,也可以配置context:component-scan,它基于annotation-config的基础上添加base-package,include,exclude等属性,用于添加扫描包的条件。

当存在配置类时,需要在其上添加ComponentScan注解,配置base-package,include,exclude属性。含义与context:component-scan标签相同。

当注解存在name属性时,容器会将name属性值作为bean的名称,当不存在name属性时,会将类名的首字母小写,作为bean的名称,需要特别注意的是类名不是类全名,当不同包下存在相同类名时,会存在同名的bean。

Spring支持自定义bean名称的生成策略,两个步骤,第一步编写MyNameGenerator实现NameGenerator接口,第二步设置该类为Component-scan注解的name-generator属性值。

2.1.1  configuration

描述

它用于创建AnnoationConfigApplicationContext,它属于配置类。地位等价于applicationContext.xml

位置

类上

  2.1.2  component

描述

它代表一个bean,当IOC容器扫描包时,会把标有该注解的类添加到容器中

位置

类上

  2.1.3  repository

描述

它是Component注解的元注解,功能与Component相等,含义缩小,只表示MVC层中的Model层,即DAO数据层

位置

类上

2.1.4  service

描述

它是Component注解的元注解,功能与Component相等,含义缩小,只表示服务层

位置

类上

2.1.5   controller

描述

它是Component注解的元注解,功能与Component相等,含义缩小,只表示控制器层

位置

类上

2.1.6   restController

描述

它是Controller与ResponseBody的组合注解,表示它是一个Controller,并且响应类型为数据流。

位置

类上

  2.1.7   bean

描述

它代表一个bean,标注在方法上,将该方法的返回值注入到IOC容器中。

属性

name:

说明:指定bean的名称,默认值为方法的名称,当指定多个name值时,本质是在设置bean的别名。

类型:字符串,

是否必填:否

属性

Init-method:

说明:对应bean标签的init-method属性。

类型:字符串,方法名称

是否必填:否,默认值为init,

属性

destory-method:

说明:对应bean标签的destory-method属性。

类型:字符串,方法名称

是否必填:否,默认值为close,shutdown

位置

方法上(居多),注解上

  定义在@Configuration下的@Bean会通过CGLIB方式调用方法,并获取相关的原信息,所以方法不能使用private,final修饰。定义在@Component下的@Bean不会。

2.2    获取

2.2.1   required

描述

在设置bean属性时,Required注解表示该属性为必备属性,如果在创建Bean时,没有对应的属性值,会抛出异常。构造器依赖也会起到同样的效果。

位置

属性上,或者属性的set方法上

   示例如下:

public class User {
	// 用户的名称,必填属性
	@Required
	private String name;
}

2.2.2   autoWired

描述

它对应bean标签的autowire属性,在注入bean的依赖时,从IOC容器中获取,获取方式有四种NONE,byName,byType,constructor。默认的方式为byName,如果查找不到,根据byType方式。

当属性为容器类型时,数组或集合时,会把找到的所有依赖都放入容器中

属性

required:

说明:当注入beanA的依赖beanB时,用于指定该依赖是否是必须的,当为true时,为必须的,此时容器中没有依赖的beanB,则beanA创建失败,并报错。

类型:布尔值,true或者false

是否必填:否,默认值为true

位置

依赖的任何地方,属性上,构造器参数,方法参数

  示例如下:

public class User {
// 自动注入Address类,当IOC容器中没有Address时,User创建失败并报错
	@Autowired(required=true)
	private Address homeAddress;
}

2.2.3   order

描述

当beanA依赖beanB的容器时,IOC容器在查找beanB时,如果beanB有多个,Order注解用来决定在容器中的顺序

位置

类上

  示例如下:

public class User {
//  IOC中存在多个Address时,IOC会根据Order注解决定Address在List<Address>
//  的顺序
	@Autowired(required=true)
	private List<Address> homeAddress;
}
//  Address类
@Order(value=1)
public class Address {
}

  它一般发生在根据byName无法找到依赖时,根据byType查找时,存在多个相同的类名,例如上例中不存在id为homeAddress的bean,存在多个Address类,com.test.Address,com.bean.Address。 

2.2.4   primary

描述

当beanA依赖beanB的容器时,IOC容器在查找beanB时,如果beanB有多个,Primary用来指定优先使用该bean。 对应bean标签的primary属性

位置

注入bean时的任何地方,方法,类上

  示例如下:  

public class User {
//  IOC中存在多个Address时,IOC会根据Order注解决定Address在List<Address>
//  的顺序
	@Autowired(required=true)
	private Address homeAddress;
}
//  Address类
@Component
@Primary
public class Address {
}

  与Order不同的是,beanA依赖beanB,它们之间的关系是1:1,而IOC容器中存在多个beanB,此时由于无法决定使用哪个beanB,导致beanA创建失败,primary用于决定优先选中哪个beanB。如果beanB只存在一个时,该注解无意义。

2.2.5   qualifier

描述

当beanA依赖beanB,并且在容器中存在多个beanB时,Qualifiler注解用于为beanB分类。

位置

注入beanB时用来为beanB归类,获取时qualifier作为过滤条件,查找合适的beanB。

  分类的方式有三种,

  1. 第一种,设置qualifier的value属性
    1. 注入Address类
<!-- 家庭住址 -->
<bean class="com.bean.Address">
	<qualifier value="homeAddress" />
</bean>
<!-- 公司住址 -->
<bean class="com.bean.Address">
	<qualifier value="companyAddress" />
</bean>

      2.获取Address 

public class User {
	// 用户的家庭地址
	@Autowired
	@Qualifier("homeAddress")
	private Address homeAddress;
	// 用户的工作地址
	@Autowired
	@Qualifier("companyAddress")
	private Address companyAddress;
}

  此时Qualifier注解的作用与bean的ID差不多,还不如指定id属性,并且使用byName的方式获取。

  2.第二种,继承Qualifier

    1.编写@HomeAddress,@CompanyAddress注解

/**
 * 
  * @Title: HomeAddress.java  
  * @Package com.annotation  
  * @Description: HomeAddress注解
  * @version V1.0
 */
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface HomeAddress {
	// 居住地址
	String value();
}

    2. 注入Address类,配置qualifier子标签,可以看到子类相当于二级分类。

<bean class="com.bean.Address">
	<qualifier type="com.annotation.HomeAddress"  value="homeTown" />
</bean>
<bean class="com.bean.Address">
	<qualifier type="com.annotation.CompanyAddress" value="working" />
</bean>

    3.获取Address类

public class User {
	// 用户的家庭地址
	@Autowired
	@HomeAddress("homeTown")
	private Address homeAddress;
	// 用户的工作地址
	@Autowired
	@CompanyAddress("working")
	private Address companyAddress;
}

  此示例中,子类相当于二级目录,其中的value属性相当于过滤条件,如果value不是homeTown和working,则找不到Address类。homeAddress和companyAddress为null值

  3.第三种,继承Qualifier,添加更多的属性值作为过滤条件,例如在上述示例中只有value作为过滤条件,可以在注解中添加其他属性。

  它与第二种方式基本相同,在第二步时,注入Address类时,多个属性的格式为:

<bean class="com.bean.Address">
	<qualifier type="com.annotation.HomeAddress">
		<attribute key="field1" value="value1"/>
		<attribute key="field2" value="value2"/>
	</qualifier>
</bean>

  当beanA依赖beanB,并且存在多个beanB时,该注解为beanB分类,支持一级分类和二级分类。

  当容器中只存在一个beanB时,该注解无任何意义

  当只需要一级分类时,完全可以由bean的ID,name,alias等属性代替。

  当需要二级分类时,可以使用该注解。

  在实际的项目中,基本不会存在二级分类的情形,所以该注解的使用频率很低。

2.2.6   resource(Java_based)

描述

当beanA依赖beanB时,Resource注解用于在IOC容器中查找beanB。方式通常是byName,当不指定name属性时,name的属性值为类名首字母小写。

属性

name:

说明:根据name在IOC中查找beanB,它对应beanB的ID,name,alias中的任意一个。

类型:字符串

是否必填:否,默认值为类名首字母小写

位置

属性上,属性的set方法上

       示例如下:

  第一步,注入Address

<bean id="address" class="com.bean.Address"/>

  第二步,获取

public class User {
	// 用户的工作地址,可以不指定,默认值也是address
	@Resource(name="address")
	private Address companyAddress;
}

2.2.7   inject(Java_based) 

  等价于@Autowired

  2.2.8   named 

  当不指定value属性值时,等价于@Component,当指定value值时,等价于@Qualifier(“value”)。

2.3     配置

  2.3.1   description

描述

Bean的一段描述文字

位置

方法上,与@Bean一起使用

  2.3.2   value

描述

用于获取Properties的值,并且把该值赋给对象的属性

属性

key:

说明:IOC容器中变量的key值,它可以从JVM变量,操作系统变量,配置文件等等获取。

类型:springEL表达式

是否必填:是

位置

属性上

  2.3.3   lazyInit

描述

用于定义bean是否延迟加载

属性

value:

说明:true时延迟加载,false时随容器的创建加载。

类型:布尔值

是否必填:否,默认值为false

位置

方法上,与@Bean一起使用

2.3.4  scope

描述

用于指定bean的作用域,

属性

value:

说明:bean作用域的值,五个值中的任意一个。

类型:枚举值,singleton,prototype,request,session,application

是否必填:否

位置

方法上,与@Bean一起使用

2.3.5   requestScope

  等价于@Scope(“request”)

  2.3.6   sessionScope

  等价于@Scope(“session”)

  2.3.7   servletContextScope

  等价于@Scope(“application”)

  2.3.8   postConstruct(Java_based)

参考bean的生命周期,它添加在方法上,触发时机在构造器之后,init方法之前。

2.3.9   preDestory(Java_based)

参考bean的生命周期,它添加在方法上,触发时机在bean的销毁阶段,destory方法之前。

3、null相关

3.1     注解

原著中介绍了4个注解。

@NonNull:用于添加对象属性,方法参数,方法返回值不为NULL的约束条件。

// 对象属性上
@NonNull
private String userName;
// 方法参数上
public static void testLiteralExp(@NonNull String str) {}
// 方法返回值上
public @NonNull String test(){}

  @Nullable:与@NonNull作用刚好相反,属性,方法参数,返回值可以为NULL

@NonNullApi:当前包下面的所有方法参数和返回值都不可以为NULL,可以被@Nullable覆盖。

@org.springframework.lang.NonNullApi
package learn.test;

  @NonNullField:当前包下面的对象的所有字段都不可以为NULL,可以被@Nullable覆盖。

3.2  IDE

当字段,方法参数,方法返回值添加上这些注解时,IDE会有相应的提示。本文以Eclipse为例演示具体的步骤

  第一步:打开Window---->preferences----->Java------>Complier---->Error/Warnings

  找到Null analysis,展开,点击enable annotation-based null analysis。

  

   第二步,在下面的use default annotation for null specifications把对勾去掉,点击configure,配置如下图,

  

   第三步,点击Apply,重新编译即可看到效果。

    

3.3   JSR305

JSR305中也有相同功能的注解,而且还有很多其他的注解,使用这些注解首先必须引入jsr305的jar包

<!-- https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305 -->
<dependency>
    <groupId>com.google.code.findbugs</groupId>
    <artifactId>jsr305</artifactId>
    <version>3.0.2</version>
</dependency>

  编写代码时,再次使用NonNull注解时,会发现有javax.annotation.NonNull。

import javax.annotation.Nonnull;

  要使编译器提示,需要重新配置第二步,将注解修改为javax.annotation.NonNull等注解

原文地址:https://www.cnblogs.com/rain144576/p/14758732.html