java動(dòng)態(tài)代理(jdk與cglib)詳細(xì)解析
JAVA的動(dòng)態(tài)代理
代理模式
代理模式是常用的java設(shè)計(jì)模式,他的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過(guò)濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會(huì)存在關(guān)聯(lián)關(guān)系,一個(gè)代理類的對(duì)象與一個(gè)委托類的對(duì)象關(guān)聯(lián),代理類的對(duì)象本身并不真正實(shí)現(xiàn)服務(wù),而是通過(guò)調(diào)用委托類的對(duì)象的相關(guān)方法,來(lái)提供特定的服務(wù)。
按照代理的創(chuàng)建時(shí)期,代理類可以分為兩種。
靜態(tài)代理:由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼,再對(duì)其編譯。在程序運(yùn)行前,代理類的.class文件就已經(jīng)存在了。
動(dòng)態(tài)代理:在程序運(yùn)行時(shí),運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成。
首先看一下靜態(tài)代理:
1、Count.java
package net.battier.dao;
/**
* 定義一個(gè)賬戶接口
*
* @author Administrator
*
*/
public interface Count {
// 查看賬戶方法
public void queryCount();
// 修改賬戶方法
public void updateCount();
}
2、CountImpl.java
package net.battier.dao.impl;
import net.battier.dao.Count;
/**
* 委托類(包含業(yè)務(wù)邏輯)
*
* @author Administrator
*
*/
public class CountImpl implements Count {
@Override
public void queryCount() {
System.out.println("查看賬戶方法...");
}
@Override
public void updateCount() {
System.out.println("修改賬戶方法...");
}
}
CountProxy.java
package net.battier.dao.impl;
import net.battier.dao.Count;
/**
* 這是一個(gè)代理類(增強(qiáng)CountImpl實(shí)現(xiàn)類)
*
* @author Administrator
*
*/
public class CountProxy implements Count {
private CountImpl countImpl;
/**
* 覆蓋默認(rèn)構(gòu)造器
*
* @param countImpl
*/
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@Override
public void queryCount() {
System.out.println("事務(wù)處理之前");
// 調(diào)用委托類的方法;
countImpl.queryCount();
System.out.println("事務(wù)處理之后");
}
@Override
public void updateCount() {
System.out.println("事務(wù)處理之前");
// 調(diào)用委托類的方法;
countImpl.updateCount();
System.out.println("事務(wù)處理之后");
}
}
3、TestCount.java
package net.battier.test;
import net.battier.dao.impl.CountImpl;
import net.battier.dao.impl.CountProxy;
/**
*測(cè)試Count類
*
* @author Administrator
*
*/
public class TestCount {
public static void main(String[] args) {
CountImpl countImpl = new CountImpl();
CountProxy countProxy = new CountProxy(countImpl);
countProxy.updateCount();
countProxy.queryCount();
}
}
觀察代碼可以發(fā)現(xiàn)每一個(gè)代理類只能為一個(gè)接口服務(wù),這樣一來(lái)程序開(kāi)發(fā)中必然會(huì)產(chǎn)生過(guò)多的代理,而且,所有的代理操作除了調(diào)用的方法不一樣之外,其他的操作都一樣,則此時(shí)肯定是重復(fù)代碼。解決這一問(wèn)題最好的做法是可以通過(guò)一個(gè)代理類完成全部的代理功能,那么此時(shí)就必須使用動(dòng)態(tài)代理完成。
再來(lái)看一下動(dòng)態(tài)代理:
JDK動(dòng)態(tài)代理中包含一個(gè)類和一個(gè)接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
參數(shù)說(shuō)明:
Object proxy:指被代理的對(duì)象。
Method method:要調(diào)用的方法
Object[] args:方法調(diào)用時(shí)所需要的參數(shù)
可以將InvocationHandler接口的子類想象成一個(gè)代理的最終操作類,替換掉ProxySubject。
Proxy類:
Proxy類是專門完成代理的操作類,可以通過(guò)此類為一個(gè)或多個(gè)接口動(dòng)態(tài)地生成實(shí)現(xiàn)類,此類提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
參數(shù)說(shuō)明:
ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實(shí)例
Ps:類加載器
在Proxy類中的newProxyInstance()方法中需要一個(gè)ClassLoader類的實(shí)例,ClassLoader實(shí)際上對(duì)應(yīng)的是類加載器,在Java中主要有一下三種類加載器;
Booststrap ClassLoader:此加載器采用C++編寫,一般開(kāi)發(fā)中是看不到的;
Extendsion ClassLoader:用來(lái)進(jìn)行擴(kuò)展類的加載,一般對(duì)應(yīng)的是jre\lib\ext目錄中的類;
AppClassLoader:(默認(rèn))加載classpath指定的類,是最常使用的是一種加載器。
動(dòng)態(tài)代理
與靜態(tài)代理類對(duì)照的是動(dòng)態(tài)代理類,動(dòng)態(tài)代理類的字節(jié)碼在程序運(yùn)行時(shí)由Java反射機(jī)制動(dòng)態(tài)生成,無(wú)需程序員手工編寫它的源代碼。動(dòng)態(tài)代理類不僅簡(jiǎn)化了編程工作,而且提高了軟件系統(tǒng)的可擴(kuò)展性,因?yàn)镴ava 反射機(jī)制可以生成任意類型的動(dòng)態(tài)代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動(dòng)態(tài)代理類的能力。
動(dòng)態(tài)代理示例:
1、BookFacade.java
package net.battier.dao;
public interface BookFacade {
public void addBook();
}
2、BookFacadeImpl.java
package net.battier.dao.impl;
import net.battier.dao.BookFacade;
public class BookFacadeImpl implements BookFacade {
@Override
public void addBook() {
System.out.println("增加圖書(shū)方法。。。");
}
}
BookFacadeProxy.java
package net.battier.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK動(dòng)態(tài)代理代理類
*
* @author student
*
*/
public class BookFacadeProxy implements InvocationHandler {
private Object target;
/**
* 綁定委托對(duì)象并返回一個(gè)代理類
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//取得代理對(duì)象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要綁定接口(這是一個(gè)缺陷,cglib彌補(bǔ)了這一缺陷)
}
@Override
/**
* 調(diào)用方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result=null;
System.out.println("事物開(kāi)始");
//執(zhí)行方法
result=method.invoke(target, args);
System.out.println("事物結(jié)束");
return result;
}
}
3、TestProxy.java
package net.battier.test;
import net.battier.dao.BookFacade;
import net.battier.dao.impl.BookFacadeImpl;
import net.battier.proxy.BookFacadeProxy;
public class TestProxy {
public static void main(String[] args) {
BookFacadeProxy proxy = new BookFacadeProxy();
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
但是,JDK的動(dòng)態(tài)代理依靠接口實(shí)現(xiàn),如果有些類并沒(méi)有實(shí)現(xiàn)接口,則不能使用JDK代理,這就要使用cglib動(dòng)態(tài)代理了。
Cglib動(dòng)態(tài)代理
JDK的動(dòng)態(tài)代理機(jī)制只能代理實(shí)現(xiàn)了接口的類,而不能實(shí)現(xiàn)接口的類就不能實(shí)現(xiàn)JDK的動(dòng)態(tài)代理,cglib是針對(duì)類來(lái)實(shí)現(xiàn)代理的,他的原理是對(duì)指定的目標(biāo)類生成一個(gè)子類,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類進(jìn)行代理。
示例
1、BookFacadeCglib.java
package net.battier.dao;
public interface BookFacade {
public void addBook();
}
2、BookCadeImpl1.java
package net.battier.dao.impl;
/**
* 這個(gè)是沒(méi)有實(shí)現(xiàn)接口的實(shí)現(xiàn)類
*
* @author student
*
*/
public class BookFacadeImpl1 {
public void addBook() {
System.out.println("增加圖書(shū)的普通方法...");
}
}
3、BookFacadeProxy.java
package net.battier.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 使用cglib動(dòng)態(tài)代理
*
* @author student
*
*/
public class BookFacadeCglib implements MethodInterceptor {
private Object target;
/**
* 創(chuàng)建代理對(duì)象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回調(diào)方法
enhancer.setCallback(this);
// 創(chuàng)建代理對(duì)象
return enhancer.create();
}
@Override
// 回調(diào)方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("事物開(kāi)始");
proxy.invokeSuper(obj, args);
System.out.println("事物結(jié)束");
return null;
}
}
4、TestCglib.java
package net.battier.test;
import net.battier.dao.impl.BookFacadeImpl1;
import net.battier.proxy.BookFacadeCglib;
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}
相關(guān)文章
使用log4j2打印mybatis的sql執(zhí)行日志方式
這篇文章主要介紹了使用log4j2打印mybatis的sql執(zhí)行日志方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09spring boot項(xiàng)目application.properties文件存放及使用介紹
這篇文章主要介紹了spring boot項(xiàng)目application.properties文件存放及使用介紹,我們的application.properties文件中會(huì)有很多敏感信息,大家在使用過(guò)程中要多加小心2021-06-06Java CAS底層實(shí)現(xiàn)原理實(shí)例詳解
這篇文章主要介紹了Java CAS底層實(shí)現(xiàn)原理實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01JDK9為何要將String的底層實(shí)現(xiàn)由char[]改成了byte[]
String 類的源碼已經(jīng)由?char[]?優(yōu)化為了?byte[]?來(lái)存儲(chǔ)字符串內(nèi)容,為什么要這樣做呢?本文就詳細(xì)的介紹一下,感興趣的可以了解一下2022-03-03struts1實(shí)現(xiàn)簡(jiǎn)單的登錄功能實(shí)例(附源碼)
本篇文章主要介紹了struts1實(shí)現(xiàn)簡(jiǎn)單的登錄功能實(shí)例(附源碼),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04