1. 程式人生 > >java設計模式--代理模式(靜態,jdk,cglib)

java設計模式--代理模式(靜態,jdk,cglib)

設計思路:

 代理(Proxy)是一種設計模式,提供了對目標物件另外的訪問方式;即通過代理物件訪問目標物件.這樣做的好處是:可以在目標物件實現的基礎上, 增強額外的功能操作,即擴充套件目標物件的功能.這裡使用到程式設計中的一個思想:不要隨意去修改別人已經寫好的程式碼或者方法,如果需改修改,可以通過代理的方式來擴充套件該方法

實際應用場景: aop

案列:

        通俗說法:談戀愛,你不想去送禮物,不想幹那些比較煩人並且不重要的事(當然重要的還是我們自己來是不),你就叫個人來代替你去送禮物等(或者我就壓根不會送禮物,需要代理擴充套件)其實你就要直接和她戀愛(這裡不寫太直接了,你們懂就好了),這就是代理模式(注意:此方法需謹慎現實別模仿,不然就搞不好就給別人做嫁衣了,到時候別跑來罵我)


程式碼:

靜態代理:

介面:

package com.sl.demo.proxy;
/**
 * 介面
 * @author pengkun
 *
 */
public interface Subject {
	
	/**
	 * 約會
	 */
	void engagement();
	
}

目標類:

package com.sl.demo.proxy;
/**
 * 小明同學
 * @author pengkun
 *
 */
public class XiaoMingStudent implements Subject {

	@Override
	public void engagement() {
		System.out.println("哈哈,小明同學約會去絡!");
	}

}

代理:

package com.sl.demo.proxy;


/**
 * 代理類
 * @author pengkun
 *
 */
public class Proxy implements Subject{
	//需要代理的學生
	private Subject student;
	
	//建立建構函式,你總得讓我知道給誰代理吧
	public Proxy(Subject student) {
		super();
		this.student = student;
	}
	//代理擴充套件送花
	public void sendFlower() {
		System.out.println("Proxy替小明送花");
	}
	//代理擴充套件送巧克力
	public void sendChocolate() {
		System.out.println("Proxy替小明送巧克力");
	}

	@Override
	public void engagement() {
		//其他雜活累活讓代理去幹
		this.sendFlower();
		this.sendChocolate();
		//約會肯定小明同學自己來啦
		student.engagement();
		System.out.println("搞定收工");
		
	}

}

測試:

@Test
	public void test() {
		//目標物件
		Subject xm=new XiaoMingStudent();
		//建立代理,給目標物件
		Proxy proxy=new Proxy(xm);
		proxy.engagement();
	}

結果:


可以看出,代理類擴充套件了送花和送巧克力的方法,而小明同學只做最爽的那下就行了。。。

靜態代理在使用時,需要定義介面或者父類,被代理物件與代理物件一起實現相同的介面或者是繼承相同父類.

總結:

優:可以不修改目標類的情況擴充套件功能,

缺:因為要實現或繼承同一個介面所有代理類會很多,而且每次擴充套件功能的時候都要修改代理和介面

那怎麼解決呢?就是下面講的動態代理了

動態代理:也叫jdk動態代理

特點:代理物件不需要實現介面,當是目標物件必須實現

package com.sl.demo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 建立動態代理物件
 * jdk代理
 * @author pengkun
 *
 */
public class ProxyFactory {
	//持有一個目標物件
	private Object target;

	public ProxyFactory(Object target) {
		super();
		this.target = target;
	}
	//給目標物件建立代理物件
	public Object getProxyInstance() {
		//1.loader:指定當前目標物件使用類載入器,獲取載入器的方法是固定的
		//2.interfaces:目標物件實現的介面的型別,使用泛型方式確認型別
		//3.h:事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的方法作為引數傳入
		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("幫小明送花");
						System.out.println("幫小明送巧克力");
						//讓小明自己去約會
						Object returnValue= method.invoke(target, args);
						System.out.println("搞定收工");
						return returnValue;
					}
				}
			);
	}
	
}

測試:

@Test
public void testJDK() {
	//目標物件
	Subject xm=new XiaoMingStudent();
	//建立代理,給目標物件
	Subject proxy=(Subject) new ProxyFactory(xm).getProxyInstance();
	proxy.engagement();
}

結果:


Cglib代理:

特點:

1.引入jar包spring-core-4.2.5.RELEASE.jar(注意spring-core-3.2以上才會包含cglib,3.2以下要匯入cglib.jar包)
2.代理的類不能為final,否則報錯
3.目標物件方法不能為final/static,否則不會攔截即不會執行擴充套件功能

4.與jdk動態代理區別就是目標類不用實現介面

注意:目標物件是沒實現介面的

package com.sl.demo.proxy;
/**
 * 小明同學
 * cglib目標類 不實現介面
 * @author pengkun
 *
 */
public class XiaoMingStudent2 {

	public void engagement() {
		System.out.println("哈哈,小明同學約會去絡!");
	}

}

package com.sl.demo.proxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
 * Cglib代理
 * MethodInterceptor:方法攔截
 * @author pengkun
 *
 */
public class CglibProxyFactory implements MethodInterceptor {
	//持有一個 目標物件(注意:目標物件沒有實現介面)
	private Object target;

	public CglibProxyFactory(Object target) {
		super();
		this.target = target;
	}
	
	//給目標物件建立個代理
	public Object getProxyInstance() {
		//建立工具類
		Enhancer en=new Enhancer();
		//設定父類
		en.setSuperclass(XiaoMingStudent2.class);
		//設定回撥函式
		en.setCallback(this);
		//建立代理物件(子類)並返回
		return en.create();
	}

	/**
	 * 攔截
	 * obj:目標物件
	 * method:目標方法
	 * args:方法引數
	 * MethodProxy:它應該是cglib生成用來代替Method物件的一個物件
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		System.out.println("幫小明送花");
		System.out.println("幫小明送巧克力");
		//讓小明自己去約會
		Object returnValue= method.invoke(target, args);
		System.out.println("搞定收工");
		return returnValue;
	}

}

測試:

@Test
public void testCGLIB() {
	//目標物件
	XiaoMingStudent2 xm=new XiaoMingStudent2();
	//建立代理,給目標物件
	XiaoMingStudent2 proxy= (XiaoMingStudent2) new CglibProxyFactory(xm).getProxyInstance();
	proxy.engagement();
}


最後總結:

目標物件有實現介面就用JDK代理,

目標物件沒有實現介面就用Cglib代理