Java代理(jdk靜態代理、動態代理和cglib動態代理)

1、代理是Java經常使用的設計模式,代理類經過調用被代理類的相關方法,並對相關方法進行加強。加入一些非業務性代碼,好比事務、日誌、報警發郵件等操做。java

2、jdk靜態代理設計模式

一、業務接口框架

/**
 * 業務接口
 * @author pc
 *
 */
public interface UserService {
	
	// 增長一個用戶
	public void addUser();
	// 編輯帳戶
	public void editUser();

}

二、業務實現類性能

/**
 * 業務實現類
 * @author pc
 *
 */
public class UserServiceImpl implements UserService {

	public void addUser() {
		System.out.println("增長一個用戶。。。");
	}

	public void editUser() {
		System.out.println("編輯一個用戶。。。");
	}

}

三、代理類測試

/**
 * 代理類
 * 
 * @author pc
 * 
 */
public class UserServiceProxy implements UserService {

	private UserServiceImpl userImpl;

	public UserServiceProxy(UserServiceImpl countImpl) {
		this.userImpl = countImpl;
	}

	public void addUser() {
		System.out.println("代理類方法,進行了加強。。。");
		System.out.println("事務開始。。。");
		// 調用委託類的方法;
		userImpl.addUser();
		System.out.println("處理結束。。。");
	}

	public void editUser() {
		System.out.println("代理類方法,進行了加強。。。");
		System.out.println("事務開始。。。");
		// 調用委託類的方法;
		userImpl.editUser();
		System.out.println("事務結束。。。");
	}

}

  

四、測試類this

public static void main(String[] args) {
	UserServiceImpl userImpl = new UserServiceImpl();
	UserServiceProxy proxy = new UserServiceProxy(userImpl);
	proxy.addUser();
	System.out.println("----------分割線----------");
	proxy.editUser();
}

五、結果.net

代理類方法,進行了加強。。。
事務開始。。。
增長一個用戶。。。
處理結束。。。
----------分割線----------
代理類方法,進行了加強。。。
事務開始。。。
編輯一個用戶。。。
事務結束。。。

  

3、jdk動態代理設計

一、業務接口代理

/**
 * 業務接口
 * @author pc
 *
 */
public interface UserService {
	
	// 增長一個用戶
	public void addUser();
	// 編輯帳戶
	public void editUser();

}

二、業務接口實現類日誌

/**
 * 業務接口實現類
 * @author pc
 *
 */
public class UserServiceImpl implements UserService {

	public void addUser() {
		System.out.println("增長一個用戶。。。");
	}

	public void editUser() {
		System.out.println("編輯一個用戶。。。");
	}
}

三、代理類

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

/**
 * 
 * @author pc
 * 
 */
public class ServiceInvocationHandler implements InvocationHandler {

	// 目標對象
	private Object target;

	public ServiceInvocationHandler(Object target) {
		super();
		this.target = target;
	}

	/**
	 * 建立代理實例
	 * @return
	 * @throws Throwable
	 */
	public Object getProxy() throws Throwable {
		return Proxy.newProxyInstance(Thread.currentThread()
				.getContextClassLoader(), this.target.getClass()
				.getInterfaces(), this);
		// 這樣寫只返回了目標對象,沒有生成代理對象。
        // return target;
	}

	/**
	 * 實現InvocationHandler接口方法
	 * 執行目標對象的方法,並進行加強
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result = null;
		System.out.println("代理類方法,進行了加強。。。");
		System.out.println("事務開始。。。");
		// 執行目標方法對象
		result = method.invoke(target, args);
		System.out.println("事務結束。。。");
		return result;
	}

}

  

四、測試類

public class Test {
	/**
	 * jdk動態代理會生成一個動態代理類,生成相應的字節碼,而後經過ClassLoader加載字節碼。
	 * 該實例繼承了Proxy類,並實現了業務接口,在實現的方法裏經過反射調用了InvocationHandler接口實現類
	 * 的invoke()回調方法。
	 * @param args
	 * @throws Throwable
	 */
	public static void main(String[] args) throws Throwable {
		UserService userService = new UserServiceImpl();
		ServiceInvocationHandler handler = new ServiceInvocationHandler(userService);
		// 根據目標生成代理對象
		UserService proxy = (UserService) handler.getProxy();
		proxy.addUser();
//		proxy.editUser();

	}

}

五、測試結果

代理類方法,進行了加強。。。
事務開始。。。
增長一個用戶。。。
事務結束。。。

  

4、cglib動態代理

須要引入cglib的jar包,

在pom.xml加入依賴:

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>2.2.2</version>
</dependency>

  

一、業務類,沒有實現接口

/**
 * 業務類
 * 沒有實現接口
 * 若是類是final的,則無法生成代理對象,報錯。
 * 若是方法是final的,代理無效
 * @author pc
 *
 */
public class UserServiceImpl {

	public void addUser() {
		System.out.println("增長一個用戶。。。");
	}

	public void editUser() {
		System.out.println("編輯一個用戶。。。");
	}
}

二、代理類

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 使用Cglib動態代理
 * @author pc
 *
 */
public class UserServiceCglib implements MethodInterceptor{

	private Object target;
	
	/**
	 * 建立代理實例
	 * @param target
	 * @return
	 */
	public Object getInstance(Object target){
		this.target = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.target.getClass());
		// 設置回調方法
		enhancer.setCallback(this);
		// 建立代理對象
		return enhancer.create();
	}
	
	/**
	 * 實現MethodInterceptor接口要重寫的方法。
	 * 回調方法
	 */
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
        System.out.println("事務開始。。。");    
        Object result = proxy.invokeSuper(obj, args);    
        System.out.println("事務結束。。。");    
        return result;    
	}

}

  

三、測試類

public class TestCglib {

	public static void main(String[] args) {
		UserServiceCglib cglib = new UserServiceCglib();
		UserServiceImpl bookFacadeImpl = (UserServiceImpl)cglib.getInstance(new UserServiceImpl());
		bookFacadeImpl.addUser();
//		bookFacadeImpl.editUser();
	}
}

四、結果:

事務開始。。。
增長一個用戶。。。
事務結束。。。

五、若是業務實現類被定義成final類,就會報如下錯誤

Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class cn.xx.xx.cgilb.UserServiceImpl
	at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
	at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
	at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
	at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
	at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
	at cn.pconline.proxy.cgilb.UserServiceCglib.getInstance(UserServiceCglib.java:30)
	at cn.pconline.proxy.cgilb.TestCglib.main(TestCglib.java:7)

 

5、總結

一、原理

   jdk靜態代理實現比較簡單,通常是直接代理對象直接包裝了被代理對象。

  jdk動態代理是接口代理,被代理類A須要實現業務接口,業務代理類B須要實現InvocationHandler接口。

    jdk動態代理會根據被代理對象生成一個繼承了Proxy類,並實現了該業務接口的jdk代理類,該類的字節碼會被傳進去的ClassLoader加載,建立了jdk代理對象實例,

   jdk代理對象實例在建立時,業務代理對象實例會被賦值給Proxy類,jdk代理對象實例也就有了業務代理對象實例,同時jdk代理對象實例經過反射根據被代理類的業務方法建立了相應的Method對象m(可能有多個)。當jdk代理對象實例調用業務方法,如proxy.addUser();這個會先把對應的m對象做爲參數傳給invoke()方法(就是invoke方法的第二個參數),調用了jdk代理對象實例的invoke()回調方法,在invoke方法裏面再經過反射來調用被代理對象的由於方法,即result = method.invoke(target, args);。

  cglib動態代理是繼承代理,經過ASM字節碼框架修改字節碼生成新的子類,重寫並加強方法的功能。

二、優缺點

     jdk靜態代理類只能爲一個被代理類服務,若是須要代理的類比較多,那麼會產生過多的代理類。jdk靜態代理在編譯時產生class文件,運行時無需產生,可直接使用,效率好。

     jdk動態代理必須實現接口,經過反射來動態代理方法,消耗系統性能。可是無需產生過多的代理類,避免了重複代碼的產生,系統更加靈活。

     cglib動態代理無需實現接口,經過生成子類字節碼來實現,比反射快一點,沒有性能問題。可是因爲cglib會繼承被代理類,須要重寫被代理方法,因此被代理類不能是final類,被代理方法不能是final。

所以,cglib的應用更加普遍一點。

參考:http://blog.csdn.net/fighterandknight/article/details/51200470

http://blog.csdn.net/jiankunking/article/details/52143504

相關文章
相關標籤/搜索