前言
這周作業比較忙,在學習上面就溫習了一下動態代理,以前也看過這塊的知識,但是一直沒有動手去寫下代碼,學習就是這樣,不動手寫一寫總有種沒有掌握的感覺,記錄下這個學習程序
動態代理有什么用呢? 它的作用就是增強,對于一個方法,我想在執行它之前執行另外一個log方法,那我可以修改代碼,我可以直接去加,但是假如有很多方法都需要加這個log,那作業量就大了,我們可以用動態代理來加上這個log方法,然后通過代理來呼叫原來的方法,達到不修改原來方法,增加功能的目的,
動態代理實作上有兩種方法JDK動態代理和CGLib動態代理,下面我們分別來演示一下
JDK動態代理
JDK動態代理是JDK自帶的,通過反射來實作,需要基于介面來做,
首先我們定義一個需要代理的介面和它的實作類
public interface IHelloWorld {
String say();
}
public class HelloWorld implements IHelloWorld{
@Override
public String say() {
System.out.println("this is helloworld");
return "Hello World";
}
}
我們需要在呼叫say前面和后面都加些日志,直接上動態代理的代碼
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
private void before(){
System.out.println("this is before");
}
private void after(){
System.out.println("this is after");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target, args);
after();
return result;
}
}
在main方法中加入如下代碼來呼叫這個動態代理
IHelloWorld helloWorld = new HelloWorld();
DynamicProxy dynamicProxy1 = new DynamicProxy(helloWorld);
IHelloWorld helloWorldProxy = (IHelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
helloWorld.getClass().getInterfaces(), dynamicProxy1);
String hello = helloWorldProxy.say();
輸出如下
this is before
this is helloworld
this is after
我們可以看到呼叫helloWorldProxy.say()方法后,
先呼叫了before(),然后呼叫helloWorld的say(), 然后再呼叫了after().
也就是DynamicProxy里面的invoke方法里面的順序來執行,
這樣寫代碼有點復雜了
IHelloWorld helloWorld1 = (IHelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
helloWorld.getClass().getInterfaces(), dynamicProxy1);
在黃勇的《架構探險-從零開始寫Java Web框架》中又包裝了一下,在DynamicProxy中定義了這個方法
@SuppressWarnings("unchecked")
public <T> T getProxy()
{
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
呼叫起來簡單明了很多
IHelloWorld helloWorld = new HelloWorld();
DynamicProxy dynamicProxy1 = new DynamicProxy(helloWorld);
IHelloWorld helloWorldProxy = dynamicProxy1.getProxy();
helloWorldProxy.say();
如果我們又有個方法需要加before()和after(),那么就可以直接用
IUserService userService = new UserService();
DynamicProxy dynamicProxy = new DynamicProxy(userService);
IUserService userServiceProxy = dynamicProxy.getProxy();
userServiceProxy.send();
這就是JDK動態代理,缺點就是必須基于介面,
也就是必須這樣寫
IUserService userServiceProxy = dynamicProxy.getProxy();
直接寫
UserService userServiceProxy = dynamicProxy.getProxy();
會報如下的轉化錯誤
“java.lang.ClassCastException: class jdk.proxy1.$Proxy0 cannot be cast to class ken.proxy.demo.UserService”
CGLib動態代理
CGLib動態代理需要引入一個包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
代碼比較簡單,直接貼代碼出來
public class CGLibProxy implements MethodInterceptor {
private void before(){
System.out.println("this is before");
}
private void after(){
System.out.println("this is after");
}
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls,this);
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(obj, args);
after();
return result;
}
}
呼叫代碼
CGLibProxy cgLibProxy = new CGLibProxy();
HelloWorld helloWorld = cgLibProxy.getProxy(HelloWorld.class);
helloWorld.say();
輸出和JDK動態代理一樣, 它不要求HelloWorld一定去實作某個介面,相對比JDK動態代理要好,據說許多框架都用到了它,
這里遇到個坑, 我用Java19 運行這段代碼會報如下錯誤
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @182decdb
網上查找是Java兼容性錯誤,在VM Option里面加上“--add-opens java.base/java.lang=ALL-UNNAMED” 可以解決, 如果直接用Java8 也不會出現這個錯誤,
總結
動態代理這塊的內容比較基礎,關鍵是要嘗試在實際的專案中用到它,理解它的思想,然后實際應用上去就比較好了, 這周內容比較簡單,堅持學習,加油!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/546513.html
標籤:Java
上一篇:讀Java性能權威指南(第2版)筆記13_堆記憶體下
下一篇:微軟的148座墳墓
