SpringIOC、DI理解

1、什么是IOC

        IOC是Inversion of Control的首字母,叫做控制反转,这不是一门什么很高大上的技术,而是一种设计思想,我们初学者要转变传统javase时候的思维。

        在传统的Java应用中,一个类想要调用另一个类中的属性或者方法,一般都会在代码中先new出来一个对象,然后通过对象再去调用属性或方法,这个时候,新对象,也就是被调用者的控制权掌握在调用者身上,更为具体一点的说是掌握在我们程序员手上,下面写个简单的例子体会一下。

        一般开发程序有dao层,service层,这边创建一个接口,里边有一个获取用户的方法,再写一个实现类,去实现这个接口

public interface UserDao {
    void getUser();
}


public class UserDaoImpl implements UserDao{

    @Override
    public void getUser() {
        System.out.println("默认获取用户的数据");
    }
}

这是dao 层的接口和实现类,然后我们去service层写个接口跟实现类,调用dao层,然后客户端就调用service层

public interface UserService {
    void getUser();
}
public class UserServiceImpl implements UserService{
    private UserDao userDao = new UserDaoImpl();
    
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

这里我们在这里new了一个userdao的实现类,然后客户端就去调userServiceImpl这个类去实现功能就可以

public class UserDAOTest {
    @Test
    public void test(){
        //用户实际上是去调用的Service层
        UserService userService = new UserServiceImpl();
       
        userService.getUser();
    }
}

通过上面这些代码会发现UserdaoImpl的对象的创建控制权是在我们程序员手上的,假如这个时候userdao有很多个实现类,因为我们不知道用户的需求是通过哪一个实现类来实现的,那么我们就需要把这些实现类的对象都私有到userServiceImpl类中,然后都得写进去,这个时候调用就会特别麻烦,这个时候我们可以通过一个set方法,来把对象创建的控制权交给客户端(也就是测试方法),假设我们这时候新增一个UserDao的实现类,注意看两段代码的区别

        新增的实现类

public class UserDaoMysqlImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("mysql获取用户数据");
    }
}

         原来这里的userdao是在这里就new死了的,然后这时候我们通过一个set方法,把这个要创建的对象,你要用啥,你就给我一个相对应的对象,然后我去调用相应对象的实现方法就可以

public class UserServiceImpl implements UserService{
    private UserDao userDao;
    //通过set方法实现动态注入

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

        客户端调用方法 ,通过set方法,客户端要用哪个对象就给程序哪个对象,这时候userdao对象的创建控制权就已经是在客户端手上了,这里对象创建的控制权由服务端也就是程序员转向了客户端,也就是用户

public class UserDAOTest {
    @Test
    public void test(){
        //用户实际上是去调用的Service层
        UserService userService = new UserServiceImpl();
        //bean的控制交给了service层
        //使用了set动态注入,程序不再具有主动性,而是变成了被动的接收对象
    ((UserServiceImpl)userService).setUserDao(new UserDaoMysqlImpl());
        userService.getUser();
    }
}

2、IOC容器

        在spring中,一个个的对象(也就是bean)的控制权是掌握在IOC容器中的,实现这个的大致步骤如下

        1、程序猿通过xml配置文件、注解、java配置类等方式,对java对象进行定义,就是将一个个的对象标志成spring能够识别的bean

        2、spring启动是,IOC容器会自动根据对象的定义,将这些对象创建出来并管理起来

        3、当程序需要使用一些对象的时候,就去找IOC容器就可以了(通过ApplicationContext对象的getBean方法),就不需要再通过new对象的方式了。

       IOC带来的最大的改变并不是代码层面的,而是思想层面的,发生了对象创建的主动被动的调换,在spring中,程序不再主动去创造对象,而是要用到的时候通过调用ioc容器,然后让ioc给到我们要用的对象

3、依赖注入(DI)

        在了解了 IoC 之后,我们还需要了解另外一个非常重要的概念:依赖注入。
        依赖注入(Denpendency Injection,简写为 DI),在面向对象中,对象和对象之间是存在一种叫做“依赖”的关系。简单来说,依赖关系就是在一个对象中需要用到另外一个对象,即对象中存在一个属性,该属性是另外一个类的对象。
        例如,有一个名为 B 的 Java 类,它的代码如下。

public class A {
private String a;
}
public class B{
private String b;
private A a;
}


        从代码可以看出,B 中存在一个 A 类型的对象属性 a,此时我们就可以说 B 的对象依赖于对象 a。而依赖注入就是就是基于这种“依赖关系”而产生的。
        我们知道,控制反转核心思想就是由 Spring 负责对象的创建。在对象创建过程中,Spring 会自动根据依赖关系,将它依赖的对象注入到当前对象中,这就是所谓的“依赖注入”。
        依赖注入本质上是 bean属性注入的一种,只不过这个属性是一个对象属性而已。

        下面的额代码,理解一下依赖注入

        首先,我们先要有一个被引用的类

public class Address {
}

        然后是实现DI的类,这里我们官方文档的各种属性注入都来一遍

package com.zhang;

import java.util.*;

public class Student {
    private String name; //String类型
    private Address address; //引用类型
    private String[] books; //数组类型
    private List<String> hobby; //集合类型
    private Map<String,String> card; //map
    private Set<String> games; //set
    private Properties properties;  //prop
    private String wife;  

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", games=" + games +
                ", properties=" + properties +
                ", wife='" + wife + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }
}

        下面是applicationContext.xml文件的内容,官方是这么命名文件的,只是一个规范,当然为了简单,写其他文件名也可以,例如beans.xml都是可以的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="address" class="com.zhang.Address"/>
    <bean id="student" class="com.zhang.Student">
        <!--普通值注入-->
        <property name="name" value="张三"/>
        <!--bean注入-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>三国演义</value>
                <value>水浒传</value>
            </array>
        </property>
        <!--集合-->
        <property name="hobby">
            <list>
                <value>sing</value>
                <value>read</value>
                <value>sleep</value>
            </list>
        </property>
        <!--map-->
        <property name="card">
            <map>
                <entry key="身份证" value="79487321759743978"/>
                <entry key="银行卡" value="23783507290894385"/>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>lol</value>
                <value>coc</value>
                <value>王者</value>
            </set>
        </property>
        <!--null-->
        <property name="wife">
            <null></null>
        </property>
        <!--properties-->
        <property name="properties">
            <props>
                <prop key="学号">2020</prop>
                <prop key="班级">8班</prop>
                <prop key="姓名">老张</prop>
            </props>
        </property>
    </bean>
</beans>

        上面的代码对student类的各个属性都进行了注入,<bean>标签就是我们的对象,里面有id,class

        下面这句话就相当于我们new对象,id 相当于对象名,class相当于对象的类
        

<bean id="address" class="com.zhang.Address"/>

         等价于

Address address = new Address();

4、IoC 的工作原理

        在 Java 软件开发过程中,系统中的各个对象之间、各个模块之间、软件系统和硬件系统之间,或多或少都存在一定的耦合关系。
        若一个系统的耦合度过高,那么就会造成难以维护的问题,但完全没有耦合的代码几乎无法完成任何工作,这是由于几乎所有的功能都需要代码之间相互协作、相互依赖才能完成。因此我们在程序设计时,所秉承的思想一般都是在不影响系统功能的前提下,最大限度的降低耦合度。
        IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。

  1. 在配置文件(官方:applicationContext.xml)中,对各个对象以及它们之间的依赖关系进行配置;
  2. 我们可以把 IoC 容器当做一个工厂,这个工厂的产品就是 Spring Bean;
  3. 容器启动时会加载并解析这些配置文件,得到对象的基本信息以及它们之间的依赖关系;
  4. IoC 利用 Java 的反射机制,根据类名生成相应的对象(即 Spring Bean),并根据依赖关系将这个对象注入到依赖它的对象中。

        由于对象的基本信息、对象之间的依赖关系都是在配置文件中定义的,并没有在代码中紧密耦合,因此即使对象发生改变,我们也只需要在配置文件中进行修改即可,而无须对 Java 代码进行修改,这就是 Spring IoC 实现解耦的原理。

5、IOC创建对象的方式

        看代码吧,一切尽在不言中

public class User {
    private String name;

   /* public User() {
        System.out.println("无参构造方法");
    }*/

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    Ioc创建对象的方式-->
<!--    1、通过无参构造创建-->
<!--    <bean id="user" class="com.zhang.User">-->
<!--        <property name="name" value="小张"/>-->
<!--    </bean>-->
<!--    2、下标赋值-->
<!--     <bean id="user" class="com.zhang.User">-->
<!--         <constructor-arg index="0" value="小张"/>-->
<!--     </bean>-->
<!--        3、通过类型创建(不建议使用)-->
<!--        <bean id="user" class="com.zhang.User">-->
<!--            <constructor-arg type="java.lang.String" value="小张"/>-->
<!--        </bean>-->
    <!--4、通过属性名创建bean-->


        <bean id="user" class="com.zhang.User" name="user3,user4 user5;user6">
            <constructor-arg name="name" value="小张" />
        </bean>

    <!--别名-->
    <alias name="user" alias="user2"/>

    <!--
    id: bean的唯一标识符,相当于我们的对象名
    class: bean对象对应的全限定名 包名+类名
    name : 也是别名,可以取多个
    -->
</beans>

测试

package com.zhang;

import com.sun.deploy.net.MessageHeader;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {
    @Test
    public void test(){
        //Spring容器,把所有bean丢到里面,要用什么通过ApplicationContext 对象去取
        //在配置文件被加载的时候,bean就已经被初始化出来了
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = classPathXmlApplicationContext.getBean("user2",User.class);
        System.out.println(user.toString());
    }
}

6、jar包

        最后附上spring要导的jar包,使用maven创建项目,这里spring其实要导挺多的,像aop,bean,core等等,导下面这个就可以直接全部导进来了。

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.22</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>


    </dependencies>

 完结,撒花~~~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长安也有星星

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

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

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

打赏作者

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

抵扣说明:

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

余额充值