来,聊聊Spring Ioc

对于Spring想必大家都不陌生,那么对控制反转(IOC)、面向切面编程(AOP)肯定也是不陌生 那么我们今天来聊聊控制反转

开始之前分享一波桌面壁纸先

来,聊聊Spring Ioc

接下来开始吧?

官方术语

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做**依赖注入(Dependency Injection,简称DI**),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中

这样听起来也太模糊、太莫能两可了吧!!!

简单来说,控制反转就是我们对对象的创建、管理交给Spring工厂去管理,而且这个工厂管理的可比我们好多了

我们在上面又多说了一个名词"工厂",那么什么是工厂呢,接下来我将"控制反转"分为三部分为大家讲解(欸,看到这里大家肯定觉得很罗嗦,我也是想着让每个人都能看懂啦~~~狗头保命)

1、工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

简单的说就是不通过new User()这样去创建对象,而是通过工厂类去帮你完成创建对象的一个过程

好处:最大的好处就是解耦合,这个解耦合就是代码之间存在强关联性,就好比我们在代码中直接UserService userService = new UserServiceImpl();这样,一旦需要换一个实现的时候改起来就很麻烦了,这样代码维护起来就很困难了。

生活小例子:相当于是创建生产电脑的工厂,客户需要购买什么样的电脑,只要输入类型编号就可以获取该电脑,而无需知道该电脑是如何被生产出来的。

1.1、实现简单的工厂模式

先给大家写一个不使用工厂模式的例子,以便对比

public interface UserService {
    void login(String name,String password);
}
​
public class UserServiceImpl implements UserService {
    @Override
    public void login(String name, String password) {
        System.out.println("账号为:" + name +"密码为:" + password + "登陆成功");
    }
}
​
@Test
public void testSpring(){
    UserService userService = new UserServiceImpl();
    userService.login("张三","123");
}

很显然上面的代码存在很强的关联性,一旦new UserServiceImpl()需要修改的时候为另外一个实现类的时候需要换掉整个代码

简单的工厂模式

//简单工厂类
public class BeanFactory {
    public static UserService getUserService(){
        return new UserServiceImpl();
    };
}
​
//调用测试
 @Test
public void testSpring(){
    //UserService userService = new UserServiceImpl();
    UserService userService = BeanFactory.getUserService();
    userService.login("张三","123");
}

这样看起来是不是好多了呢?但仔细虽然我们在调用类上消除了强关联性,但再看我们工厂类中还是指定性的去调用了UserServiceImpl (这不换汤不换药嚒)

当然今天讲的肯定不止于此呀!想想再Java里面可以通过怎样的形式去获得一个类呢?没错没错 “反射”,你肯定能想到我们接下来说的就是"反射工厂"

1.2、反射工厂

说到反射我们都知道通过一个路径我们可以拿到类的变量、类的方法、统统都能拿到,正是反射这个特性,我们便可以通过反射去帮我们去实现对象的创建

不多说,直接上代码

public class BeanFactory {
    public static UserService getUserService(){
        UserService userService = null;
        try {
            Class clazz = Class.forName("com.spring.test.model_1.UserServiceImpl");
            userService = (UserService) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return userService;
    };
​
}
​
//调用的测试还是不需要修改
@Test
public void testSpring(){
    //UserService userService = new UserServiceImpl();
    UserService userService = BeanFactory.getUserService();
​
    userService.login("张三","123");
}

通过UserServiceImpl的全限定路径我们简简单单就得到了UserServiceImpl对象,但到这里你觉得没问题了吗?当然有!!!

存在问题:虽然没有通过构造方法去创建对象了,但是我们能发现通过反射我们使用的是类的全限定路径,这样的话我们即使需要修改的话还是需要在代码中修改相应的实现类,这样的话也会出现耦合的问题。就并没有彻底解决问题

解决方法:我们可以将通过反射使用的全限定路径放到properties配置文件中,使用IO的方式获取到配置文件的内容,因为properties配置文件存储的都是key、value,所以我们可以创建一个properties集合存储配置文件中相应的key、value,这样就可以实现不修改代码层面,实现解耦

创建配置文件applicationtext.properties

userService = com.spring.test.model_1.UserServiceImpl

修改工厂类,通过IO获取配置文件内容放入集合

public class BeanFactory {
​
    //用于存储配置文件中的信息
    private static Properties env = new Properties();
​
    /**
     * 静态代码块
     *  用于一开始加载就获取到配置文件中的信息放入到存储的集合中
     */
    static {
        //第一步通过IO获取配置文件信息
        InputStream resourceAsStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        //第二步将配置文件内容封装到Properties集合中
        try {
            env.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
​
    }
​
    public static UserService getUserService(){
        UserService userService = null;
        try {
            //获取存储集合中的key 对应配置文件key
            Class clazz = Class.forName(env.getProperty("userService"));
            userService = (UserService) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return userService;
    };
​
}
​
//测试类依旧不需要修改
@Test
public void testSpring(){
    //UserService userService = new UserServiceImpl();
    UserService userService = BeanFactory.getUserService();
​
    userService.login("张三","123");
}

最后以配置文件的形式达到了我们想要的效果,并且在我们需要修改实现类的时候也不需要再修改代码层面,直接修改配置文件就可以实现我们想要达到的效果 (这里还可以将上面的工厂通过传递key实现一个通用工厂)

2、依赖注入

可以从官方解释中看到控制反转,最常见的方式就是依赖注入,当你理解了工厂模式,那么对依赖注入理解起来也就很简单了

注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值 好比通过工厂为UserService赋上UserServiceImpl
​
依赖注⼊:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置问件进行
注入(赋值)。
​
好处:解耦合

2.1、通过编码的方式注入

User user = new User();
user.setAge(18);
user.setName("张三");

这样也是一样存在耦合的,在项目上线之后,修改代码基本都是需要对整个项目进行重启的,而修改配置文件就可以不需要的

2.2、通过配置的方式注入

<bean id="user" class="com.spring.test.model_1.User" >
    <property name="age">
        <value>22</value>
    </property>
    <property name="name">
        <value>lisi</value>
    </property>
</bean>

2.3、实现原理(简易大致版)

Spring通过底层调用对象的属性对应的set方法,完成成员变量的赋值,这种方式也称为"set注入"

通过反射创建好对象之后,会根据配置文件中的"propety"标签中name对应对象中的name属性,调用set方法将value标签中的数据赋值到对象属性中,从而达到注入

3、控制反转(IOC Inverse of Control)

到这里我们就可以来总结一下控制反转了!!!理解了前面说的这下面的一点总结也就能对控制反转理解的很清晰

控制:就是对于我们成员变量赋值的控制权
控制反转:把对于成员变量的控制权,从代码直接new或者说直接Set中反转(转移)到Spring工厂和配置文件完成
好处:就是我们从头到尾讲到的"解耦合"

讲到这里今天的内容就分享到这里啦,第一次认真去分享自己的所知所想,有不当之处,希望大佬指正!

结尾再附上桌面壁纸哈哈哈哈

来,聊聊Spring Ioc

\

上一篇:AgileEAS.NET平台开发实例-药店系统-数据库还原


下一篇:Spring5 扩展篇之自定义xml标签