Spring学习笔记【01】Spring启示录

本次学习分享将从阅读典型代码开始,讨论代码中存在的问题,最终引出控制反转思想和Spring框架。

一、代码阅读

请阅读以下代码:

package com.selflearn.springbase.intro.dao;

/**
 * 获取密码
 */
public interface PassWordDao {

    /**
     * 获取公共密码
     * @return 公共密码
     */
    String getPubPassWord();
}
package com.selflearn.springbase.intro.dao.impl;

import com.selflearn.springbase.intro.dao.PassWordDao;

/**
 * 从MySQL中获取密码
 */
public class PassWordDaoForMySQL implements PassWordDao {

    /**
     * 获取公共密码
     * @return 公共密码
     */
    @Override
    public String getPubPassWord() {
        return "12345678";
    }
}
package com.selflearn.springbase.intro.service;

/**
 * 获取密码
 */
public interface PassWordService {

    /**
     * 获取公共密码
     * @return 公共密码
     */
    String getPubPassWord();
}
package com.selflearn.springbase.intro.service.impl;

import com.selflearn.springbase.intro.dao.PassWordDao;
import com.selflearn.springbase.intro.dao.impl.PassWordDaoForMySQL;
import com.selflearn.springbase.intro.service.PassWordService;

/**
 * 获取密码
 */
public class PassWordServiceImpl implements PassWordService {

    private final PassWordDao passWordDao = new PassWordDaoForMySQL();

    /**
     * 获取公共密码
     * @return 公共密码
     */
    @Override
    public String getPubPassWord() {
        return passWordDao.getPubPassWord();
    }
}
package com.selflearn.springbase.intro;

import com.selflearn.springbase.intro.service.PassWordService;
import com.selflearn.springbase.intro.service.impl.PassWordServiceImpl;

/**
 * 测试主程序
 */
public class TestApplication {
    public static void main(String[] args) {
        PassWordService service = new PassWordServiceImpl();
        System.out.println(service.getPubPassWord());
    }
}

二、代码分析

上述代码包含了两个接口、两个实现类和一个测试程序,目的是从MySQL数据库中获取公共密码。执行测试程序,控制台会输出存储在MySQL中的公共密码:12345678。

考虑下述情况,由于某个原因,数据库需要进行升级,按照要求应使用Gauss数据库,我们要怎么进行优化呢?

首先,肯定需要提供一个新的实现类PassWordDaoForGauss

package com.selflearn.springbase.intro.dao.impl;

import com.selflearn.springbase.intro.dao.PassWordDao;

/**
 * 从MGaussDB中获取密码
 */
public class PassWordDaoForGauss implements PassWordDao {

    /**
     * 获取公共密码
     * @return 公共密码
     */
    @Override
    public String getPubPassWord() {
        return "12345678";
    }
}

为了完成切换,还要修改原业务层中的实现类PassWordServiceImpl

package com.selflearn.springbase.intro.service.impl;

import com.selflearn.springbase.intro.dao.PassWordDao;
import com.selflearn.springbase.intro.dao.impl.PassWordDaoForGauss;
import com.selflearn.springbase.intro.service.PassWordService;

/**
 * 获取密码
 */
public class PassWordServiceImpl implements PassWordService {

    private final PassWordDao passWordDao = new PassWordDaoForGauss();

    /**
     * 获取公共密码
     * @return 公共密码
     */
    @Override
    public String getPubPassWord() {
        return passWordDao.getPubPassWord();
    }
}

这样做乍一看好像没什么问题,但其实已经违反了软件设计原则中的开闭原则和依赖倒置原则。

开闭原则(Open Closed Principle, OCP)指的是软件实体(类、模块、函数等)应对扩展开放,对修改关闭。通俗地说,我们在扩展新功能时,不应该再去修改原来已经运行地挺正常的程序。但为了实现数据库的切换,我们不得不去修改PassWordServiceImpl,显然违背了开闭原则。

依赖倒置原则(Dependency Inversion Principle, DIP)指的是高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。在代码中,PassWordServiceImpl直接依赖了具体的实现类PassWordDaoForMySQL,这违反了DIP。理想情况下,业务层(高层模块)只要依赖PassWordDao接口,不应该依赖其具体实现。

三、控制反转

那怎么做才能满足开闭原则和依赖倒置原则呢?首先,需要修改PassWordServiceImpl

package com.selflearn.springbase.intro.service.impl;

import com.selflearn.springbase.intro.dao.PassWordDao;
import com.selflearn.springbase.intro.service.PassWordService;

/**
 * 获取密码
 */
public class PassWordServiceImpl implements PassWordService {

    private final PassWordDao passWordDao;

    /**
     * 获取公共密码
     * @return 公共密码
     */
    @Override
    public String getPubPassWord() {
        return passWordDao.getPubPassWord();
    }
}

当然,上述代码修改完肯定跑不起来,一定会报NullPointerException,因为passWordDao并没有指向具体的对象。要解决当前情况,就必须要面对以下两个核心问题:

  • 谁来创建对象呢?
  • 谁来负责把对象赋值给passWordDao呢?

说白了,就是对象创建和对象间依赖关系维护的问题。如果这两个问题解决了,那就既满足了开闭原则,又满足了依赖倒置原则。此时,就不得不引出一种为此而生的设计思想:控制反转。

控制反转(Inversion of Control,IoC)是软件设计范式的革命性转变,其核心原则是将对象的创建权和依赖管理的控制权从应用程序代码转移到框架容器,即容器来创建对象,来维护对象之间的依赖关系。

在原始代码中,我们在PassWordServiceImpl中显式的创建了对象,这样会导致两者之间存在极强的依赖关系。使用控制反转后,就不需要直接创建对象了,对象交给容器来创建,并将其注入到任何需要使用它的地方去,注入的过程称之为依赖注入。这样一来,对象之间就没有这种强依赖关系了,程序也就更容易进行扩展了。

想要进一步深入了解控制反转思想的读者,推荐阅读:Inversion of Control Containers and the Dependency Injection pattern

四、Spring和控制反转

Spring框架的核心设计思想建立在控制反转(IoC)这一软件设计范式之上。通过将传统开发中由程序员直接控制的对象创建、依赖管理和生命周期等职责转交给Spring容器管理,实现了应用程序组件间的解耦。这种控制权的转移使得各个模块不再需要关心依赖对象的具体实现和获取方式,而是通过容器自动装配,大大提升了代码的灵活性和可维护性。

参考资料

[1] 动力节点Spring6教程
[2] DeepSeek

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值