代理模式
代理proxy是一種設(shè)計模式,提供對目標對象的另外的訪問模式。好處是可以擴展目標對象的功能。
在實際開發(fā)中,可以不修改別人已經(jīng)寫好的代碼,通過代理的方式來擴展功能。
生活中的例子:我們們要邀請明星,并不是直接聯(lián)系明星,而是通過經(jīng)紀人。
在這里插入圖片描述
1.靜態(tài)代理
靜態(tài)代理需要定義接口或者父類,被代理的對象和代理對象要實現(xiàn)相中的接口或者繼承相同的父類
/**
*接口
*/
public interface IUserDao{
public void work();
}
/**
*接口實現(xiàn) 目標對象
*/
public class UserDao implements IUserDao{
public void work(String begin){
System.out.println("----"+begin+"點開始工作!----");
}
}
/**
* 代理對象,靜態(tài)代理
*/
public class UserDaoProxy implements IUserDao{
//接收保存目標對象
private IUserDao userDao;
public UserDaoProxy(IUserDao userDao){
this.userDao=userDao;
}
public void work() {
System.out.println("---before--");
userDao.work();//執(zhí)行目標對象的方法
System.out.println("---after---");
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
//目標對象
UserDao userDao= new UserDao();
//代理對象,把目標對象傳給代理對象,建立代理關(guān)系
UserDaoProxy proxy = new UserDaoProxy(userDao);
proxy.work();//執(zhí)行的是代理的方法
}
}
1、靜態(tài)代理可以做到在不修改目標對象的功能前提下,對目標功能擴展
2、缺點是由于目標對象和代理對象實現(xiàn)共同的接口,所有會有很多的代理類,一旦接口增加方法,目標對象和代理對象都要維護,維護成本大
動態(tài)代理
1、動態(tài)代理,代理對象不需要實現(xiàn)接口
2、代理對象的生成是利用JDK的API,在內(nèi)存中動態(tài)的構(gòu)建對象。需要指定創(chuàng)建代理或目標對象的接口的類型。
代理類所在的包:java.lang.reflect.Proxy,使用newProxyInstance方法,
該方法為靜態(tài)方法,有三個參數(shù)分別為
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
ClassLoader loader:用哪個類加載器去加載代理對象
Class<?>[] interfaces:動態(tài)代理類需要實現(xiàn)的接口
InvocationHandler h:動態(tài)代理方法在執(zhí)行時,會調(diào)用h里面的invoke方法去執(zhí)行。會把當前執(zhí)行目標對象的方法作為參數(shù)傳入
/**
*代理工廠類
*/
public class ProxyFactory{
//維護目標對象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//給目標對象生成代理對象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("---before--");
//執(zhí)行目標對象方法
Object returnValue = method.invoke(target, args);
System.out.println("---after---");
return returnValue;
}
}
);
}
}
/**
* 測試類
*/
public class Test {
// 目標對象
IUserDao target = new UserDao();
// 給目標對象,創(chuàng)建代理對象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// 執(zhí)行方法
proxy.work('9');
}
結(jié)果
---before--
----9點開始工作!----
---after---
- 動態(tài)代理的代理對象不需要實現(xiàn)接口,但是目標對象需要實現(xiàn)接口。
Cglib代理
- JDK的動態(tài)代理需要目標對象實現(xiàn)接口,而Cglib能代理沒有實現(xiàn)接口的類。它可以在運行期擴展Java類與實現(xiàn)Java接口,提供方法的interception(攔截)。
- Cglib包的底層是通過使用一個小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.
- Cglib動態(tài)構(gòu)建子類,代理的類不能為final,目標對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標對象額外的業(yè)務(wù)方法
/**
* 目標對象,沒有實現(xiàn)任何接口
*/
public class UserDao {
public void work() {
System.out.println("----開始工作!----");
}
}
/**
* Cglib子類代理工廠
*/
public class ProxyFactory implements MethodInterceptor{
//維護目標對象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//給目標對象創(chuàng)建一個代理對象
public Object getProxyInstance(){
//1.工具類
Enhancer en = new Enhancer();
//2.設(shè)置父類
en.setSuperclass(target.getClass());
//3.設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//4.創(chuàng)建子類(代理對象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("befor...");
//執(zhí)行目標對象的方法
Object returnValue = method.invoke(target, args);
System.out.println("after...");
return returnValue;
}
}
/**
* 測試類
*/
public class Test{
@Test
public void test(){
//目標對象
UserDao target = new UserDao();
//代理對象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
proxy.work();
}
}
在Spring的AOP中:
如果加入容器的目標對象有實現(xiàn)接口,用JDK代理
如果目標對象沒有實現(xiàn)接口,用Cglib代理