1. 程式人生 > >細說java動態代理和cglib的動態代理

細說java動態代理和cglib的動態代理

          提到代理,想必大家對設計模式中的靜態代理和動態代理都比較熟悉,小編之前在部落格中對動態和靜態代理進行了對比,這篇博文就只探討java動態代理和cglib動態代理之間的區別;

♚  靜態代理的溫習

        在我們使用靜態代理的時候,每一個代理類只能為一個介面提供服務,這這樣一來在程式開發中會產生過多的代理,而且所有的代理操作除了呼叫的方法不一樣之外,其他的操作都是相同的,這樣就會造成過多的重複程式碼;

       為了解決上述問題,我們使用一個代理類來完成所有的代理功能,而這就需要引入動態代理;

♚  動態代理

       與靜態代理相對的是動態代理,動態代理類的位元組碼在程式執行時由java反射機制動態生成,無需程式設計師手工編寫它的原始碼。動態代理不僅簡化了程式設計工作,而且提高了軟體系統的可擴充套件性,因為java反射機制可以生成任意型別的動態代理類。

      下面我們以一個示例來說明動態代理:該示例中以一個checkSecurity()的例子來演示動態代理:

UserManager.java:

<span style="font-size:18px;">package com.ysc.spring;

/**
 * 要實現的介面
 * @author root
 *
 */
public interface UserManager {
	
	public void addUser(String username,String password);
	
	public void delUser(int userId);
	
	public String findUserById(int userId);
	
	public void modifyUser(int userId,String username,String passwordString);
}</span>

UserManagerImpl.java

<span style="font-size:18px;">package com.ysc.spring;

public class UserManagerImpl  implements UserManager {

	@Override
	public void addUser(String username, String password) {
		//checkSecurity();
		System.out.println("-------------userManager.add()---------");
	}

	@Override
	public void delUser(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.delUser()---------");
	}
		
	@Override
	public String findUserById(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.findUserById()---------");
		return "張三";
	}

	@Override
	public void modifyUser(int userId, String username, String passwordString) {
		//checkSecurity();
		System.out.println("-------------userManager.modifyUser()---------");
	}
	
}</span>

     由於檢查安全性的方法是可以獨立於當前介面中其他業務方法的,所以我們可以把這個獨立的服務拿出來,放到一個代理類中,使用動態代理把檢查安全性的方法動態的加入到每一個方法中,而這些代理的工作需要由一個單獨的代理類來完成:

SecurityHandler.java:

<span style="font-size:18px;">package com.ysc.spring;

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

public class SecurityHandler implements InvocationHandler {

	private Object targetObject;
	
	public Object createProxyInstanceObject(Object targetobObject){
		this.targetObject = targetobObject;

		return Proxy.newProxyInstance(targetobObject.getClass().getClassLoader(), 
						targetobObject.getClass().getInterfaces(),
						this);
		
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		checkSecurity();
		
		//呼叫目標方法
		Object ret = method.invoke(targetObject, args);
		
		return ret;
	}
	
	private void checkSecurity(){
		System.out.println("-----------checkSecurity----------");
	}

}</span>

     這樣,我們就可以直接在客戶端進行呼叫執行了:

Client.java:

<span style="font-size:18px;">package com.ysc.spring;

/**
 * 
 * 動態代理可以將系統中一些獨立的服務(這些獨立的服務具有橫切性)
 * 而將這些獨立的服務拿出來,使用動態代理就可以在執行時將服務自動加入到裡頭;
 * @author root
 *
 */
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		SecurityHandler handler = new SecurityHandler();
		
		UserManager userManager = (UserManager)handler.createProxyInstanceObject(new UserManagerImpl());
		
		userManager.addUser("zhangsan", "123");
	}

}</span>

          通過上面的例子可以看出,通過動態代理我們實現了動態的將檢查安全性切入到每一個方法中,但是動態代理依靠介面實現,那麼這樣的話就會導致一些沒有介面的類,就不能通過jdk實現java的動態代理,而這個時候我們就需要轉向cglib的動態代理:

♚  cglib動態代理

       cglib是針對類來實現代理的,它的原理是對指定的目標類生成一個類,並覆蓋其中方法實現增強,看上述例子的cglib動態代理的實現:

       與動態代理不同,由於是針對類來實現,所以上述UserManagerImpl類不能直接在java程式碼中實現UserManager介面,所以我們需要一個配置檔案來配置,且完成對哪些方法新增檢查安全性的方法,ApplicationContext.xml 如下:

<span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.2.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
	
	<!-- 表示使用cglib代理 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	 
	<bean id="userManager" class="com.ysc.spring.UserManagerImpl"></bean>
	<bean id="securityHandler" class="com.ysc.spring.SecurityHandler"></bean>
	
	<!-- 檢查安全性的方法要附加在哪些方法上的配置 -->
	<aop:config>
		<aop:aspect id="securityAspect" ref="securityHandler">
			<aop:pointcut expression="execution(* add*(..))" id="addAddMethod"/>
			<aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>
		</aop:aspect>
	</aop:config>
</beans></span>
       動態代理中例子保持Client.java , Security.java , UserManager.java不變,只對UserManagerImpl.java 做出如下的修改:
<span style="font-size:18px;">package com.ysc.spring;

public class UserManagerImpl  {
//implements UserManager {

	
	public void addUser(String username, String password) {
		//checkSecurity();
		System.out.println("-------------userManager.add()---------");
	}

	public void delUser(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.delUser()---------");
	}
		
	public String findUserById(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.findUserById()---------");
		return "張三";
	}

	public void modifyUser(int userId, String username, String passwordString) {
		//checkSecurity();
		System.out.println("-------------userManager.modifyUser()---------");
	}

}</span>

 ✎   總結

 spring 預設使用的jdk的動態代理;

1.如果目標物件實現了介面,在預設情況下采用jdk的動態代理實現aop

2.如果目標物件實現了介面,也可以強制使用cglib生成代理實現aop

3.如果目標物件沒有實現介面,那麼必須引入cglib,spring會在jdk的動態代理和cglib代理之間切換

如何使用cglib強制生成代理;

* 加入cglib類庫,cglib/*.jar

* 加入如下配置,表示強制使用cglib代理

<aop:aspectj-autoproxy proxy-target-class="true"/>

jdk動態代理和cglib代理的區別:

* jdk動態代理對實現了介面的類進行代理

* cglib代理可以對類代理,主要對指定的類生成一個子類,因為是繼承,所以我們的目標最好不要使用使用final宣告;

      到這裡,jdk動態代理和cglib的動態代理之間的對比就已經結束了,希望能幫大家更深刻的理解靜態代理、jdk動態代理和cglib動態代理。如果有更好的理解和建議,請留言哦!!