一、什么是 IOC
我们先来看看spring官网对IOC的定义:
IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.
就不逐句翻译了,主要意思就是,IOC也被称为依赖注入,它是一个容器创建bean时将依赖对象注入的过程,而这些依赖对象在该bean中仅仅通过构造参数、工厂方法的参数或属性来指定。
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度,提高代码的可重用性和可维护性。通过控制反转,对象在被创建的时候,由一个容器将其所依赖的对象的引用传递给它,即我们常说的,依赖被注入到对象中。要自己设计实现一个IOC,首先要理解IOC的作用, 最基础且必要功能有两个:a、存储对象, b、有注解注入的功能;
二、为什么需要 IOC
在传统java项目中对象是通过new 获得的,并需要自己维护对象的生命周期,在一个大型项目中,这个将是繁杂冗余浪费机器资源的,如图(1) ,IOC就是来解决该问题的,由IOC容器来维护类的生命周期和类之间的引用,如图(2),对象的关系图也即变成图(3) 的效果,看着是否清爽多了,对人是这样,对机器也是这样
(图1)
(图2)
(图3)
三、怎么实现IOC
Java语言允许通过程序化的方式间接对Class进行操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。下面我们着手实现一个IOC容器,先参照下最经典的Spring容器。
上图是Spring如何工作的高级视图,业务类与配置元数据相结合,这样在创建和初始化ApplicationContext(spring 容器)之后,就有了一个完全配置和可执行的系统或应用程序,Spring IOC的实现官网等都有比较详细的介绍了,这里就不做赘述。
在这里,参照spring容器视图,为了更好的理解和实现IOC我们从基础0代码出发,一步一步详细实现一个简单的通过注解的方式实现IOC容器功能, 一个IOC容器中最核心的当属容器接口,比如我们定义为:Container。那么这个容器里应该有什么呢?即需要具备什么功能呢?要能存储对象,其次包括括获取、注册、删除对象的方法,所以可以通过一个map来实现容器的核心。
1、容器接口
public interface Container {
/**
* 根据Class获取Bean
* @param clazz
* @return
*/
public <T> T getBean(Class<T> clazz);
/**
* 根据名称获取Bean
* @param name
* @return
*/
public <T> T getBeanByName(String name);
/**
* 注册一个Bean到容器中
* @param object
*/
public Object registerBean(Object bean);
/**
* 注册一个Class到容器中
* @param clazz
*/
public Object registerBean(Class<?> clazz);
/**
* 注册一个带名称的Bean到容器中
* @param name
* @param bean
*/
public Object registerBean(String name, Object bean);
/**
* 删除一个bean
* @param clazz
*/
public void remove(Class<?> clazz);
/**
* 根据名称删除一个bean
* @param name
*/
public void removeByName(String name);
/**
* @return 返回所有bean对象名称
*/
public Set<String> getBeanNames();
/**
* 初始化装配
*/
public void initWired();
}
2、容器实现
这里将所有Bean的名称存储在 beanKeys
这个map中,将所有的对象存储在 beans
中,用 beanKeys
维护名称和对象的关系。
在装配的时候步骤如下
public class ContainerImpl implements Container { /** * 保存所有bean对象,格式为com.test.TestA TestA@516 */ private Map<String, Object> beans; /** * 存储注入属性名和对象的全名称,格式testA com.test.TestA */ private Map<String, String> beanKeys; public ContainerImpl() { this.beans = new ConcurrentHashMap<String, Object>(); this.beanKeys = new ConcurrentHashMap<String, String>(); } /** * 根据类获取指定的对象 * @param clazz * @param <T> * @return */ @Override public <T> T getBean(Class<T> clazz) { String name = clazz.getName(); Object object = beans.get(name); if(null != object){ return (T) object; } return null; } /** * 获取指定的对象 * @param name 对象名称 * @param <T> * @return */ @Override public <T> T getBeanByName(String name) { String className = beanKeys.get(name); Object obj = beans.get(className); if(null != obj){ return (T) obj; } return null; } /** * 通过对象的方式进行注册 * @param bean * @return */ @Override public Object registerBean(Object bean) { String name = bean.getClass().getName(); String lastName = name.substring(name.lastIndexOf(".")+1); lastName = StringUtils.uncapitalize(lastName); beanKeys.put(lastName, name); beans.put(name, bean); return bean; } /** * 通过类方式进行注册 * @param clazz * @return */ @Override public Object registerBean(Class<?> clazz) { String name = clazz.getName(); String lastName = name.substring(name.lastIndexOf(".")+1); lastName = StringUtils.uncapitalize(lastName); beanKeys.put(lastName, name); Object bean = null; try { bean = ReflectUtil.newInstance(clazz); beans.put(name, bean); }catch (Exception e) { return null; } return bean; } /** * 通过指定对象名称方式进行注册 * @param name * @param bean * @return */ @Override public Object registerBean(String name, Object bean) { String className = bean.getClass().getName(); beanKeys.put(name, className); beans.put(className, bean); return bean; } /** * 获取对象名称集合 * @return */ @Override public Set<String> getBeanNames() { return beanKeys.keySet(); } /** * 根据类卸载一个对象 * @param clazz */ @Override public void remove(Class<?> clazz) { String className = clazz.getName(); if(null != className && !className.equals("")){ beanKeys.remove(className); beans.remove(className); } } /** * 指定卸载某个对象 * @param name */ @Override public void removeByName(String name) { String className = beanKeys.get(name); if(null != className && !className.equals("")){ beanKeys.remove(name); beans.remove(className); } } /** * 将注册的bean通过循环的方式注入的容器中,后续通过容器进行使用各个bean对象 */ @Override public void initWired() { Iterator<Entry<String, Object>> it = beans.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next(); Object object = entry.getValue(); injection(object); } } /** * 本例子是展示了,通过注解的方式进行注入 * @param object */ public void injection(Object object) { try { // 所有字段 Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { // 需要注入的字段,找到注解的对象(@autowired注解) // Autowired autoWired = field.getAnnotation(Autowired.class); // 需要注入的字段,找到注解的对象(@Resource注解) Resource autoWired = field.getAnnotation(Resource.class); if (null != autoWired) { // 要注入的字段 Object autoWiredField = null; String name = field.getName(); String className = beanKeys.get(name); if (StringUtils.isNotBlank(className)) { autoWiredField = beans.get(className); } if (autoWiredField == null) { throw new RuntimeException("Unable to load " + field.getType().getCanonicalName()); } boolean accessible = field.isAccessible(); field.setAccessible(true); field.set(object, autoWiredField); field.setAccessible(accessible); } } } catch (Exception e) { e.printStackTrace(); } } }
3、测试类
public class IocTest {
private static Container container = new ExampleContainer();
public static void baseTest(){
// 将TestB注册到容器中(通过类名方式)
container.registerBean(TestB.class);
// 将TestA注册到容器中(通过对象方式)
TestA a = new TestA();
container.registerBean(a);
// 进行初始化(依赖注入)
container.initWired();
// 获取bean
TestB test = container.getBean(TestB.class);
// 执行对应bean里的方法
test.doTest();
}
public static void main(String[] args) {
baseTest();
}
}
4、测试BEAN
public class TestA { public void work(){ System.out.println("TestA executes,implment IOC..."); } }
public class TestB { /** * TestB中注入TestA */ @Autowired private TestA testA; public void doTest (){ System.out.println("testB executes..."); testA.work(); } }