最新り系列 开发Spring項目之註解大集錦
注:本文耗费大量时间精力,对目前JavaEE开发中 常用注解
进行详细介绍与整理。欢迎点赞和收藏,若需要快速查找指定注解,考虑检索目录和使用ctrl+F相结合
序言:注解是一种标记,是给JVM看的,主要用来简化配置
在使用Spring系列框架开发项目的过程中,时常听到前辈们对所用技术进行的高度概括,诸如:"框架=注解+反射+设计模式
"、“无反射,不框架;无代理,不框架”、“基于场景做抽象,预留扩展空间,交付最小集” 等等。近日难得清闲,泡上一杯西湖龙井,抛开其他编程思想不谈,且就 Spring开发中常用注解进行一系统梳理,方便日后及时查阅。
Java注解用途广泛,熟练而优雅地使用它们有助于提高代码质量和开发效率,也是工程师水平高低的一个反映。那么,你真的了解注解吗?
说到注解(英文为Annotation),这一在框架中被广泛引用的事物,其实并不是框架所独有:
实际上,使用注解就相当于为程序的某个元素上(可以是包、类、属性、方法、方法参数以及局部变量上)打上一个标记。
你可以简单理解为:注解就是为程序打标记,而这个标记是打给JVM看的。 程序运行后JVM查看各个元素上是否标记有注解,根据标记的注解不同,进而执行相应的功能。JVM如何能够看到这个标记?就是通过反射!
当然,上面仅仅是从注解如何运行的角度来对注解进行的概况,这与实际开发是不太相称的,真正在开发中使用注解时,更多的会考虑此注解的作用, 更关注其如何更好的代替xml等配置文件来进行简化配置、更优雅地基于注解进行开发。 在使用现代Java框架开发的项目中,注解俨然已成了实现程序功能的重要组成部分。
注解的功能有三大类:
(1)协助生成帮助文档
所谓协助生成帮助文档,诸如,下面这些注解:
@author 标明开发该类模块的作者
@version 标明该类模块的版本
@see 参考转向, 也就是相关主题
@since 从哪个版本开始增加的
@param 对方法中某参数的说明, 如果没有参数就不能写
@return 对方法返回值的说明, 如果方法的返回值类型是void就不能写
@exception 对方法可能抛出的异常进行说明 , 如果方法没有用throws显式抛出的异常就不能写
(2)在编译时进行格式检查
在编译时进行格式检查,诸如:
@Deprecated:用于表示所修饰的元素(类、方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。
@SuppressWarnings:用于表示所修饰的元素(类、方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。
@Override:限定重写父类方法,该注解只能用于方法。
(3)简化配置文件(最重要)
使用注解简化配置文件,在Web开发、Spring开发中应用广泛,涉及的注解种类繁杂。对这些常用注解用法的归纳与梳理,请参考下面正文。
后文对注解的梳理是较为系统全面的,部分涉及到查看注解源码,若想更好的了解注解的运行过程,可以根据下面表格,复习"元注解"的概念。元注解即注解的注解,为注解提供相应支持
:
常见元注解 | 作用 | 属性 |
---|---|---|
@Target | 指定被修饰注解的作用目标,即被修饰的注解,可以用在哪些元素上 默认值为ElementType的枚举值 | METHOD 方法、TYPE 类or接口、FIELD 字段、CONSTRUCTOR 构造方法、PARAMETER参数 |
@Retention | 指定被修饰注解的保留阶段 。默认只在源码阶段保留值为RetentionPolicy的枚举值 | SOURCE源码上保留、CLASS源码和字节码上保留、RUNTIME所有阶段都保留 .java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME) |
@Documented | 指定被修饰的注解将保留在文档工具类中 | \ |
Spring开发常用注解梳理
一、Spring框架注解整理
对象创建:@Component @Controller @Service @Repository
- 开启组件扫描语句为:<context:component-scan base-package=“com.xxx”/>
首先需要开启组件扫描!否则,这些用于创建对象的注解都是不生效的,这个组件扫描语句的功能是让SpringIOC容器识别到这些组件。@Component标记的类或接口成为一个组件类,会将bean注册到Spring容器中"
。组件类即成为组件扫描的"候选者(candidates),可以被组件扫描识别为一个组件。@Controller
、@Service
、@Repository
本质上都是@Component。使用这些注解可以从逻辑上区分JavaEE三层架构中的不同层次,但其目的都是为了实现Spirng的控制反转,即将Java对象交给SpringIOC容器创建。- 注意:在这四个注解中,
value属性值是可以省略的,默认值是类的名称首字母小写
。- 实际开发中为什么总是将这些对象创建的注解加在实现类上而非接口上?
这四个对象创建的注解是把springIOC容器中的bean进行实例化,等同于new操作。又因为接口不能实例化,实现类可以实例化,故正确的用法是加在实现类上使用
。
属性注入:@Value @Autowired @Qualifier @Resource
(1)普通属性注入:
@Value("xxx")
:此注解用来为元素赋值。开发中更常用的方式是用来注入配置文件中的内容(如@value("${spring.jpa.databasse}"))或者是注入SpEL表达式中对应的内容。
可以通过@Value(“xxx”)为对象的普通属性注入相应值。
@Component
public class Employee {
@Value("法外狂徒-张三儿")
private String name;
@Resource
private Teacher teacher;
}
(2)对象属性注入:
@Autowired
:根据数据类型进行自动注入,亦称为自动装配。
当某个类型的Bean对象在容器中唯一且与要注入的变量类型相匹配,就可以注入成功;当某个类型的Bean对象在容器中不唯一时,就不能仅使用根据类型匹配的方式,需要在类型匹配的基础上,再使用@Qualifier注解,在类型匹配的基础上,再根据属性名称来匹配。
- 此注解存在构造器注入、setter注入、Field字段注入,这里演示的只是字段注入。
@Qualifier
:根据属性名称进行注入,此注解需要搭配@Autowired一起使用。
当某个类型的Bean对象在容器中不唯一时,就不能仅使用根据类型匹配的方式,需要在类型匹配的基础上,再使用@Qualifier注解,在类型匹配的基础上,再根据属性名称来匹配。
@Resource(name="name")
: 既可以根据类型注入,也可以根据名称注入。默认根据类型匹配和@Autowired一样,加上name="name"后再根据名称匹配。
使用@Resource(name = “xxx”)可以同时完成@Autowired和@Qualifier两个注解的功能。但需要注意的是,此注解属于javax包中的注解,属于java的扩展包,上面的两个属于spring包中的注解。官方更建议使用上面的两个注解而非此注解。
AspectJ : @Aspect @Poincut @Order
AspectJ 是springAOP实现的一种具体实现,所谓AOP面向切面思想,实际上就是在不修改源代码的基础上添加新的功能或对原有功能进行增强。AOP将业务逻辑封装成切面,通过切入点表达式动态地切入到被代理方法的指定位置。AOP的底层是动态代理,而事物操作是AOP的一个典型应用。
AOP的本质是动态代理
@Aspect
:协助生成代理对象,被此注解标记的类称为切面类。使用此注解时,需要在配置类中开启基于注解的AOP功能。
- 开启基于注解的AOP功能:
<aop:aspectj-autoproxy/>
这条语句开启基于注解的AOP功能后,若发现某个类上标记了@Aspect注解,就将这个类生成为代理对象。
切入点
@Poincut
:指定切入点表达式。value属性指定表达式的内容。
- 大多数情况下,我们都是需要对业务层的方法进行增强。这个需要被增强的方法就是切入点,建议切入到实现类,而非切入接口。
切入点表达式
- 语法:
@Pointcut("execution([权限修饰符] 返回值类型 方法所在类的全类名.方法名(参数列表))")
切入点优先级
@Order
:设置切面的优先级,value属性为数字类型值,值越小优先级越高。
当多个切面同时切入同一个方法时,就涉及到切面的优先级问题,
切入点表达式的各种写法:
// 切入到所有Service类/接口的所有方法上
execution(* *..*Service.*(..))
// 切入到所有公共方法
execution(public **(..))
// 切入到所有名称以set开始的方法
execution(* set*(..))
// 切入到com.gql.service.UserService类中的任意方法
execution(* com.gql.service.UserService.*(..))
开发者可以自定义一个切入点组件,用于定义各个切入点(需要被增强的方法),下面的代码就创建了一个名为MyPointCut的切入点组件,声明了3个切入点。这个组件被声明后,在使用通知时就可以直接引用这个组件中切入点的引用:
@Component
public class MyPointCut {
// 切入CalculatorPureImpl的add方法
@Pointcut("execution(public int com.gql.component.impl.CalculatorPureImpl.add(int,int)))")
public void addPointCut() {
}
// 切入CalculatorPureImpl的div方法
@Pointcut("execution(public int com.gql.component.impl.CalculatorPureImpl.div(int,int)))")
public void divPointCut() {
}
// 切入所有以Service结尾或Impl结尾的类或接口
@Pointcut(value = "execution(* *..*Service.*(..)) || execution(* *..*Impl.*(..))")
public void transactionPointCut() {
}
}
通知相关:@Before @AfterReturning @AfterThrowing @After
通知
@Before
:前置通知
@AfterReturning
:返回通知
@AfterThrowing
:异常通知
@After
:最终通知
这些通知的value属性可以用于指定切入点表达式、切入点的引用。
事务管理:@Transactional
首先来回顾一些事务的概念:逻辑上的一组操作,要么全部成功,要么全不成功。事务的底层使用的是AOP原理,在不修改源代码的情况下,可以增强类中的某个方法。
事务有四个重要特性。
- A原子性:指操作不能分割,要么都成功,要么都失败。
- C一致性:指操作前和操作后总量不变。
- I隔离性:多事务操作不会产生影响。
- D持久性:事务最终要提交,提交后就真正发生了变化。
@Transactional
: 此注解标记在类上,会影响到类中的每一个方法,标记在方法上,会影响被标记方法。
@Transaction注解的几个属性
propagation事务传播行为:指多事务方法(有的方法有事务,有点没有事务或都有事务)之间互相调用,这个过程中事务是如何进行管理的。假设方法A调用方法B,Spring框架提供了7种事务传播行为,下面两个是最常用的行为:
REQUIRED行为(默认)
:如果方法A有事务,方法B使用方法A中的事务;如果方法A没有事务,会创建新的事务。
REQUIRED_NEW行为
:无论方法A是否有事物,都创建新的事务。isolation 事务隔离级别:若不考虑隔离级别,会出现脏读、不可重复度、幻读问题,事务的隔离性就是为了解决这三个问题而产生。
timeout超时时间:事务需要在一定时间内进行提交,如果不提交就执行回滚。
默认值是-1,可以设置时间以秒为单位进行计算。readOnly是否只读:默认值为false,表示可以执行增删改查操作,可以设置为true表示只能执行查询操作。
建议设置查询操作为只读,这样数据库就能够针对查询操作来进行优化;另外只读只允许设置在查询操作上。rollbackFor回滚:设置出现哪些异常执行事务回滚。
noRollbackFor:回滚:设置出现哪些异常不执行回滚。
事务一般在Service层使用,若要使用事务注解,前提是配置事务管理器,具体如何配置见下图。
二、SpringMVC框架注解整理
请求控制器的概念
:由于前端控制器对浏览器发送的请求进行了统一的处理,但不同的请求有不同的处理过程,因此需要创建处理具体请求的类,称为请求控制器。
请求映射:@RequestMapping @RequestParam @RequestHeader @CookieValue
注解 | 映射了谁 |
---|---|
@RequestMapping | 请求路径<==>控制器方法 |
@RequestParam | 请求参数<==>控制器方法的形参 |
@RequestHeader | 请求头信息<==>控制器方法的形参 |
@CookieValue | cookie数据<==>控制器方法的形参 |
@RequestMapping
:此注解将请求的路径和处理请求的控制器关联起来,建立映射关系。一旦SpringMVC接收到指定的请求,就会找到在映射关系中对应的控制器方法来处理这个请求。
位置:标记在类上,设置映射请求路径的初始信息;标记在方法上,设置映射请求路径具体信息。
value属性:是一个字符串类型的数组,表示该请求映射能匹配多个请求地址所对应的请求。此属性必须设置。
method属性:是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式(get或post)的请求。
params属性:是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系。
“param”:要求请求映射所匹配的请求必须携带param请求参数
“!param”:要求请求映射所匹配的请求必须不能携带param请求参数
“param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value
“param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=valueheaders属性:是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系:
”header“:要求请求映射所匹配的请求必须携带header请求头信息。
“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息。
“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value。
“header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value。
注意
:
(1)若某个请求的请求地址无法满足请求映射的value属性,即不满足value页面报错404
,找不到资源。
(2)若某个请求的请求地址满足请求映射的value属性,但不满足method属性报错405
:Request method ‘POST’ not supported
(3)若当前请求满足@RequestMapping注解的value和method属性,但不满足params属性报错400
, Bad Request。
(4)若当前请求满足@RequestMapping注解的value和method属性,但不满足headers属性报错404
,找不到资源。
@RequestParam
:将请求参数与控制器方法的形参做映射。另外,当请求参数名与控制器方法的形参名相同时,此参数可以省略。
- value属性:指定为形参赋值的请求参数的参数名。
- required属性:设置是否必须传输此请求参数,默认值为true。
- defaultValue属性:无论required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,使用此属性指定的形参赋值。
注意:当required属性为true时,若没有传输该请求参数,且未设置defaultValue属性,
页面报错400
,Required String parameter ‘xxx’ is not present。
@RequestHeader
:将请求头信息与控制器方法的形参做映射。
- 此注解有三个属性,value、required、defaultValue,用法同@RequestParam。实际开发中并不是很常用。
@CookieValue
:将cookie数据与控制器方法的形参做映射。
- 此注解有三个属性,value、required、defaultValue,用法同@RequestParam。实际开发中并不是很常用。
路径占位符:@PathVariable
@PathVariable
:用在控制器方法的形参上,用于将占位符所表示的数据赋值给控制器方法的形参。
当请求路径中将某些数据要通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,再通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参。
- 原始方式:/deleteUser?id=1&username=zhansan
- rest方式:/deleteUser/1/‘zhangsan’
注意:若请求参数中的占位符, 与控制器方法的形参不匹配,
报错500
, Internal Server Error。
RESTful:@GetMapping @PostMapping @PutMapping @DeleteMapping
RESTful描述的是
表现层和资源之间的状态转移
。所谓表现层,就是呈现资源的形式(如html,json等);资源指的是服务器上一切皆资源;状态转移指表现层通过各种方式向服务器发送请求,由于HTTP协议是无状态协议,其所有的状态都保存在服务器端。具体的操作方式就是四个动词:GET、POST、PUT、DELETE。
@GetMapping
:处理GET请求的映射。
@PostMapping
:处理POST请求的映射。
@PutMapping
:处理PUT请求的映射。
@DeleteMapping
:处理DELETE请求的映射。
报文信息转换 :@RequestBody ResponseBody @RestController
Java中提供了一个HttpMessageConverter接口,可以将其称为报文信息转换器。它可以将请求报文转换为Java对象,或将Java对象转换为响应报文。此接口提供了两个注解和两个类型:
- @RequestBody:将请求体转换为Java数据
- @ResponseBody:将Java数据转换为响应体
- RequestEntity:获取整个请求报文。
- ResponseEntity:获取Java数据作为响应报文响应到浏览器。
关于HttpMessageConverter:当浏览器把请求发送到服务器之后,请求报文就固定了,此时我们可以将请求报文转换为Java对象;响应报文由服务器发送给浏览器,我们可以将Java对象转换为响应报文。
@RequestBody
:
功能:可以获取请求体,并将请求体中的JSON对象转换为Java对象。
用法:用在控制器方法的形参前,当前请求的请求体就会为当前注解所标识的形参赋值。
方式1:发送POST请求:
- 使用POST请求提交时,请求报文的请求体中传输的就是String类型的字符串。
方式2:发送axios请求:
- 通过axios(Ajax)的data属性传输信息到服务器,传输的是一个JSON格式的数据。
@ResponseBody
:
- 功能:可以将方法的返回值作为响应体响应给浏览器,并将Java对象转换为JSON对象。
实际上上面的@RequestBody的两个功能使用并不广泛,因为浏览器一般很少传输JSON到服务器。但是服务器向浏览器响应Java对象是经常使用的,所以这里的@Response注解使用更广泛。
①发送POST请求:
@RestController
:这是一个复合注解,标识在控制器的类上。
- 相当于为类加上了@Controller 和 @ResponseBody这两个注解。
SpringMVC注解驱动 :@EnableWebMvc
Servlet3.0环境中,可以通过继承AbstractAnnotationConfigDispatcherServletInitialize作为初始化类,代替web.xml。容器会自动发现它,并用它来配置Servlet上下文。
@EnableWebMvc
:开启MVC注解驱动
@Bean
:Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
三、Mybatis框架注解整理
@Param
@Param
:用在Mapper接口的形参处,将传递的参数放入map集合。
以@Param注解的value属性值为键,以参数为值,然后通过 #{属性名} 或 ‘${属性名}’ 访问map集合的键就可以获取对应的值。
- 注意:对象类型前如果使用了@Param注解。则sql语句中获取对象的属性值时,需要使用 #{对象名.属性名} 的方式。
@MapKey
@MapKey
:用在将多条用户信息查询为map集合的方法上,能够为map集合设置键。
- 解释:将表中的数据以map集合的方式查询,若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时就需要通过@MapKey注解来指定一个字段作为返回的Map集合中的键,一般都使用唯一键来做key。值是每条数据对应的map集合。
四、SpringBoot框架注解整理
完全注解开发:@Configuration @Bean @ComponentScan(“com.xxx”)
@Configuration
:将一个普通类标记为配置类,等价于xml配置文件。
配置类中在方法上使用@Bean为容器注册组件,默认是单实例的。
Spring5.2后新增proxyBeanMethods属性,默认为true
。此属于用来解决组件依赖问题。
若为true(Full模式):每一次组件调用时都从容器中寻找此组件是否存在。配置类组件之间有依赖关系时使用Full模式。
若为false(Lite模式):每一次组件调用时,都会产生一个新的对象。配置类组件之间无依赖关系时使用Lite模式,可加速容器启动过程,减少判断
。
@Bean
:主要用在方法上,给容器添加组件,即将bean注册到容器中。
以方法名作为组件的id,返回类型就是组件类型,返回的值就是组件在容器中的实例。
注意与@Component进行区分:
两者目的都是将bean注册到Spring容器中,但`@Bean比@Component的自定义性更强。@Component是通过类路径扫描来自动侦测以及自动装配到Spring容器中;而@Bean通常由我们在标有该注解的方法中自定义产生这个bean的逻辑。
@ComponentScan
:指定开启组件扫描的包有哪些。
- 此注解常常写在springboot项目的主程序类(也称主配置类)上、或自定义配置类上。
五、Dubbo+Zookeeper分布式注解
@Reference
:注入分布式中的远程服务对象。
六、其他杂项注解
单元测试:@RunWith @ContextConfiguration @SpringJunitConfig
Spring整合Junit4
@RunWith
:此注解需要提供一个类类型的Runner(测试运行器),来指导JUnit如何运行测试。spring项目需要提供名为SpringRunner.class
的运行器,这会使其创建测试运行所需要的Spring应用上下文。
- 你可能见过名为SpringJunit4ClassRunner.class的测试运行器,实际上SpringRunner就是SpringJunit4ClassRunner的别名,这是在spring4.3中引入的,更易于输入和阅读。
@ContextConfiguration
:加载配置文件,等价于获取容器对象的Java代码。
使用此注解加载配置文件后,可以直接使用@Autowired将需要的组件注入容器,进而执行相关测试。
Spring整合Junit5
@SpringJUnitConfig
:这是一个复合注解,包含了@ExtendWith和@ContextConfiguration两个注解的功能。
@ExtendWith
:SpringExtension.class提供了对现存Spring TestContext Framework的支持,需要使用`@ExtendWith来引用。@ContextConfiguration
:加载配置文件,等价于获取容器对象的Java代码。
使用此注解加载配置文件后,可以直接使用@Autowired将需要的组件注入容器,进而执行相关测试。
JSR305 :@Nullable @NonNull @NonNullFields @NonNullApi
JSR全称为Java Specification Request,意为Java规范提案,任何人都可以向JCP组织提出新增Java技术规范的请求。目前,JSR已成为Java界的一个重要标准。JSR提供的一系列注解是给框架的开发者来使用,而非提供给框架的使用者
。所以我们只需要对这些注解稍作了解,在使用框架时可以看懂这个提示。
注解名称 | 含义 | 可标记位置 |
---|---|---|
@Nullable | 可以为空 | 方法上、参数上、属性上 |
@NonNull | 不能为空 | 方法上、参数上、属性上 |
@NonNullFields | 在特定包下的字段不应为空 | 包上、属性上 |
@NonNullApi | 参数和方法返回值不应为空 | 包上、方法上、参数上 |
以上面的@Nullable注解为例:
- 注解用在方法上,表示方法返回值可以为空。
- 注解用在参数上,表示方法的参数可以为空。
- 注解用在属性上,表示属性值可以为空。