二、底层注解
目录
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 |
ConditionalOnDefaultWebSecurity | Web 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} 组件注册到容器中
- 必须写在配置类上