概述
IOC(Inversion of Control)“控制反转”,不过更流行的叫法是“依赖注入”(DI - Dependency Injection)。
什么是“控制反转”呢?其实就是将控制权(创建对象和对象之间的依赖关系的权利)交给Spring容器。以前我们写代码的需要某个对象的时候直接使用 new XXXImpl();,有了Spring IOC容器之后,它负责对象的创建和依赖注入,当我们需要某个对象时直接跟Spring IOC容器要就好了。
IOC 听起来很高大上,其实实现起来并不复杂。本文主要介绍基于XML配置的方式来实现一个IOC容器,后面会有单独的一章介绍如何通过注解的方式来实现IOC容器。
用法
具体用法与Spring IOC类似,如下:
1、beans.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>@H_301_17@
<beans@H_301_17@ xmlns@H_301_17@="http://www.springframework.org/schema/beans"@H_301_17@ xmlns:xsi@H_301_17@="http://www.w3.org/2001/XMLSchema-instance"@H_301_17@ xsi:schemaLocation@H_301_17@="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"@H_301_17@>@H_301_17@
<bean@H_301_17@ id@H_301_17@="userDao"@H_301_17@ class@H_301_17@="com.ricky.ioc.sample.dao.UserDaoImpl"@H_301_17@ scope@H_301_17@="singleton"@H_301_17@ init-method@H_301_17@="init"@H_301_17@ >@H_301_17@</bean@H_301_17@>@H_301_17@
<bean@H_301_17@ id@H_301_17@="userService"@H_301_17@ class@H_301_17@="com.ricky.ioc.sample.service.UserServiceImpl"@H_301_17@>@H_301_17@
<property@H_301_17@ name@H_301_17@="userDao"@H_301_17@ ref@H_301_17@="userDao"@H_301_17@>@H_301_17@</property@H_301_17@>@H_301_17@
</bean@H_301_17@>@H_301_17@
<bean@H_301_17@ id@H_301_17@="userController"@H_301_17@ class@H_301_17@="com.ricky.ioc.sample.controller.UserController"@H_301_17@>@H_301_17@
<property@H_301_17@ name@H_301_17@="userService"@H_301_17@ ref@H_301_17@="userService"@H_301_17@>@H_301_17@</property@H_301_17@>@H_301_17@
</bean@H_301_17@>@H_301_17@
</beans@H_301_17@>@H_301_17@
2、添加maven依赖
<dependency@H_301_17@>@H_301_17@
<groupId@H_301_17@>@H_301_17@com.ricky.framework</groupId@H_301_17@>@H_301_17@
<artifactId@H_301_17@>@H_301_17@ioc</artifactId@H_301_17@>@H_301_17@
<version@H_301_17@>@H_301_17@1.0.0</version@H_301_17@>@H_301_17@
</dependency@H_301_17@>@H_301_17@
3、加载bean配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"@H_301_17@);@H_301_17@
//通过id获取Bean
UserController userController =(UserController)ctx.getBean@H_301_17@("userController"@H_301_17@);@H_301_17@
userController.login@H_301_17@("ricky"@H_301_17@,"123"@H_301_17@);@H_301_17@
//通过Class获取Bean
UserService userService = ctx.getBean@H_301_17@(UserService.class@H_301_17@);@H_301_17@
System.out@H_301_17@.println@H_301_17@(userService);@H_301_17@
userService.login@H_301_17@("ricky"@H_301_17@,"abc"@H_301_17@);@H_301_17@
ctx.close@H_301_17@();@H_301_17@
运行结果如下:
UserController login name->ricky,password->123
UserServiceImpl login name->ricky,password->123
UserDaoImpl find name->ricky
com.ricky.ioc.sample.service.UserServiceImpl@214c265e
UserServiceImpl login name->ricky,password->abc
UserDaoImpl find name->ricky
container close…
具体实现
思路:
解析beans.xml获取Bean列表以及相互之间的依赖关系,然后通过反射技术构造出Bean实例,并根据Bean之间的依赖关系进行Bean装配。
首先,看看ApplicationContext类,代码如下:
package com@H_301_17@.ricky@H_301_17@.framework@H_301_17@.ioc@H_301_17@;@H_301_17@
import java.beans@H_301_17@.IntrospectionException@H_301_17@;@H_301_17@
import java.beans@H_301_17@.Introspector@H_301_17@;@H_301_17@
import java.beans@H_301_17@.PropertyDescriptor@H_301_17@;@H_301_17@
import java.lang@H_301_17@.reflect@H_301_17@.InvocationTargetException@H_301_17@;@H_301_17@
import java.lang@H_301_17@.reflect@H_301_17@.Method@H_301_17@;@H_301_17@
import org.apache@H_301_17@.commons@H_301_17@.lang@H_301_17@3.StringUtils@H_301_17@;@H_301_17@
import com@H_301_17@.ricky@H_301_17@.framework@H_301_17@.ioc@H_301_17@.model@H_301_17@.BeanDefinition@H_301_17@;@H_301_17@
import com@H_301_17@.ricky@H_301_17@.framework@H_301_17@.ioc@H_301_17@.model@H_301_17@.PropertyDefinition@H_301_17@;@H_301_17@
import com@H_301_17@.ricky@H_301_17@.framework@H_301_17@.ioc@H_301_17@.util@H_301_17@.ReflectionUtils@H_301_17@;@H_301_17@
public abstract class ApplicationContext {
public abstract Object getBean(String id);@H_301_17@
public abstract <T> T getBean(Class<T> clazz);@H_301_17@
public abstract void close();@H_301_17@
protected abstract BeanDefinition getBeanDefinition(String id);@H_301_17@
protected Object createBean(BeanDefinition bd) {
try {
Object bean = ReflectionUtils.newInstance@H_301_17@(bd.getClassName@H_301_17@());@H_301_17@
if(StringUtils.isNotEmpty@H_301_17@(bd.getInitMethodName@H_301_17@())){
ReflectionUtils.invokeMethod@H_301_17@(bean,bd.getInitMethodName@H_301_17@());@H_301_17@
}
return bean;@H_301_17@
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("create bean error,class->"@H_301_17@+bd.getClassName@H_301_17@(),e);@H_301_17@
}
}
protected void injectBeanProperties(Object bean,BeanDefinition beanDefinition){
try {
PropertyDescriptor[] ps = Introspector.getBeanInfo@H_301_17@(bean.getClass@H_301_17@()).getPropertyDescriptors@H_301_17@(); @H_301_17@
for(PropertyDefinition propertyDefinition : beanDefinition.getProperties@H_301_17@()){
for(PropertyDescriptor propertyDescriptor:ps){
if(propertyDescriptor.getName@H_301_17@().equals@H_301_17@(propertyDefinition.getName@H_301_17@())){
Method setter = propertyDescriptor.getWriteMethod@H_301_17@(); @H_301_17@
setter.setAccessible@H_301_17@(true); @H_301_17@
setter.invoke@H_301_17@(bean,getBean(propertyDefinition.getRef@H_301_17@())); @H_301_17@
}
}
}
} catch (SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| IntrospectionException e) {
throw new RuntimeException("inject bean properties error"@H_301_17@,e);@H_301_17@
}
}
}
在这涉及到两个关键类:BeanDefinition 和 PropertyDefinition,它们分别是用来描述 javabean的定义和javabean 属性的定义,一个BeanDefinition 可以有1个或多个PropertyDefinition,它们之间是1:N的关系。代码如下:
BeanDefinition.java
package@H_301_17@ com.ricky.framework.ioc.model;
import@H_301_17@ java.util.List;
public@H_301_17@ class@H_301_17@ BeanDefinition@H_301_17@ {@H_301_17@
private@H_301_17@ String id;
private@H_301_17@ String className;
private@H_301_17@ String scope; //singleton|prototype@H_301_17@
private@H_301_17@ String initMethodName;
private@H_301_17@ List<PropertyDefinition> properties;
public@H_301_17@ BeanDefinition@H_301_17@(String id,String className) {
this@H_301_17@.id = id;
this@H_301_17@.className = className;
}
public@H_301_17@ String getId@H_301_17@() {
return@H_301_17@ id;
}
public@H_301_17@ void@H_301_17@ setId@H_301_17@(String id) {
this@H_301_17@.id = id;
}
public@H_301_17@ String getClassName@H_301_17@() {
return@H_301_17@ className;
}
public@H_301_17@ void@H_301_17@ setClassName@H_301_17@(String className) {
this@H_301_17@.className = className;
}
public@H_301_17@ String getScope@H_301_17@() {
return@H_301_17@ scope;
}
public@H_301_17@ void@H_301_17@ setScope@H_301_17@(String scope) {
this@H_301_17@.scope = scope;
}
public@H_301_17@ String getInitMethodName@H_301_17@() {
return@H_301_17@ initMethodName;
}
public@H_301_17@ void@H_301_17@ setInitMethodName@H_301_17@(String initMethodName) {
this@H_301_17@.initMethodName = initMethodName;
}
public@H_301_17@ List<PropertyDefinition> getProperties@H_301_17@() {
return@H_301_17@ properties;
}
public@H_301_17@ void@H_301_17@ setProperties@H_301_17@(List<PropertyDefinition> properties) {
this@H_301_17@.properties = properties;
}
@Override@H_301_17@
public@H_301_17@ String toString@H_301_17@() {
return@H_301_17@ "BeanDefinition [id="@H_301_17@ + id + ",className="@H_301_17@ + className
+ ",scope="@H_301_17@ + scope + ",initMethodName="@H_301_17@ + initMethodName
+ ",properties="@H_301_17@ + properties + "]"@H_301_17@;
}
}
PropertyDefinition.java
package com.ricky.framework.ioc.model;
public@H_301_17@ class@H_301_17@ PropertyDefinition {
private@H_301_17@ String name;
private@H_301_17@ String ref@H_301_17@;
public@H_301_17@ PropertyDefinition@H_301_17@(String name,String ref@H_301_17@) {
this@H_301_17@.name = name;
this@H_301_17@.ref@H_301_17@ = ref@H_301_17@;
}
public@H_301_17@ String getName@H_301_17@() {
return@H_301_17@ name;
}
public@H_301_17@ void@H_301_17@ setName@H_301_17@(String name) {
this@H_301_17@.name = name;
}
public@H_301_17@ String getRef@H_301_17@() {
return@H_301_17@ ref@H_301_17@;
}
public@H_301_17@ void@H_301_17@ setRef@H_301_17@(String ref@H_301_17@) {
this@H_301_17@.ref@H_301_17@ = ref@H_301_17@;
}
@Override
public@H_301_17@ String toString@H_301_17@() {
return@H_301_17@ "PropertyDefinition [name="@H_301_17@ + name + ",ref="@H_301_17@ + ref@H_301_17@ + "]"@H_301_17@;
}
}
2、接下来是ClassPathXmlApplicationContext类,代码如下:
package@H_301_17@ com.ricky.framework.ioc;
import@H_301_17@ java.io.FileNotFoundException;
import@H_301_17@ java.util.HashMap;
import@H_301_17@ java.util.List;
import@H_301_17@ java.util.Map;
import@H_301_17@ org.apache.commons.lang3.StringUtils;
import@H_301_17@ org.dom4j.DocumentException;
import@H_301_17@ com.ricky.framework.ioc.model.BeanDefinition;
import@H_301_17@ com.ricky.framework.ioc.parser.BeanXmlConfigParser;
import@H_301_17@ com.ricky.framework.ioc.util.BeanScope;
import@H_301_17@ com.ricky.framework.ioc.util.ReflectionUtils;
public@H_301_17@ class@H_301_17@ ClassPathXmlApplicationContext@H_301_17@ extends@H_301_17@ ApplicationContext@H_301_17@ {@H_301_17@
private@H_301_17@ Map<String,BeanDefinition> beanDefinitionMap = new@H_301_17@ HashMap<String,BeanDefinition>();
protected@H_301_17@ Map<String,Object> beanInstanceMap = new@H_301_17@ HashMap<String,Object>();
public@H_301_17@ ClassPathXmlApplicationContext@H_301_17@(String xmlFilePath) {
System.out.println("****************container init begin****************"@H_301_17@);
readXml(xmlFilePath);
initBeans();
injectBeans();
System.out.println("****************container init end****************"@H_301_17@);
}
private@H_301_17@ void@H_301_17@ readXml@H_301_17@(String xmlFilePath) {
BeanXmlConfigParser beanXmlConfigParser = new@H_301_17@ BeanXmlConfigParser();
List<BeanDefinition> bean_def_list = null@H_301_17@;
try@H_301_17@ {
bean_def_list = beanXmlConfigParser.parse(xmlFilePath);
} catch@H_301_17@ (FileNotFoundException e) {
throw@H_301_17@ new@H_301_17@ RuntimeException("not found bean xml,file->"@H_301_17@+xmlFilePath,e);
} catch@H_301_17@ (DocumentException e) {
throw@H_301_17@ new@H_301_17@ RuntimeException("bean xml format error,e);
}
for@H_301_17@ (BeanDefinition beanDefinition : bean_def_list) {
if@H_301_17@(StringUtils.isEmpty(beanDefinition.getId()) || StringUtils.isEmpty(beanDefinition.getClassName())){
throw@H_301_17@ new@H_301_17@ IllegalArgumentException("bean definition is empty!"@H_301_17@);
}
if@H_301_17@ (beanDefinitionMap.containsKey(beanDefinition.getId())) {
throw@H_301_17@ new@H_301_17@ IllegalArgumentException(
"duplicated bean id,id->"@H_301_17@
+ beanDefinition.getId());
}
beanDefinitionMap.put(beanDefinition.getId(),beanDefinition);
}
}
private@H_301_17@ void@H_301_17@ initBeans@H_301_17@() {
for@H_301_17@ (Map.Entry<String,BeanDefinition> me : beanDefinitionMap.entrySet()) {
BeanDefinition bd = me.getValue();
if@H_301_17@(StringUtils.isEmpty(bd.getScope()) || bd.getScope().equals(BeanScope.SINGLETON)){
try@H_301_17@ {
Object bean = createBean(bd);
beanInstanceMap.put(bd.getId(),bean);
} catch@H_301_17@ (Exception e) {
throw@H_301_17@ new@H_301_17@ IllegalArgumentException("create bean error,class->"@H_301_17@+bd.getClassName(),e);
}
}
}
}
private@H_301_17@ void@H_301_17@ injectBeans@H_301_17@() {
for@H_301_17@ (Map.Entry<String,BeanDefinition> me : beanDefinitionMap.entrySet()) {
BeanDefinition beanDefinition = me.getValue();
//判断有没有注入属性 @H_301_17@
if@H_301_17@ (beanDefinition.getProperties() != null@H_301_17@ && beanDefinition.getProperties().size()>0@H_301_17@) {
Object bean = beanInstanceMap.get(beanDefinition.getId());
try@H_301_17@ {
injectBeanProperties(bean,beanDefinition);
} catch@H_301_17@ (Exception e) {
e.printStackTrace();
}
}
}
}
@Override@H_301_17@
public@H_301_17@ Object getBean@H_301_17@(String id) {
// System.out.println("get bean by id:"+id);@H_301_17@
if@H_301_17@ (StringUtils.isEmpty(id)) {
return@H_301_17@ null@H_301_17@;
}
if@H_301_17@ (beanDefinitionMap.containsKey(id)) {
BeanDefinition bd = beanDefinitionMap.get(id);
if@H_301_17@(StringUtils.isEmpty(bd.getScope()) || bd.getScope().equals(BeanScope.SINGLETON)){
return@H_301_17@ beanInstanceMap.get(id);
}
Object bean = null@H_301_17@;
try@H_301_17@ {
bean = createBean(bd);
injectBeanProperties(bean,bd);
beanInstanceMap.put(bd.getId(),bean);
} catch@H_301_17@ (Exception e) {
e.printStackTrace();
}
return@H_301_17@ bean;
}
throw@H_301_17@ new@H_301_17@ IllegalArgumentException("unknown bean,id->"@H_301_17@ + id);
}
@SuppressWarnings@H_301_17@("unchecked"@H_301_17@)
@Override@H_301_17@
public@H_301_17@ <T> T getBean@H_301_17@(Class<T> clazz) {
// System.out.println("get bean by type:"+clazz.getName());@H_301_17@
for@H_301_17@(Map.Entry<String,BeanDefinition> me : beanDefinitionMap.entrySet()){
BeanDefinition bd = me.getValue();
Class<?> beanClass = null@H_301_17@;
try@H_301_17@ {
beanClass = ReflectionUtils.loadClass(bd.getClassName());
} catch@H_301_17@ (ClassNotFoundException e) {
e.printStackTrace();
}
if@H_301_17@(beanClass!=null@H_301_17@ && clazz.isAssignableFrom(beanClass)){
// System.out.println("find bean by type,class->"+clazz.getName());@H_301_17@
return@H_301_17@ (T) getBean(bd.getId());
}
}
return@H_301_17@ null@H_301_17@;
}
@Override@H_301_17@
protected@H_301_17@ BeanDefinition getBeanDefinition@H_301_17@(String id) {
return@H_301_17@ beanDefinitionMap.get(id);
}
@Override@H_301_17@
public@H_301_17@ void@H_301_17@ close@H_301_17@() {
System.out.println("container close..."@H_301_17@);
// release resource@H_301_17@
beanDefinitionMap.clear();
beanDefinitionMap = null@H_301_17@;
beanInstanceMap.clear();
beanInstanceMap = null@H_301_17@;
}
}
在ClassPathXmlApplicationContext类中,主要负责三大功能:解析XML配置文件、通过反射构建Bean实例以及对Bean进行装配。
小结
以上所有代码均已上传到GitHub上,欢迎大家fork。另外由于时间比较仓促,代码设计上有不合理的地方还请包涵,后面会抽时间对代码进行重构。