文章目錄
- 動态代理的應用SpringAOP實作原理
- 動态代理是如何實作的?JDK Proxy 和 CGLib 有什麼差別?
- JDK Proxy 和 CGLib 的差別
- JDK Proxy 動态代理實作
- CGLib 的實作
- Lombok 原理分析
動态代理的應用SpringAOP實作原理
動态代理是如何實作的?JDK Proxy 和 CGLib 有什麼差別?
動态代理的重用功能是反射,反射機制是指程式在運作期間可以通路,檢測和修改其本身狀态或行為的一種能力,使用反射我們可以調用任意一個類對象,以及類對象中包含的屬性及方法
但動态代理不止有反射一種實作方式,例如通過CGlib實作,==基于ASM(一個java位元組碼操作架構)==而非反射實作的.簡單來說,動态代理是一種行為方式,而反射或者ASM隻是它的一種實作手段而已
JDK Proxy 和 CGLib 的差別
JDK Proxy 和 CGLib 的差別主要展現在以下幾個方面
- JDK Proxy 是Java語言自帶的功能,無需通過加載第三方類實作
- java對jdk Proxy 提供了穩定的支援,并且會持續地更新和更新jdk Proxy 例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
- JDK Proxy是通過攔截器加反則地反射實作地
- JDK Proxy 隻能代理繼承接口地類
- JDK Proxy實作和調用用起來比較簡單
- CGlib是第三方提供地工具,基于
實習,性能比較高ASM
- CGlib無需通過接口來實作,他是通過實作子類地方式來完成調用的
1.JDK Proxy 和 CGLib 的使用及代碼分析
JDK Proxy 動态代理實作
JDK Proxy 動态代理的實作無需引用第三方類,隻要需要實作
InvocationHandler
接口,重寫
invoke()
方法即可
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK Proxy 相關示例
*/
public class ProxyExample {
static interface Car {
void running();
}
static class Bus implements Car {
@Override
public void running() {
System.out.println("The bus is running.");
}
}
static class Taxi implements Car {
@Override
public void running() {
System.out.println("The taxi is running.");
}
}
/**
* JDK Proxy
*/
static class JDKProxy implements InvocationHandler {
private Object target; // 代理對象
// 擷取到代理對象
public Object getInstance(Object target) {
this.target = target;
// 取得代理對象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 執行代理方法
* @param proxy 代理對象
* @param method 代理方法
* @param args 方法的參數
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws InvocationTargetException, IllegalAccessException {
System.out.println("動态代理之前的業務處理.");
Object result = method.invoke(target, args); // 執行調用方法(此方法執行前後,可以進行相關業務處理)
return result;
}
}
public static void main(String[] args) {
// 執行 JDK Proxy
JDKProxy jdkProxy = new JDKProxy();
Car carInstance = (Car) jdkProxy.getInstance(new Taxi());
carInstance.running();
}
可以看出JDK Proxy實作動态代理的核心是實作Invocation 接口,我們檢視Invocation 源碼,就會發現裡面其實隻有一個invoke()方法 ,源碼如下
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
因為在的動态代理種有一個重要的角色也就是代理器,用于統一管理被代理被對象,顯然
InvocationHandler
就是這個代理器,而invoke()方法則是觸發代理的執行方法,我麼通過實作Invocation 接口來擁有動态代理的能力
CGLib 的實作
在使用CGLib 之前,我們我先引用
CGLib
架構,在
pom.xml
中添加如下配置
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
CGLib 實作代碼如下:
package com.lagou.interview;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibExample {
static class Car {
public void running() {
System.out.println("The car is running.");
}
}
/**
* CGLib 代理類
*/
static class CGLibProxy implements MethodInterceptor {
private Object target; // 代理對象
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 設定父類為執行個體類
enhancer.setSuperclass(this.target.getClass());
// 回調方法
enhancer.setCallback(this);
// 建立代理對象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method,
Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法調用前業務處理.");
Object result = methodProxy.invokeSuper(o, objects); // 執行方法調用
return result;
}
}
// 執行 CGLib 的方法調用
public static void main(String[] args) {
// 建立 CGLib 代理類
CGLibProxy proxy = new CGLibProxy();
// 初始化代理對象
Car car = (Car) proxy.getInstance(new Car());
// 執行方法
car.running();
}
可以看出 CGLib 和 JDK Proxy 的實作代碼比較類似,都是通過實作代理器的接口,再調用某一個方法完成動态代理的,唯一不同的是,CGLib 在初始化被代理類時,是通過 Enhancer 對象把代理對象設定為被代理類的子類來實作動态代理的。是以被代理類不能被關鍵字 final 修飾,如果被 final 修飾,再使用 Enhancer 設定父類時會報錯,動态代理的建構會失敗。
Lombok 原理分析
Lombok 是在編譯期就為我們生成了對應的位元組碼。
其實 Lombok 是基于 Java 1.6 實作的 JSR 269: Pluggable Annotation Processing API 來實作的,也就是通過編譯期自定義注解處理器來實作的,它的執行步驟如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SO1gDMyUmY4ATM2gTZ1IWNzYzX4UTN0ITM4IzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
在編譯期階段,當java源碼被抽象成文法樹(AST)之後,LOMBOKhui 回根據自身的注解處理器動态修改AST,增加新的代碼(節點),在這一切執行之後就生成了最終的位元組碼(.class)檔案,這既是lombok執行原理
靜态代理和動态代理差別
靜态代理其實就是事先寫好代理類,可以手工編寫 也可以使用工具生成,但他的缺點是每個業務類都要對應一個代理類,特别不靈活也不友善,于是有了動态代理