1.代理模式概述
1.1什么是代理模式
ProxyPattern(即:代理模式)23種常用的面向?qū)ο筌浖脑O(shè)計模式之一。
代理模式的定義:為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標(biāo)對象之間起到中介的作用。
作用:增強一個類中的某個方法.對程序進行擴展. Spring框架中AOP.
1.2動態(tài)代理介紹
動態(tài)代理它可以直接給某一個目標(biāo)(被代理 對象)對象(實現(xiàn)了某個或者某些接口)生成一個代理對象,而不需要代理類存在。
動態(tài)代理與代理模式原理是一樣的,只是它沒有具體的代理類,直接通過反射生成了一個代理對象。
動態(tài)代理的分類
- jdk提供一個Proxy類可以直接給實現(xiàn)接口類的對象直接生成代理對象
- spring中動態(tài)代理:cglib
2.jdk中的動態(tài)代理的使用
2.1API介紹
Java.lang.reflect.Proxy類可以直接生成一個代理對象
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一個代理對象
參數(shù)1:ClassLoader loader代理對象的類加載器 一般使用被代理對象的類加載器
參數(shù)2:Class<?>[] interfaces代理對象的要實現(xiàn)的接口 一般使用的被代理對象實現(xiàn)的接口
參數(shù)3:InvocationHandler h(接口)執(zhí)行處理類
InvocationHandler中的invoke(Object proxy, Method method, Object[] args)方法:調(diào)用代理類的任何方法,此方法都會執(zhí)行
-參數(shù)3.1Object proxy:代理對象(慎用)
-參數(shù)3.2Method method:當(dāng)前執(zhí)行的方法
-參數(shù)3.3Object[] args:當(dāng)前執(zhí)行的方法運行時傳遞過來的參數(shù)
返回值:當(dāng)前方法執(zhí)行的返回值
2.2代碼實現(xiàn)
/** 規(guī)范接口
* Created by liangtong.
*/
public interface Actor {
/**
* 唱歌
* @return
*/
public String sing();
/**
* 跳舞
* @return
*/
public String dance();
}
/** 目標(biāo)類:劉德華類,需要被代理的類。
* Created by liangtong.
*/
public class Liudehua implements Actor {
@Override
public String sing() {
return "華仔唱歌";
}
@Override
public String dance() {
return "華仔跳舞";
}
}
public class TestProxy {
/*
1.工具類 Proxy.newProxyInstance(); 運行時創(chuàng)建代理類
Object object = Proxy.newProxyInstance()
2. 參數(shù)
參數(shù)1:類加載器,負(fù)責(zé)將新創(chuàng)建的類添加到內(nèi)存中。
參數(shù)2:確定需要實現(xiàn)接口們,java可以多實現(xiàn),需要同一個數(shù)組。
參數(shù)3:處理類,接口InvocationHandler
//調(diào)用代理類的每一個方法,都將執(zhí)行處理類的invoke方法。
參數(shù)a) proxy 代理類,一般沒用
參數(shù)b) method 當(dāng)前執(zhí)行的方法
參數(shù)c) args 方法的實際參數(shù)
*/
@Test
public void testDemo2(){
//目標(biāo)類
Actor target = new Liudehua();
//1 類加載 代理對象的類加載器 責(zé)將新創(chuàng)建的類添加到內(nèi)存中
ClassLoader classLoader = TestProxy.class.getClassLoader();
//2 接口 代理對象的要實現(xiàn)的接口
Class[] interfaces = {Actor.class};
//3 處理類,接口InvocationHandler
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理收錢之后!");
return method.invoke(target,args);
}
};
//創(chuàng)建目標(biāo)類
Actor actor = (Actor) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
//調(diào)用方法并打印
System.out.println(actor.sing());
System.out.println(actor.dance());
}
}
2.4代碼實現(xiàn)(加強)
public interface Person {
void eat();
void sleep(Integer hours);
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Wash {
}
public class Student implements Person {
@Override
public void eat() {
System.out.println("學(xué)生在吃飯");
}
@Wash
@Override
public void sleep(Integer hours) {
System.out.println("學(xué)生在睡覺,睡了" + hours + "個小時");
}
}
public class PersonFactory {
public static Person getPerson() {
//增強
Student student = new Student();
ClassLoader classLoader = student.getClass().getClassLoader();
Class<?>[] interfaces = student.getClass().getInterfaces();
Person obj = (Person) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
/**
*
* @param proxy 代理對象
* @param method 代理方法
* @param args 調(diào)用代理方法時傳遞的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?>[] argClass = null;
if (args != null) {
argClass = new Class[args.length];
//獲取所有參數(shù)的類型
for (int i = 0; i < args.length; i++) {
argClass[i] = args[i].getClass();
}
}
//獲取方法名
String methodName = method.getName();
//獲取原始的方法
Method originMethod = student.getClass().getMethod(methodName, argClass);
System.out.println(originMethod);
//判斷方法是否需要增強
if (originMethod.isAnnotationPresent(Wash.class)) {
System.out.println("之前洗漱");
Object obj = method.invoke(student, args);
System.out.println("之后洗漱");
return obj;
}
return method.invoke(student, args);
}
});
return obj;
}
}
public class StudentTest {
@Test
public void test() {
Person person = PersonFactory.getPerson();
person.eat();
person.sleep(8);
}
}