自己动手写一个ioc容器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DWL0208/article/details/82693525

       控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中,我们常用的spring框架核心就是一个ioc容器。

1.首先定义一个ioc的接口

package pers.dwl.sardine.ioc;

/**
 * ioc 容器接口
 * @author dwl
 *
 */
public interface Ioc {

    /**
     * 获取实例对象
     * @param clazz
     * @return 实例对象
     */
    <T> T getEntity(Class<T> clazz);
	
    /**
     * ioc中该是否有实例对象
     * @param clazz
     * @return boolean 
     */
    boolean hasEntity(Class<?> clazz);
    
    /**
     * 将容器注销
     */
    void depose();

}

2.传统的bean都是配置在xml中,不过现在也流行通过注解来注入,所以我们定义两个注解。

@Inject 表示属性需要由ioc来注入

package pers.dwl.sardine.ioc.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 标识需要注入的属性
 * @author dwl
 *
 */
@Documented
@Retention(RUNTIME)
@Target(FIELD)
public @interface Inject {
	
	Class<?> value() default String.class;

}

@IocBean表示该类交给ioc管理

package pers.dwl.sardine.ioc.annotation;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 定义bean类 -标识该类交给IOC管理
 * @author dwl
 *
 */
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface IocBean {

}

 3.实现ioc接口,完成依赖注入的功能

package pers.dwl.sardine.ioc.impl;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import pers.dwl.sardine.ioc.Ioc;
import pers.dwl.sardine.ioc.annotation.Inject;
import pers.dwl.sardine.ioc.annotation.IocBean;
import pers.dwl.sardine.util.ArrayUtil;
import pers.dwl.sardine.util.ClassHelper;

/**
 * ioc实现类
 * @author dwl
 *
 */
public class SineIoc implements Ioc {
	
	private static final Object lock = new Object();
	
	/**
	 * 实例对象集合-未完成注入
	 */
	private static Map<Class<?>, Object> beanMap = new HashMap<Class<?>, Object>();
	
	/**
	 * 实例对象集合-已完成注入(第一次调用时注入)
	 */
	private static Map<Class<?>, Object> injectMap = new HashMap<Class<?>, Object>();
	
	//初始化
	static{
		//获取基础包下所有class
		List<Class<?>> classes = ClassHelper.getClassList();
		classes.forEach(clazz->{
			if(clazz.isAnnotationPresent(IocBean.class)){
				try {
					//加入实例对象集合
					beanMap.put(clazz, clazz.newInstance());
				} catch (InstantiationException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
			
		});
	}
	

	@SuppressWarnings("unchecked")
	public <T> T getEntity(Class<T> clazz) {
		if(!injectMap.containsKey(clazz)){
			if(!beanMap.containsKey(clazz)){
				throw new RuntimeException("没有该示例对象 - "+clazz);
			}else{
				synchronized (lock) {
					if(!injectMap.containsKey(clazz))
						setFiled(clazz);//依赖注入,加入缓存	
				}
			}
		}
		return (T)injectMap.get(clazz);
	}

	public boolean hasEntity(Class<?> clazz) {
		return injectMap.containsKey(clazz);
	}
	
	public void depose() {
		beanMap.clear();
		injectMap.clear();
	}
	
	/**
	 * 给属性赋值-依赖注入
	 * @param clazz
	 */
	public void setFiled(Class<?> clazz){
		 //获取所有属性
		 Field[] fields = clazz.getDeclaredFields();
		 //获取实例对象
		 Object instance = beanMap.get(clazz);
		 //有属性
		 if(!ArrayUtil.isEmpty(fields)){
			 for(Field field:fields){
				 //有待注入的属性
				 if(field.isAnnotationPresent(Inject.class)){
					 //获取属性上的inject信息
					 Inject inject = field.getAnnotation(Inject.class);
					 Class<?> type = inject.value();
					 //默认String.class,表示该属性类型是类不是接口
					 if(type.isAssignableFrom(String.class)){
						 Class<?> implClass = field.getType();
						//为属性对象实例进行依赖注入,不考虑循环依赖
						 setFiled(implClass);
						 //从injectMap获取已经注入好的属性对象实例
						 Object implBean =injectMap.get(implClass);
						 field.setAccessible(true);
						 try {
							 //为属性赋值
							field.set(instance, implBean);
						} catch (IllegalArgumentException | IllegalAccessException e) {
							e.printStackTrace();
						}
					 }else{//为接口时value要声明具体实现类
						 setFiled(type);//为属性对象实例进行依赖注入,不考虑循环依赖
						//从injectMap获取已经注入好的属性对象实例
						 Object implBean =injectMap.get(type);
						 field.setAccessible(true);
						 try {
							 //为属性赋值
							field.set(instance, implBean);
						} catch (IllegalArgumentException | IllegalAccessException e) {
							e.printStackTrace();
						}
					 }
				 }
			 }
			
		 }
		 injectMap.put(clazz, instance);//加入缓存
		 
	}

}

     在获取对象实例的时有需要注入的属性会先实例该属性对象,使用递归从下至上依次实例化,但是没有考虑循环依赖的情况,如果代码中不注意写出了循环依赖的话会产生死循环。

4.测试 我们写个两层依赖试一试效果。 MyService依赖MyService2   ,MyAction依赖MyService。

MyService2只有一个方法

package pers.dwl.sardine.ioc.test;

import pers.dwl.sardine.ioc.annotation.IocBean;

@IocBean
public class Myservice2 {
	
	public void doSome(){
		System.out.println("----------注入啦------------");
	}

}

 MyService依赖MyService2

package pers.dwl.sardine.ioc.test;

import pers.dwl.sardine.ioc.annotation.Inject;
import pers.dwl.sardine.ioc.annotation.IocBean;

@IocBean
public class MyService {
	
	@Inject
	private Myservice2 myservice2;
	
	public void doSome(){
		myservice2.doSome();
	}

}

MyAction依赖MyService

package pers.dwl.sardine.ioc.test;

import pers.dwl.sardine.ioc.annotation.Inject;
import pers.dwl.sardine.ioc.annotation.IocBean;

@IocBean
public class MyAction {
	
	@Inject
	private MyService myService;
	
	public void doSome(){
		myService.doSome();
	}

}

写个main方法测试效果

package pers.dwl.sardine.ioc.test;

import pers.dwl.sardine.ioc.Ioc;
import pers.dwl.sardine.ioc.impl.SineIoc;

public class test {

	public static void main(String[] args) {
		Ioc ioc= new SineIoc();
		MyAction myBean = ioc.getEntity(MyAction.class);
		myBean.doSome();
	}

}

控制台效果

 5.源代码中有两个工具类没有贴出来,感兴趣的同学可以到 https://github.com/daiwenlong/sardine 下载源码。

猜你喜欢

转载自blog.csdn.net/DWL0208/article/details/82693525