对SpringBoot后续的再补充学习:
使用IDEA创建项目不勾选任何组件
默认的POM结构:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.dai</groupId> <artifactId>spirngboot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spirngboot</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- 运行核心 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <!-- 测试环境支持 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <!-- 项目打包插件 --> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
所谓的组件也就是一个个的组件坐标,我们可以自行配置。
这里的一些常用组件可以在IDEA初始化项目之后放POM里面:
<!-- 实体类简化辅助 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- MVC支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 热部署与开发帮助支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!-- 配置文件依赖提示支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency>
BeanDefinition对象
在之前的Spring理解是:
一个类ClassA.java注解了SpringBean,或者使用了Spring的XML配置,通过注解和XML配置的信息
我们就可以得到一个Bean对象,这个Bean对象由Spring管理和创建
但是现在不一样,这个实例是由BeanDefinition创建而来:
Bean定义对象存储了Bean对象的描述信息
OriginClass.java | BeanDefinition.java | BeanOriginClass.java
所在位置:
是一个接口,其实现类众多
org.springframework.beans.factory.config.BeanDefinition
属性:
String getParentName(); 父类名称
String getBeanClassName(); Bean的类名称
getScope(); 获取对象模式,单利还是原型
isLazyInit(); 是否懒加载
...
如何证明BeanDefinition创建
首先是一个类A,标注了Spring组件:
@Component public class ClassA { public ClassA() { System.out.println("A实例被创建"); } }
其次一个类B组合了类A也标注了组件
但是类A属性不自动装配:
@Component public class ClassB { private ClassA classA; public ClassB() { System.out.println("B实例被创建"); } public ClassA getClassA() { return classA; } public void setClassA(ClassA classA) { this.classA = classA; } }
注册配置:
@Configurationpublic class TestConfiguration { @Bean public ClassB classB() { return new ClassB(); } @Bean public ClassA classA() { return new ClassA(); } }
我们运行测试类很正常的发现在类B中的类A得到的是空
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfiguration.class) public class DefinitionTest { @Autowired private ClassB classB; @Test public void testSample() { System.out.println(classB.getClassA()); } }
结果:
B实例被创建 A实例被创建 null Process finished with exit code 0
我们可以通过定义对象去改变:
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { // GenericBeanDefinition classAGenericBeanDefinition = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition("classB"); // classAGenericBeanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); RootBeanDefinition classARootBeanDefinition = (RootBeanDefinition)configurableListableBeanFactory.getBeanDefinition("classB"); classARootBeanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); } }
配置类加载:
@Configuration @Import(MyBeanFactoryPostProcessor.class) public class TestConfiguration { @Bean public ClassB classB() { return new ClassB(); } @Bean public ClassA classA() { return new ClassA(); } }
测试结果类A实例能够获取:
B实例被创建
A实例被创建。。。
cn.dzz.bean.ClassA@587e5365
Process finished with exit code 0
但是注意类B需要提供对应的GETTER & SETTER
在上面的定义修改中,Generic的实现类并不能被运行:
Caused by:
java.lang.ClassCastException:
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition
cannot be cast to org.springframework.beans.factory.support.GenericBeanDefinition
并不能强转为这个$ConfigurationClassBeanDefinition
所以需要查看下面有哪些子实现类
查看方式是点击这里:
Bean构造器参数值注入 ConstructorArgumentValues
这里有一个配置的问题,我们的类注册只能这样注解才能让定义处理器生效
就是让Spring以包扫描的方式进行
@Configuration @ComponentScan("cn.dzz") public class TestConfiguration { }
不能以Import方式和@Bean配置类写方法的方式进行导入
类C的构造器声明:
@Component public class ClassC { public ClassC() { System.out.println("无参数"); } public ClassC(int i) { System.out.println("一个整数参数"); } public ClassC(String i,int ii) { System.out.println("一个字符参数,一个整数参数"); } }
构造器方法注入定义修改
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { GenericBeanDefinition classAGenericBeanDefinition = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition("classB"); classAGenericBeanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE); // RootBeanDefinition classARootBeanDefinition = (RootBeanDefinition)configurableListableBeanFactory.getBeanDefinition("classB"); // classARootBeanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); // RootBeanDefinition classCRootBeanDefinition = (RootBeanDefinition)configurableListableBeanFactory.getBeanDefinition("classC"); // ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); // constructorArgumentValues.addIndexedArgumentValue(0,"哈哈哈"); // constructorArgumentValues.addIndexedArgumentValue(1,233); // classCRootBeanDefinition.setConstructorArgumentValues(constructorArgumentValues); GenericBeanDefinition classCGenericBeanDefinition = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition("classC"); ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); constructorArgumentValues.addIndexedArgumentValue(0,"哈哈哈"); constructorArgumentValues.addIndexedArgumentValue(1,233); classCGenericBeanDefinition.setConstructorArgumentValues(constructorArgumentValues); } }
测试结果:
A实例被创建 B实例被创建 一个字符参数,一个整数参数 cn.dzz.bean.ClassA@2638011 Process finished with exit code 0
Scope定义的修改
首先默认取两个对象测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfiguration.class) public class DefinitionTest { @Autowired private ClassB classB; @Autowired private ClassC classC01; @Autowired private ClassC classC02; @Test public void testSample() { System.out.println(classB.getClassA()); System.out.println(classC01 == classC02); } }
结果为True,因为我们知道Spring默认了Bean的Scope为单例Singleton
A实例被创建 B实例被创建 一个字符参数,一个整数参数 一个字符参数,一个整数参数 cn.dzz.bean.ClassA@117e949d true Process finished with exit code 0
但是在上面的自定义的定义处理器更改Scope为原型模式之后则为False了
classCGenericBeanDefinition.setScope("prototype");
结果:
A实例被创建 B实例被创建 一个字符参数,一个整数参数 一个字符参数,一个整数参数 cn.dzz.bean.ClassA@117e949d false Process finished with exit code 0