zoukankan      html  css  js  c++  java
  • Java Web实现IOC控制反转之依赖注入

    控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。

    控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。

    依赖注入应用比较广泛。本文介绍java实现一个简单的依赖注入

    简单而言,当你在某一个类中需要调用其他的类并且生成对象,大部分情况是new一个对象,此时如果你不确定要new哪一个对象,你就需要为所有的类作if或者switch判断,在不同情况下new不同的对象,然后给他们属性赋值

    使用最多的地方就是JavaBean类和他们的对象了,假设项目的Model里面有Teacher,StudentClass,分别代表老师和班级两个javaBean类

    public class Teacher {
    
        private String name;
        
        private String id;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        @Override
        public String toString() {
            return "Teacher [name=" + name + ", id=" + id + "]";
        }
        
    }
    Teacher.java
    public class SchoolClass {
    
        private String schoolClassName;
        
        private String schoolClassId;
        
        private Teacher manager;
    
        public String getSchoolClassName() {
            return schoolClassName;
        }
    
        public void setSchoolClassName(String schoolClassName) {
            this.schoolClassName = schoolClassName;
        }
    
        public String getSchoolClassId() {
            return schoolClassId;
        }
    
        public void setSchoolClassId(String schoolClassId) {
            this.schoolClassId = schoolClassId;
        }
    
        public Teacher getManager() {
            return manager;
        }
    
        public void setManager(Teacher manager) {
            this.manager = manager;
        }
    
        @Override
        public String toString() {
            return "The manager of "+schoolClassName+"("+schoolClassId+")"+"is Teacher"+manager.toString();
        }
        
    SchoolClass

    此时,我们有一个老师,两个班级,那么在实际使用中是不是就要new一个老师,然后使用getter,setter为他赋值属性,然后new两个班级,并且按照老师的做法赋值,这样如果有很多的老师和班级,那么就要不停的在java代码中new,new,new......

    而且每次多一个老师,班级,我们就要修改java代码,重新编译,这样耦合度就很高,能不能使用一个工具类自动新建对象,而对象的信息保存在一个文本中

    IoC的设计模式解决了这个问题,使用依赖注入,我们把类以及类相依赖的类放到动态修改的文本当中,每次从文本中读取,然后根据文本信息,动态的new出这些对象,做到灵活,多样化,易扩展。这样不在需要去修改或者添加java代码,代码重复性也减少。

    看看设计图:

    开始,新建一个web dynamic项目

    目录结构如下:

    其中两个javaBean放在app包中,确切说这是pojo类

    ioc包里面是ioc核心控制器

    test包是一个servlet,主要用于Ioc是否成功的测试

    由于个人觉得xml文件的书写和读取,传输都不是很好,尤其在spring,struts的配置均采用xml,实在厌恶

    之前在做php开发的时候,配置文件一般是json的格式或者php对象的格式(好吧,两者只是本质有区别,事实上形式相似)

    所以,我这次异想天开的在自己的IoC中使用Json作为依赖注入的动态配置文件取代大部分框架使用的xml文件

    如下:

    [
        {
            "bean": "cn.cslg.app.Teacher",
            "id": "class_manager",
            "properties": {
                "name": "黄有为",
                "id": "200500027"
            }
        },
        {
            "bean": "cn.cslg.app.SchoolClass",
            "id": "class1",
            "properties": {
                "schoolClassId": "Z094141",
                "schoolClassName": "软件工程"
            },
            "ref": {
                "manager": "class_manager"
            }
        },
        {
            "bean": "cn.cslg.app.SchoolClass",
            "id": "class2",
            "properties": {
                "schoolClassId": "Z094142",
                "schoolClassName": "软件工程"
            },
            "ref": {
                "manager": "class_manager"
            }
        }
    ]

    IocListener代码如下:

    package cn.cslg.ioc;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.annotation.WebListener;
    
    @WebListener
    public class IocListener implements ServletContextListener{
    
        private final String filename = "/WEB-INF/ioc.json";
        
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            // TODO Auto-generated method stub
            ServletContext context =sce.getServletContext();
            String configFile = context.getInitParameter("ioc-config");
            String path = context.getRealPath(filename);
            try {
                if (configFile != null) {
                    path = context.getRealPath(configFile);
                }
                ConfigParser parser = new ConfigParser(path);
                context.setAttribute("APPLICATION_CONTEXT_BEANS", parser.parse());
            } catch (IocException e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            // TODO Auto-generated method stub
            
        }
    
    }

    实现对servlet上下文的监听,主要是调用了ConfigParser创建对象并在servlet中增加了一个值

    APPLICATION_CONTEXT_BEANS

    将刚刚创建的beans对象保存在其中,以便servlet使用过程中使用

    ConfigParser实现了控制反转的依赖注入,类构成如下:

    类私有属性:

    private HashMap<String, Object> beansMap = new HashMap<>();
        private String jsonString;
        private String filename;

    类构造方法:

    public ConfigParser(String filename) {
            super();
            this.filename = filename;
            this.jsonString = ReadJsonFile(filename);
        }

    使用ReadJsonFile对json进行解析,返回json的字符串,交给jsonString

    ReadJsonFile代码如下:

    private String ReadJsonFile(String path) {
            File file = new File(path);
            BufferedReader reader = null;
            String laststr = "";
            try {
                reader = new BufferedReader(new FileReader(file));
                String tempString = null;
                int line = 1;
                while ((tempString = reader.readLine()) != null) {
                    System.out.println("line " + line + ": " + tempString);
                    laststr = laststr + tempString;
                    line++;
                }
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e1) {
                    }
                }
            }
            return laststr;
        }

    其中解析json(JsonArray,JsonObject)需要用到json-lib,javaBean是解析需要用到common-beanutils

    请使用gradle或者手动导入这两个包,如使用gradle如下:

    repositories {
        mavenCentral()
        jcenter()
        maven { url "http://repo.spring.io/release" } 
    
    }
    dependencies {
        compile group: 'net.sf.json-lib', name: 'json-lib', version: '2.4'
        compile group: 'commons-beanutils', name: 'commons-beanutils', version: '1.9.3'
        compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
        testImplementation 'junit:junit:4.12'
    }
    View Code

     instance方法,第一步“创建对象”,class.fornameclass.newInstance是核心,有了他们实现了类->对象的创建

    public void instantiate() throws IocException {
            JSONArray beans = (JSONArray) JSONArray.fromObject(jsonString);
            try {
                for (Iterator it = beans.iterator(); it.hasNext();) {
                    JSONObject bean = (JSONObject) it.next();
                    String className = (String) bean.get("bean");
                    String idName = (String) bean.get("id");
                    Object obj;
                    Class<?> class1 = Class.forName(className);
                    obj = class1.newInstance();
                    beansMap.put(idName, obj);
                }
    
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    injection方法,为instance创建的对象赋值属性,主要包括了普通值,引用值

    public void injection() throws IocException {
            JSONArray beans = (JSONArray) JSONArray.fromObject(jsonString);
            try {
                for (Iterator it = beans.iterator(); it.hasNext();) {
                    JSONObject bean = (JSONObject) it.next();
                    String beanId = bean.get("id").toString();
                    Object beanObj = beansMap.get(beanId);
                    if (beanObj != null) {
                        PropertyDescriptor pds[] = PropertyUtils.getPropertyDescriptors(beanObj);
                        JSONObject properties = (JSONObject) bean.get("properties");
                        JSONObject ref = (JSONObject) bean.get("ref");
                        Iterator keyIter;
                        String key;
                        String value;
    
                        if (properties != null) {
    
                            keyIter = properties.keys();
                            while (keyIter.hasNext()) {
                                key = (String) keyIter.next();
                                for (PropertyDescriptor pd : pds) {
                                    if (pd.getName().equals(key)) {
                                        value = properties.get(key).toString();
                                        if (value != null)
                                            pd.getWriteMethod().invoke(beanObj, (Object) value);
    
                                    }
                                }
                            }
                        }
    
                        if (ref != null) {
                            keyIter = ref.keys();
                            while (keyIter.hasNext()) {
                                key = keyIter.next().toString();
                                for (PropertyDescriptor pd : pds) {
                                    if (pd.getName().equals(key)) {
                                        value = ref.get(key).toString();
                                        if (value != null)
                                            pd.getWriteMethod().invoke(beanObj, beansMap.get(value));
                                    }
                                }
                            }
                        }
    
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    注意在json中,配置了对象的赋值,properties是普通的属性值,可以int,String值。而ref则是引用值,引用了其他的对象,这里是Teacher对象,表示班级由某一个老师管理。注意处理时要区别对待

    其中使用了两重循环迭代,第一重遍历bean对象是数组JsonArray,第二重遍历bean下的properties或者是ref是对象JsonObject,从json文件中不难看出其迭代结构

    核心方法是invoke,替代了getter,setter,直接对对象的属性进行赋值(初始化),值从json文件的properties和ref中获取,并且一一对应的赋值

    注意java中,json对象是不能直接转化为JavaBean对象的,需要对其一层层遍历手动赋值,php则可以直接将json转化为对象,因为php本身是动态语言!

    最后,parse方法返回一个HashMap,存储了所有生成的JavaBean对象:

    public HashMap<String, Object> parse() throws IocException {
            instantiate();
            injection();
            return beansMap;
        }

    使用,servlet进行测试:

    TestIocServlet.java的代码:

    @WebServlet("/TestIocServlet")
    public class TestIocServlet extends HttpServlet {
    
        private static final long serialVersionUID = 1L;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setCharacterEncoding("utf-8");
            resp.setContentType("text/html");
            try (PrintWriter out = resp.getWriter()) {
                @SuppressWarnings("unchecked")
                HashMap<String, Object> beans = (HashMap<String, Object>) this.getServletContext()
                        .getAttribute("APPLICATION_CONTEXT_BEANS");
                Set<String> keys = beans.keySet();
                for (String key : keys) {
                    out.println(key + ": " + beans.get(key) + "<br/>");
                }
            }
        }
    }
    View Code

    启动tomcat运行servlet,浏览器效果如下:

    很明显,已经实现了对文本json文件的动态对象生成和赋值,不需要在java文件中重复new对象并且赋值

    对了,项目中有个异常处理文件,IocException.java如下:

    public class IocException extends Exception{
    
        public IocException(Throwable cause){
            super(cause);
        }
    }
  • 相关阅读:
    实现货币金额中文大写转换的程序
    大数阶乘的计算(三)
    全国15亿人口中选1000个代表有多少种选法?
    DB Query Analyzer 中断SQL语句的执行
    Android_Preference存取数据
    Centos 学习笔记软件包管理
    Centos学习笔记文件搜索命令
    《计算机时代》2011年第12期刊登出《DB Query Analyzer中断SQL语句的执行》
    Centos学习笔记linux用户管理
    Centos学习笔记 linux 常用命令:压缩解压命令
  • 原文地址:https://www.cnblogs.com/devilyouwei/p/6582559.html
Copyright © 2011-2022 走看看