一.什麼是代理模式
1.代理(proxy)是一種設計模式,提供了可通過代理對象訪問目標對象的功能,這樣做的好處在於:可以在目標對象功能實現的基礎上,增加額外的功能補充,擴展目標對象的功能。
二.靜態代理
在靜態代理中,代理對象和目標對象都必須實現同一個接口或繼承同一個父類,因此要定義一個接口或抽象類。
package Test.Proxy;
public interface IUserDao {
void save();
}
package Test.Proxy;
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("已保存數據");
}
}
package Test.Proxy;
public class UserProxy implements IUserDao {
private IUserDao target;
public UserProxy(IUserDao iuseDao){
this.target = iuseDao;
}
public void save(){
System.out.println("開啓事物...");
target.save();
System.out.println("關閉事物...");
}
public static void main(String[] args){
UserDao userDao = new UserDao();
UserProxy userProxy = new UserProxy(userDao);
userProxy.save();
}
}
靜態代理:可以做到不修改目標類的代碼情況下,實現功能的擴展。
也存在一些問題,因爲代理對象和目標對象都要實現同一個接口,或者繼承同一個父類,所以當代理類太多時,修改接口就會需要維護代理對象,加大工作量。
三.動態代理
動態代理主要是在程序運行時jvm才爲被代理對象生成代理對象。
動態代理是在程序運行時通過反射機制動態創建的。主要是用到了java.lang.reflect.Proxy類和InvocationHandler接口。
JDK代理是常用的一種動態代理方式,JDK中生成代理對象的代理類就是Proxy。
//目標類接口
interface IDog{
void run();
}
//目標類
class GunDog implements IDog{
@Override
public void run() {
System.out.println("獵狗在跑");
}
}
class DogUtils{
public static void method1() {
System.out.println("增強方式一");
}
public static void method2() {
System.out.println("增強方式二");
}
}
class MyInvocationHandle implements InvocationHandler{
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
DogUtils.method1();
method.invoke(target, args);
DogUtils.method2();
return null;
}
}
//生產代理對象的工廠
class MyProxyFactory{
public static Object getProxy(Object target) {
MyInvocationHandle handle = new MyInvocationHandle();
handle.setTarget(target);
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handle);
return proxy;
}
}
public class ProxyDemo {
public static void main(String[] args) {
IDog dog = new GunDog();
IDog proxy =(IDog) MyProxyFactory.getProxy(dog);
proxy.run();
}
JDK代理,代理類不需要實現接口,但目標對象所在的類必須要實現接口,否則無法使用JDK動態代理。
CGLIB代理:
上面的靜態代理和動態代理模式有個相同點就是都要求目標對象是實現一個接口的對象,然而並不是任何對象都會實現一個接口,也存在沒有實現任何的接口的對象,
這時就可以使用繼承目標類以目標對象子類的方式實現代理,這種方法就叫做:Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
使用JDK動態代理有一個限制,就是被代理的對象必須實現一個或多個接口,若想代理沒有實現接口的類,就需要使用Cglib實現.
需要asm和cglib 這兩個jar包。
需要注意的是,cglib原理是通過字節碼技術爲一個類創建子類,並在子類攔截所有父類方法的調用,順勢織入橫切邏輯,但因爲採用的是繼承,所以不能對final修飾的類進行代理。
public class CglibProxy {
public static void main(String[] args) {
int[] arr = new int[100000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 1000);
}
//實例化一個增強器,也就是cglib中的一個class generator
Enhancer enhancer = new Enhancer();
//設置目標類
enhancer.setSuperclass(ArraySort2.class);
//設置攔截對象,這裏直接使用匿名內部類寫法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object , Method method, Object[] args, MethodProxy proxy) throws Throwable {
String sortName = method.getName();
switch (sortName) {
case "bubbleSort":
sortName = "冒泡排序";
break;
case "selectSort":
sortName = "選擇排序";
break;
case "quickSort":
sortName = "快速排序";
break;
default:
break;
}
long start = System.currentTimeMillis();
//此處一定要使用proxy的invokeSuper方法來調用目標類的方法
proxy.invokeSuper(object, args);
long end = System.currentTimeMillis();
System.out.println("本次" + sortName + "的執行時間爲: " + (end -start) + "ms");
return null;
}
});
//生成代理類並返回一個實例
ArraySort2 arraySort = (ArraySort2) enhancer.create();
arraySort.bubbleSort(arr);
arraySort.selectSort(arr);
arraySort.quickSort(arr);
}
}
class ArraySort2{
public void quickSort(int[] arr) {
Arrays.sort(arr);
}
public void selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = 0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
public void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎。
在springAOP面向切面中:
如果目標對象實現了接口,可以用jdk動態代理。
如果沒有實現接口,使用cglib動態代理。