Spring 作为一种IoC容器最大的目的就是降低组件与组件之间的依赖关系。而BeanFactory作为整个Spring IoC容器的核心使用了一种统一的方法来装配所有的应用对象,通过Java的反射机制和依赖注入来实现这一装配过程,使受之管理的对象无需知道Spring的存在,同时也降低了组件间的依赖程度,同时受管对象的生命周期由BeanFactory来统一管理,开发者就不用实现大量的自制的工厂Singleton。
BeanFactory以Factory结尾,表示它是一个工厂类(接口),是负责生产和管理bean的一个工厂,同时也是IoC容器的核心接口,
接口分析详看:https://www.cnblogs.com/zrtqsk/p/4028453.html
BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean,BeanFactory的功能通过其他的接口得到不断扩展。下面对上图涉及到的其他接口分别进行说明。
注意:BeanFactory 有众多的实现,在 Spring 3.2 之前的版本中,最常用的是 XmlBeanFactory,现已被废弃。官方建议使用 XmlBeanDefinitionReader 与 DefaultListableBeanFactory。
代码结构
People.java
public class People {
private String name;
private int age;
// 省略getter、setter
@Override
public String toString() {
return "姓名:" + getName() + ",年龄:" + getAge();
}
}
beans.xml
Main.java
public class Main {
public static void main(String[] args) throws IOException {
// 读取XML配置文件并启动容器
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("classpath:beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
// 获取bean
People people = factory.getBean("zhangsan", People.class);
System.out.println(people.toString());
}
}
1、通过 BeanFactory 启动 IoC 容器时,并不会初始化配置文件当中定义的 Bean,初始化的动作发生在第一次调用某个 Bean 的时候。对于单实例的 Bean 来说,BeanFactory 会缓存 Bean 的实例,所以第二次使用 getBean() 方法时,可以直接从 IoC 容器的缓存中来获取 Bean 的实例了。而 ApplicationContext 在初始化应用上下文时,就已经实例化所有单实例的 Bean。因此,ApplicationContext 的初始化时间会比 BeanFactory 的时间要稍微长一些。
2、在初始化 BeanFactory 时,必须为它提供一种日志框架。
网上搜到的很多教程都是用XmlBeanFactory来装载XML文件的,但是我上面的例子并没有用到。
因为XmlBeanFactory类从Spring 3.1开始已经被弃用
官方推荐使用DefaultListableBeanFactory和XmlBeanDefinitionReader
但这里还是列出XmlBeanFactory的两种方法
// 1通过文件路径找XML
// 1通过文件路径找XML
Resource resource = new FileSystemResource("D:\\demo\\src\\main\\resources\\beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
// 2通过classpath找XML
// 2通过classpath找XML
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置实现。
BeanFactorty接口提供了配置框架及基本功能,但是无法支持Spring的AOP功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,相较于BeanFactorty,ApplicationContext还提供了以下的功能:
如上图所示,ApplicationContext 继承了一些接口,通过这些接口扩展了 BeanFactory 的功能。这类接口包括:
ApplicationContext支持两种模式
1、XML模式
2、注解模式
代码结构
People.java
public class People {
private String name;
private int age;
// 省略getter、setter
@Override
public String toString() {
return "姓名:" + getName() + ",年龄:" + getAge();
}
}
beans.xml
Main.java
public class Main {
public static void main(String[] args) {
// 从类路径加载XML
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
BeanFactory factory = context;
People people = factory.getBean("zhangsan", People.class);
System.out.println(people.toString());
}
}
ApplicationContext 的主要实现类是:
// 从类路径加载配置文件
// 从类路径加载配置文件
ApplicationContext context1 = new ClassPathXmlApplicationContext("beans.xml");
// 从文件系统加载配置文件
// 从文件系统加载配置文件
ApplicationContext context = new FileSystemXmlApplicationContext("D:/beans.xml");
还可以指定一组的配置文件,Spring 会自动将多个配置文件中的内容整合起来:
// 指定一组的配置文件
// 指定一组的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext(new
String[]{"beans.xml", "beans2.xml"});
Tips:ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext 也可以显式指定带资源类型前缀的路径。
Spring从3.0开始支持基于类注解的配置方式,一个标注@Configuration注解的POJO即可提供Spring所需的Bean配置信息
和基于XML文件配置方式的优势在于,类注解的配置方式可以很容易地让开发者控制Bean的初始化过程,比基于XML的配置更加灵活。Spring为基于注解类的配置提供了专门的ApplicationContext实现类:AnnotationConfigApplicationContext。
People.java
public class People {
private String name;
private int age;
// 省略getter、setter
@Override
public String toString() {
return "姓名:" + getName() + ",年龄:" + getAge();
}
}
Beans.java
@Configuration
public class Beans {
@Bean(name = "zhangsan")
public People buildPeople() {
People people = new People();
people.setName("张三");
people.setAge(23);
return people;
}
}
Main.java
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Beans.class);
People people = context.getBean("zhangsan", People.class);
System.out.println(people.toString());
}
}
可以看到,ApplicationContext继承了BeanFactory,BeanFactory是Spring中比较原始的Factory,它不支持AOP、Web等Spring插件,而ApplicationContext不仅包含了BeanFactory的所有功能,还支持Spring的各种插件,还以一种面向框架的方式工作以及对上下文进行分层和实现继承。
BeanFactory是Spring框架的基础设施,面向Spring本身;而ApplicationContext面向使用Spring的开发者,相比BeanFactory提供了更多面向实际应用的功能,几乎所有场合都可以直接使用ApplicationContext而不是底层的BeanFactory。
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
举个例子:如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;
这样,我们就不能发现一些存在的Spring的配置问题。
而ApplicationContext则相反,它是自身初始化的时候,就会一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean
一般情况下,Spring通过反射机制利用
有些情况我们需要自己的工厂类,通过这个工厂类可以生成相应的对象。而通过实现FactoryBean接口,我们可以实现自己的工厂类
传统的Spring容器加载一个Bean的整个过程,都是由Spring控制的,换句话说,开发者除了设置Bean相关属性之外,是没有太多的自主权的。FactoryBean改变了这一点,开发者可以个性化地定制自己想要实例化出来的Bean,方法就是实现FactoryBean接口。
看一下代码例子,为了讲清楚FactoryBean,内容相对多一些,首先定义一个接口Animal:
代码结构
Animal.java
public interface Animal {
public void move();
}
定义两个实现类Monkey和Tiger:
Monkey.java
public class Monkey implements Animal {
public void move() {
System.out.println("Monkey move!");
}
}
Tiger.java
public class Tiger implements Animal {
public void move() {
System.out.println("Tiger move!");
}
}
写一个实现类,实现FactoryBean接口:
AnimalFactoryBean.java
public class AnimalFactoryBean implements FactoryBean {
private String animal;
public Animal getObject() {
if ("Monkey".equals(animal)) {
return new Monkey();
} else if ("Tiger".equals(animal)) {
return new Tiger();
} else {
return null;
}
}
public Class> getObjectType() {
return Animal.class;
}
public boolean isSingleton() {
return true;
}
public void setAnimal(String animal) {
this.animal = animal;
}
}
配置一个fb2.xml,注入属性Tiger:
Main.java
public class Main {
public static void main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("fb2.xml");
Animal animal = (Animal) factory.getBean("animal");
animal.move();
}
}
当配置文件中
那么如果就是想获得FactoryBean类型呢,只需要加上”&”符号,具体看下面代码
还是上面的例子,只修改Main.java
public class Main {
public static void main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("fb2.xml");
// Animal animal = (Animal) factory.getBean("animal");
// animal.move();
Object animal = factory.getBean("animal");
Object animalFactoryBean = factory.getBean("&animal");
System.out.println("animal类型:" + animal.getClass().getTypeName());
System.out.println("animalFactoryBean类型:" + animalFactoryBean.getClass().getTypeName());
}
}
http://liolin.com/2016/11/13/Spring%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%20IoC%20%E5%AE%B9%E5%99%A8%E4%B9%8B%20BeanFactory%20%E5%92%8C%20ApplicationContext/
https://juejin.im/post/5b9f707ce51d450e3f6b8244
https://www.jianshu.com/u/d3f1e12064a5
https://Spring.io/blog/2011/08/09/what-s-a-factorybean
https://my.oschina.net/u/2377110/blog/918659
https://www.cnblogs.com/aspirant/p/9082858.html
https://zhouxiaowu.coding.me/2018/07/19/%E7%90%86%E8%A7%A3Spring%E4%B8%AD%E7%9A%84BeanFactory%E4%B8%8EFactoryBean%E5%8C%BA%E5%88%AB/
https://www.cnblogs.com/xiaoxi/p/5846416.html
http://www.cnblogs.com/xrq730/p/5721366.html