01 Spring 概述 程序的耦合与解耦 IOC 依赖注入

目录

程序的耦合

耦合

概念

分类

解耦

概念

解耦思路

使用工厂模式创建对象

IOC

概念

简单入门

添加配置文件

ApplicationContext的三个实现类

核心容器两个接口引发的问题

ApplicationContext

BeanFactory

bean的三种创建方式

使用默认构造方法创建

使用某个类的方法来创建对象, 并存入spring容

使用某个类的静态方法来创建对象, 并存入spring容器

上面 2 3 的方法应用场景

bean标签的scope属性

作用

取值

bean的生命周期

单例对象

多例对象

给出生和死亡绑定函数

依赖注入

概念

能注入的数据有三类

注入的方法有三种

使用构造函数提供

使用set方法提供


  1. 程序的耦合

    1. 耦合

      1. 概念

        1. 程序间的依赖关系
      2. 分类

        1. 类之间的依赖
        2. 方法之间的依赖
    2. 解耦

      1. 概念

        1. 降低程序之间依赖关系
        2. 实际开发中, 应该做到, 编译期不依赖, 运行时才依赖
      2. 解耦思路

        1. 使用反射创建对象, 而避免使用new关键字
        2. 通过读取配置文件来获取要创建对象的全限定类名
        3. 个人理解
          1. new关键字会极大增加程序的耦合度, 因为一旦某一个类缺失, 就会直接出现编译期异常. 因此我们解耦的重点就是如何去除new这个关键字. 一般的解决办法是, 使用配置文件+反射的技术来创建对象, 避免使用new关键字
      3. 使用工厂模式创建对象

        1. 步骤
          1. 从配置文件中读取出类的全限定类名
          2. 使用反射, 根据全限定类名创建对象
        2. package cn.lq.factory;
          
          import java.io.InputStream;
          import java.util.Properties;
          
          /*
          author: lq
          time: 2020-04-21 15:52 
          */
          public class Factory {
              private static Properties properties = null;
          
              static {
                  InputStream is = Factory.class.getClassLoader().getResourceAsStream("bean.properties");
                  properties = new Properties();
                  try {
                      properties.load(is);
                  } catch (Exception e) {
                      throw new ExceptionInInitializerError("初始化properties失败!");
                  }
              }
          
              public static Object getBean(String beanName) {
                  String beanPath = properties.getProperty(beanName);
                  System.out.println("Factory --> beanPath = " + beanPath);
                  Object bean = null;
                  try {
                      bean = Class.forName(beanPath).newInstance();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
                  return bean;
              }
          }
          

           

        3. 存在的问题
          1. 使用方法getBean()获取到的对象是多例对象(即每一次获取到的对象都不一样), 而多例对象的代码执行效率低, 应该考虑让getBean()方法返回单例对象(单例对象: 同一个对象), 但是, 单例对象可能存在线程安全问题(即: 多个线程同时修改对象的成员变量), 又但是, 难道我们平时使用单例对象调用方法的时候真的有可能出现线程安全的情况吗??? 事实上并不会, 因为我们平时开发的时候不会去定义一个成员变量(private int i=0;), 然后又在方法里面去修改它(i++;), 因此我们就可以大胆的让方法getBean()去返回一个单例对象了!
          2. package cn.lq.factory;
            
            import java.io.InputStream;
            import java.util.Enumeration;
            import java.util.HashMap;
            import java.util.Map;
            import java.util.Properties;
            
            /*
            author: lq
            time: 2020-04-21 15:52 
            */
            public class Factory {
                private static Properties properties = null;
                private static Map<String, Object> beans = null;
            
                static {
                    InputStream is = Factory.class.getClassLoader().getResourceAsStream("bean.properties");
                    beans = new HashMap<String, Object>();
                    properties = new Properties();
                    try {
                        properties.load(is);
                        Enumeration<Object> keys = properties.keys();
                        while (keys.hasMoreElements()) {
                            String key = keys.nextElement().toString();
                            String beanPath = properties.getProperty(key);
                            Object value = Class.forName(beanPath).newInstance();
                            beans.put(key, value);
                        }
                    } catch (Exception e) {
                        throw new ExceptionInInitializerError("初始化properties失败!");
                    }
                }
            
                public static Object getBean(String beanName) {
                    return beans.get(beanName);
                }
            }
            

             

  2. IOC

    1. 概念

      1. Inversion Of Control (控制反转)
      2. 本来, 你想要什么样的对象是可以完全由你自己决定的, 但是现在你要把这个创建对象的权利交给工厂模式, 你只需要提供对象的名字即可, 然后具体如何创建对象你就不用在操心了, 工厂给你创建什么你就用什么.
      3. 仅仅是用来降低程序间的耦合度的, 别的事情他干不了.
    2. 简单入门

      1. 添加配置文件

        1. <?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="serviceUser" class="cn.lq.service.ServiceUser"></bean>
              <bean id="daoUserImpl" class="cn.lq.dao.impl.DaoUserImpl"></bean>
          </beans>
          

           

    3. ApplicationContext的三个实现类

      1. ClassPathXmlApplicationContext : 可以加在类路径下面的配置文件, 要求配置文件必须在类路径下
      2. FileSystemXmlApplicationContext : (不常用)可以加在磁盘任意位置的配置文件 
      3. AnnotationConfigApplicationContext : 读取注解创建容器
    4. 核心容器两个接口引发的问题

      1. ApplicationContext

        1. 既可以立即加载也可以延迟加载(通过配置文件设置)
          1. spring可以根据你需要的是单例对象还是多例对象来自动选择加载方式
      2. BeanFactory

        1. 采用延迟加载方式创建对象
        2. 多例对象适用
        3. 不推荐使用(接口的方法不太完善)
    5. bean的三种创建方式

      1. 使用默认构造方法创建

        1. bean标签中除了id和class属性之外没有别的属性, 这样, 就会使用默认的创建方法
        2. <bean id="user" class="cn.lq.domain.User"></bean>

           

      2. 使用某个类的方法来创建对象, 并存入spring容

      3. 使用某个类的静态方法来创建对象, 并存入spring容器

        1. <bean id="serviceUser" class="cn.lq.factory.Factory2" factory-method="getS" scope="singleton"></bean>

           

      4. 上面 2 3 的方法应用场景

        1. 有些对象, 我们在正常情况下是没办法new出来的, 而是通过先new一个别的对象, 然后调用这个对象里面的方法来返回一个我们真正想要的对象( SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); ), 这个时候, 而我们就需要用的2 3 的方法了
    6. bean标签的scope属性

      1. 作用

        1. 用于指定bean的作用范围
        2. 设置产生的对象是多例的还是单例的
      2. 取值

        1. singleton: 设置为单例
        2. prototype: 设置为多例
        3. request: 作用于web的请求范围
        4. session: 作用于web的会话范围
        5. global-session: 作用于集群环境的会话范围(全局会话范围)
    7. bean的生命周期

      1. 单例对象

        1. 出生: 当容器创建时对象出生
        2. 活着: 只要容器存在, 对象一直活着
        3. 死亡: 容器销毁, 对象死亡
        4. 总结: 单例对象的生命周期和容器相同
      2. 多例对象

        1. 出生: 当我们使用对象时spring为我们创建
        2. 活着: 对象只要在使用过程中就一直活着
        3. 死亡: 对象长时间不用, 且没有别的对象引用时, 由java的垃圾回收器回收
      3. 给出生和死亡绑定函数

        1. 方法
          1. 使用bean的init-method和destroy-method属性
          2. 让上面的两个属性分别指定类中的初始化和销毁的方法
  3. 依赖注入

    1. 概念

      1. Dependency Injection
      2. 依赖关系的维护就叫做依赖注入
        1. 个人理解: 创建对象时, 给成员变量注入数据就叫做依赖注入
    2. 能注入的数据有三类

      1. 基本类型和String
      2. 其他bean类型(在配置文件中或注解配置过的bean)
      3. 复杂类型(集合类型)
    3. 注入的方法有三种

      1. 使用构造函数提供

        1. 当你不提供默认构造函数的时候, 你就要在配置文件中指定构造方法
          1. <bean id="user" class="cn.lq.domain.User">
                <!--
                constructor-arg
                    name: (常用)传给构造方法中的哪一个参数
                    index: 使用索引找参数
                    type: 使用参数类型找参数
                    value: 要传的值
                    ref: 用于指定其他的bean类型数据. 指的是在spring的IoC核心容器中出现过的bean对象的
                -->
                <constructor-arg name="name" value="lq"></constructor-arg>
                <constructor-arg name="age" value="22"></constructor-arg>
                <constructor-arg name="birthday" ref="now"></constructor-arg>
            </bean>
            <bean id="now" class="java.util.Date"></bean>
            

             

        2. 如果成员变量里面有经常变化的数据, 就不太适合使用注入的方式啦
        3. 优点
          1. 在获取bean对象时, 注入数据时必须的操作, 否则对象无法创建成功
        4. 缺点
          1. 改变了bean对象的实例化方式, 使我们在创建对象时, 如果压根就用不到这些数据也必须提供, 就很蛋疼
      2. 使用set方法提供

        1. <bean id="user" class="cn.lq.domain.User">
               <!--
               property
                  name: 指定注入时调用的set方法名称
                  value: 注入的值(只能是基本数据类型和String)
                  ref: 传入的数据是其他的bean数据类型
               -->
              <property name="name" value="lq"></property>
              <property name="age" value="22"></property>
              <property name="birthday" ref="now"></property>
          </bean>
          <bean id="now" class="java.util.Date"></bean>
          

           

        2. 优点
          1. 创建对象时没有明确的限制, 可以直接使用默认的构造函数
        3. 缺点
          1. 如果某个成员变量必须有值, 则使用此方法将无法保证, 因为set方法可能没有执行.
        4. 注入集合数据
          1. <!--
                给list结构的集合注入数据使用标签
                    list array set
                给map结构的集合注入数据使用标签
                    map props
                    
                结构相同, 标签可以互换
            -->
            <bean id="demo01" class="cn.lq.domain.Demo01">
                <property name="a">
                    <list>
                        <value>1</value>
                        <value>2</value>
                        <value>3</value>
                    </list>
                </property>
                <property name="b">
                    <array>
                        <value>a</value>
                        <value>b</value>
                        <value>c</value>
                    </array>
                </property>
                <property name="c">
                    <set>
                        <value>e</value>
                        <value>f</value>
                        <value>g</value>
                    </set>
                </property>
                <property name="d">
                    <map>
                        <entry key="h" value="H"></entry>
                        <entry key="i" value="I"></entry>
                        <entry key="j" value="J"></entry>
                    </map>
                </property>
                <property name="e">
                    <props>
                        <prop key="q">Q</prop>
                        <prop key="w">W</prop>
                        <prop key="e">E</prop>
                    </props>
                </property>
            </bean>
            

             

      3. 使用注解提供
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值