二、底层注解

本文详细解读了Spring Boot中的关键注解如@Configuration、@Bean、@Conditional和@ConfigurationProperties,以及不同模式的使用示例。了解如何利用这些注解进行组件导入、条件依赖和配置绑定,提升应用配置管理效率。
摘要由CSDN通过智能技术生成

二、底层注解

2.1 容器功能

2.1.1. @Configuration

2.2.1.1. 基本使用

User.java

package cn.com.springboot.demo.bean;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
 * 用户信息.
 *
 * @author J
 */
@Setter
@Getter
@ToString
@NoArgsConstructor
public class User {

    /** 姓名 */
    private String name;

    /** 年龄 */
    private String age;

    public User(String name, String age) {
        this.name = name;
        this.age = age;
    }
}

Pet.java

package cn.com.springboot.demo.bean;

import lombok.*;

/**
 * 宠物信息.
 *
 * @author J
 */
@Setter
@Getter
@ToString
@NoArgsConstructor
public class Pet {

    /**
     * 品种
     */
    private String breed;

    /**
     * 昵称
     */
    private String petName;

    public Pet(String breed, String petName) {
        this.breed = breed;
        this.petName = petName;
    }
}

MyConfig01.java

package cn.com.springboot.demo.config;

import cn.com.springboot.demo.bean.Pet;
import cn.com.springboot.demo.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 第一个配置类示例。
 *
 * @author J
 */
@Configuration
public class MyConfig01 {

    /**
     * 声明一个 ID 为 user01 的组件。
     */
    @Bean
    public User user01() {
        return new User("张三", "27");
    }

    /**
     * 声明一个 ID 为 myPet 的组件。
     */
    @Bean("myPet")
    public Pet pet01() {
        return new Pet("cat", "27");
    }
}

  • @Configuration:告诉 spring-boot 这是一个配置类,也就是配置文件
  • @Bean:将该方法注册为一个组件,添加到容器中
  • 组件 ID:方法名就是这个组件的组件 ID
  • 返回值:组件在容器中实例
  • @Bean(“myPet”):定义 ID 为 myPet 的组件
  • 配置类本身也是一个组件
  • 注册的组件,默认是单实例的

MainApp.java

package cn.com.springboot.demo;

import cn.com.springboot.demo.bean.Pet;
import cn.com.springboot.demo.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MainApp {

    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);

        User user01 = run.getBean("user01", User.class);
        Pet myPet = run.getBean("myPet", Pet.class);

        System.out.println(user01);
        System.out.println(myPet);
    }
}

运行结果

User(name=张三, age=27)
Pet(breed=cat, petName=27)
2.2.1.2. 全模式(Full Mode)和轻量级模式(Lite Mode)

@Configuration 注解在 spring-boot 2 中多了以下的属性:

...
boolean proxyBeanMethods() default true;
...

默认值是 true 。如果 @Configuration(proxyBeanMethods = true) ,那么则是使用代理对象调用方法。spring-boot 总会检查这个组件是否存在容器中,如果存在则返回该组件,以此保证组件的单实例。如果 @Configuration(proxyBeanMethods = false),则会跳过上述检查,每次在调用组件时,都会返回新的组件。

  • Full Mode: proxyBeanMethods = true
    • 配置的组件如果有依赖关系,方法调用单实例组件
  • Lite Mode:proxyBeanMethods = false
    • 配置的组件如果没有依赖关系,跳过检查过程,加速容器启动

示例代码

proxyBeanMethods = true 的情况:

MyConfig01.java

package cn.com.springboot.demo.config;

import cn.com.springboot.demo.bean.Pet;
import cn.com.springboot.demo.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 第一个配置类示例。
 *
 * @author J
 */
@Configuration(proxyBeanMethods = true)
public class MyConfig01 {

    /**
     * 声明一个 ID 为 user01 的组件。
     */
    @Bean
    public User user01() {
        return new User("张三", "27");
    }

    /**
     * 声明一个 ID 为 myPet 的组件。
     */
    @Bean("myPet")
    public Pet pet01() {
        return new Pet("cat", "27");
    }
}

MainApp.java

package cn.com.springboot.demo;

import cn.com.springboot.demo.bean.User;
import cn.com.springboot.demo.config.MyConfig01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MainApp {

    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);

        // 测试 MyConfig01 的 Full Mode 和 Lite Mode
        MyConfig01 bean = run.getBean(MyConfig01.class);
        User user01 = bean.user01();
        User user02 = bean.user01();
        System.out.println("Full Mode:" + (user01 == user02));
    }
}

运行结果

Full Mode:true

proxyBeanMethods = false 的情况:

MyConfig01.java

package cn.com.springboot.demo.config;

import cn.com.springboot.demo.bean.Pet;
import cn.com.springboot.demo.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 第一个配置类示例。
 *
 * @author J
 */
@Configuration(proxyBeanMethods = false)
public class MyConfig01 {

    /**
     * 声明一个 ID 为 user01 的组件。
     */
    @Bean
    public User user01() {
        return new User("张三", "27");
    }

    /**
     * 声明一个 ID 为 myPet 的组件。
     */
    @Bean("myPet")
    public Pet pet01() {
        return new Pet("cat", "27");
    }
}

MainApp.java

package cn.com.springboot.demo;

import cn.com.springboot.demo.bean.User;
import cn.com.springboot.demo.config.MyConfig01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MainApp {

    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);

        // 测试 MyConfig01 的 Full Mode 和 Lite Mode
        MyConfig01 bean = run.getBean(MyConfig01.class);
        User user01 = bean.user01();
        User user02 = bean.user01();
//        System.out.println("Full Mode:" + (user01 == user02));
        System.out.println("Lite Mode:" + (user01 == user02));
    }
}

运行结果

Lite Mode:false
2.2.1.3. 其他同类型注解

以前用于注册组件的注解也是可以使用的,如:

@Component          // 泛指组件,将该组件交给 Spring 管理
@Controller         // 作用于 controller 上,将该组件交给 Spring 管理
@Service            // 作用于逻辑实现类上,将该实现类交给 Spring 管理
@Repository         // 作用于持久层的接口上,将该接口的实现类交给 Spring 管理
@ComponentScan      // 定义包扫描规则

2.1.2. @Import

2.2.1.1. 基本使用

通过调用指定类的无参构造,将指定类型的组件导入到容器中,可以使用数组传递多个。如:

示例

MyConfig01.java

package cn.com.springboot.demo.config;

/**
 * 第二个配置类示例。
 *
 * 演示 @Import
 * @author J
 */

import ch.qos.logback.core.db.DBHelper;
import cn.com.springboot.demo.bean.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import({User.class, DBHelper.class})
@Configuration
public class MyConfig02 {}

MainApp.java

package cn.com.springboot.demo;

import ch.qos.logback.core.db.DBHelper;
import cn.com.springboot.demo.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.Arrays;

@SpringBootApplication
public class MainApp {

    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);

        // 测试 MyConfig01 的 Full Mode 和 Lite Mode
//        MyConfig01 bean = run.getBean(MyConfig01.class);
//        User user01 = bean.user01();
//        User user02 = bean.user01();
//        System.out.println("Full Mode:" + (user01 == user02));
//        System.out.println("Lite Mode:" + (user01 == user02));
//        System.out.println("---------------------------------------");

        String[] beans = run.getBeanNamesForType(User.class);
        Arrays.asList(beans).forEach(System.out::println);

        String[] beans2 = run.getBeanNamesForType(DBHelper.class);
        Arrays.asList(beans2).forEach(System.out::println);
    }
}

运行结果

cn.com.springboot.demo.bean.User
ch.qos.logback.core.db.DBHelper
  • @Import 导入组件的默认名字:全类名
    • 例:ch.qos.logback.core.db.DBHelper

2.1.3. @Conditional

满足一定的条件时,才向容器中注入组件。@Conditional 存在很多派生注解,如:

派生注解名说明
ConditionalOnWebApplication当前项目是Web项目
ConditionalOnJava基于 Java 版本
ConditionalOnJndi基于 JNDI 存在
ConditionalOnWarDeployment当应用程序为传统 WAR 部署
ConditionalOnSingleCandidate当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选Bean
ConditionalOnProperty指定的属性是否有指定的值
Profile指定的 Profile 被激活
ConditionalOnMissingBean当容器里没有指定 Bean
ConditionalOnCloudPlatform当指定的云平台处于活动状态
ConditionalOnClass当类路径下有指定类
ConditionalOnMissingClass当类路径下没有指定类
ConditionalOnResource类路径是否有指定的值
ConditionalOnRepositoryType已启用的特定类型的 Spring 数据仓库
ConditionalOnNotWebApplication当前项目不是Web项目
ConditionalOnBean当容器里有指定 Bean
ConditionalOnDefaultWebSecurityWeb Security 可用并且用户没有自定义配置
ConditionalOnEnabledResourceChain检查Spring资源处理链是否启用。匹配WebProperties.Resources.Chain.getEnabled()是否为真,或者webjar -locator-core是否在类路径上
ConditionalOnExpression基于 SpEL 表达式

示例(ConditionalOnBean,当存在 pet03 时,才向容器中添加 user01)

MyConfig03.java

package cn.com.springboot.demo.config;

import cn.com.springboot.demo.bean.Pet;
import cn.com.springboot.demo.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 第三个配置类示例。
 *
 * 演示 @Conditional
 * @author J
 */
@Configuration
public class MyConfig03 {

    /**
     * 声明一个 ID 为 user03 的组件。
     * 当存在 pet03 时,才向容器中添加 user01
     */
    @ConditionalOnBean(name = "pet01")
    @Bean
    public User user03() {
        return new User("张三", "27");
    }

    /**
     * 声明一个 ID 为 pet 的组件。
     */
    @Bean("pet")
    public Pet pet03() {
        return new Pet("cat", "coco");
    }
}

MainApp.java

package cn.com.springboot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MainApp {

    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);

        // 测试 MyConfig03 的 @ConditionalOnBean
        System.out.println(run.containsBean("user03"));
    }
}

运行结果

false
  • @ConditionalOnBean(name = “pet01”) 可以加在方法上也可以加在类上
    • 方法上:当条件不满足时,只有当前方法不会注册
    • 类上:当条件不满足时,整个类中的组件都不会注册

2.1.4. @ImportResource

导入原生的配置文件。如果之前就存在使用 XML 配置组件的方式或者第三方使用 XML 配置的组件方式,需要向项目中导入此类配置时使用。如:

示例

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user04" class="cn.com.springboot.demo.bean.User">
        <property name="name" value="张三" />
        <property name="age" value="21" />
    </bean>

    <bean id="pet04" class="cn.com.springboot.demo.bean.Pet">
        <property name="breed" value="cat" />
        <property name="petName" value="colina" />
    </bean>
</beans>

Myconfig04.java

package cn.com.springboot.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

/**
 * 第四个配置类示例。
 *
 * 演示 @ImportResource
 * @author J
 */
@Configuration
@ImportResource("classpath:beans.xml")
public class MyConfig04 {}

MainApp.java

package cn.com.springboot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MainApp {

    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);
        
        // 测试 MyConfig04 的 @ImportResources
        System.out.println(run.containsBean("user04"));
        System.out.println(run.containsBean("pet04"));
    }
}

运行结果

true
true

2.1.5. @ConfigurationProperties

配置绑定。在开发中一般将一些变化的东西写在 properties 文件中。如,DB连接信息等。

2.1.5.1. @Component + @ConfigurationProperties

示例

Car.java

package cn.com.springboot.demo.bean;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 车辆信息.
 *
 * @author J
 */
@Setter
@Getter
@ToString
@NoArgsConstructor
@Component
@ConfigurationProperties("car01")
public class Car {

    /** 牌子 */
    private String brand;

    /** 价格 */
    private String price;

    public Car(String brand, String price) {
        this.brand = brand;
        this.price = price;
    }
}

application.properties

car01.brand=BMW
car01.price=1200000

CarController.java

package cn.com.springboot.demo.controller;

import cn.com.springboot.demo.bean.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CarController {

    @Autowired
    private Car car;

    @RequestMapping("/car")
    public Car myCar() {
        return car;
    }
}

运行结果

在这里插入图片描述

  • @Component:将 Car 注册为一个组件,才能拥有 spring-boot 提供的功能
  • @ConfigurationProperties:指定配置文件的前缀
2.1.5.1. @ConfigurationProperties

示例

Car.java

package cn.com.springboot.demo.bean;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 车辆信息.
 *
 * @author J
 */
@Setter
@Getter
@ToString
@NoArgsConstructor
// @Component
@ConfigurationProperties("car02")
public class Car {

    /** 牌子 */
    private String brand;

    /** 价格 */
    private String price;

    public Car(String brand, String price) {
        this.brand = brand;
        this.price = price;
    }
}

application.properties

car02.brand=LEXUS
car02.price=1800000

CarController.java

package cn.com.springboot.demo.controller;

import cn.com.springboot.demo.bean.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CarController {

    @Autowired
    private Car car;

    @RequestMapping("/car")
    public Car myCar() {
        return car;
    }
}

MyConfig05.java

package cn.com.springboot.demo.config;

import cn.com.springboot.demo.bean.Car;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 第五个配置类示例。
 *
 * 演示 @EnableConfigurationProperties
 * @author J
 */
@Configuration
@EnableConfigurationProperties(Car.class)
public class MyConfig05 {}

运行结果

在这里插入图片描述

  • @ConfigurationProperties:指定配置文件的前缀
  • @EnableConfigurationProperties(${value}):
    • 开启指定 ${value} 的配置绑定功能
    • 将 ${value} 组件注册到容器中
    • 必须写在配置类上
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值