プロキシ モードは、一般的な設計モードの 1 つであり、その名前が示すように、プロキシ オブジェクトが実際のオブジェクトの機能を持ち、実際のオブジェクトの代わりに対応する操作を完了し、事前の操作を強化することができます。操作の実行後。(実際のオブジェクトにプロキシを提供し、他のオブジェクトがプロキシ経由で実際のオブジェクトにアクセスできるようにします)
に分け
-
静的プロキシ
-
動的プロキシ
-
JDK動的プロキシ
-
cglib動的プロキシ
-
静的プロキシ
実クラスとプロキシ クラスは同じインターフェイスを実装する必要があり、実クラスのメソッドをプロキシ クラスに実装でき、実クラスのメソッドを同時に強化でき、複数の実オブジェクトの注入が可能です。プロキシ クラスで完了できます。
public interface IRentHouse {
void rentHouse();
}
public class RentHouse implements IRentHouse {
@Override
public void rentHouse() {
System.out.println("实现租房");
}
}
public class IntermediaryProxy implements IRentHouse {
private IRentHouse iRent;
public IntermediaryProxy(IRentHouse iRentHouse) {
iRent=iRentHouse;
}
@Override
public void rentHouse() {
System.out.println("交中介费");
iRent.rentHouse();
System.out.println("中介负责维修管理");
}
}
//client测试类
public class TestStaticProxy {
public static void main(String[] args) {
//定义租房
IRentHouse iRentHouse = new RentHouse();
//定义中介
IRentHouse intermediaryProxy = new IntermediaryProxy(iRentHouse);
//中介租房
intermediaryProxy.rentHouse();
}
}
動的プロキシ
从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理.
動的プロキシを使用すると、1 つのメソッドを持つ単一のクラス (プロキシ クラス) が、任意の数のメソッドを持つ任意のクラス (実際のクラス) の複数のメソッド呼び出しを処理できます。
JAVA リフレクション メカニズムは実行状態にあります。どのクラスについても、このクラスのすべてのプロパティとメソッドを知ることができます。どのオブジェクトについても、そのメソッドおよびプロパティのいずれかを呼び出すことができます。この動的に取得された情報と動的呼び出しの関数は、オブジェクトのメソッドは Java 言語のリフレクション機構と呼ばれます。
jdkダイナミックプロキシ(インターフェースプロキシ)
Jdk プロキシには、InvocationHandler インターフェイスと java.lang.reflect パッケージの Proxy クラスが含まれます。コア メソッドは次のとおりです。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
jdk 動的プロキシ プロセスでは、インターフェイスは実際にプロキシされます。これは、プロキシ インスタンスの作成時に、java.lang.reflect パッケージ内の Proxy クラスの newProxyInstance メソッドに依存するためです。また、このメソッドが有効になるには、このパラメータが必要です。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
……
}
以下は、jdk ダイナミック プロキシの完全なプロセスを示すケースです。
//接口
public interface Person {
void wakeup();
void sleep();
}
//实现类1
public class Student implements Person{
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
@Override
public void wakeup() {
System.out.println("学生"+name+"早晨醒来啦");
}
@Override
public void sleep() {
System.out.println("学生"+name+"晚上睡觉啦");
}
}
//代理类
public class JDKDynamicProxy implements InvocationHandler {
private Object bean;
public JDKDynamicProxy(Object bean) {
this.bean=bean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodname=method.getName();
if (methodname.equals("wakeup")){
System.out.println("早安~~~");
}else if(methodname.equals("sleep")){
System.out.println("晚安~~~");
}
return method.invoke(bean,args);
}
}
//测试类
public class TestJDKDynamicProxy {
public static void main(String[] args) {
JDKDynamicProxy proxy = new JDKDynamicProxy(new Student("张三"));
//创建代理实例
Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
student.wakeup();
student.sleep();
}
}
出力は
早安~~
学生张三早晨醒来啦
晚安~~
学生张三晚上睡觉啦
インターフェイス内のメソッドとプロキシ クラスで書き換えられた invoke メソッドですが、invoke() メソッドは明示的に呼び出されず、プロキシ インスタンスの作成プロセス中に生成されたインターフェイス仮想プロキシ クラス内で invoke メソッドが呼び出されます。(Sproxy0のインスタンスを強制的に対応するインターフェース型のリファレンスに変換し、インターフェースメソッドを実行し、プロキシクラスでinvoke()を実行します)
概要の比較:
1. 静的プロキシでは、プロキシ クラスと実際のクラスは同じインターフェイスを実装し、同じメソッドを書き換えます。JDK 動的プロキシでは、プロキシ クラスは実際のクラスとほとんど関係がなく、プロキシ クラスは非侵襲的なコードを実装します。拡大。
2. 静的プロキシではインターフェース内のメソッドが増えると、プロキシクラスのコード量も増加しますが、これは明らかに不適切ですが、jdk ダイナミックプロキシはこの問題を解決します。増えない。
3. jdk ダイナミック プロキシは、jdk 独自の InvocationHandler インターフェイスを実装します。このインターフェイスを実装するクラスは、インターセプタ クラスまたはプロキシ クラスとも呼ばれます。
cglib動的プロキシ
上記のことから、jdk ダイナミック プロキシの前提条件はインターフェイスが必要であることがわかりますが、インターフェイスのないシナリオはまだ多くあります。現時点では、cglib ダイナミック プロキシが必要です。CGLIB (コード生成ライブラリ) は ASM です-based word セクション コード生成ライブラリ。実行時にバイトコードを変更して動的に生成できます。CGLIB は継承を通じてプロキシを実装します。実装クラスのサブクラスは、cglib 動的プロキシ プロセス中に生成されます。cglib はどのようにして何もないところから実装クラスのサブクラスを作成しますか? 以下はテスト コードです。
//所需的代理类
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer=new Enhancer();
private Object bean;
public CglibProxy(Object bean) {
this.bean = bean;
}
public Object getProxy(){
//设置需要创建子类的类
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
if (methodName.equals("wakeup")){
System.out.println("早安~~~");
}else if(methodName.equals("sleep")){
System.out.println("晚安~~~");
}
return method.invoke(bean,objects);
}
}
//测试类
public class TestCglibProxy {
public static void main(String[] args) {
//生成虚拟代理类的代码,本来虚拟代理子类是看不见的,
//下面这句话的作用就是把执行过程中cglib增强后的class字节码文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\aop");
CglibProxy proxy = new CglibProxy(new Cat("咪咪"));
Cat cat = (Cat) proxy.getProxy();
cat.wakeup();
cat.sleep();
}
}
要約:
cglib ダイナミック プロキシと jdk ダイナミック プロキシの違いは明らかですが、実装ロジックは似ています。cglib プロキシ クラスは、MethodInterceptor を実装し、インターセプト メソッドを書き換え、プロキシ クラスのサブクラスを生成することで、プロキシ拡張コードの目的を達成します。一方、Jdk は、プロキシ クラスのサブクラスを生成します。 proxy は InvocationHandler を実装し、invoke メソッドを書き換え、インターフェースの proxy クラスを生成することでコード拡張の目的を達成するため、jdk 動的プロキシの実装にはインターフェースが必要ですが、cglib にはインターフェースが必要ありません Spring 5.0 以降Springboot 2.0 以降では、デフォルトの AOP で cglib 動的実装が使用されます。