ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-06-21 16:53:19  阅读:232  来源: 互联网

标签:迭代 Spring Expose PostConstruct private 注解 new public


持续更新,建议收藏

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

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

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

????覆盖Java后端所有知识的开源项目 创作中… :https://github.com/Rodert/JavaPub
GitHub
pub哥的知识清单

文章目录


@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. 发送请求

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

在这里插入图片描述


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


待续。。。

标签:迭代,Spring,Expose,PostConstruct,private,注解,new,public
来源: https://blog.51cto.com/u_14747927/2932840

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有