Spring的IOC(学习2.0)

281 阅读8分钟

Spring

我是用的 Spring 插件

 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>5.3.18</version>
 </dependency>
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>5.3.18</version>
 </dependency>

Spring 有什么优点

  • 控制反转(IOC),面向切面编程 (AOP)
  • 开源免费
  • 轻量级非入侵框架
  • 支持事务处理
  • 对框架整合的支持(几乎对所有的java框架)

总结 —— Spring 是一个轻量级是控制反转(IOC),面向切面的框架(AOC)

Spring 有什么缺点

  • 配置十分繁琐。配置地狱。非常可怕。所以我们后来使用 Spring Boot。

Spring 七大组成

image-20220403162732069

Spring Boot

  • 是一个快速开发的单个微服务框架
  • 一个快速开发的脚手架
  • 约定大于配置!(学习 maven 一样的)

Spring Cloud

  • 基于 SpringBoot 实现

目前 java 工作情况

  • 现在大多数的 java 工作都是用 Spring Boot 和 Spring Cloud。这两个框架都是基于 Spring 和 Spring MVC 来完成的。所以 Spring 非常重要!!!

Spring 里的 IOC

什么是IOC(控制反转)

  • 由用户选择调用的方法,而不是以前的程序员手上(service层)就是控制反转

  • 按照以前我们的一般操作就是通过在 serviceImpl 中创建 Dao 对象调用 DaoImpl 里具体执行的 Sql 语句。

    image-20220403171128342

    但是如果我们想要在 Service 中根据一个 Dao 对象调用 不同的 DaoImpl 只能再次创建 Dao 对象并且调用 不同的 DaoImpl

    image-20220403171705581

    所以我们可以通过在 ServiceImpl 中给出 Dao 的 Set 接口来动态实现

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

    就可以在创建 Service 对象调用 ServiceImpl 的方法时候,可以动态的通过 set 方法来选择 DaoImpl 就可以了。

  • 这样的好处就是,如果我们在 DaoImpl 里创建的类,用户可以直接通过 Service 对象来调用,中间的步骤不需要修改。

  • 这种思想,从本质上结局了问题,程序员不在需要管理对象的创建了。系统的耦合性大大降低,可以更多的专注在业务上。这是IOC原型

依赖注入

构造器注入

  • 具体代码在 Spring-01-IOC、SPring-02-HelloSpring、Spring-03-IOC2
    <bean id="user1" class="cn.hyz.pojo.User">
        <!--有构造方法的第一种赋值: 通过类型赋值-->
        <!--<constructor-arg type="java.lang.String" value="name1"/>-->

        <!--有参构造方法的第二种赋值: 通过参数下标赋值-->
        <!--<constructor-arg index="0" value="name1"/>-->

        <!--有参构造方法的第三种赋值:通过形参名称赋值-->
        <constructor-arg name="name" value="name2"/>
    </bean>

Set 方式注入

  • 依赖注入(Set 注入)
    • 依赖:bean 对象的创建依赖容器
    • 注入:bean 的所有属性赋值,由容器来完成。

【测试环境搭建】

  1. 复杂类型

    package cn.hyz.pojo;
    
    /**
     * @author workplace
     * @date 2022/4/3 21:47
     */
    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "Address{" +
                    "address='" + address + '\'' +
                    '}';
        }
    }
    
  2. 真实对象

    package cn.hyz.pojo;
    
    import java.util.*;
    
    /**
     * @author workplace
     * @date 2022/4/3 21:46
     */
    public class Student {
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbies;
        private Map<String,String> card;
        private Set<String>games;
        private String wife;
        private Properties info;
    }
    
  3. 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="cn.hyz.pojo.Address">
            <property name="address" value="jluzh"/>
        </bean>
    
        <bean id="Student" class="cn.hyz.pojo.Student">
            <!--普通注入:value-->
            <property name="name" value="hyz"/>
    
            <!--bean注入:ref-->
            <property name="address" ref="Address"/>
    
            <!--
            数组注入:
            <array>
                <value></value>
                <value></value>
            </array>
            -->
            <property name="books">
                <array>
                    <value>京瓶梅</value>
                    <value>金瓶梅</value>
                    <value>金品每</value>
                    <value>静平美</value>
                </array>
            </property>
    
            <!--list注入
            <list>
                <value>打篮球</value>
                <value>唱歌</value>
                <value>跳舞</value>
            </list>
            -->
            <property name="hobbies">
                <list>
                    <value>打篮球</value>
                    <value>唱歌</value>
                    <value>跳舞</value>
                </list>
            </property>
    
            <!--map注入:
            <map>
                <entry key="" value=""/>
            </map>
            -->
            <property name="card">
                <map>
                    <entry key="身份证" value="111111222222221111"/>
                </map>
            </property>
    
            <!--set注入:
            <set>
                <value>GTA5</value>
                <value>奇迹暖暖</value>
                <value>森林冰火人</value>
            </set>
            -->
            <property name="games">
                <set>
                    <value>GTA5</value>
                    <value>奇迹暖暖</value>
                    <value>森林冰火人</value>
                </set>
            </property>
    
            <!--null注入
             <null/>
            -->
            <property name="wife">
                <null/>
            </property>
    
            <!--Properties注入
            <props>
                <prop key="">value</prop>
                <prop key="">value</prop>
            </props>
            -->
            <property name="info">
                <props>
                    <prop key="学号">17190424</prop>
                    <prop key="姓名">hyz</prop>
                    <prop key="性别"></prop>
                </props>
            </property>
    
        </bean>
    
    </beans>
    
  4. 测试类

    package cn.hyz.test;
    
    import cn.hyz.pojo.Student;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author workplace
     * @date 2022/4/3 22:14
     */
    public class MyTest {
        @Test
        public void test() {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            Student student = (Student) context.getBean("Student");
            System.out.println(student);
        }
    }
    
  5. 结果

    Student{name='hyz', 
    address=Address{address='jluzh'}, 
    books=[京瓶梅, 金瓶梅, 金品每, 静平美],
    hobbies=[打篮球, 唱歌, 跳舞], 
    card={身份证=111111222222221111}, 
    games=[GTA5, 奇迹暖暖, 森林冰火人], 
    wife='null', 
    info={学号=17190424, 性别=男, 姓名=hyz}}
    

拓展方式注入

  • p 命名空间注入 ----> 对应着 Set 方式注入

    • 只需要在 xml 配置文件中的约束加上

      xmlns:p="http://www.springframework.org/schema/p"
      

      就可以通过以下方式来注入属性

      <!--p命名空间注入。通过 property 注入属性。property的意思-->
      <bean id="User1" class="cn.hyz.pojo.User" p:name="hyz" p:age="21"/>
      
    • 官方解释

      image-20220403231359763

  • c 命名空间注入 ----> 对应着构造器注入

    • 只需要在 xml 配置文件中加上以下约束

      xmlns:c="http://www.springframework.org/schema/c"
      

      就可以通过以下方式来注入属性

      <!--c命名空间注入。通过构造器注入属性。constructor-arg 的意思-->
      <bean id="User2" class="cn.hyz.pojo.User" c:name="hyz" c:age="21"/>
      
    • 官方解释

      image-20220403231228903

  • 注意点

    • 不能直接使用 c 命名和 p 命名,需要先导入 xml 约束

      xmlns:p="http://www.springframework.org/schema/p"
      xmlns:c="http://www.springframework.org/schema/c"
      

bean的作用域

image-20220403231937568

  1. 单例模式 ----> singleton (是 spring 的默认模式)

    <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
    

    从容器中 getBean 都是同一个对象

  2. 原型模式 ----> prototype

    <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
    

    每次从容器中 getBean 都产生一个新的对象

  3. 剩余的都只能在 web 开发中使用到。

Bean 自动装配

image-20220404134833457

  • 自动装配是 Spring 满足 bean 依赖的一种方式 ----> 给 ‘对象属性’ 赋值
  • Spring 会在上下文中自动寻找,并自动给 bean 装配属性 ----> 自动给 ‘对象属性’ 赋值

在 Spring 中有三种装配的方式

  1. 在 xml 中显示的配置

    <bean id="cat" class="cn.hyz.pojo.Cat"/>
    <bean id="dao" class="cn.hyz.pojo.Dog"/>
    <bean id="people" class="cn.hyz.pojo.People">
        <property name="name" value="hyz"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dao"/>
    </bean>
    
  2. 在 java 中显示的配置(还没学)

  3. 隐式地自动装配 bean


测试

环境搭建

  • 一个人有两个宠物,一只猫一条狗!人一个类,猫一个类,狗一个类

自动装配

<!--
autowire:自动装配
byName:会在容器上下文里自动查找,和自己对象中 set 方法后面的忽略大小写值对应的 bean id。名字必须符合要求。
byType:会在容器上下文里自动查找,和自己对象中 ‘对象属性’ 相同对象类型的 bean。容器中只能由这一种的对象类型
-->
<bean id="people" class="cn.hyz.pojo.People" autowire="byType">
    <property name="name" value="hyz"/>
</bean>

小结:

  • byName 的时候,必须保证 对象的 id 符合规范(bean 的 id 必须是 ’对象类型’
  • buType 的时候,必须保证 容器中只有一种对象类型(bean 的 class 不能重复

使用注解实现自动装配

官方表示通过注解实现自动装配是要比通过 XML 配置文件实现自动装配要更好的。所以以后如果可以的话,尽量使用自动装配。

image-20220404132150682

使用注解需知:

  1. 引入 xml 配置文件约束,增加 xmlns:context 约束,对 xsi:schemaLocation 内容进行修改。

    xmlns:context="http://www.springframework.org/schema/context"
    
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd
    
  2. 配置注解支持!这是所有使用注解的第一步!不仅仅是开启自动装配!

    <context:annotation-config/>
    

image-20220404133420263

  1. 使用示范

    我们只需要在 ‘对象属性’ / ‘对象属性’Set方法 上添加 @Autowired 注解,就可以为 ‘对象属性’ 自动装配。

    image-20220404134211306

    package cn.hyz.pojo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    /**
     * @author workplace
     * @date 2022/4/3 23:50
     */
    public class People {
        // 加入 @Autowired 注解之后 ‘对象属性’自动装配
    
        @Autowired
        private Dog dog;
        @Autowired
        private Cat cat;
        private String name;
    
        @Override
        public String toString() {
            return "People{" +
                    "dog=" + dog +
                    ", cat=" + cat +
                    ", name='" + name + '\'' +
                    '}';
        }
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Dog getDog() {
            return dog;
        }
    
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    }
    
  • 注意:

    • 使用 自动装配 的时候,你的 ’对象属性‘ 必须是在 IOC 容器里存在

    • 自动装配 默认使用 autowire="byType" 完成,如果无法判断就会使用 byName 完成。

      所以请确保你的 对象 id 名称符合规范

    • 如果自动装配环境过于复杂,没有办法通过一个@Autowired 来完成。我们可以@Autowired配合 @Qualifier(value = "对象 id") 来指定一个唯一的对象自动装载。

      @Autowired
      @Qualifier(value = "cat")
      private Cat cat;
      
  • 测试:如果 @Autowired 定义了 required 属性为 false。代表了这个属性可以为 null。否则不允许为空

    // 加入 @Autowired 注解之后 ‘对象属性’自动装配
    @Autowired
    private Dog dog;
    // 这里在 @Autowired 定义了 required 属性为 false。代表了这个属性可以为 null。否则不允许为空
    @Autowired(required = false)
    private Cat cat;
    

Resource 注解

还有一种自动装配的方法,不用调用 Spring 的注解。就是通过使用 java 的原生注解 @Resource 来完成。

  • 使用

    @Resource
    private Dog dog;
    @Resource(name = "cat")
    private Cat cat;
    private String name;
    
  • 解释

    它是先根据 byName 去容器内寻找对应的对象,如果没有符合调节就通过 byType 去容器里寻找。如果两个都没找到就会报错。

    我们也可以通过 name = "对象 id"


@Resource 和 @Autowrite 区别

  • 都可以实现自动装载,都可以放在 ‘对象属性’ 上
  • @Autowrite 默认使用 autowire="byType" 完成
  • @Resource 默认使用 byName,找不到就使用 byType。两个都找不到就报错
  • 我认为 @Resource 可以用在 Service 层,而 @Autowrite 可以用在 Servlet 层。

使用注解开发

使用注解前需要做的准备工作

  1. 在 Spring 之后使用注解开发必须存在 aop 的包image-20220404154509032

  2. 使用注解需要导入 context 约束,增加注解的支持

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--开启注解支持-->
        <context:annotation-config/>
    
        <!--针对指定包路径下所有的类开启注解支持-->
        <context:component-scan base-package="cn.hyz.pojo"/>
    
    </beans>
    

    image-20220404155252043


Spring 注解和 ‘注解 和 xml 之间的关系’

  1. Bean

    • @Component
  2. 属性如何注入 ----> @Component

    • @Value("value")
    // @Component 等价于 <bean id="user" class="cn.hyz.pojo.User"/> 就是将对象放进容器里了
    @Component
    public class User {
    
        // 等价与 <property name="name" value="hyz"/>
        @Value("hyz")
        public String name;
    }
    
  3. 衍生的注解

    @Component 有几个功能相同的衍生注解。我们在 web 开发中,会按照 mvc 三层架构分层

    • dao ----> @Repository
    • service ----> @Service
    • controller ----> @Controller
  4. 自动装配设置

    • @Autowrite 默认使用 autowire="byType" 完成
    • @Qualifier(value = "bean id")
    • @Resource 默认使用 byName,找不到就使用 byType。两个都找不到就报错
  5. 作用域 ----> 写在类上

    • @Scope("singleton")
    • @Scope("prototype")
  6. 小结

    xml 和 注解

    • xml 更加万能,适用于所有的场所!维护简单方便
    • 注解 不是自己的类不能用,维护相对复杂。

    xml 和 注解 工作中的应用场景

    • xml 用来管理bean

    • 注解 只负责属性注入

    • 我们在使用过程中,只需要注意一个问题:开启注解支持,选择 Spring 扫描的包

      <!--开启注解支持-->
      <context:annotation-config/>
      
      <!--针对指定包路径下所有的类开启注解支持-->
      <context:component-scan base-package="cn.hyz.pojo"/>
      

使用 java 的方式配置 Spring

我们现在完全使用 Spring 的xml 配置,全权交给 java 来完成!

JavaConfig 是 Spring 的子项目,在 Spring 4 之后就成为了一个核心功能

image-20220404164314834

配置类:等同于 xml

package cn.hyz.config;

import cn.hyz.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author workplace
 * @date 2022/4/4 16:34
 */
/*
 * MyConfig 也是在容器里托管的一个 bean
 * @Configuration 代表了 MyConfig 是一个配置类,等价于 MyConfig.xml
 * @ComponentScan 表示在 xml 中的 <context:component-scan base-package="packagePath"/>
 * @Import(ClassName) 表示在 xml 中的 <import resource="XmlName"/>
 * */

@Configuration
@ComponentScan("cn.hyz.pojo")
@Import(Myconfig2)
public class MyConfig {
    /*
     * 注册一个 Bean,就表示 xml 里面的 <bean></bean>
     * 这个方法的名字就表示 <bean> 标签内的 id 属性
     * 这个方法里的 return,就表示 <bean> 标签内的 class 属性
     * */

    @Bean
    public User user() {
        return new User();
    }
}

实体类:

package cn.hyz.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author workplace
 * @date 2022/4/4 16:33
 */

@Component
public class User {

    private String name;

    public String getName() {
        return name;
    }

    @Value("hyz")
    public void setName(String name) {
        this.name = name;
    }

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

测试类:

import cn.hyz.config.MyConfig;
import cn.hyz.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author workplace
 * @date 2022/4/4 16:40
 */
public class MyTest {
    @Test
    public void test() {
        /*
        * 如果完全使用配置类这样子来做,我们只能通过 AnnotationConfig 来获取容器,通过配置类的 class 对象加载!
        * */

        ApplicationContext context
                = new AnnotationConfigApplicationContext(MyConfig.class);
        User getUser = (User) context.getBean("user");
        System.out.println(getUser);
    }
}