欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java代理机制及动态代理和CGLIB代理详解

程序员文章站 2022-06-17 20:21:30
...

1.代理模式

    代理模式(Proxy)为其他对象提供一种代理以控制对这个对象的访问。可以通过代理加入一些如事务、权限、日志、监控报警等操作。

Java代理机制及动态代理和CGLIB代理详解

    应用场景:比如某个实现类有多个接口,只想暴露其中一两个,则可使用代理,再比如方法增强,在方法的执行前后增加一些事务、日志、监控等,甚至可以不执行被代理的方法,再如远程调用,通过Proxy.newProxyInstance代理一个该接口对应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就可以了,如最经典的当然是Java标准库的RMI,其它比如hessian,webservice框架中的远程调用,实现原理相似。

2.静态代理

    如果不同接口的某些类想使用代理模式来实现相同的功能 ,静态代理需要实现多个代理类,动态代理只需要实现一个代理类。 且当接口方法越来越来,静态代理均需要实现接口方法,不便于维护扩展。

public interface Subject {
	void request();
}
public class RealSubject implements Subject{

	@Override
	public void request() {
		System.out.println("这是一次real请求");
	}

}
/**
 * 
 * 静态代理类
 *
 */
public class StaticProxy implements Subject{
	
	private RealSubject realSubject = null;
	
	public StaticProxy(RealSubject realSubject) {
		this.realSubject = realSubject;
	}
	
	@Override
	public void request() {
		System.out.println("StaticProxy request bgein");
		//调用委托代理类的实际实现方法
		realSubject.request();
		System.out.println("StaticProxy request end");
	}
	
}
public class ProxyTest {
	public static void main(String[] args) {
		StaticProxy staticProxy = new StaticProxy(new RealSubject());
		staticProxy.request();
	}
}
运行结果:

StaticProxy request bgein
这是一次real请求
StaticProxy request end

3.动态代理

   java.lang.reflect.Proxy + java.lang.reflect.InvocationHandler(jdk动态代理,基于接口实现)
      java.lang.reflect.Proxy:提供用于创建动态代理类及实例的静态方法:newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
      返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
       java.lang.reflect.InvocationHandler:提供代理实例调用被代理类实现的接口的方法:invoke(Object proxy, Method method, Object[] args) 
       在代理实例上处理方法调用并返回结果。

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

public class ProxyHandler implements InvocationHandler{
	
	private Object target;
	
	public ProxyHandler(Object target) {
		super();
		this.target = target;
	}
	
	public Object getProxy()  throws Throwable{
		//Proxy.newProxyInstance  返回代理类实例
		return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
	}
	
	/**
	 * 复写invoke方法,此处调用被代理类的接口实现
	 * @param proxy   在其上调用方法的代理实例
	 * @param method  对应于在代理实例上调用的接口方法的 Method 实例
	 * @param args    包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。
	 * @param args    基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中
	 * @return
	 * @throws Throwable
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object object = null;
		System.out.println("ProxyHandler begin");
		object = method.invoke(target, args);
		System.out.println("ProxyHandler end");
		return object;
	}

}
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.lls.proxy.ProxyHandler;
import com.lls.service.Subject;
import com.lls.service.impl.RealSubject;

public class HandlerTest {
	public static void main(String[] args) {
		Subject subject = new RealSubject();
		ProxyHandler proxyHandler = new ProxyHandler(subject);
		try {
			Subject subjectProxy = (Subject) proxyHandler.getProxy();
			
			//证明subject是Proxy的实例并实现了Subject接口  
	        System.out.println(subjectProxy instanceof Proxy);    
	        //subjectProxy的Class类是$Proxy0并继承了Proxy,实现了subjectProxy接口  
	        System.out.println("subjectProxy的Class类是:"+subjectProxy.getClass().toString());  
	        System.out.print("subjectProxy中的属性有:");  
	        Field[] field=subjectProxy.getClass().getDeclaredFields();  
	        for(Field f:field){  
	            System.out.print(f.getName()+", ");  
	        }  
	        System.out.print("\n"+"subjectProxy中的方法有:");  
	        Method[] method=subjectProxy.getClass().getDeclaredMethods();  
	        for(Method m:method){  
	            System.out.print(m.getName()+", ");  
	        }  
	        System.out.println("\n"+"subjectProxy的父类是:"+subjectProxy.getClass().getSuperclass());  
	        System.out.println("subjectProxy实现的接口是:");  
	        Class<?>[] interfaces=subjectProxy.getClass().getInterfaces();  
	        for(Class<?> i:interfaces){  
	            System.out.print(i.getName()+", ");  
	        }  
	        System.out.println("\n"+"运行结果为:");  
			subjectProxy.request();
		} catch (Throwable e) {
			e.printStackTrace();
		}

	}
}
运行结果:

true
subjectProxy的Class类是:class com.sun.proxy.$Proxy0
subjectProxy中的属性有:m1, m3, m0, m2, 
subjectProxy中的方法有:equals, toString, hashCode, request, 
subjectProxy的父类是:class java.lang.reflect.Proxy
subjectProxy实现的接口是:
com.lls.service.Subject, 
运行结果为:
ProxyHandler begin
这是一次real请求
ProxyHandler end
生成的代理类:public final class $Proxy0 extends Proxy implements Subject
其中request的定义如下:

public final void request() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  //调用的是Proxy中的构造方法
    }  

Java代理机制及动态代理和CGLIB代理详解

故subjectProxy.request()调用的是代理类的request方法,

进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。

且$Proxy0代理类中所有方法最终都是父类Proxy中的h的invoke()方法.即InvocationHandler.invoke(),

然后通过Merthod.invoke方法 反射执行。

CDLIB动态代理

cglib是针对类来实现代理的,利用ASM动态生成目标类的子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类或方法进行代理。
import java.lang.reflect.Method;

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

public class CglibProxy implements MethodInterceptor {
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class clazz) {
	        enhancer.setSuperclass(clazz);  
	        enhancer.setCallback(this); 
	        return enhancer.create();// 返回代理对象,返回的对象其实就是一个封装了“实现类”的代理类,是实现类的实例。
	}

	@Override
	public Object intercept(Object paramObject, Method paramMethod, Object[] paramArrayOfObject,MethodProxy paramMethodProxy) throws Throwable {
		System.out.println("CglibProxy intercept before:" + paramMethodProxy.getSuperName());
		System.out.println(paramMethod.getName());
		Object object = paramMethodProxy.invokeSuper(paramObject, paramArrayOfObject);
		System.out.println("CglibProxy intercept end:" + paramMethodProxy.getSuperName());
		return object;
	}

}
public class CglibTest {
	public static void main(String[] args) {
		 	CglibProxy cglibProxy = new CglibProxy();  
		  
	        RealSubject rs = (RealSubject)cglibProxy.getProxy(RealSubject.class); 
	        
	        rs.request();
	}
}
运行结果:
CglibProxy intercept before:CGLIB$request$0
request
这是一次real请求
CglibProxy intercept end:CGLIB$request$0

jdk和cglib动态代理实现的区别
1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;
4、通过反射机制获得动态代理类的构建函数,从而创建动态代理类实例。如RealSubject经过proxy.newProxyInstance生成的代理对象的类名$Proxy0。
   而cglib是利用ASM生成java字节码从而动态的产生代理对象。

spring AOP 代理选择:
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

强制使用cglib
<aop:aspectj-autoproxy proxy-target-class="true"/>

代理对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。