一、实现一个简单的Bean容器

一个简单的 Spring Bean 容器实现,还需 Bean 的定义、注册、获取三个基本步骤,简化设计如下;

img

  • 定义:BeanDefinition,它会包括 singleton、prototype、BeanClassName 等。
  • 注册:这个过程就相当于我们把数据存放到 HashMap 中,只不过现在 HashMap 存放的是定义了的 Bean 的对象信息。
  • 获取:最后就是获取对象,Bean 的名字就是key,Spring 容器初始化好 Bean 以后,就可以直接获取了。

Spring Bean 容器类关系

图 2-2

  1. BeanDefinition,用于定义 Bean 实例化信息,现在的实现是以一个 Object 存放对象
  2. BeanFactory,代表了 Bean 对象的工厂,可以存放 Bean 定义到 Map 中以及获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BeanDefinition {

private Object bean;

public BeanDefinition(Object bean) {
this.bean = bean;
}

public Object getBean() {
return bean;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BeanFactory {

private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}

public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
beanDefinitionMap.put(name, beanDefinition);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test_BeanFactory(){
// 1.初始化 BeanFactory
BeanFactory beanFactory = new BeanFactory();

// 2.注册 bean
BeanDefinition beanDefinition = new BeanDefinition(new UserService());
beanFactory.registerBeanDefinition("userService", beanDefinition);

// 3.获取 bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}

二、实现 Bean 的定义、注册、获取

在 Bean 注册的时候只注册一个类信息,而不会直接把实例化信息注册到 Spring 容器中。那么就需要修改 BeanDefinition 中的属性 Object 为 Class,接下来在需要做的就是在获取 Bean 对象时需要处理 Bean 对象的实例化操作以及判断当前单例对象在容器中是否已经缓存起来了

img

定义 BeanFactory 这样一个 Bean 工厂,提供 Bean 的获取方法 getBean(String name),之后这个 Bean 工厂接口由抽象类 AbstractBeanFactory 实现。这样使用模板模式的设计方式,可以统一收口通用核心方法的调用逻辑和标准定义,也就很好的控制了后续的实现者不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体方法的逻辑实现即可。

在继承抽象类 AbstractBeanFactory 后的 AbstractAutowireCapableBeanFactory 就可以实现相应的抽象方法了,因为 AbstractAutowireCapableBeanFactory 本身也是一个抽象类,所以它只会实现属于自己的抽象方法,其他抽象方法由继承 AbstractAutowireCapableBeanFactory 的类实现。这里就体现了类实现过程中的各司其职,你只需要关心属于你的内容,不是你的内容,不要参与。

单例 SingletonBeanRegistry 的接口定义实现

DefaultSingletonBeanRegistry 对接口实现后,会被抽象类 AbstractBeanFactory 继承

现在 AbstractBeanFactory 就是一个非常完整且强大的抽象类了,也能非常好的体现出它对模板模式的抽象定义

图 3-2

  • BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的 getBean 方法
  • 而 AbstractBeanFactory 又继承了实现了 SingletonBeanRegistry 的DefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例 Bean 的注册功能。
  • AbstractBeanFactory 中又定义了两个抽象方法:getBeanDefinition(String beanName)、createBean(String beanName, BeanDefinition beanDefinition) ,而这两个抽象方法分别由 DefaultListableBeanFactory、AbstractAutowireCapableBeanFactory 实现。
  • 最终 DefaultListableBeanFactory 还会继承抽象类 AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的 createBean 方法了。

BeanDefinition 定义

1
2
3
4
5
6
7
8
9
10
public class BeanDefinition {

private Class beanClass;

public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
}
// ...get/set
}

单例注册接口定义和实现

1
2
3
4
5
6
public interface SingletonBeanRegistry {

Object getSingleton(String beanName);

}

  • 这个类比较简单主要是定义了一个获取单例对象的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

private Map<String, Object> singletonObjects = new HashMap<>();

@Override
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}

protected void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}

}

  • 在 DefaultSingletonBeanRegistry 中主要实现 getSingleton 方法,同时实现了一个受保护的 addSingleton 方法,这个方法可以被继承此类的其他类调用。包括:AbstractBeanFactory 以及继承的 DefaultListableBeanFactory 调用。

抽象类定义模板方法(AbstractBeanFactory)

1
2
3
4
5
public interface BeanFactory {

Object getBean(String name) throws BeansException;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

@Override
public Object getBean(String name) throws BeansException {
Object bean = getSingleton(name);
if (bean != null) {
return bean;
}

BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}

protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;

}

  • AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry,也就具备了使用单例注册类方法。
  • 在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作。那么 getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。
  • 后续继承抽象类 AbstractBeanFactory 的类有两个,包括:AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory

实例化Bean类(AbstractAutowireCapableBeanFactory)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
Object bean = null;
try {
bean = beanDefinition.getBeanClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new BeansException("Instantiation of bean failed", e);
}

addSingleton(beanName, bean);
return bean;
}

}

  • 在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作 newInstance
  • 在处理完 Bean 对象的实例化后,直接调用 addSingleton 方法存放到单例对象的缓存中去。

核心类实现(DefaultListableBeanFactory)

1
2
3
4
5
6
7
8
9
10
11
public interface BeanDefinitionRegistry {

/**
* 向注册表中注册 BeanDefinition
*
* @param beanName
* @param beanDefinition
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}

@Override
public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) throw new BeansException("No bean named '" + beanName + "' is defined");
return beanDefinition;
}

}

  • DefaultListableBeanFactory 在 Spring 源码中也是一个非常核心的类
  • DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactory 和 AbstractBeanFactory 等一连串的功能实现
  • 实现了接口 BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法,还会看到一个 getBeanDefinition 的实现,这个方法是抽象类 AbstractBeanFactory 中定义的抽象方法。现在注册Bean定义与获取Bean定义就可以同时使用了。接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的 beanDefinitionMap 里

Test

1
2
3
4
5
6
7
8
public class UserService {

public void queryUserInfo(){
System.out.println("查询用户信息");
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test_BeanFactory(){
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2.注册 bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 3.第一次获取 bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
// 4.第二次获取 bean from Singleton
UserService userService_singleton = (UserService) beanFactory.getBean("userService");
userService_singleton.queryUserInfo();
}

三、基于Cglib实现含构造函数的类实例化策略

上面我们实例化对象的代码里并没有考虑对象类是否含构造函数,也就是说如果我们去实例化一个含有构造函数的对象那么就要抛异常了

填平这个坑的技术设计主要考虑两部分,一个是串流程从哪合理的把构造函数的入参信息传递到实例化操作里,另外一个是怎么去实例化含有构造函数的对象。

图 4-1

  • 参考 Spring Bean 容器源码的实现方式,在 BeanFactory 中添加 Object getBean(String name, Object... args) 接口,这样就可以在获取 Bean 时把构造函数的入参信息传递进去了。
  • 另外一个核心的内容是使用什么方式来创建含有构造函数的 Bean 对象呢?这里有两种方式可以选择,一个是基于 Java 本身自带的方法 DeclaredConstructor,另外一个是使用 Cglib 来动态创建 Bean 对象。Cglib 是基于字节码框架 ASM 实现,所以也可以直接通过 ASM 操作指令码来创建对象

图 4-2

在现有工程中添加 InstantiationStrategy 实例化策略接口,以及补充相应的 getBean 入参信息,让外部调用时可以传递构造函数的入参并顺利实例化。

新增 getBean 接口

1
2
3
4
5
6
7
8
public interface BeanFactory {

Object getBean(String name) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

}

  • BeanFactory 中重载了一个含有入参信息 args 的 getBean 方法,这样就可以方便的传递入参给构造函数实例化了。

定义实例化策略接口

1
2
3
4
5
6
public interface InstantiationStrategy {

Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;

}

  • 在实例化接口 instantiate 方法中添加必要的入参信息,包括:beanDefinition、 beanName、ctor、args
  • 其中 Constructor ,它是 java.lang.reflect 包下的 Constructor 类,里面包含了一些必要的类信息,有这个参数的目的就是为了拿到符合入参信息相对应的构造函数。
  • args 就是一个具体的入参信息了,最终实例化时候会用到

JDK 实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SimpleInstantiationStrategy implements InstantiationStrategy {

@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Class clazz = beanDefinition.getBeanClass();
try {
if (null != ctor) {
return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
} else {
return clazz.getDeclaredConstructor().newInstance();
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
}
}

}

  • 首先通过 beanDefinition 获取 Class 信息,这个 Class 信息是在 Bean 定义的时候传递进去的。
  • 接下来判断 ctor 是否为空,如果为空则是无构造函数实例化,否则就是需要有构造函数的实例化。
  • 有构造函数的实例化,实例化方式为 clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);,把入参信息传递给 newInstance 进行实例化。

Cglib 实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {

@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
if (null == ctor) return enhancer.create();
return enhancer.create(ctor.getParameterTypes(), args);
}

}

创建策略调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}

addSingleton(beanName, bean);
return bean;
}

protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
Constructor constructorToUse = null;
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}

public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}

}

  • 首先在 AbstractAutowireCapableBeanFactory 抽象类中定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类。
  • 接下来抽取 createBeanInstance 方法,在这个方法中需要注意 Constructor 代表了你有多少个构造函数,通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合。
  • 接下来就需要循环比对出构造函数集合与入参信息 args 的匹配情况,这里我们对比的方式比较简单,只是一个数量对比,而实际 Spring 源码中还需要比对入参类型,否则相同数量不同入参类型的情况,就会抛异常了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null);
}

@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, args);
}

protected <T> T doGetBean(final String name, final Object[] args) {
Object bean = getSingleton(name);
if (bean != null) {
return (T) bean;
}

BeanDefinition beanDefinition = getBeanDefinition(name);
return (T) createBean(name, beanDefinition, args);
}

protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;

}

TEST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UserService {

private String name;

public UserService(String name) {
this.name = name;
}

public void queryUserInfo() {
System.out.println("查询用户信息:" + name);
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("");
sb.append("").append(name);
return sb.toString();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 2. 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);

// 3.获取bean
UserService userService = (UserService) beanFactory.getBean("userService", "zzc");
userService.queryUserInfo();
}

四、为Bean对象注入属性和依赖Bean的功能实现

鉴于属性填充是在 Bean 使用 newInstance 或者 Cglib 创建后,开始补全属性信息,那么就可以在类 AbstractAutowireCapableBeanFactory 的 createBean 方法中添加补全属性方法。

img

  • 属性填充要在类实例化创建之后,也就是需要在 AbstractAutowireCapableBeanFactory 的 createBean 方法中添加 applyPropertyValues 操作。
  • 由于我们需要在创建Bean时候填充属性操作,那么就需要在 bean 定义 BeanDefinition 类中,添加 PropertyValues 信息。
  • 另外是填充属性信息还包括了 Bean 的对象类型,也就是需要再定义一个 BeanReference,里面其实就是一个简单的 Bean 名称,在具体的实例化操作时进行递归创建和填充,与 Spring 源码实现一样。Spring 源码中 BeanReference 是一个接口

图 5-2

  • BeanReference(类引用)、PropertyValue(属性值)、PropertyValues(属性集合),分别用于类和其他类型属性填充操作。
  • 另外改动的类主要是 AbstractAutowireCapableBeanFactory,在 createBean 中补全属性填充部分。

定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PropertyValue {

private final String name;

private final Object value;

public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}

// ...get/set
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PropertyValues {

private final List<PropertyValue> propertyValueList = new ArrayList<>();

public void addPropertyValue(PropertyValue pv) {
this.propertyValueList.add(pv);
}

public PropertyValue[] getPropertyValues() {
return this.propertyValueList.toArray(new PropertyValue[0]);
}

public PropertyValue getPropertyValue(String propertyName) {
for (PropertyValue pv : this.propertyValueList) {
if (pv.getName().equals(propertyName)) {
return pv;
}
}
return null;
}

}

  • 这两个类的作用就是创建出一个用于传递类中属性信息的类,因为属性可能会有很多,所以还需要定义一个集合包装下。

Bean定义补全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class BeanDefinition {

private Class beanClass;

private PropertyValues propertyValues;

public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
this.propertyValues = new PropertyValues();
}

public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
this.beanClass = beanClass;
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}

// ...get/set
}

  • 在 Bean 注册的过程中是需要传递 Bean 的信息, new BeanDefinition(UserService.class, propertyValues);
  • 所以为了把属性一定交给 Bean 定义,所以这里填充了 PropertyValues 属性,同时把两个构造函数做了一些简单的优化,避免后面 for 循环时还得判断属性填充是否为空

Bean 属性填充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}

addSingleton(beanName, bean);
return bean;
}

protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
Constructor constructorToUse = null;
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}

/**
* Bean 属性填充
*/
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
try {
PropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {

String name = propertyValue.getName();
Object value = propertyValue.getValue();

if (value instanceof BeanReference) {
// A 依赖 B,获取 B 的实例化
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
// 属性填充
BeanUtil.setFieldValue(bean, name, value);
}
} catch (Exception e) {
throw new BeansException("Error setting property values:" + beanName);
}
}

public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}

public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}

}

  • 主要包括三个方法:createBean、createBeanInstance、applyPropertyValues,这里我们主要关注 createBean 的方法中调用的 applyPropertyValues 方法。
  • 在 applyPropertyValues 中,通过获取 beanDefinition.getPropertyValues() 循环进行属性填充操作,如果遇到的是 BeanReference,那么就需要递归获取 Bean 实例,调用 getBean 方法。
  • 当把依赖的 Bean 对象创建完成后,会递归回现在属性填充中

TEST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserDao {

private static Map<String, String> hashMap = new HashMap<>();

static {
hashMap.put("10001", "ZZC");
hashMap.put("10002", "ZZC2");
hashMap.put("10003", "ZZC3");
}

public String queryUserName(String uId) {
return hashMap.get(uId);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserService {

private String uId;

private UserDao userDao;

public void queryUserInfo() {
System.out.println("查询用户信息:" + userDao.queryUserName(uId));
}

// ...get/set
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 2. UserDao 注册
beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));

// 3. UserService 设置属性[uId、userDao]
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));
propertyValues.addPropertyValue(new PropertyValue("userDao",new BeanReference("userDao")));

// 4. UserService 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class, propertyValues);
beanFactory.registerBeanDefinition("userService", beanDefinition);

// 5. UserService 获取bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}

五、资源加载器解析文件注册对象

img

我们需要在现有的 Spring 框架雏形中添加一个资源解析器,也就是能读取classpath、本地文件和云文件的配置内容。这些配置内容就是像使用 Spring 时配置的 Spring.xml 一样,里面会包括 Bean 对象的描述和属性信息。 在读取配置文件信息后,接下来就是对配置文件中的 Bean 描述信息解析后进行注册操作,把 Bean 对象注册到 Spring 容器中

img

  • 资源加载器属于相对独立的部分,它位于 Spring 框架核心包下的IO实现内容,主要用于处理Class、本地和云环境中的文件信息。
  • 当资源可以加载后,接下来就是解析和注册 Bean 到 Spring 中的操作,这部分实现需要和 DefaultListableBeanFactory 核心类结合起来,因为你所有的解析后的注册动作,都会把 Bean 定义信息放入到这个类中。
  • 那么在实现的时候就设计好接口的实现层级关系,包括我们需要定义出 Bean 定义的读取接口 BeanDefinitionReader 以及做好对应的实现类,在实现类中完成对 Bean 对象的解析和注册。

图 6-3

  • 为了能把 Bean 的定义、注册和初始化交给 Spring.xml 配置化处理,那么就需要实现两大块内容,分别是:资源加载器、xml资源处理类,实现过程主要以对接口 ResourceResourceLoader 的实现,而另外 BeanDefinitionReader 接口则是对资源的具体使用,将配置信息注册到 Spring 容器中去。

  • 接口:BeanDefinitionReader、抽象类:AbstractBeanDefinitionReader、实现类:XmlBeanDefinitionReader,这三部分内容主要是合理清晰的处理了资源读取后的注册 Bean 容器操作。接口管定义,抽象类处理非接口功能外的注册Bean组件填充,最终实现类即可只关心具体的业务实现

相应接口的集成和实现的关系

图 6-4

  • BeanFactory,已经存在的 Bean 工厂接口用于获取 Bean 对象,这次新增加了按照类型获取 Bean 的方法:<T> T getBean(String name, Class<T> requiredType)
  • ListableBeanFactory,是一个扩展 Bean 工厂接口的接口,新增加了 getBeansOfTypegetBeanDefinitionNames() 方法,在 Spring 源码中还有其他扩展方法。
  • HierarchicalBeanFactory,在 Spring 源码中它提供了可以获取父类 BeanFactory 方法,属于是一种扩展工厂的层次子接口。Sub-interface implemented by bean factories that can be part of a hierarchy.
  • AutowireCapableBeanFactory,是一个自动化处理Bean工厂配置的接口,目前案例工程中还没有做相应的实现,后续逐步完善。
  • ConfigurableBeanFactory,可获取 BeanPostProcessor、BeanClassLoader等的一个配置化接口。
  • ConfigurableListableBeanFactory,提供分析和修改Bean以及预先实例化的操作接口,不过目前只有一个 getBeanDefinition 方法。

资源加载接口定义和实现

1
2
3
4
5
6
public interface Resource {

InputStream getInputStream() throws IOException;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ClassPathResource implements Resource {

private final String path;

private ClassLoader classLoader;

public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}

public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
this.path = path;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}

@Override
public InputStream getInputStream() throws IOException {
InputStream is = classLoader.getResourceAsStream(path);
if (is == null) {
throw new FileNotFoundException(
this.path + " cannot be opened because it does not exist");
}
return is;
}
}

  • 这一部分的实现是用于通过 ClassLoader 读取ClassPath 下的文件信息,具体的读取过程主要是:classLoader.getResourceAsStream(path)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class FileSystemResource implements Resource {

private final File file;

private final String path;

public FileSystemResource(File file) {
this.file = file;
this.path = file.getPath();
}

public FileSystemResource(String path) {
this.file = new File(path);
this.path = path;
}

@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}

public final String getPath() {
return this.path;
}

}

  • 通过指定文件路径的方式读取文件信息,这部分大家肯定还是非常熟悉的,经常会读取一些txt、excel文件输出到控制台。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class UrlResource implements Resource{

private final URL url;

public UrlResource(URL url) {
Assert.notNull(url,"URL must not be null");
this.url = url;
}

@Override
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
try {
return con.getInputStream();
}
catch (IOException ex){
if (con instanceof HttpURLConnection){
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}

}

通过 HTTP 的方式读取云服务的文件,我们也可以把配置文件放到 GitHub 或者 Gitee 上

包装资源加载器

1
2
3
4
5
6
7
8
9
10
11
public interface ResourceLoader {

/**
* Pseudo URL prefix for loading from the class path: "classpath:"
*/
String CLASSPATH_URL_PREFIX = "classpath:";

Resource getResource(String location);

}

  • 定义获取资源接口,里面传递 location 地址即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DefaultResourceLoader implements ResourceLoader {

@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
}
else {
try {
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException e) {
return new FileSystemResource(location);
}
}
}

}

  • 虽然 DefaultResourceLoader 类实现的过程简单,但这也是设计模式约定的具体结果,像是这里不会让外部调用放知道过多的细节,而是仅关心具体调用结果即可。

Bean定义读取接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface BeanDefinitionReader {

BeanDefinitionRegistry getRegistry();

ResourceLoader getResourceLoader();

void loadBeanDefinitions(Resource resource) throws BeansException;

void loadBeanDefinitions(Resource... resources) throws BeansException;

void loadBeanDefinitions(String location) throws BeansException;

}

  • 这是一个 Simple interface for bean definition readers. 其实里面无非定义了几个方法,包括:getRegistry()、getResourceLoader(),以及三个加载Bean定义的方法。

  • 这里需要注意 getRegistry()、getResourceLoader(),都是用于提供给后面三个方法的工具,加载和注册,这两个方法的实现会包装到抽象类中,以免污染具体的接口实现方法。

Bean定义抽象类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

private final BeanDefinitionRegistry registry;

private ResourceLoader resourceLoader;

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, new DefaultResourceLoader());
}

public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
this.registry = registry;
this.resourceLoader = resourceLoader;
}

@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}

@Override
public ResourceLoader getResourceLoader() {
return resourceLoader;
}

}

  • 抽象类把 BeanDefinitionReader 接口的前两个方法全部实现完了,并提供了构造函数,让外部的调用使用方,把Bean定义注入类,传递进来。
  • 这样在接口 BeanDefinitionReader 的具体实现类中,就可以把解析后的 XML 文件中的 Bean 信息,注册到 Spring 容器去了。以前我们是通过单元测试使用,调用 BeanDefinitionRegistry 完成Bean的注册,现在可以放到 XMl 中操作了

解析XML处理Bean注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}

@Override
public void loadBeanDefinitions(Resource resource) throws BeansException {
try {
try (InputStream inputStream = resource.getInputStream()) {
doLoadBeanDefinitions(inputStream);
}
} catch (IOException | ClassNotFoundException e) {
throw new BeansException("IOException parsing XML document from " + resource, e);
}
}

@Override
public void loadBeanDefinitions(Resource... resources) throws BeansException {
for (Resource resource : resources) {
loadBeanDefinitions(resource);
}
}

@Override
public void loadBeanDefinitions(String location) throws BeansException {
ResourceLoader resourceLoader = getResourceLoader();
Resource resource = resourceLoader.getResource(location);
loadBeanDefinitions(resource);
}

protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
Document doc = XmlUtil.readXML(inputStream);
Element root = doc.getDocumentElement();
NodeList childNodes = root.getChildNodes();

for (int i = 0; i < childNodes.getLength(); i++) {
// 判断元素
if (!(childNodes.item(i) instanceof Element)) continue;
// 判断对象
if (!"bean".equals(childNodes.item(i).getNodeName())) continue;

// 解析标签
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
// 获取 Class,方便获取类中的名称
Class<?> clazz = Class.forName(className);
// 优先级 id > name
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}

// 定义Bean
BeanDefinition beanDefinition = new BeanDefinition(clazz);
// 读取属性并填充
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (!(bean.getChildNodes().item(j) instanceof Element)) continue;
if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) continue;
// 解析标签:property
Element property = (Element) bean.getChildNodes().item(j);
String attrName = property.getAttribute("name");
String attrValue = property.getAttribute("value");
String attrRef = property.getAttribute("ref");
// 获取属性值:引入对象、值对象
Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
// 创建属性信息
PropertyValue propertyValue = new PropertyValue(attrName, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
if (getRegistry().containsBeanDefinition(beanName)) {
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
// 注册 BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}

}

XmlBeanDefinitionReader 类最核心的内容就是对 XML 文件的解析,把我们本来在代码中的操作放到了通过解析 XML 自动注册的方式。

  • loadBeanDefinitions 方法,处理资源加载,这里新增加了一个内部方法:doLoadBeanDefinitions,它主要负责解析 xml

  • 在 doLoadBeanDefinitions 方法中,主要是对xml的读取 XmlUtil.readXML(inputStream) 和元素 Element 解析。在解析的过程中通过循环操作,以此获取 Bean 配置以及配置中的 id、name、class、value、ref 信息。

  • 最终把读取出来的配置信息,创建成 BeanDefinition 以及 PropertyValue,最终把完整的 Bean 定义内容注册到 Bean 容器:getRegistry().registerBeanDefinition(beanName, beanDefinition)

TEST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserDao {

private static Map<String, String> hashMap = new HashMap<>();

static {
hashMap.put("10001", "ZZC");
hashMap.put("10002", "ZZC1");
hashMap.put("10003", "ZZC2");
}

public String queryUserName(String uId) {
return hashMap.get(uId);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserService {

private String uId;

private UserDao userDao;

public void queryUserInfo() {
return userDao.queryUserName(uId);
}

// ...get/set
}

important.properties

1
2
3
# Config File
system.key=OLpj9823dZ

spring.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans>

<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>

<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="userDao" ref="userDao"/>
</bean>

</beans>

  • 这里有两份配置文件,一份用于测试资源加载器,另外 spring.xml 用于测试整体的 Bean 注册功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private DefaultResourceLoader resourceLoader;      

@Before
public void init() {
resourceLoader = new DefaultResourceLoader();
}

@Test
public void test_classpath() throws IOException {
Resource resource = resourceLoader.getResource("classpath:important.properties");
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}

@Test
public void test_file() throws IOException {
Resource resource = resourceLoader.getResource("src/test/resources/important.properties");
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}

@Test
public void test_url() throws IOException {
Resource resource = resourceLoader.getResource("https://github.com/fuzhengwei/small-spring/important.properties"
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}

1
2
3
4
5
# Config File
system.key=OLpj9823dZ

Process finished with exit code 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test_xml() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 2. 读取配置文件&注册Bean
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:spring.xml");

// 3. 获取Bean对象调用方法
UserService userService = beanFactory.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}

六、实现应用上下文

在对容器中 Bean 的实例化过程添加扩展机制的同时,还需要把目前关于 Spring.xml 初始化和加载策略进行优化,因为我们不太可能让面向 Spring 本身开发的 DefaultListableBeanFactory 服务,直接给予用户使用。修改点如下:

img

  • DefaultListableBeanFactory、XmlBeanDefinitionReader,是我们在目前 Spring 框架中对于服务功能测试的使用方式,它能很好的体现出 Spring 是如何对 xml 加载以及注册Bean对象的操作过程,但这种方式是面向 Spring 本身的,还不具备一定的扩展性。

  • 就像我们现在需要提供出一个可以在 Bean 初始化过程中,完成对 Bean 对象的扩展时,就很难做到自动化处理。所以我们要把 Bean 对象扩展机制功能和对 Spring 框架上下文的包装融合起来,对外提供完整的服务

为了能满足于在 Bean 对象从注册到实例化的过程中执行用户的自定义操作,就需要在 Bean 的定义和初始化过程中插入接口类,这个接口再有外部去实现自己需要的服务。那么在结合对 Spring 框架上下文的处理能力,就可以满足我们的目标需求

img

  • 满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:BeanFactoryPostProcessBeanPostProcessor,也几乎是大家在使用 Spring 框架额外新增开发自己组建需求的两个必备接口。

  • BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息 BeanDefinition 执行修改操作

  • BeanPostProcessor,也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象。这部分与后面要实现的 AOP 有着密切的关系

  • 同时如果只是添加这两个接口,不做任何包装,那么对于使用者来说还是非常麻烦的。我们希望于开发 Spring 的上下文操作类,把相应的 XML 加载 、注册、实例化以及新增的修改和扩展都融合进去,让 Spring 可以自动扫描到我们的新增服务,便于用户使用

图 7-3

  • 在整个类图中主要体现出来的是关于 Spring 应用上下文以及对 Bean 对象扩展机制的实现
  • 以继承了 ListableBeanFactory 接口的 ApplicationContext 接口开始,扩展出一系列应用上下文的抽象实现类,并最终完成 ClassPathXmlApplicationContext 类的实现。而这个类就是最后交给用户使用的类

定义 BeanFactoryPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
public interface BeanFactoryPostProcessor {

/**
* 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制
*
* @param beanFactory
* @throws BeansException
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

定义 BeanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface BeanPostProcessor {

/**
* 在 Bean 对象执行初始化方法之前,执行此方法
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

/**
* 在 Bean 对象执行初始化方法之后,执行此方法
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

定义上下文接口

1
2
3
public interface ApplicationContext extends ListableBeanFactory {
}

  • context 是本次实现应用上下文功能新增的服务包
  • ApplicationContext,继承于 ListableBeanFactory,也就继承了关于 BeanFactory 方法,比如一些 getBean 的方法。另外 ApplicationContext 本身是 Central 接口,但目前还不需要添加一些获取ID和父类上下文,所以暂时没有接口方法的定义。
1
2
3
4
5
6
7
8
9
10
11
public interface ConfigurableApplicationContext extends ApplicationContext {

/**
* 刷新容器
*
* @throws BeansException
*/
void refresh() throws BeansException;

}

  • ConfigurableApplicationContext 继承自 ApplicationContext,并提供了 refresh 这个核心方法。如果你有看过一些 Spring 源码,那么一定会看到这个方法。 接下来也是需要在上下文的实现中完成刷新容器的操作过程

应用上下文抽象类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition
refreshBeanFactory();

// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();

// 3. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
invokeBeanFactoryPostProcessors(beanFactory);

// 4. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
registerBeanPostProcessors(beanFactory);

// 5. 提前实例化单例Bean对象
beanFactory.preInstantiateSingletons();
}

protected abstract void refreshBeanFactory() throws BeansException;

protected abstract ConfigurableListableBeanFactory getBeanFactory();

private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}

private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
}

//... getBean、getBeansOfType、getBeanDefinitionNames 方法

}

  • AbstractApplicationContext 继承 DefaultResourceLoader 是为了处理 spring.xml 配置资源的加载。
  • 之后是在 refresh() 定义实现过程,包括:
    • 1、创建 BeanFactory,并加载 BeanDefinition
    • 2、获取 BeanFactory
    • 3、在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
    • 4、BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
    • 5、提前实例化单例Bean对象
  • 另外把定义出来的抽象方法,refreshBeanFactory()、getBeanFactory() 由后面的继承此抽象类的其他抽象类实现。

获取Bean工厂和加载资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

private DefaultListableBeanFactory beanFactory;

@Override
protected void refreshBeanFactory() throws BeansException {
DefaultListableBeanFactory beanFactory = createBeanFactory();
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}

private DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory();
}

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory);

@Override
protected ConfigurableListableBeanFactory getBeanFactory() {
return beanFactory;
}

}

  • 在 refreshBeanFactory() 中主要是获取了 DefaultListableBeanFactory 的实例化以及对资源配置的加载操作 loadBeanDefinitions(beanFactory),在加载完成后即可完成对 spring.xml 配置文件中 Bean 对象的定义和注册,同时也包括实现了接口 BeanFactoryPostProcessor、BeanPostProcessor 的配置 Bean 信息。
  • 但此时资源加载还只是定义了一个抽象类方法 loadBeanDefinitions(DefaultListableBeanFactory beanFactory),继续由其他抽象类继承实现

上下文中对配置信息的加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this);
String[] configLocations = getConfigLocations();
if (null != configLocations){
beanDefinitionReader.loadBeanDefinitions(configLocations);
}
}

protected abstract String[] getConfigLocations();

}

  • 在 AbstractXmlApplicationContext 抽象类的 loadBeanDefinitions 方法实现中,使用 XmlBeanDefinitionReader 类,处理了关于 XML 文件配置信息的操作。
  • 同时这里又留下了一个抽象类方法,getConfigLocations(),此方法是为了从入口上下文类,拿到配置信息的地址描述

应用上下文实现类(ClassPathXmlApplicationContext)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {

private String[] configLocations;

public ClassPathXmlApplicationContext() {
}

/**
* 从 XML 中加载 BeanDefinition,并刷新上下文
*
* @param configLocations
* @throws BeansException
*/
public ClassPathXmlApplicationContext(String configLocations) throws BeansException {
this(new String[]{configLocations});
}

/**
* 从 XML 中加载 BeanDefinition,并刷新上下文
* @param configLocations
* @throws BeansException
*/
public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
this.configLocations = configLocations;
refresh();
}

@Override
protected String[] getConfigLocations() {
return configLocations;
}

}

  • ClassPathXmlApplicationContext,是具体对外给用户提供的应用上下文方法。
  • 在继承了 AbstractXmlApplicationContext 以及层层抽象类的功能分离实现后,在此类 ClassPathXmlApplicationContext 的实现中就简单多了,主要是对继承抽象类中方法的调用和提供了配置文件地址信息

在Bean创建时完成前置和后置处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}

addSingleton(beanName, bean);
return bean;
}

public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}

public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}

private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 1. 执行 BeanPostProcessor Before 处理
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

// 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);
invokeInitMethods(beanName, wrappedBean, beanDefinition);

// 2. 执行 BeanPostProcessor After 处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
return wrappedBean;
}

private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) {

}

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (null == current) return result;
result = current;
}
return result;
}

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (null == current) return result;
result = current;
}
return result;
}

}

  • 实现 BeanPostProcessor 接口后,会涉及到两个接口方法,postProcessBeforeInitializationpostProcessAfterInitialization,分别作用于 Bean 对象执行初始化前后的额外处理。
  • 也就是需要在创建 Bean 对象时,在 createBean 方法中添加 initializeBean(beanName, bean, beanDefinition); 操作。而这个操作主要主要是对于方法 applyBeanPostProcessorsBeforeInitializationapplyBeanPostProcessorsAfterInitialization 的使用。
  • 另外需要提一下,applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization 两个方法是在接口类 AutowireCapableBeanFactory 中新增加的

TEST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserDao {

private static Map<String, String> hashMap = new HashMap<>();

static {
hashMap.put("10001", "zzc");
hashMap.put("10002", "zzc1");
hashMap.put("10003", "zzc2");
}

public String queryUserName(String uId) {
return hashMap.get(uId);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserService {

private String uId;
private String company;
private String location;
private UserDao userDao;

public void queryUserInfo() {
return userDao.queryUserName(uId);
}

// ...get/set
}

实现 BeanPostProcessor 和 BeanFactoryPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
PropertyValues propertyValues = beanDefinition.getPropertyValues();

propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
UserService userService = (UserService) bean;
userService.setLocation("改为:北京");
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans>

<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>

<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>

</beans>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans>

<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>

<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>

<bean class="cn.bugstack.springframework.test.common.MyBeanPostProcessor"/>
<bean class="cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor"/>

</beans>

  • 这里提供了两个配置文件,一个是不包含BeanFactoryPostProcessor、BeanPostProcessor,另外一个是包含的。之所以这样配置主要对照验证,在运用 Spring 新增加的应用上下文和不使用的时候,都是怎么操作的。

不用应用上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void test_BeanFactoryPostProcessorAndBeanPostProcessor(){
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 2. 读取配置文件&注册Bean
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:spring.xml");

// 3. BeanDefinition 加载完成 & Bean实例化之前,修改 BeanDefinition 的属性值
MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);

// 4. Bean实例化之后,修改 Bean 属性信息
MyBeanPostProcessor beanPostProcessor = new MyBeanPostProcessor();
beanFactory.addBeanPostProcessor(beanPostProcessor);

// 5. 获取Bean对象调用方法
UserService userService = beanFactory.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}

使用应用上下文

1
2
3
4
5
6
7
8
9
10
11
@Test
public void test_xml() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml");

// 2. 获取Bean对象调用方法
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}

  • 使用新增加的 ClassPathXmlApplicationContext 应用上下文类,再操作起来就方便多了,这才是面向用户使用的类,在这里可以一步把配置文件交给 ClassPathXmlApplicationContext,也不需要管理一些自定义实现的 Spring 接口的类