持续更新,建议收藏
生产项目中最常用的注解,致力于讲清、说透每一个注解
通过简单代码示例,演示使用注解。需要更多注解解释请留言
微信搜「JavaPub」白嫖原创电子书和思维导图????
????覆盖Java后端所有知识的开源项目 创作中… :https://github.com/Rodert/JavaPub
GitHub
文章目录
@ModelAttribute
这个很重要
建议参考:https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation
@SerializedName
- 主要作用:属性重命名,可以将json中的属性名转为我们自己自定义的属性名
- @SerializedName 注解提供了两个属性,上面用到了其中一个’value’,别外还有一个属
alternate
:接收一个 String 数组alternate
数组中出现任意一个属性名都可以转换为自定义的属性,如果出现多个则以最后一个为准
@Expose
-
@Expose 默认有两个属性:serialize 和 deserialize,默认值都为 true;
-
使用@Expose GSON从JSON中排除字段
GSON提供了一种方法,您可以标记要排除在对象中的某些字段,以考虑序列化和反序列化到JSON。要使用此@Expose GSON批注,必须使用创建Gson new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()。创建的Gson实例将排除类中所有未标注@Expose注释的字段。
实例:
- 带有@Expose注释的字段将包含在JSON表示中。因此,未注释的字段(例如:“ created”)将不会序列化到JSON对象中。
package com.memorynotfound.json;
import com.google.gson.annotations.Expose;
import java.util.Date;
public class Product {
@Expose
private long id;
@Expose
private String name;
private Date created;
@Expose
private double amount;
public Product(long id, String name, Date created, double amount) {
this.id = id;
this.name = name;
this.created = created;
this.amount = amount;
}
}
- 创建JSON对象时,您必须使用来创建Gson new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()。否则,JSON对象将不会侦听@Expose注释。
package com.memorynotfound.json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Date;
public class ExcludeExposeGson {
public static void main(String... args){
Product product = new Product(1, "Playstation 4", new Date(), 499.99);
Gson gson = new GsonBuilder().create();
String result = gson.toJson(product);
System.out.println(result);
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
result = gson.toJson(product);
System.out.println(result);
}
}
- 输出结果
{"id":1,"name":"Playstation 4","created":"Dec 26, 2014 4:23:59 PM","amount":499.99}
{"id":1,"name":"Playstation 4","amount":499.99}
建议参考:https://sites.google.com/site/gson/gson-user-guide#TOC-Gson-s-Expose
@ControllerAdvice
@ExceptionHandler
这个注解非常有用,在生产系统中使用频率比较高(为了统一日志格式),顾名思义,这是一个增强的 Controller 。使用这个 Controller ,可以实现三个方面的功能:
- 全局异常处理
- 全局数据绑定
- 全局数据预处理
全局异常处理
@ExceptionHandler
使用 @ControllerAdvice 实现全局异常处理,只需要定义类,添加该注解即可定义方式如下:
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView customException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName("myerror");
return mv;
}
}
在该类中,可以定义多个方法,不同的方法处理不同的异常,例如专门处理空指针的方法、专门处理数组越界的方法…,也可以直接向上面代码一样,在一个方法中处理所有的异常信息。
@ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为 NullpointerException,则数组越界异常就不会进到这个方法中来。
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(NullpointerException.class)
public ModelAndView customException(NullpointerExceptione) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName("myerror");
return mv;
}
@ExceptionHandler(Exception.class)
public ModelAndView customException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName("myerror");
return mv;
}
}
全局数据绑定
全局数据绑定功能可以用来做一些初始化的数据操作,我们可以将一些公共的数据定义在添加了 @ControllerAdvice 注解的类中,这样,在每一个 Controller 的接口中,就都能够访问导致这些数据。
使用步骤,首先定义全局数据,如下:
@ControllerAdvice
public class MytestGlobalConfig {
@ModelAttribute(value = "info")
public Map<String,Object> mydata() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 99);
map.put("gender", "男");
return map;
}
@ModelAttribute(value = "info1")
public Map<String,Object> mydata1() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 999);
map.put("gender", "男");
return map;
}
@ModelAttribute(name = "info2")
public Map<String,Object> mydata2() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 11);
map.put("gender", "男");
return map;
}
}
使用 @ModelAttribute 注解标记该方法的返回数据是一个全局数据,默认情况下,这个全局数据的 key 就是返回的变量名,value 就是方法返回值,当然开发者可以通过 @ModelAttribute 注解的 name 属性去重新指定 key。
定义完成后,在任何一个Controller 的接口中,都可以获取到这里定义的数据:
@Controller
@RequestMapping("/mytest")
public class MyTestController {
@GetMapping("mytest")
@ResponseBody
public String hello(Model model){
Map<String, Object> stringObjectMap = model.asMap();
System.out.println(stringObjectMap);
System.out.println(stringObjectMap.get("info"));
return stringObjectMap.toString();
}
}
返回值:
{info={gender=男, age=99}, info2={gender=男, age=11}, info1={gender=男, age=999}}
{gender=男, age=99}
全局数据预处理
@InitBinder @ModelAttribute
栗子:
考虑我有两个实体类,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 的全局数据预处理可以解决这个问题
解决步骤如下:
- 给接口中的变量取别名
@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
System.out.println(book);
System.out.println(author);
}
- 进行请求数据预处理
在 @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前缀.
- 发送请求
请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分。
@Component
这是我们在 spring 系统中使用频率最高的注解之一。
标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>
)。
web 开发中,还提供了三个 @Component 注解的衍生注解(功能一样)。@Repository(“名称”)、@Service(“名称”)、@Controller(“名称”)
引入 component 扫描组件,在 spring mvc 中,我们需要再 applicationcontent.xml 中配置,在 SpringBoot 中,因采用零配置所以直接添加注解 @Component 。base-package
表示为需要扫描 com.javapub
下的所有子包。
<context:component-scan base-package=”com.javapub”>
@Controller
web层,controller控制器层(注入服务),作用和@Component类似
@Service
service层,service服务层(注入dao),作用和@Component类似
@Repository
dao层,dao持久层(实现dao访问),作用和@Component类似
@PostConstruct
从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。写法有如下两种方式:
@PostConstruct
public void someMethod(){}
或者
public @PostConstruct void someMethod(){}
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。PreDestroy()方法在destroy()方法知性之后执行
另外,spring中Constructor、@Autowired、@PostConstruct的顺序
其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象 a 和对象 p,才能执行注入。所以,如果一个类 A 中有个成员变量 p 被 @Autowried 注解,那么 @Autowired 注入是发生在A的构造方法执行完之后的。
如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么久无法在构造函数中实现。为此,可以使用 @PostConstruct 注解一个方法来完成初始化, @PostConstruct 注解的方法将会在依赖注入完成后被自动调用。
Constructor >> @Autowired >> @PostConstruct
public Class AAA {
@Autowired
private BBB b;
public AAA() {
System.out.println("此时b还未被注入: b = " + b);
}
@PostConstruct
private void init() {
System.out.println("@PostConstruct将在依赖注入完成后被自动调用: b = " + b);
}
}
@SelectProvider
@SelectProvider(type=xxxx.class,method=”xxxx”)
属性详解:
type 属性用于指定获取sql语句的指定类
method 属性用于指定类中要执行获取sql语句的方法
例子:
当mapper中传入的参数是使用@param 注解修饰,在xxxProvider类中必须使用Map对象接收参数。
BaseUserProvider类中:
public String selectUserById(Map<String, Object> para){
return new SQL(){{
SELECT("*");
FROM("base_user");
WHERE("id="+para.get("id"));
}}.toString();
}
此时:以上代码是借助org.apache.ibatis.jdbc.SQL类 使用固定的select from where 格式,也可以直接使用return “select * from base_user where id =” +para.get(“id”); 来实现sql拼接
例2(多个参数并加入if判断):
public String selectUserById(Map<String, Object> para){
return new SQL(){{
SELECT("*");
FROM("base_user");
WHERE("id="+para.get("id"));
if(StringUtils.isNotBlank((String)para.get("username"))){
WHERE("username="+para.get("username"));
}
}}.toString();
}
注意:
此时的sql写法在拼接sql中不需要在使用 and 进行连接 ,在where 方法中已拼入where 源码如下:
private static final String AND = ") \nAND (";
private static final String OR = ") \nOR (";
@RequiredArgsConstructor
在我们写controller或者Service层的时候,需要注入很多的mapper接口或者另外的service接口,这时候就会写很多的@AutoWired注解,代码看起来很乱
lombok提供了一个注解:
@RequiredArgsConstructor(onConstructor =@_(@Autowired))
写在类上可以代替@AutoWired注解,需要注意的是在注入时需要用final定义,或者使用@notnull注解
private final User u;
使用注解之前要去下载lombok插件哦~
@AutoConfigureAfter
@AutoConfigureAfter(MybatisConfig.class)
保证在MyBatisConfig实例化之后再实例化该类