簡述
代理模式是提供對目標對象另外一種訪問方式的模式;
優點:可以在目標對象實現的基礎上,添加額外的功能,即擴展目標對象的功能;
思想:在添加對目標對象新的操作時,不用修改別人的代碼,只需要通過代碼對象,去擴展目標對象新的功能;
三種代理模式
靜態代理
定義一個接口或者父類, 目標對象和代理對象同時繼承或者實現該接口;
優點: 不用修改目標對象的情況下,對目標對象進行新的擴展
缺點:產生過多的代理對象,造成代碼冗餘;並且當共同實現或繼承同一個父類或接口,當父類或接口改變時,目標對象與代理對象都難以維護;
代碼示例:
/**
* common interface
*/
public interface UserDao{
void save();
}
/**
* target object
*/
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("-----save the data ------");
}
}
/**
* proxy object
*/
public class UserDaoProxy implements UserDao {
private UserDao userDao; //use to receive target object who like UserDaoImpl
public UserDaoProxy(UserDao userDao){
this.userDao = userDao;
}
public void save(){
System.out.println("-----add new function----");
userDao.save();
System.out.println("------add new function----");
}
}
/**
* test method
*/
public class test {
public static void main(String[] args) {
UserDao targetObject = new UserDaoImpl(); //new a target object
//bulid relationships bethween tagert object and proxy object
UserDaoProxy proxyObject = new UserDaoProxy(targetObject);
proxyObject.save();//execute proxy method
}
}
動態代理
特點:代理對象不用實現接口;代理對象的生成,是通過JDK提供的API,動態幫我們在內存中構建代理對象;(需要我們指定目標對象的接口的類型)
JDK中動態生成代理對象的API:
包所在:java.lang.reflect.Proxy
實現方法:
/**
* loader 指定目標對象的類加載器
* interface 目標對象實現的接口的類型
* h 執行目標對象的方法時,會觸發事件處理器的方法,將當前執行目標對象的發放作爲參數傳入
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h )
代碼示例:
/**
* create proxy object factory
*/
public class ProxyFactory() {
//targer object
private Object target;
public ProxyFactory(Obkect target) {
this.target = target;
}
//return a proxy object
public Object getProxyInstance() {
/**
* InvocationHandler接口只定義了一個invoke方法,因此對於這樣的接口,我們不用單獨去定義一個類來實現該接口,
* 而是直接使用一個匿名內部類來實現該接口,new InvocationHandler() {}就是針對InvocationHandler接口的匿名實現類
*
* 在invoke方法編碼指定返回的代理對象乾的工作
* proxy : 把代理對象自己傳遞進來
* method:把代理對象當前調用的方法傳遞進來
* args:把方法參數傳遞進來
*
* 當調用代理對象的person.sing("冰雨");或者 person.dance("江南style");方法時,
* 實際上執行的都是invoke方法裏面的代碼,
* 因此我們可以在invoke方法中使用method.getName()就可以知道當前調用的是代理對象的哪個方法
*/
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getClassInterface(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] arge) {
if (method.getName().equals("save")) {
System.out.println("--execute your new method in here--");
//execute target object method in here
Object returnValue = method.invoke(target, args);
System.out.println("--execute your new method in here--");
}
return returnValue;
}
}
);
}
}
/**
* test
*/
public class test {
public static void main(String[] args) {
UserDao targetObject = new UserDaoImpl();
//create a proxy object
UserDao proxyObject = (UserDao) new ProxyFactory(target).getProxyIntence();
//use proxy object execute target method
proxyObject.save();
}
}
總結:動態代理對象不需要實現接口,但目標對象一定要實現接口,否則JDK無法生成目標對象的代理對象;
Cglib代理
靜態代理和動態代理都的目標對象都是需要實現一個接口的,但是有時候一些對象就是一個獨立的個體,並沒有實現接口,這時候就有了Cglib代理的出現;它是在內存中,構建一個目標對象的子類,從而實現對目標對象的擴展;
代碼示例:
/**
* target object
*/
public class UserDao {
public void save() {
System.out.println("----save one data ---");
}
}
/**
* Cglib procy factory
*/
public class ProxyFactory implements MethodInterceptor{
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//create proxy object
public Object getProxyInstance() {
//tool object
Enhancer en = new Enhancer();
//set target object's class
en.setSuperClass(target.getClass());
//set callback
en.setCallback(this);
//create child class(proxy object)
return en.cteate();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
System.out.println("-- your function --");
Object returnValue = method.invoke(target, args);
System.out.println(" -- your function --");
return returnValue;
}
}
/**
* test method
*/
public class test {
public static void main(String[] args) {
UserDao userDao = new UserDao();
UserDao proxyDao = (UserDao)new ProxyFactory(userDao).getProxyInstance();
proxyDao.save();
}
}
在Spring的AOP中
如果加入容器的目標對象有實現接口,則用動態代理,
如果目標對象沒有實現接口,則用Cglic代理;