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

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

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

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

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

  1. public class Teacher {
  2.  
  3. private String name;
  4.  
  5. private String id;
  6.  
  7. public String getName() {
  8. return name;
  9. }
  10.  
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14.  
  15. public String getId() {
  16. return id;
  17. }
  18.  
  19. public void setId(String id) {
  20. this.id = id;
  21. }
  22.  
  23. @Override
  24. public String toString() {
  25. return "Teacher [name=" + name + ", id=" + id + "]";
  26. }
  27.  
  28. }

Teacher.java

  1. public class SchoolClass {
  2.  
  3. private String schoolClassName;
  4.  
  5. private String schoolClassId;
  6.  
  7. private Teacher manager;
  8.  
  9. public String getSchoolClassName() {
  10. return schoolClassName;
  11. }
  12.  
  13. public void setSchoolClassName(String schoolClassName) {
  14. this.schoolClassName = schoolClassName;
  15. }
  16.  
  17. public String getSchoolClassId() {
  18. return schoolClassId;
  19. }
  20.  
  21. public void setSchoolClassId(String schoolClassId) {
  22. this.schoolClassId = schoolClassId;
  23. }
  24.  
  25. public Teacher getManager() {
  26. return manager;
  27. }
  28.  
  29. public void setManager(Teacher manager) {
  30. this.manager = manager;
  31. }
  32.  
  33. @Override
  34. public String toString() {
  35. return "The manager of "+schoolClassName+"("+schoolClassId+")"+"is Teacher"+manager.toString();
  36. }

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文件

如下:

  1. [
  2. {
  3. "bean": "cn.cslg.app.Teacher",
  4. "id": "class_manager",
  5. "properties": {
  6. "name": "黄有为",
  7. "id": "200500027"
  8. }
  9. },
  10. {
  11. "bean": "cn.cslg.app.SchoolClass",
  12. "id": "class1",
  13. "properties": {
  14. "schoolClassId": "Z094141",
  15. "schoolClassName": "软件工程"
  16. },
  17. "ref": {
  18. "manager": "class_manager"
  19. }
  20. },
  21. {
  22. "bean": "cn.cslg.app.SchoolClass",
  23. "id": "class2",
  24. "properties": {
  25. "schoolClassId": "Z094142",
  26. "schoolClassName": "软件工程"
  27. },
  28. "ref": {
  29. "manager": "class_manager"
  30. }
  31. }
  32. ]

IocListener代码如下:

  1. package cn.cslg.ioc;
  2.  
  3. import javax.servlet.ServletContext;
  4. import javax.servlet.ServletContextEvent;
  5. import javax.servlet.ServletContextListener;
  6. import javax.servlet.annotation.WebListener;
  7.  
  8. @WebListener
  9. public class IocListener implements ServletContextListener{
  10.  
  11. private final String filename = "/WEB-INF/ioc.json";
  12.  
  13. @Override
  14. public void contextInitialized(ServletContextEvent sce) {
  15. // TODO Auto-generated method stub
  16. ServletContext context =sce.getServletContext();
  17. String configFile = context.getInitParameter("ioc-config");
  18. String path = context.getRealPath(filename);
  19. try {
  20. if (configFile != null) {
  21. path = context.getRealPath(configFile);
  22. }
  23. ConfigParser parser = new ConfigParser(path);
  24. context.setAttribute("APPLICATION_CONTEXT_BEANS", parser.parse());
  25. } catch (IocException e) {
  26. // TODO: handle exception
  27. e.printStackTrace();
  28. }
  29. }
  30.  
  31. @Override
  32. public void contextDestroyed(ServletContextEvent sce) {
  33. // TODO Auto-generated method stub
  34.  
  35. }
  36.  
  37. }

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

  1. APPLICATION_CONTEXT_BEANS

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

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

类私有属性:

  1. private HashMap<String, Object> beansMap = new HashMap<>();
  2. private String jsonString;
  3. private String filename;

类构造方法:

  1. public ConfigParser(String filename) {
  2. super();
  3. this.filename = filename;
  4. this.jsonString = ReadJsonFile(filename);
  5. }

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

ReadJsonFile代码如下:

  1. private String ReadJsonFile(String path) {
  2. File file = new File(path);
  3. BufferedReader reader = null;
  4. String laststr = "";
  5. try {
  6. reader = new BufferedReader(new FileReader(file));
  7. String tempString = null;
  8. int line = 1;
  9. while ((tempString = reader.readLine()) != null) {
  10. System.out.println("line " + line + ": " + tempString);
  11. laststr = laststr + tempString;
  12. line++;
  13. }
  14. reader.close();
  15. } catch (IOException e) {
  16. e.printStackTrace();
  17. } finally {
  18. if (reader != null) {
  19. try {
  20. reader.close();
  21. } catch (IOException e1) {
  22. }
  23. }
  24. }
  25. return laststr;
  26. }

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

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

  1. repositories {
  2. mavenCentral()
  3. jcenter()
  4. maven { url "http://repo.spring.io/release" }
  5.  
  6. }
  7. dependencies {
  8. compile group: 'net.sf.json-lib', name: 'json-lib', version: '2.4'
  9. compile group: 'commons-beanutils', name: 'commons-beanutils', version: '1.9.3'
  10. compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
  11. testImplementation 'junit:junit:4.12'
  12. }

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

  1. public void instantiate() throws IocException {
  2. JSONArray beans = (JSONArray) JSONArray.fromObject(jsonString);
  3. try {
  4. for (Iterator it = beans.iterator(); it.hasNext();) {
  5. JSONObject bean = (JSONObject) it.next();
  6. String className = (String) bean.get("bean");
  7. String idName = (String) bean.get("id");
  8. Object obj;
  9. Class<?> class1 = Class.forName(className);
  10. obj = class1.newInstance();
  11. beansMap.put(idName, obj);
  12. }
  13.  
  14. } catch (Exception e) {
  15. // TODO Auto-generated catch block
  16. e.printStackTrace();
  17. }
  18. }

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

  1. public void injection() throws IocException {
  2. JSONArray beans = (JSONArray) JSONArray.fromObject(jsonString);
  3. try {
  4. for (Iterator it = beans.iterator(); it.hasNext();) {
  5. JSONObject bean = (JSONObject) it.next();
  6. String beanId = bean.get("id").toString();
  7. Object beanObj = beansMap.get(beanId);
  8. if (beanObj != null) {
  9. PropertyDescriptor pds[] = PropertyUtils.getPropertyDescriptors(beanObj);
  10. JSONObject properties = (JSONObject) bean.get("properties");
  11. JSONObject ref = (JSONObject) bean.get("ref");
  12. Iterator keyIter;
  13. String key;
  14. String value;
  15.  
  16. if (properties != null) {
  17.  
  18. keyIter = properties.keys();
  19. while (keyIter.hasNext()) {
  20. key = (String) keyIter.next();
  21. for (PropertyDescriptor pd : pds) {
  22. if (pd.getName().equals(key)) {
  23. value = properties.get(key).toString();
  24. if (value != null)
  25. pd.getWriteMethod().invoke(beanObj, (Object) value);
  26.  
  27. }
  28. }
  29. }
  30. }
  31.  
  32. if (ref != null) {
  33. keyIter = ref.keys();
  34. while (keyIter.hasNext()) {
  35. key = keyIter.next().toString();
  36. for (PropertyDescriptor pd : pds) {
  37. if (pd.getName().equals(key)) {
  38. value = ref.get(key).toString();
  39. if (value != null)
  40. pd.getWriteMethod().invoke(beanObj, beansMap.get(value));
  41. }
  42. }
  43. }
  44. }
  45.  
  46. }
  47. }
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. }
  51. }

注意在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对象:

  1. public HashMap<String, Object> parse() throws IocException {
  2. instantiate();
  3. injection();
  4. return beansMap;
  5. }

使用,servlet进行测试:

TestIocServlet.java的代码:

  1. @WebServlet("/TestIocServlet")
  2. public class TestIocServlet extends HttpServlet {
  3.  
  4. private static final long serialVersionUID = 1L;
  5.  
  6. @Override
  7. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  8. resp.setCharacterEncoding("utf-8");
  9. resp.setContentType("text/html");
  10. try (PrintWriter out = resp.getWriter()) {
  11. @SuppressWarnings("unchecked")
  12. HashMap<String, Object> beans = (HashMap<String, Object>) this.getServletContext()
  13. .getAttribute("APPLICATION_CONTEXT_BEANS");
  14. Set<String> keys = beans.keySet();
  15. for (String key : keys) {
  16. out.println(key + ": " + beans.get(key) + "<br/>");
  17. }
  18. }
  19. }
  20. }

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

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

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

  1. public class IocException extends Exception{
  2.  
  3. public IocException(Throwable cause){
  4. super(cause);
  5. }
  6. }

Java Web实现IOC控制反转之依赖注入的更多相关文章

  1. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  2. (转)Ioc控制反转和依赖注入

    转载地址:https://zhuanlan.zhihu.com/p/95869440 控制反转控制反转(Inversion of Control,简称IoC),是面向对象编程中的一种设计思想,其作用是 ...

  3. laravel服务容器(IOC控制反转,DI依赖注入),服务提供者,门脸模式

    laravel的核心思想: 服务容器: 容器:就是装东西的,laravel就是一个个的对象 放入:叫绑定 拿出:解析 使用容器的目的:这里面讲到的是IOC控制反转,主要是靠第三方来处理具体依赖关系的解 ...

  4. Spring的IOC控制反转和依赖注入-重点-spring核心之一

    IoC:Inverse of Control(控制反转): 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,好比于MVC.就是将原本在程序中手动创建对象的控制权,交由S ...

  5. spring IOC --- 控制反转(依赖注入)----简单的实例

    IoC(Inversion of Control)控制反转,对象创建责任的反转,在spring中BeanFacotory是IoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的 ...

  6. Spring 04: IOC控制反转 + DI依赖注入

    Spring中的IOC 一种思想,两种实现方式 IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入 核心业务:(a)对象的创建 ...

  7. 搞定.NET MVC IOC控制反转,依赖注入

    一直听说IOC,但是一直没接触过,只看例子好像很高达上的样子,今天抽了点时间实现了下,当然也是借助博客园里面很多前辈的文章来搞的!现在做个笔记,防止自己以后忘记! 1.首先创建MVC项目 2.然后新建 ...

  8. Java之控制反转和依赖注入

    1.简介 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念. 2.案例 1)一般情况下的类耦合 Main.java public clas ...

  9. java控制反转与依赖注入

    1.简介 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念. 2.案例 1)一般情况下的类耦合 Main.java public clas ...

随机推荐

  1. python rsa 加密解密 (编解码,base64编解码)

    最近有需求,需要研究一下RSA加密解密安全:在网上百度了一下例子文章,很少有文章介绍怎么保存.传输.打印加密后的文本信息,都是千篇一律的.直接在一个脚本,加密后的文本信息赋于变量,然后立马调用解密.仔 ...

  2. linux yum下载文件的存放位置

    默认是: /var/cache/yum也可以在 /etc/yum.conf 指定cachedir=/var/cache/yum #存放目录keepcache=1 #1为保存 0为不保存metadata ...

  3. Spring——<aop:scoped-proxy/>理解

    首先看一下Spring文档上的两个例子对比: <bean id="userPreferences" class="com.foo.UserPreferences&q ...

  4. 1.WP8.1开发_去除闪动效果,直接进入首页

    有时候希望打开软件的时候,不出现闪屏,而直接进入到第一个页面. 把第一个页面作为闪屏,可以制作一些进度条  和动画等... 很简单,有两种方法去除闪屏. 1.删除项目下 Assets 文件夹里面的Sp ...

  5. UICollectionView 适配 iPhone 7 Plus

    UICollectionView 适配 iPhone 7 Plus 需求:在屏幕上水平放置 5 张正方形图片,每张图片的宽度相等,无缝隙排列铺满一个屏幕宽度. 看似很简单的需求.用 UICollect ...

  6. iOS开发之状态栏

    从iOS7开始,状态栏默认情况下归控制器管理,比如状态栏的样式.状态栏的是否可见 控制器通过重写以下方法来控制状态栏 设置状态栏的样式,只需重写下列方法即可: - (UIStatusBarStyle) ...

  7. windos环境apache+mysql+php+Discuz的安装配置

    首先是相关软件的下载:PHP.Apache和Mysql软件以及VC库.相关软件可到我的百度网盘下载,百度网盘:http://pan.baidu.com/s/1o6DYcMu 相关软件的直接下载地址: ...

  8. javascript . 03 函数定义、函数参数(形参、实参)、函数的返回值、冒泡函数、函数的加载、局部变量与全局变量、隐式全局变量、JS预解析、是否是质数、斐波那契数列

    1.1 知识点 函数:就是可以重复执行的代码块 2.  组成:参数,功能,返回值 为什么要用函数,因为一部分代码使用次数会很多,所以封装起来, 需要的时候调用 函数不调用,自己不会执行 同名函数会覆盖 ...

  9. js精确计算

    官方文档:http://mikemcl.github.io/big.js/ 使用方法: x = new Big(0.1); y = x.plus(0.2); // '0.3' var a=Big(0. ...

  10. JAVA-随机读写文件

    File类通过使用 . 来获取当前路径,从而取得文件. File f = new File(".\\Res\\Temp.txt"); 或者直接使用空构造函数: File f = n ...