You are on page 1of 64

一、设计模式

1、六大原则
1. 【单一原则】(Single Responsibility Principle):一个类或者
一个方法只负责一项职责。

2. 【里氏替换原则】(LSP liskov substitution principle):子类


可以扩展父类的功能,但不能改变原有父类的功能。

3. 【依赖倒置原则】(dependence inversion principle):面向

om
接口编程,(通过接口作为参数实现应用场景)。

(1)抽象就是接口或者抽象类,细节就是实现类;
c
(2)上层模块不应该依赖下层模块,两者应依赖其抽象;
s.

(3)抽象不应该依赖细节,细节应该依赖抽象。
s
la

注:通俗点就是说变量或者传参数,尽量使用抽象类,或者接
口。
lc

4. 【接口隔离原则】(interface segregation principle):建立


yd

单一接口;(扩展为类也是一种接口,一切皆接口)

定义:a.客户端不应该依赖它不需要的接口;b.类之间依赖关系
应该建立在最小的接口上;

简单理解:复杂的接口,根据业务拆分成多个简单接口;(对
于有些业务的拆分多看看适配器的应用)

注:接口的设计粒度越小,系统越灵活,但是灵活的同时结构
复杂性提高,开发难度也会变大,维护性降低;。

5. 【迪米特原则】(law of demeter LOD):最少知道原则,尽


量降低类与类之间的耦合,一个对象应该对其他对象有最少的
了解。
6. 【开闭原则】(open closed principle):对扩展开放,对修改
闭合。

2、设计模式的分类

c om
s.
 
s

1. 单例模式:某个类只能有一个实例,提供一个全局的访问点。
la

2. 简单工厂:一个工厂类根据传入的参量决定创建出那一种产品
lc

类的实例。
yd

3. 工厂方法:定义一个创建对象的接口,让子类决定实例化那个
类。

4. 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体
类。

5. 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构
造。

6. 原型模式:通过复制现有的实例来创建新的实例。

java.lang.Object#clone()
7. 适配器模式:将一个类的方法接口转换成客户希望的另外一个
接口。

FileInputStream fileInput = new


FileInputStream(file);
InputStreamReader inputStreamReader = new
InputStreamReader(fileInput);

8. 组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结
构。

9. 装饰模式:动态的给对象添加新的功能。

BufferedReader bufferedReader=new

om
BufferedReader(inputStreamReader);

10. 代理模式:为其他对象提供一个代理以便控制这个对象的访
c
问。
s.
11. 亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的
s

对象。
la

12. 外观模式:对外提供一个统一的方法,来访问子系统中的一群
lc

接口。
yd

13. 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独
立的变化。将抽象与实现解耦。

jdbc中就是使用桥接模式的

14. 模板模式:定义一个算法结构,而将一些步骤延迟到子类实
现。

java.util.Collections#sort()  
java.util.AbstractList#indexOf()

15. 解释器模式:给定一个语言,定义它的文法的一种表示,并定
义一个解释器。
java.util.Pattern,java.text.Format

16. 策略模式:定义一系列算法,把它们封装起来,并且使它们可
以相互替换。

java.util.Comparator

17. 状态模式:允许一个对象在其对象内部状态改变时改变它的行
为。

18. 观察者模式:对象间的一对多的依赖关系。

javax.servlet.http.HttpSessionAttributeListener

19. 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。

20. 中介者模式:用一个中介对象来封装一系列的对象交互。
om
21. 命令模式:将命令请求封装为一个对象,使得可以用不同的请
c
s.
求来进行参数化。
s

22. 访问者模式:在不改变数据结构的前提下,增加作用于一组对
la

象元素的新功能。
lc

23. 责任链模式:将请求的发送者和接收者解耦,使得多个对象都
有处理这个请求的机会。
yd

javax.servlet.Filter#doFilter()

24. 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴
露该对象的内部结构。

java.util.Iterator

3、UML
UML(Unified Modeling Language)是一种统一建模语言,为面
向对象开发系统的产品进行说明、可视化、和编制文档的一种标准
语言。下面将对UML的九种图+包图的基本概念进行介绍以及各个
图的使用场景。

以往的开发中,特别迷信UML,当然开发过程中一定是设计先行,
但是现在的开发讲求灵活性,能用更简单的表示方法进行设计更
好,设计的目的是帮我我们更好的理解业务,更好的进行开发。

常见的工具有:powerDesigner Rational Rose proccesson

UML分静态图和动态图两种,其中常用的静态图5种,动态图4种。

常用的静态图:用例图、类图、包图、对象图、部署图
om
常用的动态图:顺序图,通信图(UML1.x 时称为协作图),状态
c
机图,活动图
s.

 
s
la

(1)类图:
lc
yd

类图(Class diagram)是显示了模型的静态结构,特别是模型中存在
的类、类的内部结构以及它们与其他类的关系等。类图不显示暂时
性的信息。类图是面向对象建模的主要组成部分。它既用于应用程
序的系统分类的一般概念建模,也用于详细建模,将模型转换成编
程代码。类图也可用于数据建模。
c om
s.
 
s

在UML类图中,常见的有以下几种关系: 泛化(Generalization),
la

实现(Realization),关联(Association),聚合
lc

(Aggregation),组合(Composition),依赖(Dependency)
yd

1、泛化(Generalization)

【泛化关系】:是一种继承关系,表示一般与特殊的关系,它指定
了子类如何特化父类的所有特征和行为。例如:老虎是动物的一
种,即有老虎的特性也有动物的共性。

【箭头指向】:带三角箭头的实线,箭头指向父类
2、实现(Realization)

om
【实现关系】:是一种类与接口的关系,表示类是接口所有特征和
行为的实现

【箭头指向】:带三角箭头的虚线,箭头指向接口
c
s s.
la
lc
yd

3、关联(Association)

【关联关系】:是一种拥有的关系,它使一个类知道另一个类的属
性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可
以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关
联有一个箭头。

【代码体现】:成员变量
【箭头及指向】:带普通箭头的实心线,指向被拥有者

上图中,老师与学生是双向关联,老师有多名学生,学生也可能有
多名老师。但学生与某课程间的关系为单向关联,一名学生可能要
上多门课程,课程是个抽象的东西它不拥有学生。

下图为自身关联:

omc
s s.
la

4、聚合(Aggregation)
lc

【聚合关系】:是整体与部分的关系,且部分可以离开整体而单独
yd

存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存
在。

聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法
上无法区分,必须考察具体的逻辑关系。

【代码体现】:成员变量

【箭头及指向】:带空心菱形的实心线,菱形指向整体
5、组合(Composition)

om
【组合关系】:是整体与部分的关系,但部分不能离开整体而单独
存在。如公司和部门是整体和部分的关系,没有公司就不存在部
门。
c
s.
组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求
普通的聚合关系中代表整体的对象负责代表部分的对象的生命周
s
la

期。
lc

【代码体现】:成员变量
yd

【箭头及指向】:带实心菱形的实线,菱形指向整体
6. 依赖(Dependency)

【依赖关系】:是一种使用的关系,即一个类的实现需要另一个类
的协助,所以要尽量不使用双向的互相依赖.

【代码表现】:局部变量、方法的参数或者对静态方法的调用

【箭头及指向】:带箭头的虚线,指向被使用者

omc
s.
 
s

 
la
lc

(2)时序图:
yd

时序图(Sequence Diagram),又名序列图、循序图,是一种
UML交互图。它通过描述对象之间发送消息的时间顺序显示多个对
象之间的动态协作。它可以表示用例的行为顺序,当执行一个用例
行为时,其中的每条消息对应一个类操作或状态机中引起转换的触
发事件。
(3)用例图:

om
是用户与系统交互的最简表示形式,展现了用户和与他相关的用例
之间的关系。通过用例图,人们可以获知系统不同种类的用户和用
c
例。用例图也经常和其他图表配合使用。
ss.
la
lc
yd

 
 

二、工厂设计模式
工厂模式分为简单工厂模式,工厂方法模式和 抽象工厂模式 ,它们
都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象
的实例化部分抽取了出来,目的是降低系统中代码耦合度,并且增
强了系统的扩展性。

还有一个很重要的原因,就是对象的创建过程比较复杂,使用工厂
模式进行封装,同时可以屏蔽掉对象的复杂的创建过程。

 
om
(1)简单工厂设计模式
c
s s.

简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,
la

将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂
lc

类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,
而且产品较多时,工厂方法代码将会非常复杂。
yd

简单工厂模式包含如下角色:

Factory:工厂角色

工厂角色负责实现创建所有实例的内部逻辑

Product:抽象产品角色

抽象产品角色是所创建的所有对象的父类,负责描述所有实例
所共有的公共接口

ConcreteProduct:具体产品角色

具体产品角色是创建目标,所有创建的对象都充当这个角色的
某个具体类的实例。
类图:

 
c om
s.
1、定义一个接口
s

/**
la

* @author itnanls(微信)
lc

* 我们的服务: 一路陪跑,顺利就业
*/
yd

public interface HuaweiPhone {


   /**
    * 打电话的方法
    */
   void call();
}

创建一个奔驰类
/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
*/
public class Mate50 implements HuaweiPhone {
   public void call() {
       System.out.println("use a mate50 to make a
call.");
  }
}

/**

om
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
*/
c
public class P50 implements HuaweiPhone {
s.
   public void call() {
s

       System.out.println("use a p50 to make a


la

call.");
  }
lc

}
yd

创建一个工厂

/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
*/
public class PhoneFactory {
   public static HuaweiPhone getCar(String type){
       if("p50".equalsIgnoreCase(type)){
           //其中可能有很复杂的操作
           return new P50();
      }else if("mate50".equalsIgnoreCase(type)){
           //其中可能有很复杂的操作
           return new Mate50();
      }else {
           throw new RuntimeException("There are no
cell phones of this brand!");
      }
  }
}

测试

om
/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
c
*/
s.
public class Client {
   public static void main(String[] args) {
s

       HuaweiPhone p50 =
la

PhoneFactory.getCar("p50");
lc

       p50.call();
       HuaweiPhone mate50 =
yd

PhoneFactory.getCar("mate50");
       mate50.call();
  }
}

(2)工厂方法模式
我们说过java开发中要遵循开闭原则,如果将来有一天我想增加一
个新的车,那么必须修改CarFactory,就不太灵活。解决方案是使
用工厂方法模式。
工厂方法模式包含如下角色:

Product:抽象产品
ConcreteProduct:具体产品
Factory:抽象工厂
ConcreteFactory:具体工厂

c om
s s.
la
lc

 
yd

我们为每一个车都构建成一个工厂:

先抽象一个工厂接口
/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
*/
public interface Factory {
   /**
    * 统一的创建方法
    * @return
    */
   HuaweiPhone create();
}

om
然后针对每一个产品构建一个工厂方法

/**
c
* @author itnanls(微信)
s.
* 我们的服务: 一路陪跑,顺利就业
s

*/
la

public class Mate50Factory implements Factory {


   public HuaweiPhone create() {
lc

       //中间省略一万行代码
yd

       return new Mate50();


  }
}

 
/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
*/
public class P50Factory implements Factory {
   public HuaweiPhone create() {
       //中间省略一万行代码
       return new P50();
  }
}

应用场景

public class Client {


om
   public static void main(String[] args) {
c
       Factory benzFactory = new BenzFactory();
s.
       Car benz = benzFactory.create();
s

       benz.run();
la

       Factory bmwFactory = new BmwFactory();


       Car bmw = bmwFactory.create();
lc

       bmw.run();
yd

  }
}

好处

此模式中,通过定义一个抽象的核心工厂类,并定义创建产品对象
的接口,创建具体产品实例的工作延迟到其工厂子类去完成。这样
做的好处是核心类只关注工厂类的接口定义,而具体的产品实例交
给具体的工厂子类去创建。当系统需要新增一个产品是,无需修改
现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,
使系统的扩展性变得很好,符合面向对象编程的开闭原则。
缺点

工厂方法模式虽然扩展性好,但是增加了编码难度,大量增加了类
的数量,所以怎么选择还是看实际的需求。

(3)抽象工厂设计模式
c om
s.
抽象工厂模式包含如下角色:
s

AbstractFactory:抽象工厂
la

ConcreteFactory:具体工厂
lc

AbstractProduct:抽象产品
Product:具体产品
yd

抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业
务、分类时,通过抽象工厂模式产生需要的对象是一种非常好的解
决方式。

类图:
 

1、定义产品抽象类
c om
s.
电脑产品线
s
la

/**
* @author itnanls(微信)
lc

* 我们的服务: 一路陪跑,顺利就业
yd

*/
public abstract class AbstractComputerProduct {

   // 生产电脑产品的共享方法
   public void sharedMethod(){
       System.out.println("这是生产电脑的产品线。");
  }

   abstract void networking();


}

手机产品线
/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
*/
public abstract class AbstractPhoneProduct {

   // 添加loge标签
   public void labelling(){
       System.out.println("这是小米的产品。");
  }

   abstract void networking();


}

om
 

2、定于具体产品
c
s.
我们定义两个华为的产品
s

public class MateBook16 extends


la

AbstractComputerProduct {
lc

   @Override
   public void networking() {
yd

       System.out.println("Use a mateBook16 to
connect to the Internet");
  }
}

 
public class Mate60 extends AbstractPhoneProduct {
   @Override
   public void networking() {
       System.out.println("Use a Mate60 to connect
to the Internet");
  }
}

定义两个小米的产品

public class Mi12 extends AbstractPhoneProduct {


   @Override

om
   public void networking() {
       System.out.println("Use a Mi12 to connect to
the Internet");
c
  }
s.
}
s

 
la
lc

public class MiBookPro15 extends


AbstractComputerProduct {
yd

   @Override
   public void networking() {
       System.out.println("Use a MiBookPro15 to
connect to the Internet");
  }
}

3、定义抽象工厂
public abstract class AbstractFactory {
   /**
    * 生产手机的方法
    * @return
    */
   abstract AbstractPhoneProduct createPhone();

   /**
    * 生产电脑的方法
    * @return
    */
   abstract AbstractComputerProduct
createComputer();
}

 
c om
4、定义举起的产品工厂
s.

小米产品线的工厂
s
la

public class MiFactory extends AbstractFactory {


lc

   @Override
yd

   AbstractPhoneProduct createPhone() {
       return new Mi12();
  }

   @Override
   AbstractComputerProduct createComputer() {
       return new MiBookPro15();
  }
}

华为产品线的工厂
public class HuaweiFactory extends AbstractFactory {

   @Override
   AbstractPhoneProduct createPhone() {
       return new Mate60();
  }

   @Override
   AbstractComputerProduct createComputer() {
       return new MateBook16();
  }
}

测试

public class Client {


om
   public static void main(String[] args) {
c
       AbstractFactory factory = new MiFactory();
s.
       AbstractPhoneProduct phone =
s

factory.createPhone();
la

       phone.networking();
  }
lc

}
yd

这一次我们的设计相对来说比较复杂,增加了产品一族的概念。如
果我们想在某一个产品线下新增一个产品,和之前的工厂方法设计
模式一样,只需要增加相应的产品和工厂类即可,必会影响其他任
何产品。但是我们因为我们的设计会导致产品无法独立于产品族单
独存在。如果我们想增加一条的的产品线,比如联想,也不需要对
其他代码进行修改。

 
三、创建者设计模式
分离对象子组件的单独构造(由Buider来负责)和装配(由
Director负责)。从而可以构造出复杂的对象。这个模式适用于:
某个对象的构建过程复杂的情况下使用。

由于实现构造和装配的解耦。不同的构造器,相同的装配,也可以
做出不同的对象;相同的构造器,不同的装配顺序也可以做出不同
的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的
复用。

最典型的就是我们的StringBuilder和StringBuffer。

om
 

建造者模式包含如下角色:
c
s.
Builder:抽象建造者
ConcreteBuilder:具体建造者
s

Director:指挥者
la

Product:产品角色
lc
yd

1、模拟一个复杂对象
public class Computer {
   // 鼠标
   public String mouse;
   // 键盘
   public String keyboard;
   // cpu
   public String cpu;
   // 内存
   public String memoryBank;
   // 主板
   public String mainBoard;

...省略getter和setter
}

  c
2、我们可以在Computer类中添加一个静态内部类,专门负责构建
om
s.
工作
s
la

public static class Builder{


lc

   private final Computer computer = new


Computer();
yd

   public Builder buildMouse(String type){


       computer.setMouse("安装了一个"+ type +"鼠标");
       return this;
  }

   public Builder buildKeyboard(String type){


       computer.setKeyboard("安装了一个"+ type +"键
盘");
       return this;
  }

   public Builder buildCpu(String type){


       computer.setCpu("安装了一个"+ type +"Cpu");
       return this;
  }

   public Builder buildMemoryBank(String type){


       computer.setMemoryBank("安装了一个"+ type
+"MemoryBank");
       return this;
  }

   public Builder buildMainBoard(String type){


       computer.setMainBoard("安装了一个"+ type
+"mainBoard");

om
       return this;
  } c
   public Computer build(){
s.
       return computer;
s

  }
la

}
lc

 
yd

我们在构造电脑的时候,就可以使用这种方案了。

public static void main(String[] args) {


   Computer computer = new Builder()
      .buildMainBoard("华硕")
      .buildCpu("英特尔")
      .buildMemoryBank("三星")
      .buildKeyboard("雷柏")
      .buildMouse("罗技")
      .build();
   System.out.println(computer);
}
 

思考这种构造方法和普通的方法有什么区别呢?

1、当我们要构造的对象相对来说比较复杂,成员变量比较多,使
用构造器不容易进行灵活配置。

2、构造的过程可以进行灵活的编排,可以随时舍掉或新增过程,
有些时候可能出现对顺序性的要求,构建的顺序不同,结果不同。

3、单个的构建过程其实也比较复杂,比如,主板的构建,本身也
是一个复杂过程,cpu的选型也是复杂过程,使用setter方法不容易
表达复杂性。

创建者模式是一个对对象的构建过程“精细化”的构建过程,每个部

om
分的构建可能是变化的,但是对象的组织过程是固定的,通过这种
统一的创建方式,无疑增加了我们设计上的灵活性,当我们在构建
c
复杂对象的时候,我们如果发现每个部分可能都是变化的,并且是
s.
多个不同的构建步骤的时候,我们可以考虑使用创建者模式。
s

相比工厂和抽象工厂模式区别还是很大的,创建者适合这类复杂对
la

象的创建,对于抽象工厂可能就无法完成这样的组装工作,而且创
lc

建者模式是把复杂对象的内部创建方法进行调用,组织协调了对象
的各个部分前后顺序的控制。简单的描述创建者就是这样的情况。
yd

四、代理设计模式
代理模式分为静态代理和动态代理。代理的核心功能是方法增
强。
1、静态代理
静态代理角色分析

抽象角色 : 一般使用接口或者抽象类来实现
真实角色 : 被代理的角色
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属
的操作
客户 : 使用代理角色来进行一些操作 .

代码实现

写一个接口

public interface Singer {


   /**
    * 歌星都能唱歌
c om
s.
    */
   void sing();
s

}
la

定义男歌手
lc
yd
public class MaleSinger implements Singer{

   private String name;

   public MaleSinger(String name) {


       this.name = name;
  }

   @Override
   public void sing() {
       System.out.println(this.name + "开始唱歌
了!");
  }
}

定义经纪人
c om
/**
s.
* @author IT楠老师
s

* @date 2020/5/28
la

*/
public class Agent implements Singer {
lc
yd

   private Singer singer;

   public Agent(Singer singer) {


       this.singer = singer;
  }

   @Override
   public void sing() {
       System.out.println("节目组找过来!需要演出,谈好
演出费用。。。。。");
       singer.sing();
       System.out.println("结算费用,下一次合作预
约。。。。。。");
  }
}

Client . java 即客户

/**
* @author IT楠老师
* @date 2020/5/28
*/
public class Client {

   public static void main(String[] args) {


       Singer singer = new MaleSinger("鹿晗");
       Singer agent = new Agent(singer);
       agent.sing();

om
  }
} c
分析:在这个过程中,你直接接触的就是鹿晗的经济人,经纪人在
s.
鹿晗演出的前后跑前跑后发挥了巨大的作用。
s

 
la

除了实现共同的接口,我们还能使用继承类的方式
lc
yd

public class Agent extends MaleSinger {

   private MaleSinger maleSinger;

   public void setMaleSinger(MaleSinger maleSinger)


{
       this.maleSinger = maleSinger;
  }

   @Override
   public void sing() {
       System.out.println("开始唱歌了-----------");
       maleSinger.sing();
       System.out.println("结束唱歌了-----------");
  }
}

public static void main(String[] args) {


   MaleSinger maleSinger = new MaleSinger("鹿晗");
   Agent agent = new Agent();
   agent.setMaleSinger(maleSinger);
   agent.sing();
}

优点

鹿晗还是鹿晗,没有必要为了一下前置后置工作改变鹿晗这个

公共的统一问题交给代理处理
公共业务进行扩展或变更时,可以更加方便
c om
s.
这不就是更加符合开闭原则,单一原则吗?
s

缺点 :
la
lc

每个类都写个代理,麻烦死了。
yd

2、动态代理
动态代理的角色和静态代理的一样

动态代理的代理类是动态生成的 ,静态代理的代理类是我们写

动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的


动态代理

基于接口的动态代理----JDK动态代理
基于类的动态代理--cglib

动态代理就是当有大量的类需要执行一些共同代码时,我们自己写
太麻烦,那能不能直接使用java代码,自动生成一个类帮助我们批
量的增强某些方法。

(1)JDK原生的动态代理
JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看

【InvocationHandler:调用处理程序】 c om
Object invoke(Object proxy, 方法 method, Object[]
s.
args);
s

//参数
la

//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法
lc

对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代
yd

理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或
null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器
类的实例中,例如java.lang.Integer或java.lang.Boolean 。

【Proxy : 代理】

Proxy.newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)

代码实现
抽象角色和真实角色和之前的一样!

还是歌星和男歌星

Agent. java 即经纪人

/**
* @author IT楠老师
* @date 2020/5/21
* 经纪人
*/
public class Agent implements InvocationHandler {

   private Singer singer;

   /**
    * 设置代理的经济人
    * @param singer
c om
s.
    */
   public void setSinger(Singer singer) {
s

       this.singer = singer;
la

  }
lc

   @Override
yd

   public Object invoke(Object proxy, Method


method, Object[] args) throws Throwable {
       System.out.println("---------------经纪人把把
关--------------");
       Object returnObj = method.invoke(singer,
args);
       System.out.println("---------------唱完了收收
钱------------------------");
       return returnObj;
  }

   /**
    * 获取一个代理对象
    * @return
    */
   public Object getProxy(){
       return
Proxy.newProxyInstance(this.getClass().getClassLoade
r(),
               new Class[]{Singer.class},this);
  }
}

Client . java

/**

om
* @author IT楠老师
* @date 2020/5/21
*/
c
public class Client {
s.
   public static void main(String[] args) {
s
la

       MaleSinger luhan = new MaleSinger();


lc

       Agent agent = new Agent();


yd

       agent.setSinger(luhan);
       Singer singer = (Singer)agent.getProxy();

       singer.sing();
  }
}

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代


理多个类,代理的是接口!

//该设置用于输出jdk动态代理产生的类
System.getProperties().put("sun.misc.ProxyGenerator.
saveGeneratedFiles", "true");  
 

(2)基于cglib
<dependencies>
   <dependency>
       <groupId>cglib</groupId>
       <artifactId>cglib</artifactId>
       <version>3.3.0</version>
   </dependency>
</dependencies>

<build>

om
   <plugins>
       <plugin>
         
c
 <groupId>org.apache.maven.plugins</groupId>
s.
           <artifactId>maven-compiler-
plugin</artifactId>
s

           <version>3.8.1</version>
la

           <configuration>
lc

               <source>1.8</source>
               <target>1.8</target>
yd

               <encoding>utf-8</encoding>
           </configuration>
       </plugin>
   </plugins>
</build>

public static void main(String[] args) {


   Enhancer enhancer=new Enhancer();
   enhancer.setSuperclass(MaleSinger.class);
   enhancer.setCallback(new MethodInterceptor() {
       public Object intercept(Object o, Method
method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
           System.out.println("---------");
           Object invoke =
methodProxy.invokeSuper(o,objects);
           System.out.println("++++++++++");
           return invoke;
      }
  });
   MaleSinger maleSinger =
(MaleSinger)enhancer.create();
   maleSinger.sing();
}

om
public static void main(String[] args) {
   Enhancer enhancer=new Enhancer();
c
   enhancer.setSuperclass(MaleSinger.class);
s.
   enhancer.setCallback(new MethodInterceptor() {
s

       public Object intercept(Object o, Method


la

method, Object[] objects, MethodProxy methodProxy)


throws Throwable {
lc

           System.out.println("---------");
yd

           Object invoke =
methodProxy.invokeSuper(o,objects);
           System.out.println("++++++++++");
           return invoke;
      }
  });
   MaleSinger maleSinger =
(MaleSinger)enhancer.create(new Class[]
{String.class},new Object[]{"小李"});
   maleSinger.sing();
}

 
/**
    * All generated proxied methods call this
method instead of the original method.
    * The original method may either be invoked by
normal reflection using the Method object,
    * or by using the MethodProxy (faster).
    * @param obj "this", the enhanced object
    * @param method intercepted Method
    * @param args argument array; primitive types
are wrapped
    * @param proxy used to invoke super (non-
intercepted method); may be called
    * as many times as needed
    * @throws Throwable any exception may be

om
thrown; if so, super method will not be invoked
    * @return any value compatible with the
signature of the proxied method. Method returning
c
void will ignore this value.
s.
    * @see MethodProxy
s

    */    
la

   public Object intercept(Object obj,


java.lang.reflect.Method method, Object[] args,
lc

                              MethodProxy proxy)
yd

throws Throwable;

//该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATI
ON_PROPERTY, "D:\\class");

1. Java动态代理只能够对接口进行代理,不能对普通的类进行代理
(因为所有生成的代理类的父类为Proxy,Java类继承机制不允
许多重继承);
2. CGLIB能够代理普通类;
3. Java动态代理使用Java原生的反射API进行操作,在生成类上比
较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执
行过程中比较高效。

3、ASM畅聊动态代理的本质
1、介绍

ASM是一个java字节码操纵和分析框架,它可以直接以二进制的形
式修改class或动态生成class。官网

2、使用 c
接下来我们使用asm框架实现一个和JDK动态代理同样的功能。
om
s.
3、引入maven依赖
s
la

<dependency>
  <groupId>org.ow2.asm</groupId>
lc

  <artifactId>asm-util</artifactId>
yd

  <version>8.0.1</version>
</dependency>
<dependency>
  <groupId>org.ow2.asm</groupId>
  <artifactId>asm</artifactId>
  <version>8.0.1</version>
</dependency>

4、定义目标对象接口
/**
* 可以唱歌的
*/
public interface Singable {
 /**
  * 唱歌
  */
 void sing();
}

5、定义目标对象

/**
* 歌手

om
*/
public class Singer implements Singable {
 @Override
c
 public void sing() {
s.
   System.out.println("I am singing...");
s

}
la

}
lc

6、定义自己的类加载器
yd

/**
* 自定义类加载器
*/
public class MyClassLoader extends ClassLoader {
 public MyClassLoader() {
 
 super(Thread.currentThread().getContextClassLoader(
));
}

 /**
  * 将字节数组转化为Class对象
  *
  * @param name 类全名
  * @param data class数组
  * @return
  */
 public Class<?> defineClassForName(String name,
byte[] data) {
   return this.defineClass(name, data, 0,
data.length);
}

7、创建代理

om
/**
* @author itnanls(微信)
* 我们的服务: 一路陪跑,顺利就业
c
*/
s.
public class SingerAgentDump implements Opcodes {
s
la

   // 生成一个class的字节数组
   public static byte[] dump() throws Exception {
lc
yd

       ClassWriter classWriter = new


ClassWriter(0);
       FieldVisitor fieldVisitor;
       RecordComponentVisitor
recordComponentVisitor;
       MethodVisitor methodVisitor;
       AnnotationVisitor annotationVisitor0;

       // 定义class版本1.8,访问权限,类名,继承类,实现接
口等信息
       classWriter.visit(V1_8, ACC_PUBLIC |
ACC_SUPER, "com/ydlclass/SingerAgent", null,
"java/lang/Object", new String[]
{"com/ydlclass/Singable"});
       classWriter.visitSource("SingerAgent.java",
null);

      {
       // 定义私有属性
           fieldVisitor =
classWriter.visitField(ACC_PRIVATE, "delegate",
"Lcom/ydlclass/Singable;", null, null);
           fieldVisitor.visitEnd();
      }
      {
       // 定义构造器
           methodVisitor =

om
classWriter.visitMethod(ACC_PUBLIC, "<init>", "
(Lcom/ydlclass/Singable;)V", null, null);
           methodVisitor.visitParameter("delegate",
c
0);
s.
           methodVisitor.visitCode();
s

           Label label0 = new Label();


la

           methodVisitor.visitLabel(label0);
           methodVisitor.visitLineNumber(10,
lc

label0);
yd

           methodVisitor.visitVarInsn(ALOAD, 0);
         
 methodVisitor.visitMethodInsn(INVOKESPECIAL,
"java/lang/Object", "<init>", "()V", false);
           Label label1 = new Label();
           methodVisitor.visitLabel(label1);
           methodVisitor.visitLineNumber(11,
label1);
           methodVisitor.visitVarInsn(ALOAD, 0);
           methodVisitor.visitVarInsn(ALOAD, 1);
           methodVisitor.visitFieldInsn(PUTFIELD,
"com/ydlclass/SingerAgent", "delegate",
"Lcom/ydlclass/Singable;");
           Label label2 = new Label();
           methodVisitor.visitLabel(label2);
           methodVisitor.visitLineNumber(12,
label2);
           methodVisitor.visitInsn(RETURN);
           Label label3 = new Label();
           methodVisitor.visitLabel(label3);
           methodVisitor.visitLocalVariable("this",
"Lcom/ydlclass/SingerAgent;", null, label0, label3,
0);
         
 methodVisitor.visitLocalVariable("delegate",
"Lcom/ydlclass/Singable;", null, label0, label3, 1);
           methodVisitor.visitMaxs(2, 2);
           methodVisitor.visitEnd();

om
      }
      {
// 定义方法sing
c
           methodVisitor =
s.
classWriter.visitMethod(ACC_PUBLIC, "sing", "()V",
s

null, null);
la

           methodVisitor.visitCode();
           Label label0 = new Label();
lc

           methodVisitor.visitLabel(label0);
yd

           methodVisitor.visitLineNumber(16,
label0);
           methodVisitor.visitFieldInsn(GETSTATIC,
"java/lang/System", "out", "Ljava/io/PrintStream;");
           methodVisitor.visitLdcInsn("before
sing...");
         
 methodVisitor.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream", "println", "
(Ljava/lang/String;)V", false);
           Label label1 = new Label();
           methodVisitor.visitLabel(label1);
           methodVisitor.visitLineNumber(17,
label1);
           methodVisitor.visitVarInsn(ALOAD, 0);
           methodVisitor.visitFieldInsn(GETFIELD,
"com/ydlclass/SingerAgent", "delegate",
"Lcom/ydlclass/Singable;");
         
 methodVisitor.visitMethodInsn(INVOKEINTERFACE,
"com/ydlclass/Singable", "sing", "()V", true);
           Label label2 = new Label();
           methodVisitor.visitLabel(label2);
           methodVisitor.visitLineNumber(18,
label2);
           methodVisitor.visitFieldInsn(GETSTATIC,
"java/lang/System", "out", "Ljava/io/PrintStream;");
           methodVisitor.visitLdcInsn("after

om
sing...");
         
 methodVisitor.visitMethodInsn(INVOKEVIRTUAL,
c
"java/io/PrintStream", "println", "
s.
(Ljava/lang/String;)V", false);
s

           Label label3 = new Label();


la

           methodVisitor.visitLabel(label3);
           methodVisitor.visitLineNumber(19,
lc

label3);
yd

           methodVisitor.visitInsn(RETURN);
           Label label4 = new Label();
           methodVisitor.visitLabel(label4);
           methodVisitor.visitLocalVariable("this",
"Lcom/ydlclass/SingerAgent;", null, label0, label4,
0);
           methodVisitor.visitMaxs(2, 1);
           methodVisitor.visitEnd();
      }
       classWriter.visitEnd();

       return classWriter.toByteArray();
  }
   // 将字节数组加载为class
   public static Singable newProxyInstance(Singable
delegate) throws Exception {
       String className =
"com.ydlclass.SingerAgent";
       byte[] classData = dump();
       Class<?> aClass = new
MyClassLoader().defineClassForName(className,
classData);
       return (Singable)
aClass.getDeclaredConstructor(Singable.class).newIns
tance(delegate);
  }
}

面的代码就是通过转换得到的。
c om
ASM提供了一个功能,可以将普通的java代码转换成ASM代码,上
s.
8、定义要转换的java代码
s
la

/**
* 歌手经纪人
lc

*/
public class SingerAgent implements Singable {
yd

 private Singable delegate;

 public SingerAgent(Singable delegate) {


   this.delegate = delegate;
}

 @Override
 public void sing() {
   System.out.println("before sing...");
   delegate.sing();
   System.out.println("after sing...");
}
}

9、转换

public class Client {


 public static void main(String[] args) throws
IOException {
   ASMifier.main(new String[]
{SingerAgent.class.getName()});
}
}

10、客户端调用

om
public class Client {
 public static void main(String[] args) throws
Exception {
c
   Singable singerAgent =
s.
SingerAgentDump.newProxyInstance(new Singer());
   singerAgent.sing();
s
la

}
lc

}
yd

输出结果为

before sing...
I am singing...
after sing...

符合预期,说明我们通过ASM创建的class是可以正常工作的,但对
比JDK创建动态代理,ASM涉及到大量对java底层字节码的操作,
我们对字节码越熟悉,使用ASM就会越容易。

11、遇到的问题
// 将字节数组加载为class
public static SingerAgent newProxyInstance(Singable
delegate) throws Exception {
   String className =
"com.imooc.sourcecode.java.dynamicproxy.asm.test3.Si
ngerAgent";
   byte[] classData = dump();
   Class<?> aClass = new
MyClassLoader().defineClassForName(className,
classData);
   return (SingerAgent)
aClass.getDeclaredConstructor(Singable.class).newIns
tance(delegate);
}

om
刚开始我将加载后的class强转为了SingerAgent 类,但报异常
c
Exception in thread "main"
s.
java.lang.ClassCastException: class
s

com.imooc.sourcecode.java.dynamicproxy.asm.test3.Sin
la

gerAgent cannot be cast to class


com.imooc.sourcecode.java.dynamicproxy.asm.test3.Sin
lc

gerAgent
yd

(com.imooc.sourcecode.java.dynamicproxy.asm.test3.Si
ngerAgent is in unnamed module of loader
com.imooc.sourcecode.java.dynamicproxy.asm.test3.MyC
lassLoader @4590c9c3;
com.imooc.sourcecode.java.dynamicproxy.asm.test3.Sin
gerAgent is in unnamed module of loader 'app')
at
com.imooc.sourcecode.java.dynamicproxy.asm.test3.Sin
gerAgentDump.newProxyInstance(SingerAgentDump.java:9
8)
at
com.imooc.sourcecode.java.dynamicproxy.asm.test3.Cli
ent.main(Client.java:5)
原因是因为生成的SingerAgent 类和原有的SingerAgent 类不是同
一个类加载器加载的,所以不能转换。

12、ASM使用场景

ASM是一个非常强大的字节码工具,以下都使用到了ASM

the OpenJDK, to generate the lambda call sites, and also in


the Nashorn compiler, OpenJDK 对lambda 的实现
the Groovy compiler and the Kotlin compiler,Cobertura and
Jacoco, to instrument classes in order to measure code
coverage, Groovy和Kotlin的编译器
CGLIB, to dynamically generate proxy classes (which are
used in other projects such as Mockito and EasyMock), Cglib
框架
om
Gradle, to generate some classes at runtime. Gradle框架
c
 
s.

 
s
la

 
lc

五、实战
yd

1、hikari.properties
username=root
password=root
jdbcUrl=jdbc:mysql://127.0.0.1:3306/ssm?
useUnicode=true&characterEncoding=utf8&serverTimezon
e=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=
false
driverClassName=com.mysql.cj.jdbc.Driver
 

druid.properties

url=jdbc:mysql://127.0.0.1:3306/ssm?
useUnicode=true&characterEncoding=utf8&serverTimezon
e=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=
false
username=root
password=root
driverClassName=com.mysql.cj.jdbc.Driver

2、mybatis-config.properties
c om
s.
<mybatis>
s

   <dataSource>hikari</dataSource>
la

   <mapper>mapper/userMapper.xml</mapper>
   <mapper>mapper/studentMapper.xml</mapper>
lc

</mybatis>
yd

3、UserMapper.xml
<mapper namespace="cn.itnanls.UserDao">
   <insert id="saveUser"
resultType="cn.itnanls.User"
paramType="cn.itnanls.User">
      insert into user values(?,?,?)
   </insert>
   <select id="findUser"
resultType="cn.itnanls.User"
paramType="cn.itnanls.User">
      select * from user where id = ?
   </select>
</mapper>

4、User
c om
s.
/**
s

* @author zn
la

* @date 2021/1/25
*/
lc
yd

public class User implements Serializable {


   private int id;
   private String username;
   private String password;

   public User() {
  }

   public User(int id, String username, String


password) {
       this.id = id;
       this.username = username;
       this.password = password;
  }

   public int getId() {


       return id;
  }

   public void setId(int id) {


       this.id = id;
  }

   public String getUsername() {


       return username;
  }

om
   public void setUsername(String username) {
       this.username = username;
  }
c
s.
   public String getPassword() {
s

       return password;
la

  }
lc

   public void setPassword(String password) {


yd

       this.password = password;
  }

   @Override
   public String toString() {
       return "User{" +
               "id=" + id +
               ", username='" + username + '\'' +
               ", password='" + password + '\'' +
               '}';
  }
}

 
5、UserDao
/**
* @author zn
* @date 2021/1/25
*/
public interface UserDao {

   Integer saveUser(User user);

   List<User> findUser(Integer id);


}

om
 

6、DaoWrapper
c
s.

/**
s

* 用于描述一个Dao的方法的必要条件
la

* @author zn
lc

* @date 2021/1/25
*/
yd

public class DaoWrapper {

   /**
    * 类型,insert|update|delete
    */
   private String type;
   /**
    * 返回值的类型
    */
   private String resultType;
   /**
    * 参数的类型
    */
   private String paramType;
   /**
    * sql语句
    */
   private String sql;

   public DaoWrapper(String type, String


resultType, String paramType, String sql) {
       this.type = type;
       this.resultType = resultType;
       this.paramType = paramType;
       this.sql = sql;
  }

om
   public String getType() {
       return type;
  }
c
s.
   public void setType(String type) {
s

       this.type = type;
la

  }
lc

   public String getResultType() {


yd

       return resultType;
  }

   public void setResultType(String resultType) {


       this.resultType = resultType;
  }

   public String getParamType() {


       return paramType;
  }

   public void setParamType(String paramType) {


       this.paramType = paramType;
  }
   public String getSql() {
       return sql;
  }

   public void setSql(String sql) {


       this.sql = sql;
  }

   @Override
   public String toString() {
       return "DaoWrapper{" +
               "type='" + type + '\'' +
               ", resultType='" + resultType + '\''

om
+
               ", paramType='" + paramType + '\'' +
               ", sql='" + sql + '\'' +
c
               '}';
s.
  }
s

}
la

 
lc

7、DataSourceFactory
yd

/**
* 数据源工厂
* 简单工厂的应用
* @author zn
* @date 2021/1/25
*/
public class DataSourceFactory {

   public static DataSource createDataSource(String


type){
       DataSource dataSource = null;
       Properties properties = new Properties();
       if("hikari".equals(type)){
           try {
             
 properties.load(DataSourceFactory.class.getClassLoa
der().getResourceAsStream("hikari.properties"));
          } catch (IOException e) {
               e.printStackTrace();
          }
           HikariConfig hikariConfig = new
HikariConfig(properties);
           dataSource = new
HikariDataSource(hikariConfig);

     
om
} else if ("druid".equals(type)){
           try {
c
             
s.
 properties.load(DataSourceFactory.class.getClassLoa
s

der().getResourceAsStream("druid.properties"));
la

          } catch (IOException e) {
               e.printStackTrace();
lc

          }
yd

           DruidDataSource druidDataSource = new


DruidDataSource();
         
 druidDataSource.configFromPropety(properties);
           dataSource = druidDataSource;
      }

       return dataSource;
  }

 
 

8、Session
/**
* 会话对象
* @author zn
* @date 2021/1/25
*/
public class Session {

   /**
    * 每个会话持有一个链接

om
    */
   private Connection connection;
   /**
c
    * 当前会话的上下文
s.
    */
s

   private Map<String, Map<String, DaoWrapper>> env


la

= new HashMap<>(8);
lc

   public Session(Connection connection,Map<String,


yd

Map<String, DaoWrapper>> env) {


       this.connection = connection;
       this.env = env;
  }

   /**
    * 拿到一个包装类
    * @param clazz
    * @param <T>
    * @return
    */
   public <T> T getMapper(Class<T> clazz) {
       T t = (T)
Proxy.newProxyInstance(this.getClass().getClassLoade
r(),
               new Class[]{clazz},
               new SQLHandler(connection,
clazz,env.get(clazz.getName())));
       return t;
  }

   // 开始会话
   public void begin() {
       try {
           connection.setAutoCommit(false);
      } catch (SQLException e) {

om
           e.printStackTrace();
      }
  }
c
s.
   // 提交
s

   public void commit() {


la

       try {
           connection.commit();
lc

      } catch (SQLException e) {
yd

           e.printStackTrace();
      }
  }

   // 回滚
   public void rollback() {
       try {
           connection.rollback();
      } catch (SQLException e) {
           e.printStackTrace();
      }
  }
}

9、SessionFactory
/**
* @author zn
* @date 2021/1/25
*/
public class SessionFactory {

   private DataSource dataSource;

= new HashMap<>(8);
c om
   private Map<String, Map<String, DaoWrapper>> env
s.
   public SessionFactory(String config) {
       loadXml(config);
s

  }
la
lc

   // 打开一个会话
   public Session openSession() {
yd

       Connection connection = null;


       try {
           connection = dataSource.getConnection();
      } catch (SQLException e) {
           e.printStackTrace();
      }
       return new Session(connection, env);
  }

   // 加载资源环境
   public void loadXml(String config) {
       try {
           SAXReader reader = new SAXReader();
           Document configDom =
reader.read(Session.class.getClassLoader().getResour
ceAsStream(config));
           Element configRoot =
configDom.getRootElement();
           String dataSourceType =
configRoot.element("dataSource").getTextTrim();
           dataSource =
DataSourceFactory.createDataSource(dataSourceType);

           List mapperElements =
configRoot.elements("mapper");
           List<String> mapperPaths = new
ArrayList<>();

om
           for (Object element : mapperElements) {
               Element mapper = (Element) element;
             
c
 mapperPaths.add(mapper.getTextTrim());
s.
          }
s
la

           for (String mapperPath : mapperPaths) {


               Map<String, DaoWrapper> wrapper =
lc

new HashMap<>(2);
yd

               Document document =
reader.read(Session.class.getClassLoader().getResour
ceAsStream(mapperPath));
               Element root =
document.getRootElement();
               String namespace =
root.attribute("namespace").getValue();
               Iterator iterator =
root.elementIterator();
               while (iterator.hasNext()) {
                   Element el = (Element)
iterator.next();
                   String type = el.getName();
                   String id =
el.attribute("id").getValue();
                   String resultType =
el.attribute("resultType").getValue();
                   String paramType =
el.attribute("paramType").getValue();
                   String sqlStr =
el.getTextTrim();

                   wrapper.put(id, new
DaoWrapper(type, resultType, paramType, sqlStr));
              }
               env.put(namespace, wrapper);
          }

om
      } catch (DocumentException e) {
           e.printStackTrace();
      }
c
s.
  }
s

}
la
lc

 
yd

10、SQLHandler
/**
* @author zn
* @date 2021/1/25
*/
public class SQLHandler implements InvocationHandler
{

   /**
    * 需传入一个链接
    */
   private Connection connection;
   /**
    * 需传入一个dao的类型
    */
   private Class clazz;
   /**
    * 需传入一个独立的环境
    */
   private Map<String, DaoWrapper> env;

   public SQLHandler(Connection connection, Class


clazz,Map<String, DaoWrapper> env) {
       this.connection = connection;

om
       this.clazz = clazz;
       this.env = env;
  }
c
s.
   /**
s

    * 生成代理对象
la

    * @param proxy
    * @param method
lc

    * @param args
yd

    * @return
    * @throws Throwable
    */
   @Override
   public Object invoke(Object proxy, Method
method, Object[] args) throws Throwable {

       // 拿到包装
       DaoWrapper wrapper =
env.get(method.getName());

       PreparedStatement statement =
connection.prepareStatement(wrapper.getSql());
       // 对每一种sql语句进行独立的操作
       if ("insert".equals(wrapper.getType())) {
           String paramType =
wrapper.getParamType();
           // 暂定传入一个对象
           Class<?> clazz = args[0].getClass();
           Field[] fields =
clazz.getDeclaredFields();
           for (int i = 0; i < fields.length; i++)
{
               fields[i].setAccessible(true);
               statement.setObject(i + 1,
fields[i].get(args[0]));
          }

om
           return statement.executeUpdate();

      } else if
c
("delete".equals(wrapper.getType())) {
s.
           for (int i = 0; i < args.length; i++) {
s

               statement.setObject(i + 1, args[i]);
la

          }
           return statement.executeUpdate();
lc
yd

      } else if
("select".equals(wrapper.getType())) {
           for (int i = 0; i < args.length; i++) {
               statement.setObject(i + 1, args[i]);
          }
           ResultSet result =
statement.executeQuery();
           List list = new ArrayList();
           while (result.next()) {
               Class<?> clazz =
Class.forName(wrapper.getResultType());
               Object object = clazz.newInstance();
               Field[] fields =
clazz.getDeclaredFields();
               for (int i = 0; i < fields.length;
i++) {
                   fields[i].setAccessible(true);
                   fields[i].set(object,
result.getObject(fields[i].getName()));

              }
               list.add(object);
          }
           return list;
      }
       return null;
  }
}

 
com
s s.
la
lc
yd

You might also like