一、使用在类上常用注解
1、@RestController
使用处:类
说明:代表这个类是REST风格的控制器,返回JSON/XML类型的数据,不能返回HTML页面,作用相当于@Controller(作用类上)+@ResponseBody(作用类或方法上)
2、@Controller
使用处:类
说明:代表这个类是mvc模式的控制器(主要用于构建MVC模式的程序),返回的字符串是视图(view)的位置和名称,比如下面的例子,视图位于example文件夹下,视图文件为hello.html:
3、@Service
使用处:类
说明:用于声明一个业务处理类(用作类,而不是接口),标记其是一个服务层,处理业务逻辑
4、@Repository
使用处:类
说明:标记其是一个数据访问层
5、@Component
使用处:类
说明:把普通POJO纳入Spring容器,当类不属于控制器层、服务层、数据访问层的角色时,就可以使用@Component注解,这个注解可以配合CommandLineRunner使用:再程序启动后执行一些基础任务。
6、@Configuration
使用处:类
说明:用于标注这是一个配置类,从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
注意:@Configuration注解的配置类有如下要求:
- @Configuration不可以是final类型;
- @Configuration不可以是匿名类;
- 嵌套的configuration必须是静态类
7、@Resource
使用处:类、属性 或 setter方法上 或者 构造函数参数
说明:Spring不但支持自己定义的@Autowired注解,还支持JSR-250规范定义的几个注解。如:@Resource、@PostConstruct及@PreDestroy
1. @Autowired
由spring提供,只按照byType注入
2. @Resource
由J2EE提供(java自己的注解),默认是按照byName自动注入
@Resource有两个重要的属性,name和type:
Spring将@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略;而使用type属性则使用byType自动注入策略;如果既不指定name也不指定type属性,这时通过反射机制使用byName自动注入策略。
@Resource装配的顺序:
- 如果同时指定了name和type属性,则从spring上下问中找到唯一匹配的bean进行装配,如果没有找到,则会抛出异常。
- 如果指定了name属性,则从spring上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定type属性,则从spring上下文中找到类型匹配的唯一bean进行装配,找不到或者是找到多个,则抛出异常。
- 如果既没有指定name属性,也没有指定type属性,则默认是按照byName的方式进行装配;如果没有匹配,则返回一个原始的类型进行装配,如果匹配则自动装配。
@Resource的作用是相当于@Autowired,只不过@Autowired是按照byType进行装配。
3. 使用区别
- @Resource和@Autowired都可以用来装配bean,都可以写在属性上或者是写在setter方法上。
- @Autowired默认是按照byType进行装配的,所以默认情况下是必须依赖的对象存在,如果要允许为空,可以设置它的required属性为false,如果想使用byName进行装配,可以结合@Qualifier注解进行使用。
- @Resource默认是按照byName进行装配的,名称可以通过name属性进行制定,如果没有指定name属性,注解写在字段上时,默认是取字段名作为名称进行查找。如果注解写在setter方法上则默认是取属性的名称进行装配,当找不到与名称匹配的bean时才按照类型进行装配。但是值得注意的是,一旦指定了name属性,就会按照名称进行装配。
推荐使用@Resource注解在字段上,这样就不需要写setter方法了,并且这个属性是属于J2EE的,从而可以减少对spring的耦合
如果@Resource或者是@Autowired注解写在setter方法之上,则程序会走set方法内部,如果是写在filed上,则不糊走set方法内部。
场景代码示例参考下面这篇博客:
8、@Autowired
使用处:类、属性 或 setter方法上 或 构造函数参数
说明:它表示被修饰的类需要注入对象,spring会扫描所有被@Autowired标注的类,然后根据类型在IOC容器中找到匹配的类进行注入。
9、@RequestMapping("/hello")
使用处:类 或 方法上
说明:配置访问URL和controller类或方法之间进行映射,同时注释在类和方法上时,叠加生效,比如:
访问URL为:http://localhost:8080/hello1/hello2
该注解有6个属性:
其他:应该尽量使用@GetMapping、@PostMapping、@PutMapping
10、@Transactional
使用处:接口、接口方法、类、类方法,通常在Service层使用
说明:声明方法是事务的,但是是否真正支持事务,需要看数据库是否支持,详见我的另一篇博文《01.两小时学会springboot-入门》,spring不建议在接口、接口方法上使用该注解,因为这样使用只有基于接口的代理才会生效,基于类的代理不会生效;还有就是使用时要注意:比如使用这个注解修饰了某个方法,如果方法内部对异常进行了捕获,则事务不会回滚,如果想让事务回滚,则必须再往外抛出异常。
11、@Qualifier
使用处:类、属性
说明:用于标注哪一个实现类才是需要注入的。
需要注意的是:@Qualifier的参数名称为被注入的类中的注解@Service标注的名称。也就是@Qualifier的参数对应必须是@Service注解类的@Service("xxxx")参数xxxx
@Qualifier常与@Autowired一起使用:
而@Resource不需要与@Qualifier一起使用,因为@Resource自带name属性。
二、使用在方法上常用注解
注解 | 使用位置 | 说明 |
---|---|---|
@PathVariable | 方法参数前 | 将URL路径某一级映射到方法参数上 |
@RequestBody | Controller的方法参数前 | 注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 @RequestBody常用来处理application/json、application/xml等数据,这意味着HTTP消息是JSON/XML格式,需要将其转换为指定类型参数。 通过@RequestBody注解,可以将请求体中的JSON/XML字符串绑定到相应的Bean上,也可以将其绑定到Map上,也可以将其分别绑定到对应的字符串上 |
@RequestParam | Controller的方法参数前 | @RequestParam用来处理: 1、通过Params传值 2、通过body的x-www-form-urlencoded传值 所以在postman中,要选择body的类型为 x-www-form-urlencoded,这样在headers中就自动变为了 Content-Type : application/x-www-form-urlencoded 编码格式。如下图所示: 3、@RequestParam也可用于其它类型的请求,例如:POST、DELETE等请求。 |
@Bean | Configuration的方法上 | 声明该方法的返回结果是一个由Spring容器管理的Bean,,通常在JavaConfig类中使用(用@Configuration注解修饰的类) |
@ResponseBody | Controller的方法上 | Spring会将控制器中方法返回的对象(可以是字符串或者其他对象)通过对应的HttpMessageConverter转换为指定格式(JSON/XML)后,写入Response对象的body数据区: 这个注解也使用在Controller的方法上,代表对返回的对象需要进行转换处理(转换成JSON/XML) |
三、其他注解
- 上图中@Aspec应该是@Aspect
@ExceptionHandler
@ControllerAdvice
三种使用场景
1、全局异常处理
使用 @ControllerAdvice 实现全局异常处理,只需要定义类,添加该注解即可定义方式如下
在该类中,可以定义多个方法,不同的方法处理不同的异常,例如专门处理空指针的方法、专门处理数组越界的方法...,也可以直接向上面代码一样,在一个方法中处理所有的异常信息。
@ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为 NullpointerException,则数组越界异常就不会进到这个方法中来。
package com.mzj.springboot.actionpai.exceptionhandler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class MyGlobalExceptionHandler {
// @ExceptionHandler(Exception.class)
// public ModelAndView customException(Exception e) {
// ModelAndView mv = new ModelAndView();
// mv.addObject("message123", e.getMessage());
// mv.setViewName("myerror123");
// return mv;
// }
@ExceptionHandler(Exception.class)
public Map<String, Object> exceptionHandler(Exception e) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "1002");
map.put("message", e.getMessage());
//发生异常进行日志记录,此处省略
return map;
}
}
疑问1??:这里统一异常处理了,怎么返回给前台指定结构,比如map,目前上面代码是无法返回给前台这个map的
答:
疑问2??:正常业务处理结束后怎么能返回前台对象,比如map?GET、POST请求类型不同有区别吗?
答:下图的方式,其中ServerResponse就是普通的Java对象
2、全局数据绑定
全局数据绑定功能可以用来做一些初始化的数据操作,我们可以将一些公共的数据定义在添加了 @ControllerAdvice 注解的类中,这样,在每一个 Controller 的接口中,就都能够访问导致这些数据。全局数据绑定功能可以用来做一些初始化的数据操作,我们可以将一些公共的数据定义在添加了 @ControllerAdvice 注解的类中,这样,在每一个 Controller 的接口中,就都能够访问导致这些数据。
使用步骤,首先定义全局数据,如下:
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ModelAttribute(name = "md")
public Map<String,Object> mydata() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 99);
map.put("gender", "男");
return map;
}
}
使用 @ModelAttribute 注解标记该方法的返回数据是一个全局数据,默认情况下,这个全局数据的 key 就是返回的变量名,value 就是方法返回值,当然开发者可以通过 @ModelAttribute 注解的 name 属性去重新指定 key。
定义完成后,在任何一个Controller中,都可以获取到这里定义的数据:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
Map<String, Object> map = model.asMap();
System.out.println(map);
return "hello controller advice";
}
}
注意:上面的GetMapping方法,如果想返回这个Map给前台,直接返回这个全局的map对象是不可以的,需要创建另一个map对象返回才行
3、全局数据预处理
考虑我有两个实体类,Book 和 Author,分别定义如下:
public class Book {
private String name;
private Long price;
//getter/setter
}
public class Author {
private String name;
private Integer age;
//getter/setter
}
此时,如果我定义一个数据添加接口,如下
@PostMapping("/book")
public void addBook(Book book, Author author) {
System.out.println(book);
System.out.println(author);
}
这个时候,添加操作就会有问题,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分。此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题
解决步骤如下:
1.给接口中的变量取别名
@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
System.out.println(book);
System.out.println(author);
}
2.进行请求数据预处理
在 @ControllerAdvice 标记的类中添加如下代码:
@InitBinder("b")
public void b(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void a(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}
@InitBinder("b") 注解表示该方法用来处理和Book和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有b前缀.
3.发送请求
请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分.