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

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

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去创建和装备。我们简单的实现了依赖注入的原理!