猿思考系列4反射之一文学会java的斗转星移

52次阅读

共计 6545 个字符,预计需要花费 17 分钟才能阅读完成。

看完上一个章节,相信你已经掌握了一些思考的本领了。今天我们来聊一个新的话题。上一篇文章是一个简约的话题,希望简约而不简单。当然,如果觉得太浅了也请立刻告知猿人工厂君,可以考虑做一些调整来更好的帮助到你,另外真的很感谢大家的支持,和巨兽的斗争暂时进入僵持阶段,猿人工厂君已经说了,虽千万人,吾往矣。中间细节,猿人工厂君,会在方便的时候公开,程序猿鸭,且行且珍惜。

猿思考是一个原创系列文章,帮助你从一个小白快速掌握基础知识,很多基础知识,在于思考的变通,更多精彩内容,敬请大家关注公主号 猿人工厂 ,点击 猿人养成获取

IOC 是 Inversion ofControl 的英文缩写,中文译名控制反转。控制反转?哈哈,是不是看起来怪怪的?控制反转,也是面向对象的一种编程原则,是一种编程思想。它是指将创建对象的管理权交给容器,由容器来装配和创建对象, 从而降低代码之间的耦合度。IOC 思想的实现,一般有两种方式,一种是依赖查找(Dependency Lookup)一种就是最常见的依赖注入(Dependency Injection)。依赖查找是指程序运行时,程序使用容器提供的回调接口和上下文条件来查找资源和对象,从而让程序获得相应资源。这个种方式应用不是很多,需要应用服务器的支持,也就是 JAVAEE 规范的套路,比如 JNDI,EJB… 本文就不详细讨论了。而依赖注入(Dependency Injection)是什么呢? 依赖注入是指,程序在运行时,程序不用做依赖的定位查询,提供普通的 java 方法去让容器决定依赖关系,容器全权负责程序使用对象的装配,把符合依赖关系的对象,通过属性或者是构造器的方式传递给依赖的对象。

我们看个简单的例子,我们有三个类:分别是 TestServiceTestManager TestDao. 其中 TestService 种有一个 test()方法,调用了 TestManager 种的 test()方法,而 TestManager 的 test 方法又调用了 TestDao 的 test()方法。如果我们要调用 TestService 的 test()方法,在正常情况下我们编写 java 代码,必然需要 new TestService() newTestManager(), new TestDao(). 然后将 TestManager 对象作为 TestService 的一个属性传递给 TestService(testService.setXXX 或者是构造函数的方式传递),TestDao 对象作为 TestManager 的一个属性,那么在使用 TestService 的类中,必然就需要在代码中 import TestManager,

这样新编写的这个类代码依赖程度就高,耦合性强,不好维护。那现在改为依赖注入的方式,我们要使用 TestService,就找容器拿 TestService, 容器提供的 TestService 对象里已经存在 TestManager 对象了,那么我们使用 TestService 时,就只用在代码里 import TestService 就好了!新编写的类耦合性就没那么强了,易于维护多了。

那怎么去做到它呢?

不就是提供一个容器,获取对象嘛,只是获取的对象里的属性,已经由容器设置好了嘛。怎么办?通过名字创建对象!嗯,说白了,还是反射。下面我们来看一个小例子,实现简单的依赖注入。

先定义两个 annotation:

package  com.pz.study.frame.ioc;

import  java.lang.annotation.ElementType;
import  java.lang.annotation.Retention;
import  java.lang.annotation.RetentionPolicy;
import  java.lang.annotation.Target;


/**
 * 用于注解需要由容器管理的类
 *
 * @author pangzi
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default"";

}
package com.pz.study.frame.ioc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用于注解需要注入的属性
 * @author pangzi
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceAutowared {}

定义一个简单的容器类:

package  com.pz.study.frame.ioc;

import  java.io.File;
import  java.io.IOException;
import  java.io.InputStream;
import  java.lang.reflect.Field;
import  java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {public static final Map<String,Object> applicationContext = new ConcurrentHashMap<String, Object>();
       static {InputStream stream =ApplicationContext.class.getClassLoader().getResourceAsStream("ioc-frame.properties");
          Propertiesproperties = new Properties();
        try {properties.load(stream);
        } catch (IOException e) {e.printStackTrace();
        }

        StringpackageName=properties.getProperty("package").toString();
              try {instanceBean(packageName);
              } catch (Exception e) {e.printStackTrace();
              }
       }

       /**
        * 根据 annotation 上的名字获取对象
        * @param beanId
        * @return
        */
    public static Object getBean(String beanId){returnapplicationContext.get(beanId);

       }

       /**
        * 创建包名下的对象并完成依赖注入
        * @param packageName
        * @throws Exception
        */
       private static void instanceBean(StringpackageName) throws Exception {instanceByPackageName(packageName);
              autoWared();}




       // 获取指定包路径下使用了 ComponentAnnotationBean 的实例
       private static void instanceByPackageName(String packageName) {
              try {List<String>classNames= getClassName(packageName,true);

                     for(String className:classNames){Class<?>clazz = Class.forName(className);
                            if (clazz.isAnnotationPresent(Component.class)) {clazz.getAnnotation(Component.class).value();
                                   applicationContext.put(clazz.getAnnotation(Component.class).value(), clazz.newInstance());
                            }

                     }


              } catch (Exception e) {e.printStackTrace();
              }
       }


        /**
     * 获取某包下所有类
     *
     * @param packageName
     *            包名
     * @param childPackage
     *            是否遍历子包
     * @return 类的完整名称
     */
    private static List<String> getClassName(String packageName, boolean childPackage) {
        List<String> fileNames= null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath =packageName.replace(".", "/");
        URL url =loader.getResource(packagePath);
        if (url != null) {String type =url.getProtocol();
            if (type.equals("file")) {fileNames = getClassNameByFile(url.getPath(), null, childPackage);
            }
        }
        return fileNames;
    }


    /**
     * 从项目文件获取某包下所有类
     *
     * @param filePath
     *            文件路径
     * @param className
     *            类名集合
     * @param childPackage
     *            是否遍历子包
     * @return 类的完整名称
     */
    private static List<String> getClassNameByFile(String filePath,List<String> className, boolean childPackage) {List<String> myClassName= new ArrayList<>();
        File file = new File(filePath);
        File[] childFiles =file.listFiles();
        for (File childFile :childFiles) {if (childFile.isDirectory()) {if (childPackage) {myClassName.addAll(getClassNameByFile(childFile.getPath(),myClassName, childPackage));
                }
            } else {String childFilePath= childFile.getPath();
                if (childFilePath.endsWith(".class")) {childFilePath =childFilePath.substring(childFilePath.indexOf("/classes") + 9,
                           childFilePath.lastIndexOf("."));
                    childFilePath =childFilePath.replace("/", ".");
                    myClassName.add(childFilePath);
                }
            }
        }

        return myClassName;
    }



       /**
        * 遍历 map 注入属性
        */
       private static void autoWared() {
        Map<String, Object> map= applicationContext;

        try {for(StringbeanId:map.keySet()){Object obj=map.get(beanId);

              Field[] fields =obj.getClass().getDeclaredFields();
             for (Field field : fields) {if (field.isAnnotationPresent(Autowared.class)) {field.setAccessible(true);
                     Object fieldObj= map.get(field.getName());
                     field.set(obj,fieldObj);
                 }
             }

        }
        }catch(Exception e){e.printStackTrace();
        }
    }
}

我们如果要使用容器创建的对象直接使用 ApplicationContext.getBean()就好了。

接下来我们编写一单验证代码,使用我们的 annotation 和 ApplicationContext 提供的对象。

编写 3 个接口:

package com.pz.study.frame.test;

public interface TestService {public void test();

}
package com.pz.study.frame.test;

public interface TestManager {public void test();

}
package com.pz.study.frame.test;

public interface TestDao {public void test();

}
package com.pz.study.frame.test;

import com.pz.study.frame.ioc.Autowared;
import com.pz.study.frame.ioc.Component;

@Component(value="testService")
public class TestServiceImpl implements TestService {
       @Autowared
       private TestManager testManager;

       public void test(){testManager.test();
       }

}
package com.pz.study.frame.test;

import com.pz.study.frame.ioc.Autowared;
import com.pz.study.frame.ioc.Component;

@Component(value="testManager")
public class TestManagerImpl implements TestManager {

       @Autowared
       private TestDao testDao;

       publicvoid test(){testDao.test();
       }

}
package com.pz.study.frame.test;

import com.pz.study.frame.ioc.Component;

@Component(value="testDao")
public class TestDaoImpl implements TestDao {public void test(){System.out.println("ioc 小框架测试");
       }

}

最后编写运行程序

package com.pz.study.frame.test;

import com.pz.study.frame.ioc.ApplicationContext;

public class TestIoc {public static void main(String args[]){TestService testService=(TestService) ApplicationContext.getBean("testService");
              testService.test();}

}

运行程序,我们发现代码正确运行,而我们的程序只依赖了 TestService 这个接口,程序将需要依赖的对象交由自己定义的容器 ApplicationContext 去创建和装备。我们简单的实现了依赖注入的原理!

正文完
 0