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'
- }
instance方法,第一步“创建对象”,class.forname和class.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控制反转之依赖注入的更多相关文章
- Spring专题2: DI,IOC 控制反转和依赖注入
合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...
- (转)Ioc控制反转和依赖注入
转载地址:https://zhuanlan.zhihu.com/p/95869440 控制反转控制反转(Inversion of Control,简称IoC),是面向对象编程中的一种设计思想,其作用是 ...
- laravel服务容器(IOC控制反转,DI依赖注入),服务提供者,门脸模式
laravel的核心思想: 服务容器: 容器:就是装东西的,laravel就是一个个的对象 放入:叫绑定 拿出:解析 使用容器的目的:这里面讲到的是IOC控制反转,主要是靠第三方来处理具体依赖关系的解 ...
- Spring的IOC控制反转和依赖注入-重点-spring核心之一
IoC:Inverse of Control(控制反转): 读作"反转控制",更好理解,不是什么技术,而是一种设计思想,好比于MVC.就是将原本在程序中手动创建对象的控制权,交由S ...
- spring IOC --- 控制反转(依赖注入)----简单的实例
IoC(Inversion of Control)控制反转,对象创建责任的反转,在spring中BeanFacotory是IoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的 ...
- Spring 04: IOC控制反转 + DI依赖注入
Spring中的IOC 一种思想,两种实现方式 IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入 核心业务:(a)对象的创建 ...
- 搞定.NET MVC IOC控制反转,依赖注入
一直听说IOC,但是一直没接触过,只看例子好像很高达上的样子,今天抽了点时间实现了下,当然也是借助博客园里面很多前辈的文章来搞的!现在做个笔记,防止自己以后忘记! 1.首先创建MVC项目 2.然后新建 ...
- Java之控制反转和依赖注入
1.简介 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念. 2.案例 1)一般情况下的类耦合 Main.java public clas ...
- java控制反转与依赖注入
1.简介 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念. 2.案例 1)一般情况下的类耦合 Main.java public clas ...
随机推荐
- python rsa 加密解密 (编解码,base64编解码)
最近有需求,需要研究一下RSA加密解密安全:在网上百度了一下例子文章,很少有文章介绍怎么保存.传输.打印加密后的文本信息,都是千篇一律的.直接在一个脚本,加密后的文本信息赋于变量,然后立马调用解密.仔 ...
- linux yum下载文件的存放位置
默认是: /var/cache/yum也可以在 /etc/yum.conf 指定cachedir=/var/cache/yum #存放目录keepcache=1 #1为保存 0为不保存metadata ...
- Spring——<aop:scoped-proxy/>理解
首先看一下Spring文档上的两个例子对比: <bean id="userPreferences" class="com.foo.UserPreferences&q ...
- 1.WP8.1开发_去除闪动效果,直接进入首页
有时候希望打开软件的时候,不出现闪屏,而直接进入到第一个页面. 把第一个页面作为闪屏,可以制作一些进度条 和动画等... 很简单,有两种方法去除闪屏. 1.删除项目下 Assets 文件夹里面的Sp ...
- UICollectionView 适配 iPhone 7 Plus
UICollectionView 适配 iPhone 7 Plus 需求:在屏幕上水平放置 5 张正方形图片,每张图片的宽度相等,无缝隙排列铺满一个屏幕宽度. 看似很简单的需求.用 UICollect ...
- iOS开发之状态栏
从iOS7开始,状态栏默认情况下归控制器管理,比如状态栏的样式.状态栏的是否可见 控制器通过重写以下方法来控制状态栏 设置状态栏的样式,只需重写下列方法即可: - (UIStatusBarStyle) ...
- windos环境apache+mysql+php+Discuz的安装配置
首先是相关软件的下载:PHP.Apache和Mysql软件以及VC库.相关软件可到我的百度网盘下载,百度网盘:http://pan.baidu.com/s/1o6DYcMu 相关软件的直接下载地址: ...
- javascript . 03 函数定义、函数参数(形参、实参)、函数的返回值、冒泡函数、函数的加载、局部变量与全局变量、隐式全局变量、JS预解析、是否是质数、斐波那契数列
1.1 知识点 函数:就是可以重复执行的代码块 2. 组成:参数,功能,返回值 为什么要用函数,因为一部分代码使用次数会很多,所以封装起来, 需要的时候调用 函数不调用,自己不会执行 同名函数会覆盖 ...
- js精确计算
官方文档:http://mikemcl.github.io/big.js/ 使用方法: x = new Big(0.1); y = x.plus(0.2); // '0.3' var a=Big(0. ...
- JAVA-随机读写文件
File类通过使用 . 来获取当前路径,从而取得文件. File f = new File(".\\Res\\Temp.txt"); 或者直接使用空构造函数: File f = n ...