conanan's blog conanan's blog
首页
关于
  • 分类
  • 标签
  • 归档
  • Java
  • Java Web
  • 工具

    • Maven
  • MySQL
  • Redis
  • Git
  • Vim
  • Nginx
  • Docker
GitHub

Evan Xu

前端界的小学生
首页
关于
  • 分类
  • 标签
  • 归档
  • Java
  • Java Web
  • 工具

    • Maven
  • MySQL
  • Redis
  • Git
  • Vim
  • Nginx
  • Docker
GitHub
  • Jakarta EE

  • Tomcat

  • Spring

    • 概述
    • IoC
      • 问题—程序间耦合
      • OCP—开闭原则
        • awkward 版
        • abstraction 版
        • factory 版
        • reflect 版
      • 接口 + 工厂实现 OCP 🔥
      • 如何理解 IoC、DI 、DIP 🔥
        • IoC
        • DI(Dependency Injection)
        • DIP(Dependency Inversion Principle)
    • 配置&容器&注册、注入组件
    • Bean的生命周期
    • AOP
    • 声明式事务
    • 工具类
  • SpringMVC

  • SpringBoot

  • SpringDataJPA

  • Test
  • Shiro
  • Thymeleaf
  • Java Web
  • Spring
conanan
2021-06-21

IoC

# IoC

# 问题—程序间耦合

  • 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。

  • Bean:计算机英语中,有可重用组件的含义

    JavaBean:用Java语言编写的可重用组件。JavaBean>实体类

  • 实际开发中,应做到:编译期不依赖,运行时才依赖(想到多态了吧,只是类似)

  • 解耦的思路(工厂模式):

    1. 读取配置文件的key获取要创建对象的全限定类名value;
    2. 使用反射创建对象,避免使用new关键字;
    3. 定义一个map容器,在静态代码块中创建所有对象并存放。获取对象时直接返回对应key的value,是单例的

# OCP—开闭原则

# awkward 版

/**
 * 青冈影
 */
public class Camille {

    public void q(){
        System.out.println("Camille Q");
    }

    public void w(){
        System.out.println("Camille W");
    }

    public void e(){
        System.out.println("Camille E");
    }

    public void r(){
        System.out.println("Camille R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 黛安娜
 */
public class Diana {

    public void q(){
        System.out.println("Diana Q");
    }

    public void w(){
        System.out.println("Diana W");
    }

    public void e(){
        System.out.println("Diana E");
    }

    public void r(){
        System.out.println("Diana R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {

    public static void main(String[] args) {
        String heroName = getPlayerInput();
        switch (heroName){
            case "Diana":
                Diana diana = new Diana();
                diana.r();
                break;
            case "Irilia":
                Irilia irilia = new Irilia();
                irilia.r();
                break;
            case "Camille":
                Camille camille = new Camille();
                camille.r();
                break;
            default:
                break;
        }
      	// 每次根据用户输入 new 新对象,并用该对象调用技能方法
    }

    private static String getPlayerInput(){
        System.out.print("Enter a hero's name: ");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# abstraction 版

public interface ISkill {

    void q();

    void w();

    void e();

    void r();
}
1
2
3
4
5
6
7
8
9
10
/**
 * 青冈影
 */
public class Camille implements ISkill {

    public void q(){
        System.out.println("Camille Q");
    }

    public void w(){
        System.out.println("Camille W");
    }

    public void e(){
        System.out.println("Camille E");
    }

    public void r(){
        System.out.println("Camille R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 黛安娜
 */
public class Diana implements ISkill {

    public void q(){
        System.out.println("Diana Q");
    }

    public void w(){
        System.out.println("Diana W");
    }

    public void e(){
        System.out.println("Diana E");
    }

    public void r(){
        System.out.println("Diana R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {

    public static void main(String[] args) {
        String heroName = getPlayerInput();
        ISkill iSkill;
        // 对象实例化
        switch (heroName){
            case "Diana":
                iSkill = new Diana();
                break;
            case "Irilia":
                iSkill = new Irilia();
                break;
            case "Camille":
                iSkill = new Camille();
                break;
            default:
                throw new RuntimeException();
        }
        // 面向对象:实例化对象,调用方法(完成业务逻辑)
        // 单纯的 Interface 只能统一方法的调用,不能统一对象的实例化。即多态好处:运行时确定要调用的方法
        iSkill.r();

    }

    private static String getPlayerInput(){
        System.out.print("Enter a hero's name: ");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# factory 版

public interface ISkill {

    void q();

    void w();

    void e();

    void r();
}
1
2
3
4
5
6
7
8
9
10
/**
 * 青冈影
 */
public class Camille implements ISkill {

    public void q(){
        System.out.println("Camille Q");
    }

    public void w(){
        System.out.println("Camille W");
    }

    public void e(){
        System.out.println("Camille E");
    }

    public void r(){
        System.out.println("Camille R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 黛安娜
 */
public class Diana implements ISkill {

    public void q(){
        System.out.println("Diana Q");
    }

    public void w(){
        System.out.println("Diana W");
    }

    public void e(){
        System.out.println("Diana E");
    }

    public void r(){
        System.out.println("Diana R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HeroFactory {

    public static ISkill getHero(String heroName){

        ISkill iSkill;
        // 对象实例化
        switch (heroName){
            case "Diana":
                iSkill = new Diana();
                break;
            case "Irilia":
                iSkill = new Irilia();
                break;
            case "Camille":
                iSkill = new Camille();
                break;
            default:
                throw new RuntimeException();
        }
        return iSkill;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {

    public static void main(String[] args) {
        String heroName = getPlayerInput();
        // HeroFactory 如何消除?
        // 此时是静态调用,在工厂类中实例化时还是 new 操作,并且强耦合了,新增时还需修改工厂类
        ISkill iSkill = HeroFactory.getHero(heroName);
        iSkill.r();

    }

    private static String getPlayerInput(){
        System.out.print("Enter a hero's name: ");
        Scanner scanner = new Scanner(System.in);
        return scanner.nextLine();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# reflect 版

public interface ISkill {

    void q();

    void w();

    void e();

    void r();
}
1
2
3
4
5
6
7
8
9
10
/**
 * 青冈影
 */
public class Camille implements ISkill {

    public void q(){
        System.out.println("Camille Q");
    }

    public void w(){
        System.out.println("Camille W");
    }

    public void e(){
        System.out.println("Camille E");
    }

    public void r(){
        System.out.println("Camille R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 黛安娜
 */
public class Diana implements ISkill {

    public void q(){
        System.out.println("Diana Q");
    }

    public void w(){
        System.out.println("Diana W");
    }

    public void e(){
        System.out.println("Diana E");
    }

    public void r(){
        System.out.println("Diana R");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HeroFactory {

    public static ISkill getHero(Class<? extends ISkill> clazz) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        // 对象实例化
        Constructor<? extends ISkill> constructor = clazz.getDeclaredConstructor();
        return constructor.newInstance();
    }
}
1
2
3
4
5
6
7
8
9
public class Main {

    public static void main(String[] args) throws 
      InstantiationException, 
  IllegalAccessException, 
  NoSuchMethodException, 
  InvocationTargetException {
        ISkill hero = HeroFactory.getHero(Irilia.class);
        hero.r();
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 接口 + 工厂实现 OCP 🔥

面向对象:实例化对象,调用方法(完成业务逻辑)

  • 单纯的 Interface 只能统一方法的调用,不能统一对象的实例化。即多态好处:运行时确定要调用的方法

  • 只有一段代码中没有 new 出现,才能保持代码的相对稳定,才能逐步实现 OCP。但是代码中总会存在不稳定,需要隔离这些不稳定因素(采用配置类、配置文件等方式),保证其他的代码是稳定的。即对象的实例化应该和其他分开!

    不稳定表面看是由于 new 实例化对象,其实是**用户的输入(变化)**导致需要频繁修改 new 实例化

    • 配置文件的集中性
    • 配置文件清晰(但是多了后也不清晰),没有业务逻辑
  • 使用简单工厂模式后,该工厂获取 Bean 的方法是静态的,虽然看起来没有 new 对象,但是其实是依赖了具体实现。且需要不同类型的 Bean 时总是需要新建一个工厂类,同一类型的不同 Bean 也需要修改工厂类

  • 此时可以使用抽象工厂模式,当然它也有类似的问题

  • 只有这个工厂特别大,可以获得所有 Bean 时,才认为它相对稳定。如 IoC 容器,特别是 Spring 的 ApplicationContext 等接口容器

  • 但是!工厂模式 + 反射并不是 IoC 和 DI

# 如何理解 IoC、DI 、DIP 🔥

# IoC

该概念的由来是 Java 社区的轻量级容器能够帮助开发者将来自不同项目的组件(或服务,理解即可)装配(组合)成一个内聚的项目(应用),而这种操作就是 IoC,它决定了这些容器进行组件装配的方式

IoC 的主要实现的作用就是将组件(Bean)注册到到容器中,并能给使用者提供自动注入功能

控制反转,把创建对象的权利交给容器(或工厂)。但其实需要配合接口(抽象)才能算将其使用更好。

解决了程序和对象之间的耦合,其具体实现就是 DI

# DI(Dependency Injection)

提示

martinfowler 博客 及 ThoughtWork 的翻译

DI(依赖注入) 的目的在于将组件的配置与使用分离开。如何在运行时(不是编译时)将组件(抽象的具体实现可能有多个,所以编译时无法确定哪个)动态连入程序中(这里是不是想起接口的概念了)

这里的组件可以指代服务(Service也行)

即调用类对某一接口实现类的依赖关系由第三方容器或协作类注入,移除调用类对某一接口实现类的依赖。

一般完成特定的业务逻辑可能会需要多个类之间进行协作。按传统的做法,每个对象负责管理与自己互相协作的对象(它所依赖的对象)的引用,这会导致高度耦合并难以测试的代码。利用依赖注入,每个对象可以直接获取所依赖的对象

最开始的 new 对象,强依赖;后来使用工厂,要对象,弱依赖;最后使用 IoC 和 DI,IoC 容器注入对象(被动)。⚙️

  • 属性注入(set)
  • 构造注入
  • 接口注入

# DIP(Dependency Inversion Principle)

依赖倒置原则

  • 高层模块(抽象)不该依赖低层模块(实现),两者都应该依赖抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

总之就是说要依赖抽象。比如依赖的 Service 不应该是具体类,而应该是接口(注入的只能是具体类,理解即可)

@Autowired
private UserService userService;
1
2

此时使用的是类型注入,依赖的是 UserService 接口,IoC 容器会在运行时将该类型的具体对象注入!

编辑
上次更新: 2021/06/21, 15:45:42
概述
配置&容器&注册、注入组件

← 概述 配置&容器&注册、注入组件→

最近更新
01
线程生命周期
07-06
02
线程安全理论
06-24
03
并发简史
06-24
更多文章>
Theme by Vdoing | Copyright © 2019-2021 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×