欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring学习——IOC&DI学习笔记

程序员文章站 2022-06-22 16:06:16
...

Spring学习——IOC&DI学习笔记

SOLID:面向对象设计的五个基本原则

  • S:单一职责原则(Single-Responsibility Principle)
    • 定义:对于一个类而言,应该只有一个能够引起它变化的原因,也就是一个类只做一件事
    • 为什么要遵循SPR:
      • 减少类之间的耦合:当需求变化的时候,只修改一个类,从而隔离了变化
      • 提高类的复用性
  • O:开放-封闭原则(Open-Closed Principle)
    • Open for extension:模块的行为是开放的,可扩展的
    • Closed for modification:模块是不可修改的
    • 实现开放封闭原则的核心就是抽象编程,让类依赖于固定的抽象类,所以修改就是封闭的,通过面向对象的继承和多态,可以实现抽象类的继承,所以是可扩展的,开放的
  • L:里氏替换原则(Liskov-Substitution Principle)
    • 定义:子类必须能够替换成它们的父类,并且能够出现在父类能够出现的任何地方,但是父类不一定能替换子类
    • 如何遵循里氏替换原则:
      • 父类的方法都要在子类中实现或者重写,派生类只实现其抽象类中声明的方法,而不应当给出多余的方法定义或实现
      • 在程序中应当只出现父类对象,而不是直接使用子类对象,这样可以实现运行期绑定(多态绑定)
    • 违反了里氏替换原则,就必然会违反开放封闭原则
  • I:接口隔离原则(Interface-Segregation Principle)
    • 定义:避免使用胖接口,应该把胖接口中的方法分组,用多个接口替代它,每个接口服务于一个子模块,也就是说,一个模块不应该依赖于它不需要的接口方法,一个类对另一个类的依赖性应该是建立在最小的接口上的
    • 如果能够隔离接口,那么就不会发生接口污染
    • 接口污染:比如模块A依赖于方法A,模块B依赖于方法B,模块C依赖于方法C,而此时提供服务的抽象类同时包含ABC三个方法,那么模块ABC就同时依赖于同一个服务提供类,如果模块A的服务方法A需要发生改变,此时BC模块也会受到影响
    • 利用委托和多继承分离接口
  • D:依赖倒置原则(Dependency-Inversion Principle)
    • 定义:上层模块不应该依赖于下层模块,它们共同依赖于一个抽象;抽象不能依赖与具体,具体应该依赖于抽象
    • 面向接口编程,而不是面向实现编程
    • 依赖倒置的核心原则就是解耦,SpringIoC容器的核心就是解耦和加载依赖

工厂方法模式

  • 简单工厂模式:负责生产对象的一个类成为工厂类(定义了静态方法创建产品实例)
    • 解决的问题:把类的实例化操作和类的使用操作分离,让使用者不需要知道具体参数就可以实例化出所需要的产品类,避免在客户端中显示指定,实现了解耦
    • 具体实例:
      package org.Pattern;
      
      abstract class Product{
          public abstract void productShow();
      }
      
      class ProductA extends Product{
          @Override
          public void productShow(){
              System.out.println("Product A has been created");
          }
      }
      
      class ProductB extends Product{
          @Override
          public void productShow(){
              System.out.println("Product B has been created");
          }
      }
      
      class SimpleFactory{
          public static Product createProduct(String productName){
              switch (productName){
                  case "A":
                      return new ProductA();
                  case "B":
                      return new ProductB();
                  default:
                      return null;
              }
          }
      }
      
      public class SimpleFactoryPattern {
          public static void main(String[] args) {
              SimpleFactory simpleFactory=new SimpleFactory();
              try{
                  simpleFactory.createProduct("A").productShow();
              }catch (NullPointerException e){
                  System.out.println("There is no product A");
              }
      
              try{
                  simpleFactory.createProduct("B").productShow();
              }catch (NullPointerException e){
                  System.out.println("There is no product B");
              }
      
              try{
                  simpleFactory.createProduct("C").productShow();
              }catch (NullPointerException e){
                  System.out.println("There is no product C");
              }
          }
      }
      
      
    • 问题:
      • 工厂类集中了所有实例的创建,一旦不能工作整个系统都会瘫痪
      • 违背了开放关闭原则,一旦添加新产品就要修改工厂类的逻辑
      • 采用静态方法创建实例,无法被继承和重写
  • 工厂方法模式
    • 定义:在简单工厂模式的基础上,使用公共接口作为工厂父类,工厂子类负责创建具体类的对象

    • 解决了简单工厂模式的违背开放关闭原则的问题

    • 工厂方法模式的类图如下:

      Spring学习——IOC&DI学习笔记

    • 代码如下:

      package org.Pattern;
      
      abstract class FactoryPatternProduct{
          public abstract void show();
      }
      
      abstract interface FactoryPatternFactory{
          public abstract FactoryPatternProduct create();
      }
      
      class AProduct extends FactoryPatternProduct{
      
          @Override
          public void show() {
              System.out.println("Product A has been created.");
          }
      }
      
      class BProduct extends FactoryPatternProduct{
      
          @Override
          public void show() {
              System.out.println("Product B has been created");
          }
      }
      
      class AFactory implements FactoryPatternFactory{
      
          @Override
          public FactoryPatternProduct create() {
              return new AProduct();
          }
      }
      
      class BFactory implements FactoryPatternFactory{
      
          @Override
          public FactoryPatternProduct create() {
              return new BProduct();
          }
      }
      
      public class FactoryPattern {
          public static void main(String[] args) {
              AFactory aFactory=new AFactory();
              BFactory bFactory=new BFactory();
      
              FactoryPatternProduct aProduct=aFactory.create();
              FactoryPatternProduct bProduct=bFactory.create();
      
              aProduct.show();
              bProduct.show();
          }
      }
      
      
  • Spring底层如何使用工厂方法模式
    • FactoryBean可以被认为是抽象工厂类,FactoryBean是一个抽象接口,其定义了一个接口:getObject(),
      public interface FactoryBean<T> {
      T getObject() throws Exception;
      
      Class<?> getObjectType();
      
      boolean isSingleton();
      
    }
    ```
    • AbstractFactoryBean是一个抽象类(也可认为AbstractFactoryBean是Spring底层的抽象工厂类),其实现了FactoryBean接口,这个抽象类中的getObject方法返回了Bean实例
      public final T getObject() throws Exception {
          if (this.isSingleton()) {
              return this.initialized ? this.singletonInstance : this.getEarlySingletonInstance();
          } else {
              return this.createInstance();
          }
      }
      
    • 在创建Bean实例时,如果不是单例模式,则调用了createInstance()方法,这是一个抽象方法,在继承了AbstractFactoryBean的具体类中进行实现,比如MapFactoryBean就是一个具体工厂类

IoC容器

  • 容器:为某种特定组件的运行提供必要支持的一个软件环境

  • IoC思想:Inversion of Control 控制反转。传统的资源查找方式是组件向容器发起请求,容器查找资源并返回资源。IoC思想要求容器主动将资源推送给所管理的组件,组件只需要采用一种合适的方式来接收资源。这主要是一种解耦合的思想。

  • 依赖注入DI(Dependency Injection):依赖注入可以理解为IoC思想的一种实现方式,组件可以以一些预定义的方式接受来自容器的资源注入。

  • 举个例子来说明IoC和DI:

    • 现在我们有两份代码:myApp.jar和lib.jar,myApp.jar实现了项目的主要逻辑,lib.jar中定义了一个接口Person和该接口的实现类Student和Teacher,如下图:

    Spring学习——IOC&DI学习笔记

    • 上图中,myApp.jar中new了一个Student对象出来,也就是说myApp.jar把Student的实例注入到了对象p中。我们可以发现,上图中的依赖注入方式(也就是通过new一个对象来实现依赖注入)是强耦合的,如果有一天我们不再需要Student类了,那么我们还需要在myApp.jar中修改相应的代码,这显然不符合开放关闭原则,如果引入Spring中的IoC机制就可以很好的实现解耦:

    Spring学习——IOC&DI学习笔记

    这样,我们只需要通过修改xml文件中相应的配置就可以在myApp.jar中移除Student对象的注入,实现了解耦。

  • IoC容器:

    • Spring提供了两种IoC容器:BeanFactory和ApplicationContext,其中BeanFactory是ApplicationContext的子类
    • BeanFactory:
      • BeanFactory是Spring最底层的接口,包含了Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
      • BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”。注意,BeanFactory 只能管理单例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。
    • ApplicationContext是在BeanFactory的基础上实现的,在BeanFactory所支持的功能基础上扩展支持了其他功能,比如资源访问、事件传递、Bean的自动装配、各种不同应用层的Context实现
    • BeanFactory和ApplicationContext的区别:
      • BeanFactory采用延迟加载的形式来注入Bean,只有在getBean()的时候才对该Bean进行加载实例化;ApplicationContext在容器启动的时候一次性创建了所有的Bean(单例Bean),这样的好处就是在容器启动的时候就可以发现配置上的错误,但是占用内存空间较大。
      • BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
      • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
    • 使用BeanFactory获取Bean:
      BeanFactory factory = new XmlBeanFactory("XXX.xml");
      factory.getBean("xxxx");
      
    • ApplicationContext:
      • ClassPathXmlApplicationContext:从类路径下的XML配置文件加载上下文定义,把应用上下文当作类资源

      • FileSystemXmlApplicationContext:读取文件系统下的XML文件配置加载上下文定义

      • XmlWebApplicationContext:读取Web应用下的XML配置文件加载上下文定义

      • 举个例子:

        实现代码:

        • IUserDao接口:
          package org.IoCExample;
          
          public interface IUserDao{
              public void FindUserAge();
          }
          
        • IUserService接口:
          package org.IoCExample;
          
          public interface IUserService {
              public void login();
          }
          
        • UserDaoImplement:
          package org.IoCExample;
          
          public class UserDaoImplement implements IUserDao{
          
              @Override
              public void FindUserAge() {
                  System.out.println("UserDaoImplement的FindUserAge方法被调用");
              }
          }
          
        • UserServiecImplement:
          package org.IoCExample;
          
          public class UserServiceImplement implements IUserService{
              private IUserDao userDao;
          
              //当Spring框架创建UserDaoImplement对象之后,通过setUserDao给私有成员userDao赋值,注意,xml文件中property中的name要和此处相对应
              public void setUserDao(IUserDao userDao){
                  this.userDao=userDao;
              }
          
              public IUserDao getUserDao(){
                  return this.userDao;
              }
          
              @Override
              public void login() {
                  System.out.println("UserServiceImplement的login方法被调用");
                  userDao.FindUserAge();
              }
          }
          
          
        • IoCTest:
          package org.IoCExample;
          
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.ClassPathXmlApplicationContext;
          
          public class IoCTest {
              public static void main(String[] args) {
                  ApplicationContext applicationContext=new ClassPathXmlApplicationContext("org/IoCExample/applicationContext.xml");
          
                  /*
                  // 根据bean的class类型从IoC容器中获取bean对象
                  IUserService userService=applicationContext.getBean(UserServiceImplement.class);
                  userService.login();
                  */
          
                  /*
                  // 根据bean的id从IoC容器中获取bean对象
                  IUserService userService=(IUserService)applicationContext.getBean("UserService");
                  userService.login();
                  IUserService userService=applicationContext.getBean("UserService", IUserService.class);
                  userService.login();
                  */
          
              }
          }
          
        • 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"
              xmlns:aop="http://www.springframework.org/schema/aop"
              xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/aop
                  http://www.springframework.org/schema/aop/spring-aop.xsd">
          
              <bean id="userDao" class="org.IoCExample.UserDaoImplement"></bean>
              <bean id="UserService" class="org.IoCExample.UserServiceImplement">
                  <property name="userDao" ref="userDao"></property>
              </bean>
          
          </beans>
          
    • bean标签:
      • 作用:配置托管给spring的对象,默认情况下调用类的无参构造函数,如果没有无参构造函数则不能成功
      • 属性:
        • id:指定对象在容器中的标识,将其作为参数传入到getBean()方法中可以获取对应的对象
        • class:指定类全类名,默认情况下调用无参构造函数
        • scope:指定对象的作用范围,可选值如下:
          • singleton:单例对象,默认值
          • prototype:多例对象
          • request:将对象存入到web项目的request域中
          • session:将对象存入到web项目的session域中
          • global session:将对象存入到web项目集群的session域中,若不存在集群,则global session相当于session
        • init-method:指定类中的初始化方法名称,在对象创建成功之后执行
        • destroy-method:指定类中销毁方法名称,对prototype多例对象没有作用,因为多利对象的销毁时机不受容器控制

Spring中Bean的生命周期

Spring学习——IOC&DI学习笔记


参考文章

相关标签: Java spring java