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();
}
}
测试结果:
1.在内存中创建出来的代理类与原始类的关系是继承关系(子父类关系)
2.设置方法的调用拦截,需要实现Callback接口,但是这个接口中并没有方法,所以要其实现类MethodInterceptor进行实现,并且实现interceptor();方法
3.
在方法拦截中,调用原始方法,注意:需要使用invokeSuper();方法,进行调用原始方法,因为是对原始类做代理,而且和代理类又是继承关系,那么就需要找代理类的父类进行方法调用。如果使用invoke();方法,那么系统会把当前这个代理类作为父类,然后在内存中再创建一个代理类作为子类,然后系统再把这个代理类作为父类,然后再去创建一个代理类作为子类... ...这样子无穷无尽(最终造成栈内存溢出)。所以这里要注意:使用invokeSuper();方法,进行调用原始类的方法,这样子系统就可以找到代理类,然后执行原始类的方法(多态的体现)。
总结
动态代理应用,基本都是别人写好的,我们不必理解,而且找工作过程中确实不太会问,如果有问到,那么就按照下边的回答即可:
Q:说一说java中的动态代理?
A:java中的动态代理一共分两种。一种是JDK动态代理,是对对象做代理,另外需要被代理的类要实现接口。另一种是cglib动态代理,因为没有实现接口,那么就要对类做代理,然后代理类再生成代理对象。