控制反转(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 下载源码。