分了六步来完成一个简易的IOC功能的搭建:
- 最基本的容器
- 将 bean 创建放入工厂
- 为 bean 注入属性
- 读取 xml 配置来初始化 bean
- 为 bean 注入 bean
- 引入 ApplicationContext
首先,我们知道 bean 实例化的几种方式分别是:
- 普通构造函数
- 静态工厂
- 实例工厂
IoC即 控制反转,在对象层级嵌套的情况下,IoC帮助我们在创建对象时,自动创建依赖的对象并注入,IoC容器创建对象是自底向上的:
这篇博客很好地解释了 IoC 和 DI:https://www.iteye.com/blog/jinnianshilongnian-1413846
下面,我们逐步实现一个简易的 IoC 容器
Step 1 最基本的容器
- 首先创建了一个
BeanDefinition
类来封装 bean 对象和元信息
public class BeanDefinition {
private Object bean;
public BeanDefinition(Object bean) {
this.bean = bean;
}
public Object getBean() {
return bean;
}
}
- 创建
BeanFactory
负责保存 bean 对象到容器中(这里用到的是Hashmap保存),并且可以通过 name 来获取保存的BeanDefinition
public class BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
beanDefinitionMap.put(name, beanDefinition);
}
}
- 测试代码
// 1.初始化beanfactory
BeanFactory beanFactory = new BeanFactory();
// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
Step 2 将 bean 创建放入工厂
在上一步中,工厂类的功能只是保存和获取 BeanDefinition,这一步中,将 bean 的创建也放到工厂中完成,在这一步中,把简单工厂模式改造成了提取了接口的抽象工厂模式
- 改造 BeanDefinition
public class BeanDefinition {
@Getter
@Setter
private Object bean;
@Getter
@Setter
private Class beanClass;
@Getter
private String beanClassName;
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
- 首先,定义工厂接口
public interface BeanFactory {
Object getBean(String name);
void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception;
}
- 创建抽象工厂,并在工厂类中创建 bean(doCreateBean)
public abstract class AbstractBeanFactory implements BeanFactory {
private Map<String, BeanDefinition> beanMap = new ConcurrentHashMap<>();
@Override
public Object getBean(String name) {
return beanMap.get(name).getBean();
}
@Override
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
Object bean = doCreateBean(beanDefinition);
beanDefinition.setBean(bean);
beanMap.put(name, beanDefinition);
}
/**
* 初始化 Bean
*
* @param beanDefinition
* @return
*/
protected abstract Object doCreateBean(BeanDefinition beanDefinition);
}
- 具体的工厂类实现,利用反射创建 bean
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) {
try {
return beanDefinition.getBeanClass().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
- 测试代码
// 1.初始化BeanFactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.注入Bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("wu.easyioc.HelloWorldService");
beanFactory.registerBeanDefinition("helloworld", beanDefinition);
// 3.获取Bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloworld");
helloWorldService.helloworld();
Step 3 为 bean 注入属性
上面的步骤中,无法为 bean 注入属性,下面是在创建 bean 的时候,利用反射来为 bean 注入属性
- 创建
PropertyValue
来保存需要注入的字段信息
public class PropertyValue {
@Getter
private final String name;
@Getter
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
}
- 创建
PropertyValues
来保存多个PropertyValue
,实际上就是一个 List
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<>();
public PropertyValues() {
}
public void addPropertyValue(PropertyValue propertyValue) {
this.propertyValueList.add(propertyValue);
}
public List<PropertyValue> getPropertyValues() {
return this.propertyValueList;
}
}
- 改造后的
AutowireCapableBeanFactory
,根据 Class 对象创建对象实例后,再把PropertyValues
中的属性名 - 属性值 循环注入到对象中
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
Object bean = createBeanInstance(beanDefinition);
applyPropertyValues(bean, beanDefinition);
return bean;
}
protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
return beanDefinition.getBeanClass().newInstance();
}
protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
declaredField.set(bean, propertyValue.getValue());
}
}
}
HelloService
public class HelloWorldService {
String text; // 需要注入值的属性
public void helloworld() {
System.out.println(text);
}
}
- 测试代码
// 1.初始化BeanFactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.注入Bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("wu.easyioc.HelloWorldService");
// 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text","helloworld from bot..."));
beanDefinition.setPropertyValues(propertyValues);
// 4.生成Bean
beanFactory.registerBeanDefinition("helloworld", beanDefinition);
// 5.获取Bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloworld");
helloWorldService.helloworld();
Step 4 读取 xml 配置来初始化 bean
在步骤3中需要手动初始化 bean,下面我们通过xml配置文件的方式来自动注入 bean 信息
- 创建
Resource
接口,负责获取内部资源,以输入流的形式返回
public interface Resource {
InputStream getInputStream() throws IOException;
}
- 创建
UrlResource
类,实现Resource
接口,传入一个URL
来获取输入流
public class UrlResource implements Resource {
private final URL url;
public UrlResource(URL url) {
this.url = url;
}
@Override
public InputStream getInputStream() throws IOException{
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
}
}
ResourceLoader
根据 Class对象 来获取指定路径的URL
对象,并注入UrlResource
中
public class ResourceLoader {
public Resource getResource(String location) {
// 获取Class对象 -> 获取类装载器 -> 根据路径获取URL对象
URL resource = this.getClass().getClassLoader().getResource(location);
return new UrlResource(resource);
}
}
- 创建 Bean 读取接口
BeanDefinitionReader
,负责从xml配置文件中读取 bean 信息
public interface BeanDefinitionReader {
void loadBeanDefinitions(String location) throws Exception;
}
- 实现接口
BeanDefinitionReader
的抽象类AbstractBeanDefinitionReader
,内部的 registry 集合保存了需要注册的 bean 信息
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
// 保存需要注册的Bean的容器
private Map<String,BeanDefinition> registry;
private ResourceLoader resourceLoader;
protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
this.registry = new HashMap<String, BeanDefinition>();
this.resourceLoader = resourceLoader;
}
public Map<String, BeanDefinition> getRegistry() {
return registry;
}
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
- 自动初始化 bean 信息的实现类
XmlBeanDefinitionReader
,功能是将 xml 配置文件中配置的 bean 初始化信息存入到内部的 registy 集合中
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws Exception {
// 获取输入流,输入流是为了输入xml资源
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(inputStream);
}
protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
// 解析bean
registerBeanDefinitions(doc);
inputStream.close();
}
public void registerBeanDefinitions(Document doc) {
Element root = doc.getDocumentElement();
parseBeanDefinitions(root);
}
protected void parseBeanDefinitions(Element root) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
processBeanDefinition(ele);
}
}
}
protected void processBeanDefinition(Element ele) {
String name = ele.getAttribute("name");
String className = ele.getAttribute("class");
BeanDefinition beanDefinition = new BeanDefinition();
processProperty(ele,beanDefinition);
beanDefinition.setBeanClassName(className);
getRegistry().put(name, beanDefinition);
}
private void processProperty(Element ele,BeanDefinition beanDefinition) {
NodeList propertyNode = ele.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name,value));
}
}
}
}
- 测试代码
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
- 大致流程图如下:
Step 5 为 bean 注入 bean
前面的步骤3中,利用反射完成了属性的注入,但是无法为 bean 注入 bean,下面完成为 bean 注入 bean 的实现
- 创建
BeanReference
类,表示这个属性是对另外一个bean的引用
@Data
@AllArgsConstructor
public class BeanReference {
private String name;
private Object bean;
}
- 在处理xml配置文件的时候,判断xml中需要注入的是
ref
还是value
,对不同的属性进行处理,所以需要改造处理的方法,对ref
封装成BeanRefrence
private void processProperty(Element ele, BeanDefinition beanDefinition) {
NodeList propertyNode = ele.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
String ref = propertyEle.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("Configuration problem: <property> element for property '"
+ name + "' must specify a ref or value");
}
BeanReference beanReference = new BeanReference(ref);
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
}
}
}
}
- 在
AutowireCapableBeanFactory
中的注入属性方法中,判断注入属性是否为BeanReference
类,如果是则调用getBean(...)
方法获取 bean 对象并注入
protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
// getDeclaredField 获取一个类的指定成员变量
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
// 需要对 private 的成员变量进行set操作,必须setAccessible(true)
declaredField.setAccessible(true);
Object value = propertyValue.getValue();
if (value instanceof BeanReference) {
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getName());
}
declaredField.set(bean, value);
}
}
基于以上修改,已经可以对普通的bean依赖进行处理,但是遇到循环依赖的情况,会造成死锁。解决的方案是改造为单例的注入,先将Bean对象实例化后在设置对象属性,而不是在实例化的时候就注入属性。
- 在注册bean的方法
registerBeanDefinition
中先不 createBean ,而是新建一个容器存放需要实例的 beanName
// 旧方法:
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
Object bean = doCreateBean(beanDefinition);
beanDefinition.setBean(bean);
beanMap.put(name, beanDefinition);
}
// 新的方法:
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
beanDefinitionMap.put(name, beanDefinition);
beanDefinitionNames.add(name); // List<String> beanDefinitionNames
}
- 创建
preInstantiateSingletons
方法,在调用 getBean 前,先将 beanDefinitionNames 容器中的实例初始化
public void preInstantiateSingletons() throws Exception {
for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {
String beanName = (String) it.next();
getBean(beanName); // 初始化bean,单例形式
}
}
- 对
getBean
方法的重写,对 bean 进行非空判断,为空则调用doCreateBean
方法,如果不为空则返回 bean(单例)
public Object getBean(String name) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
throw new IllegalArgumentException("No bean named " + name + " is defined");
}
Object bean = beanDefinition.getBean();
if (bean == null) {
bean = doCreateBean(beanDefinition);
}
return bean;
}
- 引入两个Service,并循环依赖,测试代码
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.初始化bean,当然不需要这一步也是可以的,因为在下面的getBean(...)方法中,也有对null的Bean判断并创建,相当于 Lazy-init
beanFactory.preInstantiateSingletons();
// 4.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
Step 6 引入 ApplicationContext
上面已经完成了简单的 IoC 功能,但是使用起来还是比较繁琐,所以引入了 ApplicationContext 来对过程进行进一步地封装
- 创建
ApplicationContext
接口
public interface ApplicationContext {}
- 抽象实现
ApplicationContext
接口的抽象类AbstractApplicationContext
,这里主要有两个方法:refresh(...)
加载xml配置方法 和getBean(...)
获取bean方法
public class AbstractApplicationContext implements ApplicationContext {
protected AbstractBeanFactory beanFactory;
public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void refresh() throws Exception {}
@Override
public Object getBean(String name) throws Exception {
return beanFactory.getBean(name);
}
}
- 具体的实现类
ClassPathXmlApplicationContext
,主要作用是实现了refresh(...)
方法,该类主要用于加载指定路径的 xml 配置文件并保存
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
private String configLocation;
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
this(configLocation, new AutowireCapableBeanFactory());
}
public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
super(beanFactory);
this.configLocation = configLocation;
refresh();
}
@Override
public void refresh() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
}
}
- 测试代码
// 1. 载入 xml 配置
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
// 2. 获取 bean
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
// 3. 执行方法
helloWorldService.helloWorld();
总结
以上实现了一个简易的 IoC 功能,在以上设计中,大量用到了 模板方法模式
和 抽象工厂模式
,后续可以基于此实现:1. 基于注解的 IoC 功能 2. 简易的 AOP 功能