从Spring及Mybatis框架源码中学习设计模式(结构型)

pull/27/head
AmyliaY 5 years ago
parent 55b24e1596
commit 6951a5b6ce

@ -27,7 +27,7 @@
- [SpringMVC 的设计与实现](/docs/Spring/SpringMVC/SpringMVC的设计与实现.md)
- [SpringMVC 跨域解析](/docs/Spring/SpringMVC/SpringMVC-CROS.md)
### SpringJDBC
努力编写中...
### Spring事务
- [Spring与事务处理](/docs/Spring/SpringTransaction/Spring与事务处理.md)
@ -87,10 +87,13 @@
- [把被说烂的BIO、NIO、AIO再从头到尾扯一遍](docs/Netty/IO/把被说烂的BIO、NIO、AIO再从头到尾扯一遍.md)
### 设计原理
努力编写中...
## Redis
努力编写中...
## Tomcat
努力编写中...
## 学习心得
### 个人经验
@ -103,6 +106,7 @@
- [从Spring及Mybatis框架源码中学习设计模式(创建型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(创建型).md)
- [从Spring及Mybatis框架源码中学习设计模式(行为型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(行为型).md)
- [从Spring及Mybatis框架源码中学习设计模式(结构型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(结构型).md)
- [从框架源码中学习设计模式的感悟](docs/LearningExperience/DesignPattern/从框架源码中学习设计模式的感悟.md)
## 贡献者
感谢以下所有朋友对 [GitHub 技术社区 Doocs](https://github.com/doocs) 所做出的贡献,[参与项目维护请戳这儿](https://doocs.github.io/#/?id=how-to-join)。

@ -3,8 +3,8 @@
本篇博文主要看一下创建型的几个设计模式,即,单例模式、各种工厂模式 及 建造者模式。
## 单例模式
#### 目标
确保某个类只有一个实例,并提供该实例的获取方法。
#### 个人理解
确保某个类只有一个实例,并提供该实例的获取方法。实际应用很多不管是框架、JDK还是实际的项目开发但大都会使用“饿汉式”或“枚举”来实现单例。“懒汉式”也有一些应用但通过“双检锁机制”来保证单例的实现很少见。
#### 实现方式
最简单的就是 使用一个私有构造函数、一个私有静态变量以及一个公共静态方法的方式来实现。懒汉式、饿汉式等简单实现就不多BB咯这里强调一下双检锁懒汉式实现的坑以及枚举方式的实现吧最后再结合spring源码 扩展一下单例bean的实现原理。
@ -62,7 +62,7 @@ public enum SqlCommandType {
}
```
#### 实际范例
#### JDK中的范例
**1. java.lang.Runtime**
```java
/**

@ -1,6 +1,6 @@
设计模式是解决问题的方案从大神的代码中学习对设计模式的使用可以有效提升个人编码及设计代码的能力。本系列博文用于总结阅读过的框架源码Spring系列、Mybatis及JDK源码中 所使用过的设计模式,并结合个人工作经验,重新理解设计模式。
本篇博文主要看一下结构型的几个设计模式,即,策略模式、模板方法模式、迭代器模式 及 观察者模式。
本篇博文主要看一下行为型的几个设计模式,即,策略模式、模板方法模式、迭代器模式 及 观察者模式。
## 策略模式
#### 个人理解

@ -1,13 +0,0 @@
## 六大原则
1. 单一职责:一个类只负责唯一一项职责
2. 依赖倒置:即面向接口编程,系统的高层模块(顶层接口、顶层抽象类等)不应该依赖底层模块(具体实现类),当需求发生变化时,对外接口不变,只要提供新的实现类即可。
3. 接口隔离:尽量设计出功能单一的接口,避免实现类实现很多不必要的接口方法
4. **开放-封闭:对扩展开放,对修改关闭**,本原则是设计模式的终极目标
5. 迪米特法则:尽量减少类之间的耦合性
6. 里氏替换:继承体系的设计要合理
## 装饰器模式

@ -1,10 +1,9 @@
最近在看 SpringAOP 部分的源码,所以对 JDK 动态代理具体是如何实现的这件事产生了很高的兴趣,而且能从源码上了解这个原理的话,也有助于对 spring-aop 模块的理解。话不多说,上代码。
最近在看spring AOP部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对spring-aop模块的理解。话不多说上代码。
```java
/**
* 一般会使用实现了 InvocationHandler接口的类 作为代理对象生成工厂,
* 并且通过持有被代理对象 target invoke() 方法中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke() 方法又是如何被调用的呢?
* 一般会使用实现了 InvocationHandler 的类作为代理对象的生产工厂,
* 并且通过持有被代理对象target在invoke()方法中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke()方法又是如何被调用的呢?
*/
public class ProxyFactory implements InvocationHandler{
@ -30,13 +29,14 @@ public class ProxyFactory implements InvocationHandler{
}
/**
* 实现了接口 MyInterface 和接口的 play() 方法,可以作为被代理类
* 实现了接口MyInterface和接口的play()方法,可以作为被代理类
*/
public class TargetObject implements MyInterface {
@Override
public void play() {
System.out.println("妲己,陪你玩 ~");
System.out.println("妲己,陪你玩 ~");
}
}
@ -47,25 +47,26 @@ public class ProxyTest {
public static void main(String[] args) {
TargetObject target = new TargetObject();
// ProxyFactory 实现了 InvocationHandler 接口,其中的 getInstanse() 方法利用 Proxy 类帮助生成了
// target 目标对象的代理对象,并返回;且 ProxyFactory 持有对 target 的引用,可以在 invoke() 中完成对 target 相应方法
// 的调用,以及目标方法前置后置的增强处理
// ProxyFactory 实现了 InvocationHandler接口其中的 getInstanse() 方法利用 Proxy 类
// 生成了target目标对象的代理对象并返回且ProxyFactory持有对target的引用可以在
// invoke() 中完成对 target 相应方法的调用,以及目标方法前置后置的增强处理
ProxyFactory proxyFactory = new ProxyFactory();
// 这个 mi 就是 JDK 的 Proxy 类生成的代理类$Proxy0 的对象,这个对象中的方法都持有对 invoke() 方法的回调
// 所以当调用其方法时,就能够执行 invoke() 中的增强处理
MyInterface mi = (MyInterface) proxyFactory.getInstanse(target);
// 这个mi就是JDK的 Proxy 类动态生成的代理类 $Proxy0 的实例,该实例中的方法都持有对
// invoke() 方法的回调,所以当调用其方法时,就能够执行 invoke() 中的增强处理
MyInterface mi = (MyInterface)proxyFactory.getInstanse(target);
// 这样可以看到 mi 的 Class 到底是什么
System.out.println(mi.getClass());
// 这里实际上调用的就是$Proxy0 中对 play() 方法的实现,可以看到 play 方法通过 super.h.invoke()
// 完成了对 InvocationHandler 对象 proxyFactory 的 invoke() 方法的回调
// 所以我才能够通过 invoke() 方法实现对 target 对象方法的前置后置增强处理
// 这里实际上调用的就是 $Proxy0代理类中对 play() 方法的实现,结合下面的代码可以看到
// play() 方法通过 super.h.invoke() 完成了对 InvocationHandler对象(proxyFactory)中
// invoke()方法的回调,所以我们才能够通过 invoke() 方法实现对 target 对象方法的
// 前置后置增强处理
mi.play();
// 总的来说,就是在 invoke() 方法中完成 target 目标方法的调用,及前置后置增强
// 然后通过生成的代理类完成对对 invoke() 的回调
// 总的来说就是在invoke()方法中完成target目标方法的调用及前置后置增强
// JDK动态生成的代理类中对 invoke() 方法进行了回调
}
/**
* 将 ProxyGenerator 生成的动态代理类的输出到文件中,利用反编译工具 luyten 等就可
* 将ProxyGenerator生成的动态代理类的输出到文件中利用反编译工具luyten等就可
* 以看到生成的代理类的源码咯,下面给出了其反编译好的代码实现
*/
@Test
@ -92,7 +93,7 @@ public class ProxyTest {
}
/**
* Proxy 生成的代理类,可以看到,其继承了 Proxy并且实现了被代理类的接口
* Proxy生成的代理类可以看到其继承了Proxy并且实现了被代理类的接口
*/
public final class $Proxy0 extends Proxy implements MyInterface
{
@ -105,7 +106,7 @@ public final class $Proxy0 extends Proxy implements MyInterface
try {
$Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
$Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
//实例化 MyInterface play 方法
//实例化MyInterface的play方法
$Proxy0.m3 = Class.forName("com.shuitu.test.MyInterface").getMethod("play", (Class<?>[])new Class[0]);
$Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
}
@ -123,9 +124,9 @@ public final class $Proxy0 extends Proxy implements MyInterface
public final void play() {
try {
//这个 h 其实就是我们调用 Proxy.newProxyInstance() 方法时传进去的 ProxyFactory 对象
//该对象的 invoke() 方法中实现了对目标对象的目标方法的增强。看到这里,利用动态代理实现方法增强的
//调用原理就全部理清咯
// 这个 h 其实就是我们调用 Proxy.newProxyInstance() 方法时传进去的ProxyFactory(InvocationHandler对象)
// 该对象的 invoke() 方法中实现了对目标对象的目标方法的增强。看到这里,利用动态代理实现方法增强的
// 实现原理就全部理清咯
super.h.invoke(this, $Proxy0.m3, null);
}
catch (Error | RuntimeException error) {
@ -171,11 +172,5 @@ public final class $Proxy0 extends Proxy implements MyInterface
throw new UndeclaredThrowableException(t);
}
}
}
```
#### 动态代理原理图
![avatar](/images/动态代理原理图1.png)
![avatar](/images/动态代理原理图2.png)
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Loading…
Cancel
Save