Spring常用注解【维护持续迭代跟新】

持续更新,建议收藏

生产项目中最常用的注解,致力于讲清、说透每一个注解

通过简单代码示例,演示使用注解。需要更多注解解释请留言

微信搜「JavaPub」白嫖原创电子书和思维导图????

????覆盖Java后端所有知识的开源项目 创作中… :https://github.com/Rodert/JavaPub
GitHub
Spring常用注解【维护持续迭代跟新】

文章目录


@ModelAttribute

这个很重要

建议参考:https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation


@SerializedName

  1. 主要作用:属性重命名,可以将json中的属性名转为我们自己自定义的属性名
  2. @SerializedName 注解提供了两个属性,上面用到了其中一个’value’,别外还有一个属 alternate :接收一个 String 数组 alternate 数组中出现任意一个属性名都可以转换为自定义的属性,如果出现多个则以最后一个为准

@Expose

  1. @Expose 默认有两个属性:serialize 和 deserialize,默认值都为 true;

  2. 使用@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 ,可以实现三个方面的功能:

  1. 全局异常处理
  2. 全局数据绑定
  3. 全局数据预处理

全局异常处理

@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 的全局数据预处理可以解决这个问题

解决步骤如下:

  1. 给接口中的变量取别名
@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
    System.out.println(book);
    System.out.println(author);
}
  1. 进行请求数据预处理

在 @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前缀.

  1. 发送请求

请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分。

Spring常用注解【维护持续迭代跟新】


@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常用注解【维护持续迭代跟新】

另外,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实例化之后再实例化该类


待续。。。

上一篇:docker-compose


下一篇:HTML5调用本地摄像头画面,拍照,上传服务器