Spring学习之Ioc(控制反转)原理

Ioc(控制反转)的发展是发现三层架构(MVC)的问题,对其解决从而开发完善的框架,让我让我们来看看它的发展过程和原理。

1.第一代代码:传统三层架构(MVC)的弊端,new关键字的高耦合

先来看一个简单的MVC分层。项目结构如下,代码如下:

在这里插入图片描述
1.Dao层:被service调用

dao层接口代码:

package com.hai.Dao;

public interface UserDao {
    void userImpl01();
}

dao层UserDaoImpl类代码如下:

package com.hai.Dao.Impl;

import com.hai.Dao.UserDao;

public class UserDaoImpl implements UserDao {
    public void userImpl01(){
        System.out.println("我是dao层方法,我需要被service层调用。。。");
    }
}

2.service层被controller层调用

service层的接口代码:

package com.hai.service;

public interface UserService {
    void userImpl02();
}

service层实现类UserService类的方法代码:

import com.hai.Dao.Impl.UserDaoImpl;
import com.hai.Dao.UserDao;
import com.hai.service.UserService;

public class UserServiceImpl implements UserService {
    public void userImpl02() {
        UserDao userDao=new UserDaoImpl();
        //调用userDao层
        userDao.userImpl01();
        System.out.println("我是service层的方法,我需要被Controller层调用。。。");
    }
}

3.controller层调用service层的方法,这里我使用mainf方法来测试

controller层的UserController类方法代码:

import com.hai.Dao.Impl.UserDaoImpl;
import com.hai.Dao.UserDao;
import com.hai.service.UserService;

public class UserServiceImpl implements UserService {
    public void userImpl02() {
        UserDao userDao=new UserDaoImpl();
        //调用userDao层
        userDao.userImpl01();
        System.out.println("我是service层的方法,我需要被Controller层调用。。。");
    }
}

运行结果:
在这里插入图片描述

弊端

这是基于传统的MVC三层架构,但是我们可以看到的是当业务逻辑层(service)调用数据层(dao层)时,我们需要通过new 关键字先创建一个UserDaoImpl的实现类,同样的我们在controller层使用service层时,也需要先new一个UserServiceImpl对象。new关键字关键字加强了对象与对象之间的耦合读 因此我们需要想一个办法来解决,不要使用new,来降低他们之间的耦合度,我们学过IO流知道,我们可以通过类的全路径名来创建一个对象,这样就可以避免使用new关键字。所以我们的解决方式就是通过配置文件类降低耦合度。

2.第二代代码:通过IO流的方式创建对象,降低耦合。

要使用IO流的方式创建对象,需要新建一个配置文件,添加工厂类Beanfactory。
新建一个配置文件,新的目录结构如下:
在这里插入图片描述

配置文件代码如下:

userService=com.hai.service.Impl.UserServiceImpl

工厂类代码如下:

package com.hai.factory;

public class BeanFactory {
    public static Object getBean(String classPath)  {
        try {
            return Class.forName(classPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

UsreControllerl类代码更新为:

import com.hai.factory.BeanFactory;
import com.hai.service.Impl.UserServiceImpl;
import com.hai.service.UserService;

public class UserControllerImpl {
    public static void main(String[] args) throws Exception {
        //    调用sercvice的方法
        UserService userService= (UserService) BeanFactory.getBean("com.hai.service.Impl.UserServiceImpl");
        userService.userImpl02();
    }
}

运行结果:
在这里插入图片描述

弊端

我们通过IO流,使用类的全限定名的方式创建对象,而从而降低他们之间的耦合度。但是我都知道java代码是运行在服务器之上的,当我们需要创建多个对象时,就要在代码中修改,这样我就需要重启服务了,拿我们需要很多对象时,就需要多次重启服务器,像这样的问题叫做硬编码问题,这样很是麻烦,为了解决这个问题,我们就把所有类的全限定名提出来,生成一个配置文件(bean.properties)这样不就可以了吗,配置文件时以键值对的方式出现的,我们默认接受的变量作为全限定名的键,在我们需要修改时,只需要修改配置文件就可以了。比如这个案例中使用userService是接口类型的变量作为接受。以后在读取配置文件时创建对象即可,好,下面我来继续调整代码。
在这里插入图片描述

3.第三代代码:通过配置文件,解决硬编码问题。

下面将BeaFactory工厂类修改成以下代码:

package com.hai.factory;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BeanFactory {
    private static Properties p=new Properties();
    private static Map<String,Object> map=new HashMap<String,Object>();

    static {
        //IO流读取配置文件
        InputStream is= BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");

        try {
            //Properties工具类的使用
            p.load(is);

            //使用for循环遍历配置文件的键集合,获取它的值,就是对应类的全限定名。
            for (Object o :p.keySet()) {
                String key= o.toString();
                String classpath=p.get(key).toString();
                Object obj = Class.forName(classpath).newInstance();
                //使用map容器保存对象,其他对象问这个容器拿,这个容器就是Ioc容器
                map.put(key,obj);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Object getBean(String key)  {

        try {
            return map.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

UserControllerImpl类代码修改成以下代码:

import com.hai.service.UserService;

public class UserControllerImpl {
    public static void main(String[] args) throws Exception {
        //    调用sercvice的方法
        UserService userService= (UserService) BeanFactory.getBean("userService");//调用配置文件的键。
        userService.userImpl02();
    }
}

关于工厂类(BeanFactory)的静态代码块:

这需要解释一些静态代码块的使用,静态代码块的是所有的代码只要执行一次,和类同时加载,这样配置文件就可以和类同时放入内存,需要使用到工厂类时,就可以快速拿到,提高速度.

关于多例模式和单例模式:
我们都知道静态代码只执行一次,同样的如果我们把创建对象的代码 (Object obj = Class.forName(classpath).newInstance(); )放入静态代码块中,这样对象只创建一次,map容器中也只有一个对象,那其他对象拿到的时同一个对象,这个就是单例模式,多例模式就是将创建对象的这行代码放在静态代码之外,这样就可以多次创建对象,这个就是多例模式。

小结第三代的代码

通过第三代的代码我们明白可以通过配置文件来解决硬编码问题,这样我们就开始了配置文件的编程;使用map容器来保存所有的对象,这个容器就是Ioc容器。

4.总结

通过上面代码发展,我可以知道通过解决了传统项目的new对象,诞生了一个专门创建对象的工厂,这个工厂就是Bean工厂,为了解决硬编码问题,我们使用了配置文件,使用map容器保存创建的对象,spring就是这样通过配置文件来创建对象的一个完善的框架,控制反转就是将改变了创建对象的人,传统的是通过new关键字,程序员来控制,耦合性高,spring是通过配置文件和反射的机制来创建对象,降低了耦合度。也就是说,我们自己也可以开发出spring框架,但是为了避免重复造轮子,已经有人开发好了,我们只需要会用就可以了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值