spring学习笔记(1):程序间的耦合

一、环境准备

本文想以jdbc的开发代码为例子来说明程序间的耦合问题,所以事先建立起数据库以及对于的maven工程

1.数据库环境

1.1创建数据表

create table account(
	id int primary key auto_increment,
	name varchar(40),
	money float
)character set utf8 collate utf8_general_ci;

insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);

2.maven工程

就是新建一个普通的maven工程即可

2.1导入mysql的jar包坐标

<packaging>jar</packaging>

    <dependencies>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

    </dependencies>

2.2新建java类

新建com.xpt.jdbc.JdbcDemo01

public class JdbcDemo1 {
    public static void main(String[] args) throws Exception {
        //1.注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        //2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/eesy", "root", "admin");
        //3.获取数据库操作预处理对象
        PreparedStatement ps = connection.prepareStatement("select * from account");
        //4.执行sql,获得结果集
        ResultSet rs = ps.executeQuery();
        //5.遍历结果集
        while (rs.next()){
            System.out.println(rs.getString("name"));
        }
        //6.释放资源
        rs.close();
        ps.close();
        connection.close();
    }
}

2.3执行上述代码

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

二、编译期依赖

1注释掉上述代码中mysql对应的jar包

  • 注释
    在这里插入图片描述- 程序提示错误
    在这里插入图片描述- 运行报错
    在这里插入图片描述

三、耦合

  • 耦合:程序间的依赖关系

  • 包括:

    • 类之间的依赖
    • 方法之间的依赖
  • 解耦:

    • 降低程序间的依赖关系(只能降低,不能避免)
  • 解决思路:

    • 实际开发中,要做到:编译期不依赖,运行期才依赖
      • 对于上述例子,我们可以在注册驱动时采用Class.forName()
        在这里插入图片描述
      • 显然上述例子依然无法运行,但这个时候只会发生运行期异常了
        在这里插入图片描述
    • 上述的例子带给我们的一个解决思路就是:
      • 使用反射来创建对象,避免使用new
    • 继续思考,这里还有一个问题在于,上述是通过一个字符串来指定具体的类,如果在后续的开发中需要更换那个类,我们还是需要修改那个字符串,所以进一步的:
    • 通过配置文件的方式来得到要创建对象的全限定类名

四、曾经代码中的问题分析

1.曾经的业务层和持久层代码

业务层调用持久层的dao类来实现对应实体类的CRUD操作

在这里插入图片描述

  • 上述的代码中存在的问题,正是我们前面提到的需要避免的一个问题。
  • 同样的,在以前的开发习惯中,会发现有大量的通过new关键字来创建对象的代码,这正是我们目前急需避免的

五、工厂模式解耦

1.新建配置文件

  • bean.properties
  • 通过键值对来指定唯一的全限定类名
accountService=com.xpt.service.impl.AccountServiceImpl
accountDao=com.xpt.dao.impl.AccountDaoImpl

2.新建工厂类

  • 说明
    在这里插入图片描述
  • 代码
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;
    //使用静态代码块为props对象赋值
    static {
        try {
        //实例化对象
        props = new Properties();
        //获取Properties文件的流对象
        //注意这里的获取方式 一定不要直接写url目录,因为需要考虑到项目编译,发布后资源路径改变带来的问题
        InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");

            props.load(in);
        } catch (Exception e) {
           throw new ExceptionInInitializerError("初始化properties失败");
        }
    }
    //注意返回值 和参数
    public static Object getBean(String beanName){

        Object bean = null;
        String beanPath = props.getProperty(beanName);
        try {
            bean = Class.forName(beanPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}

3.使用工厂类重新获取对象

  • 3.1客户端层

public class Client {

    public static void main(String[] args) {
//        IAccountService as = new AccountServiceImpl();
        //使用工厂类进行解耦
        IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
        as.saveAccount();
    }
}

3.2业务层

public class AccountServiceImpl implements IAccountService {
//    IAccountDao accountDao = new AccountDaoImpl();
    IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
    public void saveAccount() {
        accountDao.saveAccount();
    }

}
  • 通过观察运行结果,利用上述工厂类可以完成解耦

4.上述代码的问题

4.1打印通过工厂得到的对象

  • 代码
public class Client {

    public static void main(String[] args) {
//        IAccountService as = new AccountServiceImpl();
        //使用工厂类进行解耦
        for (int i = 0; i < 5; i++) {
            IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
            System.out.println(as);
        }
//        as.saveAccount();
    }
}
  • 结果
    在这里插入图片描述

4.2分析

  • 上述的方式得到的对象是多例的,而学习过Servlet知道,它是单例的,多例在效率上不及单例
  • 原因:
    在这里插入图片描述
  • 解决思路:由于每一次调用都会获取一个新的实例,当该程序被执行完毕后就被垃圾回收机制回收了。这里要怎么解决呢?如果我们可以每次获取一个新实例后将它存储起来,使用完了归还回去,这样就能解决这个问题。那具体用什么来存储呢?
  • 这里我们往往使用一个map来存储,并把它定义为容器

5.工厂模式解耦升级

将其改造为单例的

5.1定义map存储实例对象

  • 代码
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;
    //定义容器 存储
    private static Map<String,Object> beans;


    //使用静态代码块为props对象赋值
    static {
        try {
            //实例化对象
            props = new Properties();
            //获取Properties文件的流对象
            //注意这里的获取方式 一定不要直接写url目录,因为需要考虑到项目编译,发布后资源路径改变带来的问题
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            //实例化容器
            beans = new HashMap<String, Object>();
            //获取map所有的key
            Enumeration keys = props.keys();
            while (keys.hasMoreElements()){
                //取出每个Key
                String key = keys.nextElement().toString();
                //根据Key 获取value
                String beanPath  = props.getProperty(key);
                //根据beanPath 反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //存入map
                beans.put(key, value);


            }

        } catch (Exception e) {
           throw new ExceptionInInitializerError("初始化properties失败");
        }
    }

    //根据bean的名称获取单例对象
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }
}
  • 结果
    在这里插入图片描述- 对象是在静态代码块中被创建的,然后保存在了map中,后面的getBean方法是从这里获取的,所以这里每一次获取的对象都是通过map来获取的,并没有创建新的对象,所以这里最后的效果就算单例的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值