Spring Boot常用注解


配置类相关

@Configuration

声明一个类为配置类,用于取代bean.xml配置文件注册bean对象。

@Configuration注解中含有@Component注解,所以被@Configuration标记的配置类会作为一个bean对象被加载到ioc容器中。

它通常搭配@Bean注解和@Scope注解使用。

@Configuration有一个属性proxyBeanMethods,用于指定是否为懒加载,默认值为true。
proxyBeanMethods = true的情况下,保持单实例对象,在spring容器启动时就会创建bean对象。这种情况主要用在bean对象的依赖情况下,如果存在一个bean依赖另一个bean时,一般会采用Full模式。
proxyBeanMethods = false的情况下,不检查IOC容器中是否存在,而是简单的调用方法进行创建对象,无法保持单实例。当不存在bean对象的依赖问题时,会才有轻量级的配置。

测试代码如下:

@SpringBootApplication
public class TestSpringBootApp {
    public static void main(String[] args) {
        //获取ioc容器
        ConfigurableApplicationContext run = SpringApplication.run(TestSpringBootApp.class);

        Object dogBean = run.getBean(Dog.class);
        System.out.println(dogBean);
        User userBean = (User)run.getBean(User.class);
        System.out.println(userBean);
        System.out.println(userBean.getDog()==dogBean);
    }
}

User类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String email;

    private Dog dog;
}

Dog类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Dog {
    private Integer id;
    private String name;
    private Integer age;
}

testConfiguration类:
@Configuration(proxyBeanMethods = false)//多例模式
public class TestConfiguration {

    public static int count = 0;

    @Bean
    public User userBean(){
        System.out.println("User对象创建");
        return new User(1,"dfy", 15, null, dogBean());
    }

    @Bean
    public Dog dogBean(){
        System.out.println("Dog对象第"+ (++count)+"次创建");
        return new Dog(1,"六六",3);
    }
}

输出结果如下:
输出结果

如果改成单例模式,即proxyBeanMethods = true,那么输出结果将变成:
输出结果

@Bean

@Bean注解通常标记一个Supplier方法,用于声明一个bean对象的注册,默认的加载模式为饿汉单例。

使用@Bean声明一个bean:

@Configuration
public class TestConfiguration {

    @Bean
    public User userBean(){
        System.out.println("User对象正在创建");
        return new User(1,"dfy", 15, null);
    }
}
@SpringBootApplication
public class TestSpringBootApp {
    public static void main(String[] args) {
        //获取ioc容器
        ConfigurableApplicationContext run = SpringApplication.runTestSpringBootApp.class);
        Object user = run.getBean(User.class);
        System.out.println(user);
    }
}

输出结果为:
输出结果

@Bean注解含有如下属性:

public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

  • value和name

    可以看到value和name两个属性是一个别名对,它们的作用是指定该bean的名称。
    如果在使用时未指定bean的名称,默认使用带注解方法的名称;如果指定了,方法本身的名称就会被忽略。

@Configuration
public class TestConfiguration {

    @Bean
    public User userBean(){
        System.out.println("未指定名称的User对象正在创建");
        return new User(1,"dfy", 15, null);
    }

    @Bean("User01")
    public User userBean01(){
        System.out.println("指定名称的User对象正在创建");
        return new User(1,"dfy", 15, null);
    }
}
@SpringBootApplication
public class TestSpringBootApp {
    public static void main(String[] args) {
        //获取ioc容器
        ConfigurableApplicationContext run = SpringApplication.run(TestSpringBootApp.class);
        //获取所有bean对象的名称
        String[] beanDefinitionNames = run.getBeanDefinitionNames();
        for(String s : beanDefinitionNames){
            System.out.println(s);
        }
    }
}

输出结果如下:
输出结果

  • autowire

    已弃用。
    autowire确定自动装配状态,即该bean创建时是否使用它的setter方法对它的成员变量进行自动装配。
    Autowire是一个枚举类,有三个值:

    • NO:不自动装配
    • BY_NAME:根据名称自动装配
    • BY_TYPE:根据类型自动装配
      默认值是NO,即不自动装配。
  • autowireCandidate

    autowireCandidate和配置文件bean标签的autowireCandidate属性一样,就是让其他的bean在自动注入时,是否会装配当前的bean。 默认值true。

@Configuration
public class TestConfiguration {

    @Bean
    public User userBean(){
        return new User(1,"dfy", 15, null, null);
    }

    @Bean
    public Dog dog1(){
        return new Dog(1,"狗宝",5);
    }

    @Bean(autowireCandidate = false)//不作为自动注入的候选者
    public Dog dog2(){
        return new Dog(2,"六六",3);
    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
    @Autowired
    private Dog dog;
}
@SpringBootApplication
public class TestSpringBootApp {
    public static void main(String[] args) {
        //获取ioc容器
        ConfigurableApplicationContext run = SpringApplication.run(TestSpringBootApp.class);

        Object userBean = run.getBean("userBean");
        System.out.println(userBean);
    }
}

输出结果为:
User(id=1, name=dfy, age=15, email=null, dog=Dog(id=1, name=狗宝, age=5))
如果取消dog2的autowireCandidate = false,spring容器会报NoUniqueBeanDefinitionException异常。
有趣的是,虽然spring容器启动时可以正常装配第二个Dog对象,但是idea会编译报错:
自动装配报错

  • initMethod和destroyMethod

    用于指定bean的初始化和销毁方法。

@Configuration
public class TestConfiguration {

    @Bean(initMethod = "userInit", destroyMethod = "userDestroy")
    public User userBean(){
        return new User(1,"dfy", 15, null, null);
    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
    public void userInit(){
        System.out.println("User init...");
    }

    public void userDestroy(){
        System.out.println("User destroy...");
    }
}

输出结果:
输出结果

@Value

常用于标记成员变量,注入外部化属性
@Value的三种用法:

  • 注入普通属性
    语法为@Value(…),直接使用字面量即可
  • 注入配置文件
    语法为@Value(“${…}”),注入配置文件中写好的属性值
  • 注入表达式并运算
    语法为@Value(“#{…}”),写入spring框架的SpEl表达式即可
    每个框架有自己的一套SpEl表达式,详情可以参考官方文档

代码示例如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class User {
    @Value("10")//注入普通属性
    private Integer id;
    @Value("${user_name}")//注入外部配置文件
    //application.yml中写入属性: user_name: user01
    private String name;
    @Value("#{2+1+3}")//注入表达式
    private Integer age;
    private String email;
    public void userInit(){
        System.out.println("User init...");
    }

    public void userDestroy(){
        System.out.println("User destroy...");
    }
}

@Configuration
public class TestConfiguration {

    @Bean
    public User userBean(){
        return new User(1,"dfy", 15, null);
    }
}

该bean的输出结果为:
User(id=10, name=user01, age=6, email=null)
可以看到属性值已经成功注入,并且在new User对象时指定的默认值没有起到作用,被@Value的值覆盖掉了。

@Scope

@Scope通常和@Bean配合使用,用于指定该bean的作用域。它具有以下几种作用域:

  • singleton 单例模式(默认值)
  • prototype 多例模式
  • request 每次http请求产生一个bean,该bean在本次HTTP request内有效
  • session 每次http请求产生一个bean,该bean在本次HTTP session内有效

生命周期相关

@Lazy(true)

@Lazy表明一个bean是否被延迟加载,当@Lazy的值设定为true(默认值即为true)时,表明该bean被延迟加载。

  • 标记在方法上,表示该方法被延迟加载
  • 标记在@Component的类上或是@Component作为元注解的类上(其实就是标记在一个bean上),表示该bean被延迟加载

测试代码:

@Data
@AllArgsConstructor
@ToString
@Component
@Lazy//延迟加载
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
    private Dog dog;

    public User() {
        System.out.println("User 对象被创建");
    }
}

在spring启动时未看到User对象的创建:
输出结果

@PostConstruct 和 @PreDestory

实现初始化和销毁bean之前进行的操作,只能有一个方法可以用此注释进行注释,方法不能有参数,返回值必需是void,方法需要是非静态的,不能抛出异常。

  • @PostConstruct:在构造方法和init方法(如果有的话)之间得到调用,且只会执行一次。
  • @PreDestory:注解的方法在destory()方法调用后得到执行。

代码示例:

@Configuration
public class TestConfiguration {

    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Dog dogBean(){
        System.out.println("Dog对象配置类中创建...");
        return new Dog(1,"六六",3);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Dog {
    private Integer id;
    private String name;
    private Integer age;

    public void init(){
        System.out.println("Dog对象初始化...");
    }

    public void destroy(){
        System.out.println("Dog对象销毁...");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("Dog对象初始化之前...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("Dog对象销毁之后...");
    }
}

spring启动后结果为:
输出结果

bean对象相关

@Controller, @Service, @Repository, @Compnent

这四个注解的作用没有区别,都是声明一个bean对象注册到ioc容器中。但是为了程序可读性和开发规范,在使用时依然要按照java类的实际作用去使用。

在使用时只需要把它们标注在一个类上,spring容器启动时就会把对象创建好注入ioc:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class Dog {
    private Integer id;
    private String name;
    private Integer age;
}

输出结果

也可以给这些注解一个值,作为bean对象的名称:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component("firstDog")
public class Dog {
    private Integer id;
    private String name;
    private Integer age;
}

输出结果

@Named和@Inject

这两个注解是JSR-330的一部分。在Spring 3中,开始支持JSR-330的注解。这些注解在使用上和Spring的注解一样,所不同的只是需要额外的相关jar包。你可以使用下面的注解在spring 3应用中。
@Inject替代@Autowired来执行注入
@Named替代@Component来声明一个Bean

在springboot项目中,使用这两个注解需要先引入依赖:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

  • @Named功能和@Component注解相同,它的值用于指定bean的名称,如果没有值默认使用类名作为bean名称。
  • @Inject注解在spring环境下,和@Autowired是相同的。

@Autowired和@Resource

这两个注解的作用相同,用于bean对象的自动装配。

使用示例:

//@Autowired
@Resource
private Dog dog;

下面详细说一下@Autowired的装配原理:

  • @Autowired默认按照ByType类型进行装配。
  • 若ByType类型匹配到多个bean,那么按照ByName类型装配:
    • 若没有和@Qualifier搭配使用,默认按照变量名作为bean名称去匹配(例如上面Dog的例子,默认使用变量名dog作为bean名称),这种情况下若没有匹配到bean,会报NoUniqueBeanDefinationException异常。
    • 若搭配@Qualifier使用,会按照@Qualifier注解指定的bean名称进行匹配,如果没有匹配到,会报NoSuchBeanDefinitionException异常。
  • @Autowired有一个required属性,默认值为true,表示不接受空值,若没有匹配到bean报错。如果设置为false,表示允许null。

@Resource和@Autowired的区别:

@Resource默认按照Byname方式匹配bean,匹配失败会回滚到@Autowired。
可以手动指定bean,它有2个属性分别是name和type,使用name属性,则使用byName的自动注入,而使用type属性时则使用byType自动注入。
@Resource(name=”bean名字”)或@Resource(type=”bean的class”)

@Primary

自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常。

多线程

@Async

  1. 在方法上使用该@Async注解,申明该方法是一个异步任务;
  2. 在类上面使用该@Async注解,申明该类中的所有方法都是异步任务;
  3. 使用此注解的方法的类对象,必须是spring管理下的bean对象;
  4. 要想使用异步任务,需要在主类上开启异步配置,即,配置上@EnableAsync注解
    https://blog.csdn.net/qq_44750696/article/details/123960134

    参数校验

    @Valid,@Valided

    https://blog.csdn.net/weixin_51439775/article/details/128386125

WEB相关

@RequestBody

获取request请求体中的内容,常用来处理content-type不是默认的application/x-www-form-urlcoded编码(form表单格式)的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。

@RequestBody注解可以解析请求体中json格式的数据,并将其封装到javaBean中。

测试:

使用postman发送请求,请求体中使用json格式字符串。
发送请求
在后端代码可以用String来接收,接收结果是原json字符串:

@Controller
public class TestController {

    @RequestMapping("/")
    public String welcome(@RequestBody(required = false) String dog){
        System.out.println(dog);
        return "index";
    }
}

控制台输出:{"id":"1","name":"六六","age":"3"}

也可以用对象来接收,接收结果是json字符串封装后的对象:

@Controller
public class TestController {

    @RequestMapping("/")
    public String welcome(@RequestBody(required = false) Dog dog){
        System.out.println(dog);
        return "index";
    }
}

控制台输出:Dog(id=1, name=六六, age=3)

如果使用@RequestBody注解来接收form表单的请求(即application/x-www-form-urlcoded编码)会报415,并在控制台报出编码格式不支持的错误:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]

@RequestParam

用于将请求参数映射到控制层方法的参数上。
@RequestParam含有以下三个属性:

  • value:设置传入的参数的名称,若不设置默认使用变量值。
  • required:设置该参数是否必填。表示请求中一定要传入对应的参数,否则会报404错误,如果设置为false时,当请求中没有此参数,将会默认为null,而对于基本数据类型的变量,则必须有值,这时会抛出空指针异常。
  • defaultValue:参数的默认值,如果请求中没有同名的参数时,该变量默认为此值。注意默认值可以使用SpEL表达式。

    @PathVariable

    处理请求的URL,把占位符绑定到controller的方法参数上。
    该注解有两个属性:
  • value:设置匹配的占位符的名称,若不设置,默认使用参数名称匹配占位符。
  • required:设置该参数是否必填,默认值为true,表示URL中必须含有匹配的占位符,否则将抛出异常。

使用方法例如:

@GetMapping({"/{page}/{pageSize}","/delete/{page}/{pageSize}"})
public String toEmpPage(HttpServletRequest request,
                        @PathVariable("page") Integer page,
                        @PathVariable("pageSize") Integer pageSize) {
    //业务部分
}

上述代码即是把URL中的{page}和{pageSize}分别绑定到方法参数中的page和pageSize上。

@RequestHeader

用于把请求头中的参数绑定到方法参数上。
它有三个属性,和@RequestParam注解完全相同。

@CookieValue

用于把请求中的cookie绑定到方法参数上。
它的属性同上。

@ModelAttribute

@ModelAttribute的作用是把数据添加进模型对象中,供前端页面渲染使用。它的用法有很多:

  1. 入参处使用

    将@ModelAttribute注解标记在入参上,在渲染页面之前就会自动把标记的入参添加到模型对象中。

示例代码:

@Controller
public class TestController {

    @RequestMapping("/")
    public String welcome(@ModelAttribute("msg2")String msg2){
        return "index";
    }
}

前端代码:

<form th:action="@{/}" method="post">
    <label>
        msg2:
        <input type="text" name="msg2"/>
    </label>
    <input type="submit">
    <p th:text="${msg2}"></p><!--  前端提交表单后,后端再次返回该页面,并将上次输入的信息显示在此处   -->
</form>

结果:
发送之前:页面效果
发送之后:页面效果

  1. 方法上使用
    被@ModelAttribute注释的方法会在此controller的每个方法执行前被执行 ,如果有返回值,则自动将该返回值加入到ModelMap中。
    • 若注解没有返回值的方法,一般在方法体内部用model对象手动加入:
@Controller
public class TestController {

    @ModelAttribute
    public void before(@RequestParam String msg2, Model model){
        model.addAttribute("msg2",msg2);
    }

    @RequestMapping("/")
    public String welcome(){
        return "index";
    }
}
  • 若注解有返回值的方法,建议通过@ModelAttribute(value = “”)指定名称,若不指定,默认使用返回类型小写作为名称。
@Controller
public class TestController {

    @ModelAttribute//不指定名称,属性名为返回类型小写,即string
    public String before(@RequestParam String msg2){
        System.out.println(msg2);
        return msg2;
    }

    @RequestMapping("/")
    public String welcome(){
        return "index";
    }
}

@SessionAttributes

将ModelMap等方式传到前端的属性值共享到session中。

@SessionAttributes注解只能使用在类上,用于在多个请求之间传递参数,类似于Session的Attribute,但不完全一样,一般来说@SessionAttributes设置的参数只用于暂时的传递(存入sessionAttributeStore),而不是长期的保存,长期保存的数据还是要放到Session中。

有两种方式将ModelMap中的属性值共享到session中:

  • 使用注解的value属性:可以通过属性名指定需要放到会话中的属性;
  • 使用注解的types属性:还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。

@CrossOrigin

@RestController

@ControllerAdvice

@ControllerAdvice的作用也是声明一个控制层组件,通常用于全局异常处理、添加全局数据以及请求参数预处理。

  • 全局异常处理

    搭配@ExceptionHandler注解使用。@ExceptionHandler注解只有一个属性value,是一个Throwable类型的数组,它的作用是设置匹配异常的种类,当Controller层出现value数组内的异常时,将调用被@ExceptionHandler标记的方法。

    方法的参数可以包括异常实例、HttpServletRequest、HttpServletResponse、Model等;返回值可以是void、Json(需要用@ResponseBody标记)、ModelAndView(可以添加数据、设置视图名称)、甚至是逻辑视图名。

示例代码:

@ControllerAdvice
public class TestControllerAdvice {
    @ExceptionHandler(ArithmeticException.class)
    public ModelAndView exceptionHandler(){
        System.out.println("出现异常");
        ModelAndView model = new ModelAndView();
        model.addObject("msg2","出现异常!");
        model.setViewName("index");
        return model;
    }
}

@Controller
public class TestController {

    @RequestMapping("/")
    public String welcome(){
        int a = 1/0;
        return "index";
    }
}

浏览器访问该方法时触发ArithmeticException异常,返回页面如下:
页面

  • 添加全局数据

    搭配@ModelAttribute注解使用。@ModelAttribute标记一个方法,该方法的返回值将作为全局数据,所有Controller层方法和页面都可以访问到。

    示例代码如下:
    controllerAdvice:

    @ControllerAdvice
    public class TestControllerAdvice {
    
        @ModelAttribute(value = "user")//model中的key
        public Map<String,String> GlobalConfig(){
            HashMap<String,String> map = new HashMap<>();
            map.put("username", "dfy");
            map.put("age", "18");
            return map;//model中的value
        }
    }

    controller:

    @Controller
    public class TestController {
    
        @RequestMapping("/")
        public String welcome(Model model){
            Map<String, Object> map = model.asMap();
            Set<String> keySet = map.keySet();
            Iterator<String> iterator = keySet.iterator();
            while(iterator.hasNext()){
                String key = iterator.next();
                Object value = map.get(key);
                System.out.println(key + "=" + value);
            }
            return "index";
        }
    }

    页面:

    <p th:text="${user.username}"></p>
    <p th:text="${user.age}"></p>

    控制台输出为:
    user={age=18, username=dfy}
    页面输出为:
    页面

  • 请求参数预处理
    搭配@InitBinder注解使用。被@InitBinder标记的方法,参数中必须要含有WebDataBinder,具体用法如下:

    @InitBinder
    public void initBinder(WebDataBinder binder){
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    通过WebDataBinder类的registerCustomEditor方法来注册请求参数的预处理器,它的方法参数为:
    registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor)
    requiredType代表该预处理器处理的数据类型;propertyEditor代表预处理器,它的实现类有很多,这里不一一列举。
    自定义属性编辑器可以通过继承java.beans.PropertyEditorSupport类并重写其setAsText方法实现,最后调用setValue(Object Value)方法完成转换后的值的设置。

    还有其他花式用法,具体可参见博客https://blog.csdn.net/wang0907/article/details/108357696

元注解包括 @Retention @Target @Document @Inherited四种

@RequestMapping

@GetMapping和@PostMapping


文章作者: 山川大海
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 山川大海 !
  目录