前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >猿思考系列4——一文学会java的斗转星移动

猿思考系列4——一文学会java的斗转星移动

作者头像
山旮旯的胖子
发布2020-07-28 16:51:03
2450
发布2020-07-28 16:51:03
举报
文章被收录于专栏:猿人工厂猿人工厂

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

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:

代码语言:javascript
复制
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"";
 
}
代码语言:javascript
复制
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 {
 
}

定义一个简单的容器类:

代码语言:javascript
复制
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个接口:

代码语言:javascript
复制
package com.pz.study.frame.test;
 
public interface TestService {
       public void test();
 
}
代码语言:javascript
复制
package com.pz.study.frame.test;
 
public interface TestManager {
       public void test();
 
}
代码语言:javascript
复制
package com.pz.study.frame.test;
 
public interface TestDao {
       public void test();
 
}
代码语言:javascript
复制
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();
       }
 
}
代码语言:javascript
复制
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();
       }
 
}
代码语言:javascript
复制
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 小框架测试");
       }
 
}

最后编写运行程序

代码语言:javascript
复制
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去创建和装备。我们简单的实现了依赖注入的原理!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿人工厂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档