ConfigurationClassPostProcessor 之 @Configuration 和 @Component
发现问题
@Configuration 和 @Component 都可以添加在类上面,从而被 Spring 容器实例化,那么二者之间有什么区别呢?
@Configuration 和 @Component 注解的异同
两个都会被 Spring 实例化,主要区别如这个类中所示:
在 StudentFactory 中有 this.student(); 那么问题来了,通过 applicationContext.getBean(Student.class) 和 applicationContext.getBean(StudentFactory.class).getStudent() 拿到的 Student 是同一个对象吗(Hash 值是否一样)?
分两种情况:
- 如果这个类加的 @Configuration 注解,实例化 Student 时,会走代理生成代理对象,此时两种打印方式都是从 factoryBean.getObject() 获取的,是单例的,因此一样。
- 如果这个类加的 @Component 注解,那么 this.student() 就是一个普通方法,重新创建了一个对象,因此打印的结果就不一样,破坏了 Spring 的单例,这是错误的用法。
总结:@Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
java
package com.github.mengweijin.mybatisplus.demo.configuration;
import com.github.mengweijin.mybatisplus.demo.configuration.entity.Student;
import com.github.mengweijin.mybatisplus.demo.configuration.entity.StudentFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
//@Component
@Configuration
public class StudentConfiguration {
@Bean
public Student student() {
return new Student();
}
@Bean
public StudentFactory studentFactory() {
StudentFactory factory = new StudentFactory();
factory.setStudent(student());
return factory;
}
}
java
/**
* 注意:测试的时候,不要用 Lombok 加 @Data 注解,这个会重写 Student 和 StudentFactory 的 hashcode 方法,造成观测结果不正确。
* 也不能手动重写 hashcode 方法。
* 可以使用 == 来判断两个对象是不是同一个对象(相同的 hashcode)。
*/
@Getter @Setter
public class StudentFactory {
private Student student;
}
@Getter @Setter
public class Student {
}
测试
- 当 StudentConfiguration 类上面是 @Configuration 时,单元测试通过。
- 当 StudentConfiguration 类上面是 @Component 时,单元测试报错。 说明 Spring 容器中的 Student 对象和 StudentFactory 中持有的不是同一个对象。
所以,当我们需要保持为同一个对象注入到 StudentFactory 时,需要使用 @Configuration 注解。
java
package com.github.mengweijin.mybatisplus.demo.configuration;
import com.github.mengweijin.mybatisplus.demo.configuration.entity.Student;
import com.github.mengweijin.mybatisplus.demo.configuration.entity.StudentFactory;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@Slf4j
public class TestConfigurationAndComponentAnnotation {
@Test
public void testComponentAnnotation() {
String basePackages = "com.github.mengweijin.mybatisplus.demo.configuration";
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(basePackages);
// 看下面这两个打印的 HashCode 是否一样
Student student = applicationContext.getBean(Student.class);
StudentFactory studentFactory = applicationContext.getBean(StudentFactory.class);
Student student2 = studentFactory.getStudent();
Assertions.assertTrue(student == student2);
Assertions.assertTrue(student.hashCode() == student2.hashCode());
}
}