动态代理(JDK + cglib)

Java中的动态代理

其实对于找工作方面,并没有太大的用处(好像被问及的很少尴尬,如果有问到也是比较基础的)。另外难度稍微有点大,但真正理解后,也就没有什么了,挺简单的一东西,那接下来就说说这个java中的动态代理。

Java中的动态代理一共分为两种:

分别是JDK动态代理、和CGLIB(code generator library 代码生成库)动态代理。

为什么要用动态代理,当我们需要实现某个功能的时候,但原有的类或方法不能够达到我们预期想要的功能,这时我们就可以使用动态代理,在原有的基础上创建出功能更强大的代理对象,来完成我们的需求。

JDK动态代理
JDK动态代理:原始类必须实现接口,然后对实现了接口的类产生代理

API

newProxyInstance(ClassLoader loader, class<?>[] interfaces, InvocationHandler h);返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
ClassLoader 类加载器
Class[] 类实现的所有的接口的Class的对象
IvocationHandler 真正的处理对象

案例

接口
package com.wanghang.proxy.jdk;

/**
 * 原始类实现的接口
 * 
 * @author Hang.W
 * @version 1.0, 2017-01-04 17:48:42
 */
public interface RunBehavior {

	public void run();
}
原始类
package com.wanghang.proxy.jdk;

/**
 * 原始类
 * 
 * @author Hang.W
 * @version 1.0, 2017-01-04 17:50:07
 */
public class Man implements RunBehavior {

	@Override
	public void run() {
		System.out.println("Running man 跑男");
	}
	
	public static void main(String[] args) {
		new Man().run();
	}

}
没有进行代理的类的对象运行结果:

使用工具类生成动态代理
package com.wanghang.proxy.jdk;

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

/**
 * 生成JDK代理的工具类
 * 
 * @author Hang.W
 * @version 1.0, 2017-01-04 17:54:06
 */
public class JDKProxy implements InvocationHandler {
	
	/** 声明被代理对象 */
	private RunBehavior man;

	/**
	 * 创建代理对象方法
	 * 
	 * @param man 传入被代理的对象
	 * @return 返回代理对象
	 */
	public RunBehavior createProxyObject(RunBehavior man) {
		this.man = man;
		
		// 1.获取被代理对象的类加载器(对谁做代理,就用谁的类加载器)
		ClassLoader loader = man.getClass().getClassLoader();
		
		// 2.获取被代理对象的所有实现接口
		Class<?>[] interfaces = man.getClass().getInterfaces();
		
		// 3.获取用于方法增强拦截的Handler对象(使用当前类作为InvocationHandler接口的实现类,需要实现接口)
		InvocationHandler h = this;
		
		// 获取当前接口的代理对象(需要强转)
		Object superMan = Proxy.newProxyInstance(loader, interfaces, h);
		return (RunBehavior) superMan;
	}

	/*
	 * 对原始方法进行拦截(增强)
	 * 
	 * (non-Javadoc)
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 对原始方法进行增强
		System.out.println("中国,大黑牛");
		// 对原始方法进行调用(反射)
		method.invoke(man, args);
		// 对原始方法进行增强
		System.out.println("韩国,金钟国");
		return null;
	}
	
	public static void main(String[] args) {
		// 进行测试 1.创建一个被代理对象
		RunBehavior man = new Man();
		// 2.为原始对象创建一个代理对象
		RunBehavior manProxy = new JDKProxy().createProxyObject(man);
		// 3.使用代理对象进行操作
		manProxy.run();
	}
	
}
使用动态代理对象运行结果
简单总结JDK动态代理:
1.JDK动态代理的前提是要继承一个接口,然后对实现接口的类做代理
2.在工具类中,使用newProxyInstance();方法生成代理对象
3.使用newProxyInstance();方法时,需要传入三个参数,需要注意第三参数是个拦截接口,需要实现(因为具体的增强规则需要我们自己定义),所以可以用当前类作为实现类实现 InvocationHandler接口,然后再实现invoke();方法,实现增强后的方法

cglib动态代理

cglib是对原始类做代理。当一个类没有实现接口时,但又要想增强某项功能的时候,就可以使用cglib动态代理

API

Enhance 对象,作用是在内存中创建出一段动态的类字节码

案例

使用的还是原来的类
package com.wanghang.proxy.cglib;

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.wanghang.proxy.jdk.Man;

/**
 * cglib动态代理生成工具类
 * 
 * @author Hang.W
 * @version 1.0, 2017-01-04 21:04:42
 */
public class CGLIBProxy implements MethodInterceptor {

	/**
	 * 提供用于创建Man类的代理对象方法
	 * 
	 * @return
	 */
	public Man createCGLIBObject() {
		// 1.在内存中创建一个动态的类的字节码
		Enhancer enhancer = new Enhancer();
		
		// 2.为这个类指定父类(对谁做代理,把谁设为父类)
		enhancer.setSuperclass(Man.class);
		
		// 3.指定其回掉操作(设置方法的调用拦截)
		enhancer.setCallback(this);
		
		// 4.使用该类创建对象
		Man man = (Man) enhancer.create();
		
		return man;
	}

	/**
	 * 方法拦截器,对原始方法进行拦截(增强)
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		// 对原始方法进行增强
		System.out.println("齐天大圣,孙悟空");
		
		// 代理对象调用原始方法
		methodProxy.invokeSuper(proxy, args);
		
		// 对原始方法进行增强
		System.out.println("... ...");
		return null;
	}
	
	public static void main(String[] args) {
		// 进行测试 1.创建代理对象
		Man superman = new CGLIBProxy().createCGLIBObject();
		// 2.进行操作
		superman.run();
	}
	
}
测试结果:

相对于JDK动态代理,cglib动态代理需要有以下几点要 特别注意的:
1.在内存中创建出来的代理类与原始类的关系是继承关系(子父类关系)
2.设置方法的调用拦截,需要实现Callback接口,但是这个接口中并没有方法,所以要其实现类MethodInterceptor进行实现,并且实现interceptor();方法
3. 在方法拦截中,调用原始方法,注意:需要使用invokeSuper();方法,进行调用原始方法,因为是对原始类做代理,而且和代理类又是继承关系,那么就需要找代理类的父类进行方法调用。如果使用invoke();方法,那么系统会把当前这个代理类作为父类,然后在内存中再创建一个代理类作为子类,然后系统再把这个代理类作为父类,然后再去创建一个代理类作为子类... ...这样子无穷无尽(最终造成栈内存溢出)。所以这里要注意:使用invokeSuper();方法,进行调用原始类的方法,这样子系统就可以找到代理类,然后执行原始类的方法(多态的体现)。

总结

动态代理应用,基本都是别人写好的,我们不必理解,而且找工作过程中确实不太会问,如果有问到,那么就按照下边的回答即可:
Q:说一说java中的动态代理?
A:java中的动态代理一共分两种。一种是JDK动态代理,是对对象做代理,另外需要被代理的类要实现接口。另一种是cglib动态代理,因为没有实现接口,那么就要对类做代理,然后代理类再生成代理对象。














  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值