学习IOC之简单实现

1、什么是IOC?

控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛(来自百度百科)。

自己的理解:不是开发者自己通过new的方式来实例化一个对象,而是把对象的创建交给框架来完成。

2、IOC的优缺点?

  • 优点:
    解决了程序之间的松耦合问题,当我们需要换一个接口实现类的子类时,我们只需要修改配置文件即可(一般代码中都是基于接口编程,而在配置文件中使用具体的实现),这样就无需修改源代码。(实际上我认为其实并没有解决这种耦合关系,只是把这种耦合关系从代码转移到了配置文件,我想这就是所谓的控制反转吧)。
  • 缺点:
    (1)IOC是基于反射的原理,来实现对象的实例化的,可能效率上会有所损耗;
    (2)可能对某些人来说,生成对象的方式变复杂了,其实也没那么复杂。

3、IOC的简单实现?
最简单的方式就是,首先获取所有的BeanMap(是一个map,记录了这个类的字节码与这个类的实例的映射关系)。然后遍历这个映射关系,分别取出Bean类与Bean实例,进而通过反射获取类中的所有的成员变量。继续遍历这些成员变量,在循环中判断当前成员变量是否带有Inject注解(自己定义的一个注解,可参考后面的代码),若带有该注解,则从BeanMap中根据Bean类取出Bean实例。最后通过反射来修改该成员变量的值。

  1. 首先我们需要获取指定包名下的所有类,这个方法可以参考spring中的实现,另外网上有很多现成的代码。
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.jmx.LayoutDynamicMBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClassUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

    /*
     * 获取类加载器
     */
    public static ClassLoader getClassLoader(){

        return Thread.currentThread().getContextClassLoader();
    }
    /*
     * 加载类
     */
    public static Class<?> loadClass(String className,boolean isInitialized){
        Class<?> cls;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
            //Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            LOGGER.error("load class failure",e);
            throw new RuntimeException(e);
        }
        return cls;
    }
    /*
     * 获取指定包名下的所有类
     */
    public static Set<Class<?>> getClassSet(String packageName){

        Set<Class<?>> classSet = new HashSet<Class<?>>();
        try {
            //获取指定包下的资源
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".","/"));
            while(urls.hasMoreElements()){
                URL url = urls.nextElement();
                if(url != null){
                    //获取协议的名称(file或者jar)
                    String protocol = url.getProtocol();
                    if(protocol.equals("file")){
                        //获取url的路径并解码,空格的url编码值就是%20
                        String packagePath = url.getPath().replaceAll("%20"," ");
                        addClass(classSet,packagePath,packageName);
                    }else if(protocol.equals("jar")){
                        JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();
                        if(jarURLConnection != null){
                            //如果协议是jar,获取一个jarFile
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if(jarFile != null){
                                //从jarFile中得到一个枚举类
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while(jarEntries.hasMoreElements()){
                                    //获取一个实体
                                    JarEntry jarEntry = jarEntries.nextElement();
                                    String jarEntryName = jarEntry.getName();
                                    //如果以.class结尾,则截取类的全限定名
                                    if(jarEntryName.endsWith(".calss")){
                                        String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                        //将类实例化,并加入classSet中
                                        doAddClass(classSet,className);
                                    }
                                }
                            }

                        }
                    }
                }
            }
        } catch (IOException e) {
            LOGGER.error("get class set failure", e);
            throw new RuntimeException(e);
        }
        return classSet;
    }
    private static void doAddClass(Set<Class<?>> classSet, String className) {
        //获取类的实体
        Class<?> cls = loadClass(className, false);
        //加入classSet集合中
        classSet.add(cls);

    }
    private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {

        File[] files = new File(packagePath).listFiles(new FileFilter() {
            //过滤
            @Override
            public boolean accept(File file) {
                return file.isFile()&&file.getName().endsWith(".class")||file.isDirectory();
            }
        });
        for(File file:files){
            String fileName = file.getName();
            if(file.isFile()){
                //截取类名
                String className = fileName.substring(0,fileName.lastIndexOf("."));
                if(StringUtils.isNotEmpty(packageName)){
                    //获取类的全限定名
                    className = packageName+"."+className;
                }
                //取得类的实例并加入classSet
                doAddClass(classSet, className);
            }else{
                //如果是目录,则递归调用函数
                String subPackagePath = fileName;
                if(StringUtils.isNotEmpty(packagePath)){
                    subPackagePath = packagePath+"/"+subPackagePath;
                }
                String subPackageName = fileName;
                if(StringUtils.isNotEmpty(packageName)){
                    subPackagePath = packageName+"."+subPackagePath;
                }
                addClass(classSet, subPackagePath, subPackageName);
            }
        }
    }

2.简单起见,我们可以将带有controller和service注解的类所产生的对象,理解为框架需要管理的bean,下面的工具类用来获取应用包名下的所有Bean类的方法。

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
import org.smart4j.framework.ConfigHelper;
import org.smart4j.framework.annotation.Controller;
import org.smart4j.framework.annotation.Service;
import org.smart4j.framework.utils.ClassUtil;

public class ClassHelper {

    private static final Set<Class<?>> CLASS_SET;
    static{
        //自己定义的应用包常量,你也可以定义在配置文件中
        String basePackage = ConfigConstant.BASE_PACKAGE;
        CLASS_SET = ClassUtil.getClassSet(basePackage);

    }
    /*
     * 获取应用包下的所有类
     */
    public static Set<Class<?>> getClassSet(){
        return CLASS_SET;
    }
    /*
     * 获取应用包下的所有service类
     */
    public static Set<Class<?>> getServiceClassSet(){
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for(Class<?> cls: CLASS_SET){
            if(cls.isAnnotationPresent(Service.class)){
                classSet.add(cls);
            }
        }
        return classSet;
    }
    /*
     * 获取应用包下的所有Controller
     */
    public static Set<Class<?>> getControllerClassSet(){
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for(Class<?> cls: CLASS_SET){
            if(cls.isAnnotationPresent(Controller.class)){
                classSet.add(cls);
            }
        }
        return classSet;
    }
    /*
     * 获取应用包名下的所有Bean类
     */
    public static Set<Class<?>> getBeanClassSet(){
        Set<Class<?>> beanclassSet = new HashSet<Class<?>>();
        //认为所有的service类和controller类是我们需要管理的bean
        beanclassSet.addAll(getServiceClassSet());
        beanclassSet.addAll(getControllerClassSet());
        return beanclassSet;
    }
    /*
     * 获取应用包名下某父类(或接口)的所有子类(或实现类)
     */
    public static Set<Class<?>> getClassSetBySuper(Class<?> superClass){

        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for(Class<?> cls : CLASS_SET){
            if(superClass.isAssignableFrom(cls)&&!superClass.equals(cls)){
                classSet.add(cls);
            }
        }
        return classSet;
    }
    /*
     * 获取应用包名下所有带有某注解的所有类
     */
    public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){

        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for(Class<?> cls : CLASS_SET){
            if(cls.isAnnotationPresent(annotationClass)){
                classSet.add(cls);
            }
        }
        return classSet;
    }
}

3.使用ClassHelper类可以获取所有加载的类,这里提供一个反射工具类,通过反射来实例化相应的类

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReflectionUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);

    /*
     * 创建实例
     */
    public static Object newInstance(Class<?> cls){
        Object instance;
        try {
            instance = cls.newInstance();
        } catch (Exception e) {
            LOGGER.error("new instance failure",e);
            throw new RuntimeException(e);
        }
        return instance;
    }
    /*
     * 调用方法
     */
    public static Object invokeMethod(Object obj,Method method,Object...args){
        Object result;

        try {
            method.setAccessible(true);
            result = method.invoke(obj, args);
        } catch (Exception e) {
            LOGGER.error("invoke method failure", e);
            throw new RuntimeException(e);
        }
        return result;

    }
    //设置成员变量的值
    public static void setField(Object obj,Field field,Object value){

        field.setAccessible(true);
        try {
            field.set(obj, value);
        } catch (Exception e) {
            LOGGER.error("set field failure",e);
            throw new RuntimeException(e);
        }   
    }

4.该工具类用于生成bean类和bean实例的映射关系

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.smart4j.framework.utils.ReflectionUtil;

public class BeanHelper {

    //用于存放bean类及bean实例之间的映射关系
    private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>,Object>();
    static{
        Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
        for(Class<?> cls : beanClassSet){
            Object obj = ReflectionUtil.newInstance(cls);
            BEAN_MAP.put(cls, obj);
        }
    }

    /*
     * 获取bean映射
     */
    public static Map<Class<?>,Object> getBeanMap(){

        return BEAN_MAP;
    }
    /*
     * 获取bean实例
     */
    public static <T> T getBean(Class<T> cls){
        if(!BEAN_MAP.containsKey(cls)){
            throw new RuntimeException("can not get bean by class");
        }
        return (T)BEAN_MAP.get(cls);
    }

    public static void setBean(Class<?> cls,Object obj){
        BEAN_MAP.put(cls, obj);
    }
}

5.实现依赖注入

import java.lang.reflect.Field;
import java.util.Map;
import org.smart4j.framework.annotation.Inject;
import org.smart4j.framework.utils.ArrayUtil;
import org.smart4j.framework.utils.ReflectionUtil;

public class IocHelper {

    static{
        //获取bean类及bean实例之间的映射关系
        Map<Class<?>,Object> beanMap = BeanHelper.getBeanMap();
        for(Map.Entry<Class<?>,Object> entry: beanMap.entrySet()){
            //获取bean类
            Class<?> beanClass = entry.getKey();
            //获取bean实例
            Object beanInstance = entry.getValue();
            //通过反射机制获取bean类的所有成员变量
            Field[] fields = beanClass.getDeclaredFields();
            if(ArrayUtil.isNotEmpty(fields)){
                for(Field field : fields){
                    //如果该成员变量有Inject注解,则注入该bean实例
                    if(field.isAnnotationPresent(Inject.class)){
                        Class<?> beanFieldClass = field.getType();
                        Object beanFiledInstance = beanMap.get(beanFieldClass);
                        //通过反射注入该成员变量的实例
                        ReflectionUtil.setField(beanInstance, field, beanFiledInstance);

                    }
                }
            }
        }

    }

}

总结:
其实整个IOC的实现逻辑并不复杂,只是工具类写的很多,实际上有以下大概几步:
(1)首先获取所有bean类及bean实例的映射关系,本文实现方式:首先扫描并获取应用包下的所有类;
(2)然后获取需要管理的bean类,这里认为所有拥有service注解和controller注解的类都是需要管理的bean类;
(3)然后获取bean类及其bean的映射关系(通过反射)——beanMap结构,这里也是为了后来注入时方便;
(4)然后遍历beanMap,扫面每一个bean里的成员变量,查看是否拥有Inject注解,如果拥有,则通过反射注入。
本文实现的IOC当然和Spring的还有很大差距,重点是让大家了解如何使用注解实现IOC的,另外本文大量参考了《架构探险,从零开始写java web 框架》。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值