控制反转(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'
}

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/>");
}
}
}
}

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

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

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

public class IocException extends Exception{

    public IocException(Throwable cause){
super(cause);
}
}

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. selenium+python 自动化中界面滚动条操作方法

    虽然webdriver提供了操作浏览器的前进和后退的方法,但对于浏览器滚动条并没有提供相应的操作方法,以下使用的方法: 借助JavaScript来控制浏览器的滚动条,webdriver提供了execu ...

  2. rpm包相关操作

    1.查找已安装的rpm:rpm -qa|grep ewp2.卸载已安装的rpm: 先切换到虚拟机共享路径,执行卸载命令: rpm -e 已安装rpm包名称3.安装新rpm包:rpm -ivh(更新的话 ...

  3. java操作txt文本(一):遇到指定字符换行

    想法由来:有时查看网页源代码的css文件内容,竟是恼人的压缩后代码(不换行),如下图所示-- 它的可读性很差,所以写了下面这个简单的程序,实现自动换行. 适用条件:遇到指定字符换行(本例中遇到'}'换 ...

  4. 防火墙之netfailt、iptables详解

    [TOC] Iptables入门 # 1.iptables介绍 linux的包过滤功能,即linux防火墙,它由netfilter 和 iptables 两个组件组成. netfilter 组件也称为 ...

  5. .net平台的MongoDB使用

    前言 最近花了点时间玩了下MongoDB.Driver,进行封装了工具库,平常也会经常用到MongoDB,因此写一篇文章梳理知识同时把自己的成果分享给大家. 本篇会设计到Lambda表达式的解析,有兴 ...

  6. 读书笔记 effective c++ Item 36 永远不要重新定义继承而来的非虚函数

    1. 为什么不要重新定义继承而来的非虚函数——实际论证 假设我告诉你一个类D public继承类B,在类B中定义了一个public成员函数mf.Mf的参数和返回类型并不重要,所以假设它们都是void. ...

  7. (转)关闭iptables和SELinux

    1. 关闭SELinux setenforce 0   #临时关闭 编辑/etc/selinux/config,找到SELINUX 行修改成为:SELINUX=disabled:     #永久关闭, ...

  8. ngrok完成内网映射外网

    项目需求: 完成微信公众号开发配置搭建,其中需要一个可以外部访问的域名 实现技术: www.ngrok.cc 实现方式: 在该网站最下方下载需要的ngrok文件,在最上方点击注册,获取token,之后 ...

  9. Hibernate基础学习(六)—Hibernate二级缓存

    一.概述      Session的缓存是一块内存空间,在这个内存空间存放了相互关联的Java对象,这个位于Session缓存内的对象也被称为持久化对象,Session负责根据持久化对象的状态来同步更 ...

  10. 为Jquery EasyUI 组件加上“清除”功能

    1.背景 在使用 EasyUI 各表单组件时,尤其是使用 ComboBox(下拉列表框).DateBox(日期输入框).DateTimeBox(日期时间输入框)这三个组件时,经常有这样的需求,下拉框或 ...