动态代理设计模式
這個(gè)時(shí)候講一下動(dòng)態(tài)的代理,動(dòng)態(tài)和靜態(tài)到底有什么區(qū)別呢,其實(shí)剛才我已經(jīng)詳細(xì)給大家說(shuō)一下,靜態(tài)代理需要生成目標(biāo)代理對(duì)象,動(dòng)態(tài)代理是不需要生成目標(biāo)代理對(duì)象,如果你們用動(dòng)態(tài)的話,就不會(huì)有UserServiceProxy這一層了,這一層是在內(nèi)存里面動(dòng)態(tài)虛擬出來(lái)的,所以給你們講一下,你們也知道,動(dòng)態(tài)代理核心是通過(guò)JDK動(dòng)態(tài)代理和CGLIB這個(gè)方式進(jìn)行實(shí)現(xiàn)的,那這個(gè)時(shí)候我再回顧一遍,JDK動(dòng)態(tài)代理是采用反射虛擬生成,CGLIB是基于ASM字節(jié)碼技術(shù)虛擬生成代理類(lèi),這我們講一下,這個(gè)代碼我就不去寫(xiě)了,寫(xiě)的話就比較浪費(fèi)時(shí)間,核心就講事務(wù)里面去,動(dòng)態(tài)代理我就不細(xì)說(shuō)了,動(dòng)態(tài)代理是不需要生成代理類(lèi)對(duì)象的,那么怎么做呢,第一種方式是JDK動(dòng)態(tài)代理方式,底層是采用反射的機(jī)制,這段代碼我們來(lái)講一下
package com.learn.proxy001;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;import com.learn.service.MemberService;
import com.learn.service.UserService;
import com.learn.service.impl.MemberServiceImpl;
import com.learn.service.impl.UserServiceImpl;/*** 每次生成動(dòng)態(tài)代理類(lèi)對(duì)象時(shí),實(shí)現(xiàn)了InvocationHandler接口的調(diào)用處理器對(duì)象* 實(shí)現(xiàn)動(dòng)態(tài)代理的時(shí)候都需要實(shí)現(xiàn)這樣的一個(gè)接口* 實(shí)現(xiàn)接口它是干嘛用的呢* * @author Leon.Sun**/
public class InvocationHandlerImpl implements InvocationHandler {/*** 首先要把這樣的一個(gè)目標(biāo)代理對(duì)象傳進(jìn)來(lái)* */private Object target;// 這其實(shí)業(yè)務(wù)實(shí)現(xiàn)類(lèi)對(duì)象,用來(lái)調(diào)用具體的業(yè)務(wù)方法// 通過(guò)構(gòu)造函數(shù)傳入目標(biāo)對(duì)象public InvocationHandlerImpl(Object target) {this.target = target;}/*** 在實(shí)現(xiàn)方法之前或之后做一個(gè)處理* * 在你方法之前和之后* 做了攔截* 使用JDK* * * */public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;/*** 使用JDK動(dòng)態(tài)代理開(kāi)啟事務(wù)*/System.out.println("使用jdk動(dòng)態(tài)代理 開(kāi)啟事務(wù)");result = method.invoke(target, args);/*** 使用JDK動(dòng)態(tài)代理提交事務(wù)*/System.out.println("使用jdk動(dòng)態(tài)代理 提交事務(wù)");/*** 這里返回一個(gè)result結(jié)果*/return result;}/*** 這里我寫(xiě)了一個(gè)main函數(shù)* @param args* @throws NoSuchMethodException* @throws SecurityException* @throws InstantiationException* @throws IllegalAccessException* @throws IllegalArgumentException* @throws InvocationTargetException*/public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,IllegalAccessException, IllegalArgumentException, InvocationTargetException {// 被代理對(duì)象// IUserDao userDao = new UserDao();/*** 這里寫(xiě)一個(gè)UserService的實(shí)現(xiàn)* 在這里為就可以拿到UserService* 拿到UserService之后* 然后在這邊怎么處理呢* * 我只要把我們的目標(biāo)代理對(duì)象傳進(jìn)去就=行了* 它會(huì)自動(dòng)的在你方法之前之后做這樣的處理* 就是這樣的* 有些人可能沒(méi)有學(xué)過(guò)AOP編程* 沒(méi)有學(xué)過(guò)代理設(shè)計(jì)模式* 像靜態(tài)代理是需要有一個(gè)接口的* 什么接口呢* 你必須有個(gè)接口* CGLIB可能不需要你有這個(gè)接口* 這個(gè)大體我說(shuō)一下* 后面我們使用Spring事務(wù)的時(shí)候比這個(gè)簡(jiǎn)單* 我們?cè)贏OP的基礎(chǔ)上使用事務(wù)* 就方便多了* 如果你真的需要寫(xiě)的情況下* 那這些代碼都要寫(xiě)的* 這里可以傳入任何的類(lèi)* 這樣就可以在方法之前和之后做處理的* 這個(gè)就比較簡(jiǎn)單* 你們注意看* 靜態(tài)代理是有這么一個(gè)靜態(tài)代理類(lèi)對(duì)象的* 他每一個(gè)都要這么去寫(xiě)的* 記住動(dòng)態(tài)代理是不需要寫(xiě)目標(biāo)代理對(duì)象的* 是沒(méi)有寫(xiě)代理對(duì)象的* 而且你們要記住啊* 我這里可以支持很多方法和任何接口* 都可以做一個(gè)這樣的代理的* * */UserService userService = new UserServiceImpl();
// InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userService);/*** ClassLoader有多少人了解過(guò)* 可能不了解ClassLoader是干嘛用的* 他其實(shí)可以加載類(lèi)的信息* 加載到JVM的內(nèi)存里面去* 這個(gè)我記得是昨天剛講了* 這個(gè)我相信你們都記得* 熱部署其實(shí)就是通過(guò)ClassLoader進(jìn)行實(shí)現(xiàn)的* 動(dòng)態(tài)的把最新的字節(jié)碼文件讀取到JVM里面去* 就是這樣的一個(gè)原理*/
// ClassLoader loader = userService.getClass().getClassLoader();
// Class<?>[] interfaces = userService.getClass().getInterfaces();// 主要裝載器、一組接口及調(diào)用處理動(dòng)態(tài)代理實(shí)例/*** 這里我們可以強(qiáng)轉(zhuǎn)一下UserService*/
// UserService newProxyInstance = (UserService) Proxy.newProxyInstance(loader, interfaces,
// invocationHandlerImpl);/*** 然后ADD方法就OK了* 不過(guò)這個(gè)例子非常簡(jiǎn)單* */
// newProxyInstance.add();/*** new一個(gè)MemberServiceImpl* 是不是這樣的* 注意這里不需要代理對(duì)象* 我們這里沒(méi)有封裝* 正常只需要傳一個(gè)類(lèi)進(jìn)來(lái)* * */MemberService memberServiceImpl = new MemberServiceImpl();InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(memberServiceImpl);ClassLoader loader = memberServiceImpl.getClass().getClassLoader();Class<?>[] interfaces = memberServiceImpl.getClass().getInterfaces();// 主要裝載器、一組接口及調(diào)用處理動(dòng)態(tài)代理實(shí)例MemberService newProxyInstance = (MemberService) Proxy.newProxyInstance(loader, interfaces,invocationHandlerImpl);/*** JDK動(dòng)態(tài)代理開(kāi)啟事務(wù)* 這里是方法業(yè)務(wù)* 在方法之前和之后做一些業(yè)務(wù)處理* 只要任何有接口的類(lèi)* 我這里是寫(xiě)死的* 因?yàn)橹皇侵vDEMO* 還有CGLIB就先不講了* 因?yàn)闀r(shí)間有限* 也是一樣的道理* 也是比較簡(jiǎn)單* 只不過(guò)底層用的ASM* 就是通過(guò)字節(jié)碼技術(shù)生成一個(gè)代理類(lèi)的* 目標(biāo)類(lèi)寫(xiě)死沒(méi)關(guān)系的* 這只是舉個(gè)列子* */newProxyInstance.memberAdd();}
}
package com.learn.service;public interface MemberService {public void memberAdd();
}
package com.learn.service.impl;import com.learn.service.MemberService;/*** 如果我們要實(shí)現(xiàn)靜態(tài)代理的話* 我們就需要再寫(xiě)一個(gè)代理類(lèi)* 那就寫(xiě)的很麻煩* * * @author Leon.Sun**/
public class MemberServiceImpl implements MemberService {public void memberAdd() {System.out.println("memberAdd");}}
動(dòng)態(tài)代理
什么是動(dòng)態(tài)代理
1.代理對(duì)象,不需要實(shí)現(xiàn)接口
2.代理對(duì)象的生成,是利用JDK的API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類(lèi)型)
3.動(dòng)態(tài)代理也叫做:JDK代理,接口代理
JDK動(dòng)態(tài)代理
1)原理:是根據(jù)類(lèi)加載器和接口創(chuàng)建代理類(lèi)(此代理類(lèi)是接口的實(shí)現(xiàn)類(lèi),所以必須使用接口 面向接口生成代理,
位于java.lang.reflect包下)
2)實(shí)現(xiàn)方式:
1. 通過(guò)實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器
IvocationHandler handler = new InvocationHandlerImpl(…);
2. 通過(guò)為Proxy類(lèi)指定ClassLoader對(duì)象和一組interface創(chuàng)建動(dòng)態(tài)代理類(lèi)
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3. 通過(guò)反射機(jī)制獲取動(dòng)態(tài)代理類(lèi)的構(gòu)造函數(shù),其參數(shù)類(lèi)型是調(diào)用處理器接口類(lèi)型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4. 通過(guò)構(gòu)造函數(shù)創(chuàng)建代理類(lèi)實(shí)例,此時(shí)需將調(diào)用處理器對(duì)象作為參數(shù)被傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
缺點(diǎn):jdk動(dòng)態(tài)代理,必須是面向接口,目標(biāo)業(yè)務(wù)類(lèi)必須實(shí)現(xiàn)接口
CGLIB動(dòng)態(tài)代理
原理:利用asm開(kāi)源包,對(duì)代理對(duì)象類(lèi)的class文件加載進(jìn)來(lái),通過(guò)修改其字節(jié)碼生成子類(lèi)來(lái)處理。
什么是CGLIB動(dòng)態(tài)代理
使用cglib[Code Generation Library]實(shí)現(xiàn)動(dòng)態(tài)代理,并不要求委托類(lèi)必須實(shí)現(xiàn)接口,底層采用asm字節(jié)碼生成框架生成代理類(lèi)的字節(jié)碼
CGLIB動(dòng)態(tài)代理與JDK動(dòng)態(tài)區(qū)別
java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類(lèi),在調(diào)用具體方法前調(diào)用InvokeHandler來(lái)處理。
而cglib動(dòng)態(tài)代理是利用asm開(kāi)源包,對(duì)代理對(duì)象類(lèi)的class文件加載進(jìn)來(lái),通過(guò)修改其字節(jié)碼生成子類(lèi)來(lái)處理。
Spring中。
1、如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP
2、如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP
3、如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)了接口,必須采用CGLIB庫(kù),spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換
JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理,而不能針對(duì)類(lèi) 。
CGLIB是針對(duì)類(lèi)實(shí)現(xiàn)代理,主要是對(duì)指定的類(lèi)生成一個(gè)子類(lèi),覆蓋其中的方法 。
因?yàn)槭抢^承,所以該類(lèi)或方法最好不要聲明成final ,final可以阻止繼承和多態(tài)。
?
超強(qiáng)干貨來(lái)襲 云風(fēng)專(zhuān)訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
- 上一篇: 静态代理设计模式
- 下一篇: 使用springaop技术面向切面编程