spring boot是后端开发最主流的框架,spring boot的核心之一就是注解,它提供了很多注解来帮助我们简化配置,通过各种组合注解,极大地简化了spring项目的搭建和开发。为了方便我们在日常开发注解的使用,本文将开发所需要的注解统一并进行归类起来,并结合用例进行解析,这样收藏起来以便日后使用。
依赖注入
组件注解
@component,而其余 @Controller、@Service、@Repository都组合了@component注解,主要为便于使用者Class组件进行归类。默认加载IOC容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
| 注解 | 解析 | 用法 |
|---|---|---|
| @Component | 组件注解,使用了该注解会基于注释的配置和类路径扫描时。 会自动扫描并加载Class到ICO容器中 |
类上 |
| @Controller | 使用@Controller 注解类,在对应的方法上,视图解析器可以解析return 的 jsp,html页面 并且跳转到相应页面。此注解属于MVC中的C层(视图控制层) |
类上 |
| @RestController | 相当于@ResponseBody + @Controller合在一起的作用。使用@RestController注解, 无法返回jsp页面,或者html。只返回JSON格式的数据 |
类上 |
| @Service | 应用在service层(业务逻辑层) | 类上 |
| @Repository | 应用在dao层(数据访问层) | 类上 |
| @Mapper | 如果使用@Repository则需要使用@MapperScan(“xxx.xxx.mapper”)进行扫描 然后生成Dao层的Bean才能被注入到Service层中。 @Mapper=@Repository+@MapperScane |
类上 |
依赖注入注解
Autowired注解和@Inject、@Resource,可以与@Qualifier或者@Name配合使用,防止多实例注入时出错,以及值注入@Value。
| 注解 | 解析 | 用法 |
|---|---|---|
| @Autowired | 通过AutowiredAnnotationBeanPostProcessor类实现的依赖注入,默认是根据类型进行注入的 因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常。 |
字段上 方法上 |
| @Inject | 作用与@Autowired一样 | 字段上 方法上 |
| @Resource | 默认按照名称进行装配,名称可以通过name属性进行指定 | 字段上 方法上 |
| @Qualifier | 限定描述符除了能根据名字进行注入,更能进行更细粒度的控制如何选择候选者 可与@Autowired或者@Inject进行组合使用,进行精确注入 |
字段上 方法上 |
作用域和生命过程
| 注解 | 解析 | 用法 |
|---|---|---|
| @Scope | 具有4个作用域,默认是单例模式,也就是singleton Singleton(单例式):在整个应用中,只创建bean的一个实例。 Prototype(原型式):每次注入或者通过Spring应用上下文获取时,会创建一个新的bean实例。等同于new方式创建对象 Session(会话式):在Web应用中,为每个会话创建一个bean实例。(电子商务应用中,一个bean代表一个用户的购物车,只要同一个session一个bean)。 Request(请求式):在Web应用中,为每个请求创建一个bean实例。 |
类上 |
| @PostConstruct | 相当于init-method,在类的实例被初始化时执行,作用于方法上。 | 方法上 |
| @PreDestroy | 相当于destory-method,使用在方法上,当Bean销毁时执行 | 方法上 |
下面代码例子:
@Service //组件注入,注明为service组件
@Scope("prototype")//声明Scope为Prototype
public class UseFunctionService {
@Autowired //默认按type注入
@Qualifier("functionService") //精确注入
FunctionService functionService;
@Resource(name="baseDao")//默认按name注入,可以通过name和type属性进行选择性注入
private BaseDao baseDao;
@Inject
@Qualifier("userServiceImpl") //精确注入
public IUserService userService;
@PostConstruct//在UseFunctionService类实例执行完构造函数后执行
public postConstruct(){
System.out.println("postConstruct");
}
@PreDestroy//在UseFunctionService类实例Bean被销毁前执行
public perDestroy(){
System.out.println("perDestroy");
}
@Autowired
public void setUserDao(@Qualifier("userDao") UserDao userDao) {
this.userDao = userDao;
}
public String SayHello(String word){
return functionService.sayHello(word);
}
}
配置注解
Configuration配置注解
@Configuration可替换xml配置文件进行配置。被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。可与@PropertySource一起使用。
| 注解 | 解析 | 用法 |
|---|---|---|
| @Configuration | 配置类注解,可以与@Beae、@PropertySource一起使用,进行配置 | 在类、接口、枚举上 |
| @SpringBootConfiguration | 组合注解,@Configuration配置、@EnableAutoConfiguration启用自动配置 @ComponentScan默认扫描@SpringBootApplication所在类的同级目录以及它的子目录 |
类上 |
| @AutoConfigureAfter | 在指定的自动配置类之后再配置 | 类上 |
扫描注解
@ComponentScan注解,被@Configuration注解标注的类上面,涉及了@filter过滤器注解
| 注解 | 解析 | 用法 |
|---|---|---|
| @ComponentScan | 定义扫描的路径,默认就会加载标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中 excludeFilters 指定扫描的时候需要排除的组件,includeFilters 指定扫描的时候只包含的组件 |
类上 |
| @ComponentScans | 包含着@ComponentScan数组 | 类上 |
| @filter | 声明要用作包含过滤器或排除过滤器的类型过滤器 | 可注解在@ComponentScan中 |
资源、值等注入注解
可以将配置文件、配置文件中的属性、以及系统属性等注入所需的字段中,或者bean中
| 注解 | 解析 | 用法 |
|---|---|---|
| @Value | 值注入,可以注入普通字符,系统属性,表达式运算结果,其他Bean的属性 文件内容,网址请求内容,配置文件属性值等等 |
字段上 方法上 参数上 |
| @Bean | 声明当前方法的返回值为一个Bean,而且返回的Bean对应的类中可以定义init()方法和destroy()方法 然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy |
方法上 注解上 |
| @PropertySource | 指定配置文件位置,与@configuration类一起使用 | 类上 接口上 |
| @ImportResource | 加载xml配置文件 | 类上 接口上 |
| @ConfigurationProperties | 将properties属性与一个Bean及其属性相关联 | 类上 接口上 |
| @Import | 用来导入配置类的 | 类上 接口上 |
条件注解
Conditional根据满足某一特定条件创建特定的Bean,基于@Conditional元注解可延伸很多条件注解。在spring boot的源码中很多配置类的实例化都使用到了条件注解,例如数据源类DataSource的实例化
| 注解 | 解析 | 用法 |
|---|---|---|
| @ConditionalOnBean | Spring容器中是否存在对应的实例,可以通过实例的类型、类名、注解、昵称去容器中查找 可以配置从当前容器中查找或者父容器中查找或者两者一起查找。这些属性都是数组,通过”与”的关系进行查找 |
方法上 |
| @ConditionalOnClass | 类加载器中是否存在对应的类,逻辑跟@ConditionalOnBean类似 | 类上 接口上 方法上 |
| @ConditionalOnExpression | 判断SpEL 表达式是否成立 | 类上 接口上 方法上 |
| @ConditionalOnJava | 指定Java版本是否符合要求 | 类上 接口上 方法上 |
| @ConditionalOnMissingBean | Spring容器中是否缺少对应的实例,逻辑跟@ConditionalOnBean类似 | 类上 接口上 方法上 |
| @ConditionalOnMissingClass | Spring容器中是否缺少对应的实例,逻辑跟@ConditionalOnBean类似 | 类上 接口上 方法上 |
| @ConditionalOnNotWebApplication | 应用程序是否是非Web程序,没有提供属性,只是一个标识 | 类上 接口上 方法上 |
| @ConditionalOnProperty | 是否存在指定的资源文件。只有一个属性resources,是个String数组。 会从类加载器中去查询对应的资源文件是否存在 |
类上 接口上 方法上 |
| @Profile | 指定某个bean属于哪一个profile:spring.profiles.active和spring.profiles.default(默认) | 类上 接口上 方法上 |
代码使用注解例子
@Configuration
//@SpringBootConfiguration
@ComponentScan(value="com.cn",ComponentDefaultFilters=true,
includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
})
@ImportResource("classpath:condition.xml")//导入xml配置
@Import(ConditionConfig.class)//导入ConditionConfig Class,并会实例化到容器中以及ConditionConfig中的配置也一起注入
public class Config {
@Value("I Love You!") //注入普通字符
private String normal;
@Value("#{systemProperties['os.name']}") //注入操作系统属性
private String osName;
@Value("#{ T(java.lang.Math).random() * 100.0 }") //注入表达式运算结果
private double randomNumber;
@Value("#{demoService.another}") //注入其他Bean的属性
private String fromAnother;
@Value("classpath:org/light4j/sping4/usually/el/test.txt") //注入文件内容
private Resource testFile;
@Value("http://www.baidu.com") //注入网址内容
private Resource testUrl;
@Value("${book.name}") //注入属性文件
private String bookName;
@Bean//加载bean
@Conditional(WindowsCondition.class) // 通过@Conditional注解,符合Windows条件则true
@ConditionalOnResource(resources="classpath:windows.ini")//在类路径下是否存在windows.ini文件,存在为true,最后进行注解与操作,为true实例化Bean
public ListService windowsListService(){
return new WindowsListService();
}
@Bean
@ConditionalOnClass(LinuxCondition.class) // 通过@ConditionalOnClass注解,符合Linux条件则true
@ConditionalOnProperty(name = "synchronize", havingValue = "true"))//如果synchronize在配置文件中并且值为true
public ListService linuxListService(){
return new LinuxListService();
}
//加载配置文件中前缀为spring.datasource的属性
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource jwcDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConditionalOnMissingClass({LinuxCondition.class,WindowsCondition.class})//当容器中缺失这两个Class时为true
@Profile("dev")//在dev环境下为true 最后结果为注解和之与,true实例化该Bean
public ListService macListService(){
return new MacListService();
}
}
Spring boot注解
web注解
做web开发时需要用到的注解,主要在controller层。用于请求参数,请求头数据,cookie数据获取,以及跨域支持。支持restful风格的web应用开发
| 注解 | 解析 | 用法 |
|---|---|---|
| @EnableWebMvc | 会开启一些默认配置,如一些ViewResolver或者MessageConverter等 | 类上 |
| @RequestMapping | 用来映射Web请求(访问路径和参数),处理类和方法的(即配置URL和方法之间的映射) 注解在方法上的@RequestMapping路径会继承注解在类上的路径 |
类上 方法上 |
| @GetMapping | 与@RequestMapping功能类似,但指定为get请求 | 方法上 |
| @PostMapping | 与@RequestMapping功能类似,但指定为post请求 | 方法上 |
| @PutMapping | 与@RequestMapping功能类似,但指定为put请求 | 方法上 |
| @DeleteMapping | 与@RequestMapping功能类似,但指定为delete请求 | 方法上 |
| @ResponseBody | 支持将返回值放在response体内 | 方法上 |
| @RequestBody | http请求需携带请求体 | 参数上 |
| @PathVariable | 用来接收路径参数,如/ccww/003,可接收003作为参数 | 参数上 |
| @RequestParam | 请求url后面需携带的参数 | 参数上 |
| @CrossOrigin | 用于支持跨域请求,此注解有两个参数 origins : 允许可访问的域列表 maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。 |
类上 方法上 |
| @RequestHeader | 用于映射请求头数据到Controller方法的对应参数 | 参数上 |
| @CookieValue | 可以把Request header中关于cookie的值绑定到方法的参数上。 | 参数上 |
RequestHeader注解可获取请求头中的数据,下面介绍一下请求头(header)数据主要有哪些:
| Header | 解释 | 示例 |
|---|---|---|
| Accept | 客户端能够接收的内容类型 | Accept:text/html,image/* [告诉服务器,浏览器可以接受文本,网页图片] |
| Accept-Charaset | 浏览器可接受的字符编码集 | Accept-Charaset:ISO-8859-1 [接受字符编码:iso-8859-1] |
| Accept-Encoding | 浏览器支持的服务器返回的压缩编码类型 | Accept-Encoding:gzip,compress [可以接受 gzip,compress压缩后数据] |
| Accept-Language | 浏览器可接受的语言 | Accept-Language:zh-cn [浏览器支持的语言] |
| Accept-Ranges | 用于告知客户端服务器是否能处理范围请求,以指定获取服务器端某个部分的资源 | Accept-Ranges:bytes |
| Authorization | Http授权的授权证书 | Authorization:Basic YWRtaW46YWRtaW4= |
| Cache-Control | 指定请求和响应遵循的缓存机制 | Cache-Control:no-cache |
| Connection | 表示连接是否持久(HTTP1.1默认进行持久连接) | Connection:close |
| Cookie | HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器 | Cookie:version=1;skin=new |
| Content-Length | 请求内容长度 | Content-Length:348 |
| Content-Type | 请求与实体对于的MIME信息 | Content-Type:application/x-www-form-urlencoded |
| Date | 请求发送的时间和日期 | Date:Mon, 17 Mar 2015 05:34:54 GMT |
| Host | 浏览器要找的主机 | Host:localhost:8080 |
| Referer | 告诉服务器我来自哪里,常用于防止下载,盗链 | Referer:www.baidu.com |
| User-Agent | 告诉服务器我的浏览器内核 | User-Agent:Nozilla/4.0(Com…) |
注解使用示例:
@Controller //声明此类是一个控制器
@RequestMapping("/ccww") //映射此类的访问路径是/ccww
//origin="*"代表所有域名都可访问 使用@CrossOrigin注解版本要求jdk1.8+ ,Spring4.2+
//maxAge飞行前响应的缓存持续时间的最大年龄,简单来说就是Cookie的有效期 单位为秒
//若maxAge是负数,则代表为临时Cookie,不会被持久化,Cookie信息保存在浏览器内存中,浏览器关闭Cookie就消失
@CrossOrigin(origins = "http://test.com",maxAge = 3600)
//@RestController // 使用@RestController,声明是控制器,并且返回数据时不需要@ResponseBody
//@RequestMapping("/ccww")
public class DemoAnnoController {
//此方法未标注路径,因此使用类级别的路径/anno;produces可定制返回的response的媒体类型和字符集,或返回值是json对象,则设置porduces="application/json;charset=UTF-8"
@RequestMapping(produces = "text/plain;charset=UTF-8")
public @ResponseBody String index(HttpServletRequest request) { //可接受HttpServletRequest作为参数,当然也可以接受HttpServletResponse作为参数。此处的@ResponseBody用在返回值前
return "url:" + request.getRequestURL() + " can access";
}
@RequestMapping(value = "/demoPathVar/{str}", produces = "text/plain;charset=UTF-8")//
public @ResponseBody String demoPathVar(@PathVariable String str, //接受路径参数,并在方法参数前结合@PathVariable使用,访问路径为/ccww/demoPathVar/xxx
HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access,str: " + str;
}
//常规的request参数获取,访问路径为/ccww/requestParam?id=1
@RequestMapping(value = "/requestParam", produces = "text/plain;charset=UTF-8")
public @ResponseBody String passRequestParam(Long id,
HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access,id: " + id;
}
@RequestMapping(value = "/obj", produces = "application/json;charset=UTF-8")
@ResponseBody // 可注解在方法上
public String passObj(DemoObj obj, HttpServletRequest request) {
return "url:" + request.getRequestURL()
+ " can access, obj id: " + obj.getId()+" obj name:" + obj.getName();
}
//映射不同的路径到相同的方法,访问路径为/anno/name1或/anno/name2
@RequestMapping(value = { "/name1", "/name2" }, produces = "text/plain;charset=UTF-8")
public @ResponseBody String remove(HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access";
}
@RequestMapping(value = "/helloWorld")
//以“user”为名称添加到模型对象中供视图页面展示使用
public String helloWorld(@ModelAttribute User user) {
return "helloWorld";
}
// 通过RequestHeader获取请求头中的数据
@RequestMapping("/boweifeng")
@ResponseBody
public String queryUser(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive,@CookieValue("JSESSIONID") String cookie) {
return "encoding:" + encoding + "cookie:"+cookie;
}
}
应用注解
| 注解 | 解析 | 用法 |
|---|---|---|
| @SpringBootApplication | Spring Boot核心注解,组合注解(@Configuration、@EnableAutoConfiguration、@ComponentScan) 主要是为了开启自动配置 |
启动类上 |
| @EnableAutoConfiguration | 让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置 开源组件如mybatis,redis都是通过此注解实现启动自动注入的 |
类上 |
JPA注解
下面是jpa和hibernate的常用注解
| 注解 | 解析 | 用法 |
|---|---|---|
| @entity | 修饰一个实体类,接受一个name属性作为该实体类名称,可省略默认为该类名 | 类上 |
| @Table | 指定持久化类所映射的表名,可接受以下属性 | 类上 |
| @Column | 指定表列名和实体类的的映射关系 | 字段上 |
| @Id | 必须的,定义映射到数据库表的主键属性 | 字段上 |
| @Transient | 表示该属性并非一个到数据库表字段的映射,ORM框架将忽略该属性 | 字段上 |
| @OneToOne | 一对一双向外键关联:主控方的配置同一对一单向外键关联。 | 字段上 |
| @ManyToOne | 多对一单向外键关联:多对一,多方设置EAGER。fetch有两种:EAGER和LAZY | 字段上 |
| @ManyToOne | 多对一双向外键关联 。多方:多方持有一方的引用 | 字段上 |
| @ManyToMany | 多对多双向外键关联:双方都持有对方的集合对象,其中一方设置 | 字段上 |
| @JoinColum | 用于表示两个实体的关联关系,和OneToOne,ManyToOne等注解配合使用 | 字段上 |
使用详解,通过例子来理解一对多,多对一几个注解的使用
一对多(多对一)
客户(Customer)和订单(Order)为例,客户(Customer)类为一的一方,订单(Order)类为多的一方。分别创建Customer和Order两个java类,通过jpa注解来表示他们的关系
客户(Customer)类
// 客户 ---- 一的一方
@Entity
@Table(name="t_customer")
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id; // 主键
private String name; // 姓名
// 描述客户可以有多个订单
/*
* targetEntity="...":相当于<one-to-many class="...">
* mappedBy="...":相当于inverse=true,即放弃关联关系的维护,不然会生成一个中间表
*/
@OneToMany(targetEntity=Order.class,mappedBy="c")
private Set<Order> orders = new HashSet<Order>();
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
订单(Order)类
// 订单 ---- 多的一方
@Entity
@Table(name="t_order")
public class Order {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private Double money;
private String receiverInfo; // 收货地址
// 订单与客户关联
@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name="c_customer_id") // 指定外键列
private Customer c; // 描述订单属于某一个客户
public Customer getC() {
return c;
}
public void setC(Customer c) {
this.c = c;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public String getReceiverInfo() {
return receiverInfo;
}
public void setReceiverInfo(String receiverInfo) {
this.receiverInfo = receiverInfo;
}
}
多对多
以学生与老师为例,使用@ManyToMany注解来配置多对多,只需要在一端配置中间表,另一端使用mappedBy表示放置外键的维护权。
学生(Student )类
@Entity
@Table(name="t_student")
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
@ManyToMany(targetEntity=Teacher.class)
// @JoinTable:使用@JoinTable来描述中间表,并描述中间表中外键与Student、Teacher的映射关系
// joinColumns:它是用来描述Student与中间表的映射关系
// inverseJoinColumns:它是用来描述Teacher与中间表的映射关系
@JoinTable(name="s_t", joinColumns={@JoinColumn(name="c_student_id",referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="c_teacher_id")})
private Set<Teacher> teachers = new HashSet<Teacher>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
}
老师(Teacher )类
@Entity
@Table(name="t_teacher")
public class Teacher {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
@ManyToMany(targetEntity=Student.class, mappedBy="teachers") // 代表由对方来维护外键
private Set<Student> students = new HashSet<Student>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}