課程回顧
反射的概念: 運行期創建對象並調用對象的方法
①反射類的信息: 類名稱, 類的父類,類的接口,類的包信息,類的註解; newInstance創建對象
②反射類的屬性: 屬性名稱,屬性的類型,屬性的修飾符,調用屬性的set/get方法, 強制訪問
③反射類的構造方法: Constructor構造方法, 可以獲取無參,有參; newInstance(Object … args)
④反射類的普通方法: Method, 獲取所有聲明的方法,獲取自身和父類的方法,獲取指定的方法, invoke方法
⑤反射類的main方法: getMethod(“main”, String[].class),mm.invoke(null, new Object[]{ new String[]{} })
⑥反射越過泛型檢查:
⑦反射jar包的類信息: 獲取類信息,獲取類的屬性,類的方法
⑧發射創建數組: Array.newInstance(Integer.class, 10)
⑨反射獲取類泛型信息: 獲取方法的泛型信息,獲取返回值泛型信息
⑩手寫Spring框架: 讀取配置文件, 創建對象,放到Map, getBean根據key獲取對象
靜態代理
代理: 代表公司, 區域代理; 李寧鄭州總代理; 代表公司在本區域銷售李寧公司的產品. 媒婆: 代理,
靜態代理一般只代理一個公司的商品, 李寧專賣店: 只銷售李寧公司的產品.
還有一種商業形式: 大型商場, 連鎖超市; 代理的衆多公司的商品, 不再限於一家公司. 簡單理解可以說動態代理.
說說寶寶的故事: 王寶強的故事, 漢語八級考試試題: 寶寶生的寶寶不象寶寶, 嚇死寶寶了.
王寶強: 被代理人
宋喆: 代理人 經紀人
我想找王寶強做個品牌形象代言人, 我先找宋喆, 經紀人負責討價還價; 但是真正的做廣告的是王寶強. 靜態代理
package com.hanker.staticproxy;
//娛樂共同的約束--接口
public interface Enterment {
static final public String AAA="XXX";
public abstract void dance();
void sing();
}
//==================
package com.hanker.staticproxy;
//真實對象---演員類
public class Actor implements Enterment{
private String name;//演員名稱
public Actor(String name) {
super();
this.name = name;
}
public void dance() {
System.out.println(name+"跳舞......");
}
public void sing() {
System.out.println(name+"唱歌.....");
}
}
//=================
package com.hanker.staticproxy;
//代理對象---中間人,代理人, 知名社會活動家
public class MiddleMan implements Enterment{
private String name;//姓名
private Actor actor;//被代理的演員對象
public MiddleMan(String name, Actor actor) {
super();
this.name = name;
this.actor = actor;
}
public void dance() {
System.out.println(name + " 哥們收點好處費.....");
actor.dance();
System.out.println(name+ " 下次繼續合作...");
}
public void sing() {
System.out.println("--開始唱歌-----");
actor.sing();
System.out.println("--結束唱歌-----");
}
}
//===============
package com.hanker.staticproxy;
//測試類
public class Test {
public static void main(String[] args) {
Actor actor = new Actor("寶寶");//創建一個演員
MiddleMan mm = new MiddleMan("宋喆",actor);
mm.dance();
mm.sing();
}
}
以上是靜態代理, 代理類只能代理一類對象. 如何實現代理任意類型的對象? 動態代理: 在java反射包中提供的有幫助類: Proxy 代理, InvocationHandler 調用處理器.
可以通過一個真實對象, 對應一個代理對象; 這樣的問題是: 代理對象的泛濫.
動態代理
還有一種業務: 影視公司, 會簽約一批藝人, 影視公司不再是一個明星的代理; 而是代理一批明星. 也可以理解爲動態代理.
需求:
在執行方法之前答應日誌信息, 在執行方法之後打印日誌信息;
統計方法執行的時間: 在方法執行之前記錄一個時間, 在方法執行之後再記錄時間; 兩者相減即可.
也可以是執行權限檢查: 開當前用戶有沒有權限執行該方法.核心: 在不改變原始方法的前提下
動態代理Proxy類簡介
public class Proxy extends Object implements Serializable
//屬性
protected InvocationHandler h //該代理實例的調用處理程序。
//構造方法
protected Proxy(InvocationHandler h)
//從一個子類(通常是一個動態代理類)構造一個新的 Proxy實例,具有指定的調用處理程序的值。
//普通方法
//返回指定代理實例的調用處理程序。
static InvocationHandler getInvocationHandler(Object proxy)
//給出類加載器和接口數組的代理類的 java.lang.Class對象。
static Class<?> getProxyClass(ClassLoader loader, 類<?>... interfaces)
//如果且僅當使用 getProxyClass方法或 newProxyInstance方法將指定的類動態生成爲代理類時,則返回true。
static boolean isProxyClass(Class<?> cl)
//返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序。
static Object newProxyInstance(ClassLoader loader, //類加載器
Class<?>[] interfaces,//接口數組
InvocationHandler h) //調用處理器
動態代理類加載器
ClassLoader : 類加載器 ,把java的字節碼文件加載到jvm執行; 當然之前要做一些列的檢查,連接,校驗.
公共汽車: jvm
ClassLoader: 售票員, 乘務員, 負責把客人拉到車上. 保證加載到jvm中的字節碼是有效的,安全的,沒有被人爲修改過.
Class<?>[] interfaces,//接口數組 被代理對象實現的接口數組
InvocationHandler h : 調用處理器, 纔是真正調用業務方法的地方.
動態代理案例
package com.hanker.dynamicproxy;
public interface ProductManager {
int addProduct();//添加方法
int deleteProduct(int no);//刪除方法
}
//===========================
package com.hanker.dynamicproxy;
//要代理的業務對象, 不允許修改源代碼
public class ProductManagerImpl implements ProductManager {
@Override
public int addProduct() {
System.out.println("執行添加商品");
return 1;
}
@Override
public int deleteProduct(int no) {
System.out.println("執行刪除商品..."+no);
return 1001;
}
}
//=============沒有代理對象==============
package com.hanker.dynamicproxy;
public class Test {
public static void main(String[] args) {
test1();
}
private static void test1() {
ProductManger manager =new ProductMangerImpl();
manager.addProduct();
int result = manager.deleteProduct(1002);
System.out.println("返回值:"+result);
}
}
//=============通過Proxy創建代理對象======================
package com.hanker.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
//代理工廠類
public class ProxyFactory {
//被代理對象
private Object target;
//初始化代理對象
public ProxyFactory(Object target) {
this.target = target;
}
//獲取代理對象
public Object getProxy() {
Object proxy = null;
//調用處理器
InvocationHandler h = new MyLogInvocationHanler(target);
//僅僅是創建代理對象,方法調用在哪裏?
proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(), //被代理對象的類加載器
target.getClass().getInterfaces(), //被代理對象實現的接口
h); //調用處理器
//返回代理對象
return proxy;
}
}
//============調用處理器 =============
package com.hanker.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyLogInvocationHanler implements InvocationHandler {
//被代理的對象
private Object object;
//初始化被代理對象
public MyLogInvocationHanler(Object object) {
this.object = object;
}
/**
* Object proxy 被代理對象
* Method method 被調用的方法
* Object[] args 被調用的方法的參數
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//獲取調用方法名稱
String name = method.getName();
before(name);//添加日誌
//調用真實對象的方法
Object result = method.invoke(object, args);
after(name);//添加日誌
return result;
}
public void before(String methodName) {
System.out.println("開始執行"+methodName);
}
public void after(String methodName) {
System.out.println("結束執行"+methodName);
}
}
//===============測試方法==================
package com.hanker.dynamicproxy;
public class Test {
public static void main(String[] args) {
test2();
}
private static void test2() {
//創建被代理對象
ProductManger manager =new ProductMangerImpl();
//把被代理對象傳入代理工廠
ProxyFactory factory = new ProxyFactory(manager);
//獲取代理對象
ProductManger proxy = (ProductManger) factory.getProxy();
//查看代理類的名稱
System.out.println("代理對象的名稱:"+proxy.getClass().getName());
//調用代理對象的方法
proxy.addProduct();
proxy.deleteProduct(10000);
}
}
動態代理執行過程
測試類----代理對象------InvocationHandler的實現類-------真實對象.
ProxyFactory只負責創建代理對象,並指定一個調用處理器類: MyLogInvocationHandler
MyLogInvocationHandler再調用真實對象的業務方法, 在調用真實業務對象的方法之前和之後可以添加日誌處理
案例2:
package com.hanker.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyTimeInvocationHandler implements InvocationHandler {
private Object target;
public MyTimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();//當前時間的毫秒數
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end-start) + "毫秒");
return result;
}
}
//=============================
package com.hanker.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
//代理工廠類
public class ProxyFactory {
//被代理對象
private Object target;
//初始化代理對象
public ProxyFactory(Object target) {
this.target = target;
}
//獲取代理對象
public Object getProxy() {
Object proxy = null;
//調用處理器
//InvocationHandler h = new MyLogInvocationHanler(target);
InvocationHandler timeHandler = new MyTimeInvocationHandler(target);
//僅僅是創建代理對象,方法調用在哪裏?
proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(), //被代理對象的類加載器
target.getClass().getInterfaces(), //被代理對象實現的接口
timeHandler); //調用處理器
//返回代理對象
return proxy;
}
}
//==========測試類沒有修改==============
多級代理: 日誌+時間
//被代理對象
package com.hanker.dynamicproxy;
//要代理的業務對象, 不允許修改源代碼
public class ProductMangerImpl implements ProductManger {
@Override
public int addProduct() {
System.out.println("執行添加商品");
try {
Thread.sleep(1000);//模擬業務方法的執行
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}
@Override
public int deleteProduct(int no) {
System.out.println("執行刪除商品..."+no);
try {
Thread.sleep(2000);//模擬業務方法的執行
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1001;
}
}
//===========代理工廠類==================
package com.hanker.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
//代理工廠類
public class ProxyFactory {
//被代理對象
private Object target;
//初始化代理對象
public ProxyFactory(Object target) {
this.target = target;
}
//獲取代理對象
public Object getProxy() {
Object proxy = null;
//調用處理器
InvocationHandler timeHandler = new MyTimeInvocationHandler(target);
//僅僅是創建代理對象,方法調用在哪裏?
proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(), //被代理對象的類加載器
target.getClass().getInterfaces(), //被代理對象實現的接口
timeHandler); //調用處理器
//返回代理對象
return proxy;
}
}
//===================測試類======================
package com.hanker.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//如果我想即輸出日誌又記錄時間怎麼辦?
// 鞋廠(200元)--省代(300元)--市代理(400元)--縣級代理(500元)
//需要兩級代理: 公司--省代---市代理---縣級代理
//創建被代理對象
ProductManger manager =new ProductMangerImpl();
//把被代理對象傳入代理工廠
ProxyFactory factory = new ProxyFactory(manager);
//獲取代理對象: proxy省代理
ProductManger proxy = (ProductManger) factory.getProxy();
//查看代理類的名稱
System.out.println("代理對象的名稱:"+proxy.getClass().getName());
//proxy.addProduct();//一級代理
//二級代理要增加的內容
InvocationHandler h = new MyLogInvocationHanler(proxy);//傳入一級代理對象
ProductManger proxy2 = (ProductManger)Proxy.newProxyInstance(
proxy.getClass().getClassLoader(),
proxy.getClass().getInterfaces(), h);
//調用代理對象的方法
proxy2.addProduct();
proxy2.deleteProduct(10000);
}
}
執行效果如下圖所示, 需要強調的是MyBatis的插件開發就是多級代理模式實現的.
深入理解動態代理
常識問題: 字節碼文件是怎麼產生的, 由java源碼編譯出來的. 也就意味着字節碼都有對應的源碼.但是:我們看到代理對象的類名是: com.sun.proxy.$Proxy0. 問題: 該類的源代碼在哪裏? 我們不會寫源碼保存到com.sun.proxy包的.那這個類就沒有源代碼, 而是直接在內存中生成的字節碼文件.磁盤也沒有.
動態代理源碼分析
Proxy類的newProxyInstance方法
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
throws IllegalArgumentException{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 查找或者生成指定的代理類, 下面繼續分析
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
* 使用指定的調用處理器調用構造方法
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//根據參數獲取構造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;//把屬性 h 賦值給ih
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});//創建對象,使用調用處理器
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
newProxyInstance()方法幫我們執行了生成代理類----獲取構造器----生成代理對象這三步;
生成代理類: Class<?> cl = getProxyClass0(loader, intfs);
獲取構造器: final Constructor<?> cons = cl.getConstructor(constructorParams);
生成代理對象: cons.newInstance(new Object[]{h});
Proxy.getProxyClass0()如何生成代理類? getProxyClass0方法源碼
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//cache是緩存的意思, 看緩存中是否有代理對象了
return proxyClassCache.get(loader, interfaces);
}
繼續跟蹤 proxyClassCache.get(loader, interfaces);
public V get(K key, P parameter) {// key:類加載器;parameter:接口數組
// 檢查指定類型的對象引用不爲空null。當參數爲null時,拋出空指針異常。
Objects.requireNonNull(parameter);
// 清除已經被GC回收的弱引用
expungeStaleEntries();
// 將ClassLoader包裝成CacheKey, 作爲一級緩存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
// 獲取得到二級緩存
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
// 沒有獲取到對應的值
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// 根據代理類實現的接口數組來生成二級緩存key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 通過subKey獲取二級緩存值
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 這個循環提供了輪詢機制, 如果條件爲假就繼續重試直到條件爲真爲止
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
// 在這裏supplier可能是一個Factory也可能會是一個CacheValue
// 在這裏不作判斷, 而是在Supplier實現類的get方法裏面進行驗證
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
// 新建一個Factory實例作爲subKey對應的值
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
// 到這裏表明subKey沒有對應的值, 就將factory作爲subKey的值放入
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
// 到這裏表明成功將factory放入緩存
supplier = factory;
}
// 否則, 可能期間有其他線程修改了值, 那麼就不再繼續給subKey賦值, 而是取出來直接用
// else retry with winning supplier
} else {
// 期間可能其他線程修改了值, 那麼就將原先的值替換
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
// 成功將factory替換成新的值
supplier = factory;
} else {
// retry with current supplier
// 替換失敗, 繼續使用原先的值
supplier = valuesMap.get(subKey);
}
}
}
}
該方法中的Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
subKeyFactory調用apply,具體實現在ProxyClassFactory中完成。
ProxyClassFactory.apply()實現代理類創建。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
// prefix for all proxy class names
// 統一代理類的前綴名都以$Proxy
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
// 使用唯一的編號給作爲代理類名的一部分,如$Proxy0,$Proxy1等
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
* 驗證指定的類加載器(loader)加載接口所得到的Class對象(interfaceClass)是否與intf對象相同
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
* 驗證該Class對象是不是接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
* 驗證該接口是否重複
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
// 聲明代理類所在包
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
* 驗證所有非公共的接口在同一個包內;公共的就無需處理
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
// 截取完整包名
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
/*如果都是public接口,那麼生成的代理類就在com.sun.proxy包下如果報java.io.FileNotFoundException: com\sun\proxy\$Proxy0.class
(系統找不到指定的路徑。)的錯誤,就先在你項目中創建com.sun.proxy路徑*/
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
* nextUniqueNumber 是一個原子類,確保多線程安全,防止類名重複,類似於:$Proxy0,$Proxy1......
*/
long num = nextUniqueNumber.getAndIncrement();
// 代理類的完全限定名,如com.sun.proxy.$Proxy0.calss
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
* 生成類字節碼的方法(重點)
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
代理類創建真正在ProxyGenerator.generateProxyClass()方法中,方法簽名如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to generate proxy dispatching code for.
* 步驟1:爲所有方法生成代理調度代碼,將代理方法對象集合起來。
*/
//增加 hashcode、equals、toString方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
// 獲得所有接口中的所有方法,並將方法添加到代理方法中
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
/*
* 驗證方法簽名相同的一組方法,返回值類型是否相同;意思就是重寫方法要方法簽名和返回值一樣
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of fields and methods in the class we are generating.
* 爲類中的方法生成字段信息和方法信息
*/
try {
// 生成代理類的構造函數
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
// 生成代理類的代理方法
methods.add(pm.generateMethod());
}
}
// 爲代理類生成靜態代碼塊,對一些字段進行初始化
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* ============================================================
* Step 3: Write the final class file.
* 步驟3:編寫最終類文件
*/
/*
* Make sure that constant pool indexes are reserved for the following items before starting to write the final class file.
* 在開始編寫最終類文件之前,確保爲下面的項目保留常量池索引。
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
/*
* Disallow new constant pool additions beyond this point, since we are about to write the final constant pool table.
* 設置只讀,在這之前不允許在常量池中增加信息,因爲要寫常量池表
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 次要版本;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 主版本
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 訪問標識;
dout.writeShort(accessFlags);
// u2 本類名;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 父類名;
dout.writeShort(cp.getClass(superclassName));
// u2 接口;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
// u2 字段;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 方法;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 類文件屬性:對於代理類來說沒有類文件屬性;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
}
手動生成字節碼文件
生成字節碼文件的方法:
① :配置類的訪問規則, 默認 sun包下的類不允許訪問
選擇項目----右鍵—properties菜單( alt+enter )
後面一路OK就可以了.
package com.hanker.dynamicproxy;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
public class Test {
public static void main(String[] args) {
testProxy();
}
public static void testProxy() {
//生成代理對象的字節碼文件
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{ProductMangerImpl.class});
try (FileOutputStream fos =
new FileOutputStream(new File("E:\\java20191202\\1.java核心\\第15章 反射\\$Proxy0.class")))
{
fos.write(bytes);
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
生成一個字節碼文件:
反編譯字節碼文件
使用 jd-gui.exe 工具反編譯class文件.
基於CGLIB實現動態代理
CGLIB簡介
CGLIB(Code Generation Library)是一個開源項目!是一個強大的,高性能,高質量的Code生成類庫,它可以在運行期擴展Java類與實現Java接口。Hibernate用它來實現PO(Persistent Object 持久化對象)字節碼的動態生成。CGLIB是一個強大的高性能的代碼生成包。它廣泛的被許多AOP的框架使用,例如Spring AOP爲他們提供方法的interception(攔截)。CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。
除了CGLIB包,腳本語言例如Groovy和BeanShell,也是使用ASM來生成java的字節碼。當然不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
CGLIB依賴兩個jar包:cglib-3.3.0.jar , asm-7.1.jar; 添加到構建路徑.
CGLIB動態代理
創建業務類,注意這個業務類並沒有實現任何接口
package com.hanker.cglib;
//要代理的業務對象
public class ProductMangerImpl {
public ProductMangerImpl() {
System.out.println("創建ProductMangerImpl對象");
}
public int addProduct() {
System.out.println("執行添加商品");
return 1;
}
//該方法不能被子類覆蓋,Cglib是無法代理final修飾的方法
public final int deleteProduct(int no) {
System.out.println("執行刪除商品..."+no);
return 1001;
}
}
創建方法攔截器實現類
package com.hanker.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//實現MethodInterceptor接口的方法攔截器
public class MyMethodInterceptor implements MethodInterceptor{
/**
* obj:cglib生成的代理對象
* method:被代理對象方法
* args:方法入參
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("調用方法之前====前置通知=====");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("調用方法之後====後置通知=====");
return result;
}
}
生成CGLIB代理對象調用目標方法:
package com.hanker.cglib;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
// 指定代理類字節碼文件的路徑
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\cglib");
// 通過CGLIB動態代理獲取代理對象的過程
Enhancer enhancer = new Enhancer();
// 設置enhancer的父類
enhancer.setSuperclass(ProductMangerImpl.class);
// 設置enhancer的回調對象
enhancer.setCallback(new MyMethodInterceptor());
// 創建代理對象
ProductMangerImpl proxy = (ProductMangerImpl) enhancer.create();
//通過代理對象調用目標方法
proxy.addProduct();//帶日誌
int result = proxy.deleteProduct(1);//不帶日誌
System.out.println(result);
}
}
執行效果:
CGLIB動態代理源碼分析
實現CGLIB動態代理必須實現MethodInterceptor(方法攔截器)接口,源碼如下:
package net.sf.cglib.proxy;
public interface MethodInterceptor extends Callback{
/**
* All generated proxied methods call this method instead of the original method.
* The original method may either be invoked by normal reflection using the Method object,
* or by using the MethodProxy (faster).
* @param obj "this", the enhanced object
* @param method intercepted Method
* @param args argument array; primitive types are wrapped
* @param proxy used to invoke super (non-intercepted method); may be called
* as many times as needed
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
* @see MethodProxy
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}
這個接口只有一個intercept()方法,這個方法有4個參數:
1)obj表示增強的對象,即實現這個接口類的一個對象;
2)method表示要被攔截的方法;
3)args表示要被攔截方法的參數;
4)proxy表示要觸發父類的方法對象;
在上面的Test代碼中,通過Enhancer.create()方法創建代理對象,create()方法的源碼:
/**
* Generate a new class if necessary and uses the specified
* callbacks (if any) to create a new object instance.
* Uses the no-arg constructor of the superclass.
* @return a new instance
*/
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}
該方法含義就是如果有必要就創建一個新類,並且用指定的回調對象創建一個新的對象實例,
使用的父類的參數的構造方法來實例化父類的部分。核心內容在createHelper()中,源碼如下:
private Object createHelper() {
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
preValidate()方法校驗callbackTypes、filter是否爲空,以及爲空時的處理。通過newInstance()方法創建EnhancerKey對象,作爲Enhancer父類AbstractClassGenerator.create()方法創建代理對象的參數。
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}
真正創建代理對象方法在nextInstance()方法中,該方法爲抽象類AbstractClassGenerator的一個方法,簽名如下:abstract protected Object nextInstance(Object instance) throws Exception;
在子類Enhancer中實現,實現源碼如下:
protected Object nextInstance(Object instance) {
EnhancerFactoryData data = (EnhancerFactoryData) instance;
if (classOnly) {
return data.generatedClass;
}
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
return data.newInstance(argumentTypes, arguments, callbacks);
}
看看data.newInstance(argumentTypes, arguments, callbacks)方法,
第一個參數爲代理對象的構成器類型,第二個爲代理對象構造方法參數,第三個爲對應回調對象。
最後根據這些參數,通過反射生成代理對象,源碼如下:
/**
* Creates proxy instance for given argument types, and assigns the callbacks.
* Ideally, for each proxy class, just one set of argument types should be used,
* otherwise it would have to spend time on constructor lookup.
* Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},
* with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"
*
* @see #createUsingReflection(Class)
* @param argumentTypes constructor argument types
* @param arguments constructor arguments
* @param callbacks callbacks to set for the new instance
* @return newly created proxy
*/
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
setThreadCallbacks(callbacks);
try {
// Explicit reference equality is added here just in case Arrays.equals does not have one
if (primaryConstructorArgTypes == argumentTypes ||
Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
// If we have relevant Constructor instance at hand, just call it
// This skips "get constructors" machinery
return ReflectUtils.newInstance(primaryConstructor, arguments);
}
// Take a slow path if observing unexpected argument types
return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
} finally {
// clear thread callbacks to allow them to be gc'd
setThreadCallbacks(null);
}
}
最後生成代理對象:
將其反編譯後代碼如下:
public class ProductMangerImpl$$EnhancerByCGLIB$$e5b0daae
extends ProductMangerImpl implements Factory{
....
public final int addProduct(){
...
if (tmp17_14 != null){
// 調用攔截器
Object tmp36_31 = tmp17_14.intercept(this, CGLIB$addProduct$0$Method,
CGLIB$emptyArgs, CGLIB$addProduct$0$Proxy);
tmp36_31;
return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
}
return super.addProduct();
}
....
}
最後我們總結一下JDK動態代理和Gglib動態代理的區別:
1.JDK動態代理是實現了被代理對象的接口,Cglib是繼承了被代理對象。
2.JDK和Cglib都是在運行期生成字節碼,JDK是直接寫Class字節碼,Cglib使用ASM框架寫Class字節碼,Cglib代理實現更復雜,生成代理類比JDK效率低。
3.JDK調用代理方法,是通過反射機制調用,Cglib是通過FastClass機制直接調用方法,Cglib執行效率更高。