1、编程式驱动 IOC 有什么特点?

编程式更贴近于Spring的底层原理,不管是SpringBoot原理还是Spring集成第三方框架,都是可以利用编程式驱动来完成。

2、编程式驱动与声明式 / 配置式的关联是什么?

编程式因为是Spring的底层原理,所以它也是声明式与配置式在Spring的底层处理方式。

3、编程式实战

github代码地址:​​编程式驱动IOC​

准备

抽象一个Animal类,接着有Cat、Dog和Rabbit三个子类

@Data
public abstract class Animal {
private String name;
private Person person;
}

public class Cat extends Animal{

public Cat(){
System.out.println("cat 初始化。。。。");
}
}

public class Dog extends Animal{

public Dog(){
System.out.println("dog 初始化。。。。");
}
}

public class Rabbit extends Animal{

public Rabbit(){
System.out.println("rabbit 初始化。。。。");
}
}

3.1、手动生成&注入BeanDefinition

测试应用:

public class Application {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

// 注册person
BeanDefinition personBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
personBeanDefinition.getPropertyValues().addPropertyValue("name","laowang");
context.registerBeanDefinition("laowang",personBeanDefinition);

// 注册cat,如果是属性是需要注入其他Bean实例对象的,需要在 BeanDefinitionBuilder 构建 BeanDefinition 时完成,不然只能添加常量的属性值
BeanDefinition catBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class)
.addPropertyValue("name","small cat")
.addPropertyReference("person","laowang")
.getBeanDefinition();
context.registerBeanDefinition("myCat",catBeanDefinition);

// 注入dog,测试原型bean ApplicationContext#refresh 方法执行时不会对myDog进行初始化,即不会构建原型Bean的实例,只有当获取Bean实例时才会开始初始化。
BeanDefinition dogBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class)
.addPropertyValue("name","small dog")
.addPropertyReference("person","laowang")
.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
.getBeanDefinition();
context.registerBeanDefinition("myDog",dogBeanDefinition);

// 注入rabbit,测试延迟加载 ApplicationContext#refresh 方法执行时不会对myRabbit进行初始化,即不会构建原型Bean的实例,只有当获取Bean实例时才会开始初始化。
BeanDefinition rabbitBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Rabbit.class)
.addPropertyValue("name","small rabbit")
.addPropertyReference("person","laowang")
.setLazyInit(true)
.getBeanDefinition();
context.registerBeanDefinition("myRabbit",rabbitBeanDefinition);

// 因为没有传入配置类,需要手动刷新
context.refresh();
System.out.println("application context refreshed.....");
Optional.ofNullable((Person) context.getBean("laowang")).ifPresent(System.out::println);
Optional.ofNullable((Cat) context.getBean("myCat")).ifPresent(System.out::println);
Optional.ofNullable((Dog) context.getBean("myDog")).ifPresent(System.out::println);
Optional.ofNullable((Rabbit) context.getBean("myRabbit")).ifPresent(System.out::println);
context.close();
}
}

3.2、利用 ClassPathBeanDefinitionScanner 根据指定路径查找&注入BeanDefinition

测试应用:

public class ClassPathScanApplication {

public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// ClassPathBeanDefinitionScanner 需要注入 BeanDefinitionRegistry
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(applicationContext);
// scanner 需要增加过滤条件,否则一个都扫描不出来
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
// getClassMetadata可以获取到扫描类的相关元信息,此处把Animal的子类都拿出来
// metadataReader 包含类元信息、注解元信息,所以我们可以联想到 @Component 等系列注解可以靠这个来完成注解注入组件
return metadataReader.getClassMetadata().getSuperClassName().equals(Animal.class.getName());
});
/**
* scan方法,会根据过滤器扫描到合适的类,然后生成BeanDefinitio并注入到IOC容器中
*/
/*int beanDefinitionCount = scanner.scan("com.github.howinfun.demo.ioc.program");
System.out.println("共扫描"+beanDefinitionCount+"个bean定义");*/

/**
* 返回所有符合扫描条件的BeanDefinition,但是这些BeanDefinition没有被注入到IOC容器中
*/
Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents("com.github.howinfun.demo.ioc.program");
// 手动注入
beanDefinitions.stream().forEach(beanDefinition -> applicationContext.registerBeanDefinition(beanDefinition.getBeanClassName(),beanDefinition));
// 手动调用refreshed
applicationContext.refresh();
System.out.println(applicationContext.getBean(Cat.class));
System.out.println(applicationContext.getBean(Dog.class));
}
}

3.3、XmlBeanDefinitionReader 扫描xml文件并注入BeanDefinition

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="laowang" class="com.github.howinfun.demo.ioc.program.Person">
<property name="name" value="老王"/>
</bean>
</beans>

测试应用:

public class XmlBeanDefinitionReaderApplication {

public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();;
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(applicationContext);
//reader.loadBeanDefinitions(new ClassPathResource("program/beans.xml"));
reader.loadBeanDefinitions("program/beans.xml");
applicationContext.refresh();
System.out.println(applicationContext.getBean(Person.class));
}
}