自分を信じてください、自分を信じてください
前の章でフライウェイト モード (14) について簡単に紹介しました。まだ読んでいない場合は、前の章をご覧ください。
1.プロキシモード
ルーキー チュートリアルのプロキシ パターンの紹介を参照してください: https://www.runoob.com/design-pattern/proxy-pattern.html
プロキシ パターンでは、あるクラスが別のクラスの機能を表します。このタイプのデザインパターンは構造パターンです。
プロキシ パターンでは、外部への機能インターフェイスを提供するために、既存のオブジェクトを使用してオブジェクトを作成します。
1.1 はじめに
目的:このオブジェクトへのアクセスを制御するために、他のオブジェクトにプロキシを提供すること。
主に解決するのは、オブジェクトへの直接アクセスによって引き起こされる問題です。たとえば、アクセスされるオブジェクトがリモート マシン上にある場合などです。オブジェクト指向システムでは、何らかの理由(オブジェクトの作成に多額のコストがかかる、特定の操作にセキュリティ制御が必要、プロセス外のアクセスが必要など)により、直接アクセスするとユーザーやシステム構造に多大な問題が発生します。このオブジェクトにアクセスするときに、このオブジェクトにアクセス レイヤを追加できます。
使用する場合:クラスへのアクセスを制御したい場合。
解決方法:中間層を増やす。
キーコード:実装と委任クラスの組み合わせ。
応用例: 1. Windows のショートカット。2. 朱八傑は高翠嵐を探しに行きましたが、孫悟空がそれを変更しました。抽象的な高翠嵐の外見、高翠蘭自身と孫悟空がこのインターフェイスを実現した、というように理解できます。プロキシ クラス。3. 鉄道の切符を購入するには、駅で購入する必要はなく、営業所に行っても購入できます。4. 小切手または銀行預金証明書は、口座内の資金の代理となります。小切手は市場取引で現金の代わりに使用され、発行者の口座内の資金を管理します。5.スプリングオープ。
利点: 1. 責任が明確です。2. 高い拡張性。3. インテリジェント。
欠点: 1. クライアントと実際のサブジェクトの間にプロキシ オブジェクトが追加されるため、一部の種類のプロキシ モードではリクエストの処理速度が遅くなる可能性があります。2. プロキシ モードの実装には追加の作業が必要で、一部のプロキシ モードの実装は非常に複雑です。
使用シナリオ:責任ごとに分けると、通常、次の使用シナリオがあります。 1. リモート エージェント。2. 仮想エージェント。3. コピーオンライトエージェント。4. エージェントを保護またはアクセスします。5. キャッシュエージェント。6. ファイアウォールエージェント。7. 同期エージェント。8. スマートリファレンスエージェント。
注: 1. アダプター モードとの違い: アダプター モードは主に対象のオブジェクトのインターフェイスを変更しますが、プロキシ モードはプロキシされたクラスのインターフェイスを変更できません。2. デコレータモードとの違い: デコレータモードは機能拡張、プロキシモードは制御です。
2.プロキシモード
静的プロキシ、動的プロキシ (JDK プロキシ)、Cglib プロキシ (メモリ内にオブジェクトを動的に作成できる) があります。
2. 静的プロキシ
2.1.1 教師の講義インターフェース
public interface ITeacher {
public void talk();
}
2.1.2 教師の講義のインターフェース実現
@Slf4j
public class RealTeacher implements ITeacher{
@Override
public void talk() {
log.info("老师正在讲课");
}
}
2.1.3 プロキシを使用しない
@Test
public void noProxyTest() {
ITeacher iTeacher = new RealTeacher();
iTeacher.talk();
}
情報 [メイン] (RealTeacher.java:16) - 教師が講義を行っています
2.1.4 プロキシの使用
@Slf4j
public class TeacherProxy implements ITeacher{
private ITeacher iTeacher ;
public TeacherProxy (ITeacher iTeacher) {
this.iTeacher = iTeacher;
}
@Override
public void talk() {
log.info("老师提前备课--> 用户 prepare 操作");
iTeacher.talk();
log.info("老师课后总结--> 用户 after 操作");
}
}
2.1.5 代理テスト方法
@Test
public void oneTest() {
RealTeacher realTeacher = new RealTeacher();
TeacherProxy teacherProxy = new TeacherProxy(realTeacher);
teacherProxy.talk();
}
2.2 動的プロキシ
2.2.1 インターフェース
public interface ITeacher2 {
public void talk();
public void hello (String name);
}
2.2.2 インターフェースの実装
@Slf4j
public class RealTeacher2 implements ITeacher2{
@Override
public void talk() {
log.info("老师正在讲课");
}
@Override
public void hello(String name) {
log.info("你好啊 {}", name);
}
}
2.2.3 JDKプロキシの実装
@Slf4j
public class TeacherProxy2 {
private Object target ;
public TeacherProxy2 (Object target) {
this.target = target;
}
/**
获取动态代理
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("JDK代理开始");
Object returnVal = method.invoke(target, args);
log.info("JDK代理结束");
return returnVal;
}
}
);
}
}
2.2.4 プロキシテスト
@Test
public void twoTest() {
ITeacher2 teacher2 = new RealTeacher2();
// 处理转换代理信息
ITeacher2 target = (ITeacher2)(new TeacherProxy2(teacher2).getProxyInstance());
// 进行处理
target.hello("张三");
}
2.3 Cglib プロキシ
2. 3. 1 つの共通クラス
@Slf4j
public class RealTeacher3 {
public void talk() {
log.info("老师正在讲课");
}
public void hello(String name) {
log.info("你好啊 {}", name);
}
}
2.3.2 このクラスのプロキシ TeacherProxy3
@Slf4j
public class TeacherProxy3 implements MethodInterceptor {
private Object target;
public TeacherProxy3(Object target) {
this.target = target;
}
public Object getProxyInstance () {
// 创建工具类 Enhancer
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调函数, 主要是这个。
enhancer.setCallback(this);
// 创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.info("CGLIB 代理开始");
Object returnVal = methodProxy.invoke(target, objects);
log.info("CGLIB 代理结束");
return returnVal;
}
}
2.3.3 プロキシテスト
@Test
public void threeTest() {
RealTeacher3 realTeacher3 = new RealTeacher3();
// 处理转换代理信息
RealTeacher3 target = (RealTeacher3)new TeacherProxy3(realTeacher3).getProxyInstance();
// 进行处理
target.hello("张三");
}
3. HutoolのJDKエージェント
プロキシにはhutoolのProxyUtil ProxyFactoryを使用できます。
@Test
public void fourTest() {
ITeacher2 teacher2 = new RealTeacher2();
ITeacher2 proxy = ProxyUtil.proxy(teacher2, new SimpleAspect());
proxy.hello("hutool 工具类的代理");
ITeacher2 proxy2 = ProxyFactory.createProxy(teacher2, new Aspect() {
@Override
public boolean before(Object target, Method method, Object[] args) {
log.info(">>>> 代理之前执行的操作");
return true;
}
@Override
public boolean after(Object target, Method method, Object[] args, Object returnVal) {
log.info(">>>> 代理之后执行的操作");
return true;
}
@Override
public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
log.info(">>>> 异常时执行的操作");
return true;
}
});
proxy2.hello("hutool 工厂的代理");
}
CGLIB で作成された動的プロキシ オブジェクトは、JDK で作成された動的プロキシ オブジェクトよりもパフォーマンスが高くなりますが、CGLIB はプロキシ オブジェクトの作成に JDK よりもはるかに時間がかかります。
したがって、シングルトン オブジェクトの場合は、頻繁にオブジェクトを作成する必要がないため、CGLIB を使用するのが適切であり、それ以外の場合は、JDK メソッドを使用するのが適切です。
同時に、CGLib は動的にサブクラスを作成する方法を使用するため、最終的に変更されたメソッドをプロキシすることはできません。
この章のコードは github にあります。
https://github.com/yuejianli/DesignPattern/tree/develop/Proxy
ご覧いただきありがとうございます、気に入ったらフォローしてください、またよろしくお願いします!