Java动态代理的实现

    百度百科说:代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。

    根据代理关系建立的时间不同,可以将代理分为两类:静态代理与动态代理。静态代理的实现参考笔者的上一篇博客。

    动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(如代理工厂类)在程序运行时由 JVM 根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。

    对比静态代理,静态代理是指在程序运行前就已经定义好了目标类的代理类。代理类与目标类的代理关系在程序运行之前就确立了。

动态代理的实现方式常用的有两种:

(1)使用 JDK 的 Proxy。

(2)通过 CGLIB 生成代理。


一、JDK动态代理的实现:

通过JDK的java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 会 使 用 其 静 态 方 法newProxyInstance(),依据目标对象、业务接口及业务增强逻辑三者,自动生成一个动态代理对象。其方法声明如下:

public static newProxyInstance ( ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)

loader:就是目标类的类加载器

interfaces:目标类实现的所有接口,是数组的形式

handler:业务增强逻辑,这里面定义你需要对目标对象的方法进行加强的逻辑。

JDK动态代理的需要目标类实现业务逻辑的接口。


首先定义业务逻辑的接口类如下:

package proxy;

/**
 * 业务逻辑的接口类,该类中的方法需要被代理 -》 实现增强
 * @author gmcc
 *
 */
public interface ISomeService {
	
	String doSome();
	
	String doOther();
}

然后是业务接口的实现类,也就是需要代理的目标类

package proxy;

/**
 * 目标类:需要被代理的类实现增强的类
 * @author gmcc
 *
 */
public class SomeServiceImpl implements ISomeService{

	@Override
	public String doSome() {
		return "hello world!";
	}

	@Override
	public String doOther() {
		return "hi, Java!";
	}
}
下面就是JDK的动态代理对象的创建及使用了
package proxy;

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

public class JDKProxy {
	
	public static void main(String[] args) {
		//创建目标对象的实例
		final ISomeService target = new SomeServiceImpl();
		//由Proxy类的newProxyInstance()方法生成一个动态代理对象proxy
		ISomeService proxy = (ISomeService) Proxy.newProxyInstance(
				target.getClass().getClassLoader(),  //目标对象的类加载器
				target.getClass().getInterfaces(),   //目标对象所实现的所有接口
				new InvocationHandler() {            //匿名内部类
					
					// proxy : 代理对象
					// method : 目标方法
					// args : 目标方法的参数列表
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) 
							throws Throwable {
						//通过代理对象执行目标对象的方法,获得返回的结果
						Object result = method.invoke(target, args);
						if(result != null)
						// 对返回结果做出处理,将其转变为全大写
							result = ((String) result).toUpperCase();
						return result;
					}
				});
		//若是需要对目标方法增强则通过代理对象调用目标方法
		String result1 = proxy.doSome();
		System.out.println(result1);
		
		//若是不需要对目标方法增强,则直接通过目标对象调用目标方法
		String result2 = target.doOther();
		System.out.println(result2);
	}
}

执行结果如下:

    可以从结果看到,对target目标对象的doSome()方法返回的“hello world!”,其静态代理对象proxy对其进行了增强,将其结果转变为了全大写“HEELO WORLD!”,也就是说,客户真正想访问的是目标对象target,而实际上它访问到的是target目标对象的代理对象proxy,代理对象在这中间做了“中介”,这就是代理的设计模式。


二、cglib动态代理的实现

cglib动态代理的出现弥补JDK动态代理的不足:JDK动态代理需要目标类与代理类对象实现同一接口,而cglib动态代理不需要目标类实现业务接口,因为cglib动态代理的原理是子类扩展父类,通过将目标类作为父类,扩展父类的方法来达到增强业务逻辑的效果。

(注意:使用cglib动态代理时需要引入cglib.jar到配置路径。)

cglib的直接目标类

package proxy;
/**
 * 目标类:没有实现任何业务接口
 * @author gmcc
 *
 */
public class SomeService {

	public String doSome() {
		return "hello world!";
	}

	public String doOther() {
		return "hi, Java!";
	}
}

cglib代理对象的生成类(需要事先MethodInterceptor接口,该接口中声明了回调方法)。

package proxy;

import java.lang.reflect.Method;

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

/**
 * cglib动态代理对象的原理:子类扩展父类
 * 因此,使用cglib动态代理的类不能被final修饰,
 * 因为被final修饰的类是最终实现类,它不能被继承扩展
 * @author gmcc
 */
public class CGLIBProxy implements MethodInterceptor{
	private SomeService target;
	
	public CGLIBProxy(SomeService target) {
		this.target = target;
	}
	
	public SomeService myCglibCreator() {
		Enhancer enhancer = new Enhancer();
		//指定父类,即目标类,因为cglib动态代理的原理就是:子类扩展父类
		enhancer.setSuperclass(SomeService.class);
		//设置回调接口对象
		enhancer.setCallback(this);
		//create()方法用于创建cglib动态代理对象,即目标类的子类对象
		return (SomeService) enhancer.create();
	}
	
	//回调接口的方法
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) 
			throws Throwable {
		Object result = method.invoke(target, args);
		if(result != null) {
			result = ((String)result).toUpperCase();
		}
		return result;
	}

}

cglib动态代理的使用:

package proxy;

public class ProxyTest {
	
	//cglib无接口的动态代理
	public static void main(String[] args) {
		//获取目标对象
		SomeService target = new SomeService();
		
		//目标对象target需要代理,则传递自身得到它的代理对象proxy
		SomeService proxy = (SomeService) new CGLIBProxy(target).myCglibCreator();
		
		//需要对方法的结果进行改变就通过代理对象来调用目标方法来实现改变。
		System.out.println(proxy.doSome());
		
		//不需要对方法的结果进行改变则直接调用目标对象的方法。
		System.out.println(target.doOther());
	}

}

测试的结果当然和JDK动态代理一样



既然cglib是针对无接口目标类的动态代理,那么有接口的目标类能否用cglib动态代理呢,当然能!

修改上面的代码,实现有接口的cglib动态代理如下:

业务接口:

package proxy;

/**
 * 业务逻辑的接口类,该类中的方法需要被代理 -》 实现增强
 * @author gmcc
 *
 */
public interface ISomeService {
	
	String doSome();
	
	String doOther();
}

目标类:

package proxy;

/**
 * 目标类:需要被代理的类实现增强的类
 * @author gmcc
 *
 */
public class SomeServiceImpl implements ISomeService{

	@Override
	public String doSome() {
		return "hello world!";
	}

	@Override
	public String doOther() {
		return "hi, Java!";
	}

}

cglib代理生成类:

package proxy;

import java.lang.reflect.Method;

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

/**
 * cglib动态代理对象的原理:子类扩展父类
 * 因此,使用cglib动态代理的类不能被final修饰,
 * 因为被final修饰的类是最终实现类,它不能被继承扩展
 * 
 * 目标对象有接口
 * @author gmcc
 */
public class CGLIBProxy2 implements MethodInterceptor{
	private ISomeService target;
	
	public CGLIBProxy2(ISomeService target) {
		this.target = target;
	}
	
	public ISomeService myCglibCreator() {
		Enhancer enhancer = new Enhancer();
		//指定父类,即目标类,因为cglib动态代理的原理就是:子类扩展父类
		enhancer.setSuperclass(ISomeService.class);
		//设置回调接口对象
		enhancer.setCallback(this);
		//create()方法用于创建cglib动态代理对象,即目标类的子类对象
		return (ISomeService) enhancer.create();
	}
	
	//回调接口的方法
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) 
			throws Throwable {
		Object result = method.invoke(target, args);
		if(result != null) {
			result = ((String)result).toUpperCase();
		}
		return result;
	}

}

使用cglib测试如下:

package proxy;

public class ProxyTest {
	
	//cglib有接口的动态代理
	public static void main(String[] args) {
		//获取目标对象
		ISomeService target = new SomeServiceImpl();
		
		//目标对象target需要代理,则传递自身得到它的代理对象proxy
		ISomeService proxy = (ISomeService) new CGLIBProxy2(target).myCglibCreator();
		
		//需要对方法的结果进行改变就通过代理对象来调用目标方法来实现改变。
		System.out.println(proxy.doSome());
		
		//不需要对方法的结果进行改变则直接调用目标对象的方法。
		System.out.println(target.doOther());
	}

}

执行的结果当然是一样的。

阅读更多

没有更多推荐了,返回首页