一、前言
1、代理模式概念
爲一個對象提供一個替身,以控制對這個對象的訪問,即通過代理對象訪問目標對象(被代理對象),這樣做的好處:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。
被代理對象可以是遠程對象、創建開銷大的對象和需要安全控制的對象。
2、分類
java中的代理模式分爲三種:靜態代理、動態代理(JDK代理、接口代理)、Cglib代理。
其中Cglib代理有些書上也把它歸爲了動態代理。
二、示例
1、靜態代理
在靜態代理當中,不管是被代理對象(也稱目標對象),還是代理對象,都需要去實現共同的接口(或者是繼承共同的父類)。
優點: 在不修改目標對象功能的前提下,能夠通過代理對象對目標功能進行擴展。
缺點: 因爲代理對象與目標對象需要實現相同的接口,所以會有很多代理類。
UML類圖如下圖所示:
(其中TeacherDao是被代理對象,TeacherDaoProxy是代理對象)
代碼:
- ITeacherDao.java
public interface ITeacherDao
{
public abstract void teach();
}
- TeacherDao.java
public class TeacherDao implements ITeacherDao
{
@Override
public void teach()
{
System.out.println("老師正在上課。。。。。");
}
}
- TeacherDaoProxy.java
public class TeacherDaoProxy implements ITeacherDao
{
private ITeacherDao dao;
public TeacherDaoProxy(ITeacherDao dao)
{
this.dao=dao;
}
@Override
public void teach()
{
System.out.println("準備上課。。。");
dao.teach();
System.out.println("結束上課。。。");
}
}
- Client.java
public class Client
{
public static void main(String[] args)
{
TeacherDaoProxy teacherDaoroxy=new TeacherDaoProxy(new TeacherDao());
teacherDaoroxy.teach();
}
}
2、動態代理(JDK代理、接口代理)
動態代理與靜態代理比較明顯的一個區別是,動態代理中的代理對象不需要去實現接口,僅要求目標對象(亦稱被代理對象)去實現接口。
代理對象的生成是利用JDK的API,動態地在內存中構建對象。
用到的是java.lang.reflect.Proxy這個類中的一個靜態方法newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h),
該方法接收三個參數,分別是:
loader - 定義代理類的類加載器
interfaces - 代理類要實現的接口列表
h - 指派方法調用的調用處理程序
UML類圖如下:
代碼:
- ITeacherDao.java
public interface ITeacherDao
{
void teach();
}
- TeacherDao.java
public class TeacherDao implements ITeacherDao
{
@Override
public void teach()
{
System.out.println("教師正在上課。。。");
}
}
- ProxyFactory.java
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("開始上課。。。");
Object object=method.invoke(target, args);
System.out.println("結束代理。。。");
return object;
}
}
);
}
}
- Client.java
public class Client
{
public static void main(String[] args)
{
ITeacherDao dao=new TeacherDao();
ProxyFactory factory=new ProxyFactory(dao);
ITeacherDao instance=(ITeacherDao)factory.getProxyInstance();
instance.teach();
}
}
3、Cglib代理
Cglib代理與前面兩種代理不同的是,目標對象不需要去實現接口,它使用目標對象的子類來實現代理,Cglib代理也稱爲子類代理,有些書也將其歸爲動態代理。
Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口,被許多AOP框架使用,例如Spring AOP,實現方法攔截。
Cglib包的底層使用字節碼處理框架ASM來轉換字節碼並生成新的類。
要想使用Cglib代理,首先得導入jar包(共4個),引入依賴。點我下載jar包 。密碼:c6ca
Cglib代理的UML類圖如下:
代碼:
- TeacherDao.java
public class TeacherDao
{
public String teach()
{
System.out.println("老師正在授課中……Cglib代理,目標對象無需實現接口!");
return "hello";
}
}
- ProxyFactory.java
public class ProxyFactory implements MethodInterceptor
{
private Object target;
public ProxyFactory(Object target)
{
this.target=target;
}
public Object getProxyInstance()
{
Enhancer enhancer=new Enhancer();//創建一個工具類
enhancer.setSuperclass(target.getClass());//設置父類
enhancer.setCallback(this);//設置回調函數
return enhancer.create();//創建子類對象,即代理對象
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
{
System.out.println("代理開始~");
Object object=arg1.invoke(target, arg2);
System.out.println("代理結束~");
return object;
}
}
- Client.java
public class Client
{
public static void main(String[] args)
{
ProxyFactory proxyFactory=new ProxyFactory(new TeacherDao());
TeacherDao teacherDao=(TeacherDao)proxyFactory.getProxyInstance();
String string=teacherDao.teach();
System.out.println("方法的返回值:"+string);
}
}
三、總結
- 靜態代理中,目標對象和代理對象都需要實現接口。
- 動態代理中,目標對象需要實現接口。
- Cglib代理中,目標對象不需要實現接口。