2. JavaConfig

空~2022年9月6日
  • SpringBoot
大约 6 分钟

2. JavaConfig

JavaConfig

JavaConfig: 使用 java 类作为 xml 配置文件的替代, 是配置 spring 容器的纯 java 的方式. 在这个 java 类这可以创建 java 对象, 把对象放入 spring 容器中(注入到容器), 该配置类本身也是组件

使用两个注解:

@Configuration: 放在一个类的上面, 表示这个类是作为配置文件使用的.

@Bean: 声明对象, 把对象注入到容器中, 默认为单实例.

@Configuration
public class MyConfig {

    /**
     * 创建方法, 方法的返回值是对象. 在方法的上面加入@Bean
     * 方法的返回值对象就注入到容器中.
     * `@Bean`: 把对象注入到spring容器中. 作用相当于 xml文件的 <bean> 标签
     * 位置: 方法的上面
     * 说明: `@Bean`,不指定对象的名称, 默认是方法名是 id
     */
    @Bean
    public Student createStudent() {
        Student s1 = new Student();
        s1.setName("张三");
        s1.setAge(26);
        s1.setSex("男");
        return s1;
    }

    /**
     * 指定对象在容器中的名称(指定<bean>的id属性)
     * `@Bean`的name属性, 指定对象的名称(id)
     */
    @Bean(name = "lisiStudent")
    public Student makeStudent() {
        Student s2 = new Student();
        s2.setName("李四");
        s2.setAge(22);
        s2.setSex("男");
        return s2;
    }
}

获取 bean 实例对象

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        // 获取容器内的对象, 默认单实例
        Student createStudent = run.getBean("createStudent", Student.class);
        System.out.println(createStudent);

        Student lisiStudent = run.getBean("lisiStudent", Student.class);
        System.out.println(lisiStudent);
    }
}

尝试获取配置类本身

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        MyConfig myConfig = run.getBean(MyConfig.class);
        System.out.println(myConfig);
    }
}

com.example.config.MyConfig$$EnhancerBySpringCGLIB$$78d620be@3122b117

如果 @Configuration(proxyBeanMethods = true) 该配置类会被 SpringCGLIB 增强为代理对象, SpringBoot 总会检查这个组件是否在容器中.

所以通过配置类直接调用 createStudent() 方法返回的对象也会是单实例的.

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        MyConfig myConfig = run.getBean(MyConfig.class);
        Student student = myConfig.createStudent();
        Student student1 = myConfig.createStudent();
        System.out.println(student == student1);
    }
}

指定 @Configuration(proxyBeanMethods = false) 后 MyConfig 类将会变成一个普通类, 通过该类调用 createStudent() 方法将直接创建新对象.

@Configuration(proxyBeanMethods = false)
public class MyConfig {

    @Bean
    public Student createStudent() {
        return new Student();
    }
}

测试

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        MyConfig myConfig = run.getBean(MyConfig.class);
        Student student = myConfig.createStudent();
        Student student1 = myConfig.createStudent();
        System.out.println(myConfig);
        System.out.println(student == student1);
    }
}

com.example.config.MyConfig@469d003c false

Full 模式与 Lite 模式:

Full(proxyBeanMethods = true) [保证每个@Bean 方法被调用多少次返回的组件都是单实例的]

Lite(proxyBeanMethods = false) [每个@Bean 方法被调用多少次返回的组件都是新创建的]

组件依赖必须使用 Full 模式默认, 其他默认是否 Lite 模式

@Import

该注解会自动创建导入的组件, 参数是一个数组, 默认组件为全类名

@Import({Student.class})
@Configuration
public class MyConfig {
}

获取组件

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        System.out.println(run.getBean(Student.class));
    }
}

Student(id=null, name=null, age=null)

@Conditional

条件装配:满足指定的条件,则进行组件注入

@Conditional 本身是个根注解, 底下派生了非常多的子注解

idea 可使用 Ctrl + N 搜索 @Conditional Ctrl + H 打开继承树查看

image-20220921200352655

当容器内存在名字为 school 的组件时才注册 student 组件

@Configuration
public class MyConfig {
    // 当容器内存在名字为 school 的组件时才注册 student 组件
    @ConditionalOnBean(name = "school")
    @Bean(name = "student")
    public Student getStudent() {
        return new Student();
    }
}

使用 containsBean() 方法验证容器内是否存在该组件

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        System.out.println(run.containsBean("school"));
        System.out.println(run.containsBean("student"));
    }
}

false false

如果 @ConditionalOnBean(name = "school") 标注在类上, 则对整个类生效

// 当容器内存在名字为 school 的组件时才配置类才会加载组件
@ConditionalOnBean(name = "school")
@Configuration
public class MyConfig {}

相关信息

SpringBoot 底层有非常多的条件装配, SpringBoot 的按需加载就是使用条件装配实现的.

@ImporResource

@ImportResource 作用是导入其他的 xml 配置文件, 一般用于适配老项目.

xml 实现方式:

<import resources="其他配置文件"/>

SpringBoot 实现方式:

@ImportResource 底层实现是一个数组:

public @interface ImportResource {
    @AliasFor("locations")
    String[] value() default {};

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

    Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;
}

所以可以同时导入多个配置文件:

@Configuration
@ImportResource(value ={"classpath:applicationContext.xml","classpath:beans.xml"})
public class SpringConfig {
}

使用 SpringBoot 配置文件实例化一个 配置文件内的 bean:

image-20220913192101427

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="myCat" class="com.example.bean.Cat">
        <property name="name" value="myCat"/>
        <property name="age" value="20"/>
        <property name="carId" value="1"/>
    </bean>
</beans>

在配置类里面导入:

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class MyConfig {}

测试:

@Test
void contextLoads3() {
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
    Cat myCat = context.getBean("myCat", Cat.class);
    System.out.println(myCat);
}

@PropertyResource

@PropertyResource: 读取 properties 属性配置文件, 进行配置绑定.

使用属性配置文件可以实现外部化配置, 在程序代码之外提供数据.

只有在容器中的组件,才会拥有 SpringBoot 提供的强大功能

@Configuration
@PropertySource("classpath:config.properties")
@ComponentScan("com.example.bean")
public class MyConfig {}

步骤:

  1. 在 resources 目录下, 创建 properties 文件, 使用 k=v 的格式提供数据
  2. 在 PropertyResource 指定 properties 文件的位置
  3. 使用 @Value(value="${key}")

先确定项目文件的编码类型

image-20220913194456872

创建一个 Tiger 类, 并实例化到 spring 容器中

使用 @Value(value="${key}") 指定 K

@Data
@Component("tiger") // 只有在容器中的组件,才会拥有SpringBoot提供的强大功能.
public class Tiger {
    @Value("${tiger.name}")
    private String name;
    @Value("${tiger.age}")
    private int age;
}

创建一个 properties 配置文件

在配置文件中对 tiger 实例的属性赋值, 指定 V

tiger.name=东北虎
tiger.age=2

@ConfigurationProperties

和 @PropertyResource 一样, 都是读取 properties 属性配置文件, 进行配置绑定, 但 @ConfigurationProperties 是作用在类实例上

使用要求也一样: 只有在容器中的组件,才会拥有 SpringBoot 提供的强大功能

application.properties

student.id=1
student.name=Student
student.age=20

使用 @ConfigurationProperties(prefix = "student") 指定前缀

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component // 只有在容器中的组件,才会拥有SpringBoot提供的强大功能.
@ConfigurationProperties(prefix = "student")
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

测试

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        System.out.println(run.getBean("student"));
    }
}

Student(id=1, name=Student, age=20)

@ConfigurationProperties 的第二种实现: 配合 @EnableConfigurationProperties 注解

在配置类上使用 @EnableConfigurationProperties 注解指定实例类开启配置绑定功能

@Configuration
@EnableConfigurationProperties(Student.class)
public class MyConfig {
}

类实例就不需要 @Component 注解

@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "student")
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

@EnableConfigurationProperties 的两个功能

开启 Student 配置绑定功能 把 Student 这个组件自动注册到容器中