Spring基础专题——第三章(反转控制与依赖注入)

前言:去年到现在一直没有很好的时间完成这个spring基础+源码的博客目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事情,总之不对的地方,多多指出,我们一起徜徉代码的海洋!

 

我这里做每个章节去说的前提,不是一定很标准的套用一些官方名词,目的是为了让大家可以理解更加清楚,如果形容的不恰当,可以留言出来,万分感激

1、反转(转移)控制(Inverse of Control)

所谓反转控制,就是我们常说的IOC,大学老师经常告诉我们,ioc就是反转控制,就是控制反转,一头雾水你也听不明白,听我说来

我们在没有spring的时候,对于对象的创建,我们是在代码里做这个事情的,new 对象,既然我们需要让spring来托管对象,我们引出下面的概念

  • 控制:对于成员变量赋值的控制权
  • 反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring⼯⼚和配置⽂件中完成
  • 好处:解耦合
  • 底层实现:工厂设计模式

我画了一张图,就可以描述很清楚

对于成员变量赋值的控制权==Spring配置文件+Spring工厂(ApplicationContext)

2、依赖注入

 依赖注入是Spring的编程方式,简称DI(Dependency Injection)

  • 注入:通过Spring的⼯⼚及配置⽂件,为对象(bean,组件)的成员变量赋值
  • 依赖:当⼀个类需要另⼀个类时,就意味着依赖,⼀旦出现依赖,就可以把另⼀个类作为本
    类的成员变量,最终通过Spring配置⽂件进⾏注⼊(赋值)。

我的UserService是需要访问数据库对吧,所以我必须要有数据库的操作UserDao,没他不行,非他不可!这就叫做依赖!

我需要你的时候,等于我就要依赖你,所以我们就可以按照依赖注入的想法,当然你要提供set方法,我就可以把你注入进来,把你做为我的成员变量,进而我们可以通过Spring配置文件,注入进来!

所以面试不会问这个概念,而属于一种Spring的思想。

好处:解耦合!

3、Spring工厂创建复杂对象

3.1、什么是简单对象

指的是可以直接通过new构造方法,来创建对象,反射底层,也是调用构造方法。

 3.2、复杂对象

不能直接通过new创建,比如Connection,SqlSessionFactory对象,这种对象不能直接new,创建过程比较复杂。

所以我们希望Spring既能帮我们创建简单对象,也可以帮我们创建复杂对象,这样Spring是不是就可以帮我们创建一切对象了?

3.3、Spring工厂创建复杂对象的三种方式

既然知道Spring要创建复杂对象,那么他有几种方式呢,下面我们来认识下:

  • FactoryBean接口

1、开发步骤

实现FactoryBean接口

getObject:用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回出去

类似哪些需要在这里创建呢,比如Connection,SqlSessionFactory

getObjectType:同理上面创建什么样的复杂对象,就返回什么样的复杂对象对应的Class对象

isSingleton:如果这个复杂对象只需要创建一次,返回true;如果这个复杂对象每一次调用都需要创建一个新的对象,返回false;

public class ConnectionFactoryBean implements FactoryBean {
    /**
     * 用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回出去
     */
    @Override
    public Object getObject() throws Exception {
        return null;
    }

    /**
     * 返回所创建复杂对象的Class对象
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return null;
    }

    /**
     * 如果这个复杂对象只需要创建一次的话,返回true
     * 如果这个复杂对象每一次调用都需要创建一个新的对象,返回false
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}

 

所以Spring把这个复杂对象的创建过程交给了用户去自定义复杂对象!


public class ConnectionFactoryBean implements FactoryBean<Connection> {
    /**
     * 用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回出去
     */
    @Override
    public Connection getObject() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false","root","root");
        return connection;
    }

    /**
     * 返回所创建复杂对象的Class对象
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return Connection.class;
    }

    /**
     * 如果这个复杂对象只需要创建一次的话,返回true
     * 如果这个复杂对象每一次调用都需要创建一个新的对象,返回false
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}

 applicationContext.xml配置文件如下

<!--如果Class中指定的类型 是FactoryBean接⼝的实现类,那么通过id值获得的是
这个类所创建的复杂对象Connection-->
<bean id="conn" class="com.chenxin.spring5.factorybean.ConnectionFactoryBean">

    </bean>

看看结果对不对

conn = com.mysql.jdbc.JDBC4Connection@481a15ff

 思考下,咦为毛是Connection对象,我不是应该是获取ConnectionFactoryBean对象吗?

想想我们要得到的结果是不是Connection对象?所以Spring在这个实现FactoryBean接口上,就发生了变化,此时你再通过工厂创建对象,Spring就不会按照你以为的那样,给你一个ConnectionFactoryBean对象,而是真正给你一个你要创建的复杂对象!

细节:

1、如果就想获得FactoryBean类型的对象 ctx.getBean("&conn") 获得就是ConnectionFactoryBean对象

2、isSingleton⽅法 返回 true-只会创建⼀个复杂对象,返回false--每⼀次都会创建新的对象

问题:用户需要根据这个对象的特点 ,决定是返回true还是false ,(SqlSessionFactory返回true比较合理,因为是重量级对象,只用一个即可,不存在Connection的问题), (Connection也是一样,是不是每次都需要创建连接对象,连接对象是不能被公用的,因为有可能会有事务的操作,比如,有个conn1,第一个客户t1,t1开启了事务,开始运行,此时第二个用户t2也拿到连接conn1,也开启了事务,如果t1执行的快,直接提交了,那么t2就会被影响,所以对吧!)
 

3、mysql⾼版本连接创建时,需要制定SSL证书,解决问题的⽅式

4、依赖注入的体会:对于URL,username,password,是不是在ConnectionFactoryBean中是非常重要的,是需要依赖这些字符串的对吧,既然是依赖,我就可以作为我的成员变量,通过Spring的配置文件来完成注入,赋值,所以我们可以修改下代码


public class ConnectionFactoryBean implements FactoryBean<Connection> {

    private String driverClassName;

    private String url;

    private String username;

    private String password;

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 用于书写创建复杂对象的代码,并把复杂对象作为方法的返回值返回出去
     */
    @Override
    public Connection getObject() throws Exception {
        Class.forName(driverClassName);
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }

    /**
     * 返回所创建复杂对象的Class对象
     *
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return Connection.class;
    }

    /**
     * 如果这个复杂对象只需要创建一次的话,返回true
     * 如果这个复杂对象每一次调用都需要创建一个新的对象,返回false
     *
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}

配置文件改为

<bean id="conn" class="com.chenxin.spring5.factorybean.ConnectionFactoryBean">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/users?useSSL=false"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

 这样结果是不是一样的?所以这里还是体会了依赖注入的思想!解耦合。

相信到这很多人会有疑问,我们来分析下

1、为什么Spring规定FactoryBean接口,实现,并且要把复杂对象的创建代码写在getObject里?

实际上有个东西叫做接口回调!任何开源框架内,反射+接口回调,什么都能做!

说明下,我们在学java基础的时候学过instanceof判断两个对象是否属于同一个类型的,Spring在拿到bean标签id=conn的时候,会判断当前这个ConnectionFactoryBean这个类是不是FactoryBean的子类,如果不是的话,就说明是简单对象,Spring直接帮我们new就可以了;如果是的话,那么你肯定就要实现三个方法,getObject,getObjectType和isSingleton,既然你实现了,Spring就能找到getObject方法,Spring就能调用这个方法,就可以获取到这个连接对象Connection;于是就可以返回出去。有人肯定会问,你怎么知道是Spring调用的,很明显你要是自己调用,你肯定要写new ConnectionFactoryBean(),然后自己去getObject方法,但是你知道你并没有这么做,所以是Spring按照约定来调用的!

总结:Spring中⽤于创建复杂对象的⼀种⽅式,也是Spring原⽣提供的,后续讲解Spring整合其他框架,也是⼤量应⽤FactoryBean;

  • 实例工厂

为什么有了FactoryBean,Spring还要整个实例工厂呢,原因以下几点

1、避免框架的侵入

如果选择Spring框架进行复杂对象开发的话,我们要实现FactoryBean接口,如果哪天我离开了Spring框架呢,那么之前实现FactoryBean接口的代码就没法用了

实际上FactoryBean接口输入Spring框架的侵入

2、整合遗留系统:比如我现有公司已经有了一个ConnectionFactory,里面有个getConnection方法已经可以帮我们获取Connection对象了,给我们的是class文件,我们只能拿到class文件

那么我们没法通过改源码的方式来进行创建复杂对象,所以我们有了实例工厂

遗留代码比如:

public class ConnectionFactory {

    public Connection getConnection(){
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false", "root", "root");
        }catch (Exception e){
            e.printStackTrace();
        }
        return conn;
    }
}

类和工厂实际上以前公司代码已经有了, 我们只需要去解决配置文件的问题

先把这个ConnectionFactory对象由Spring创建出来,进而要创建connection连接对象的时候,再通过Spring来关联这个ConnectionFactory工厂,调用getConnection方法,来得到connection对象

<!--  先交给Spring工厂创建这个工厂的对象  -->
    <bean id="connectionFactory" class="com.chenxin.spring5.factorybean.ConnectionFactory">
    </bean>

    <!--  然后把之前的ConnectionFactory和Spring整合进来,让Spring帮忙创建这个conn对象  -->
    <bean id="conn" factory-bean="connectionFactory" factory-method="getConnection"></bean>
  • 静态工厂

作为静态工厂,和实例工厂的区别,体现在这个getConnection方法是不是静态的

public class StaticConnectionFactory {

    public static Connection getConnection(){
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false", "root", "root");
        }catch (Exception e){
            e.printStackTrace();
        }
        return conn;
    }
}

之前我们通过new ConnectionFactory().getConnection()得到对象,这里就是直接StaticConnectionFactory.getConnection()即可

所以配置文件这么写就可以了

    <bean id="connectionFactory" class="com.chenxin.spring5.factorybean.StaticConnectionFactory" factory-method="getConnection"/>

三种方式我这边就讲解完了!

4、Spring创建工厂总结

Spring即可以创建简单对象,也可以创建复杂对象,目的都是为了解耦!如果看了我前面两集讲解的Spring,这张图你一定会很清楚!

下一节我们来分析Spring创建工厂的次数是如何控制的,敬请期待!

 

 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风清扬逍遥子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值