本文参考自:https://www.cnblogs.com/liantdev/p/10132680.html
一、JDK动态代理
JDK动态代理与静态代理一样,目标类需要实现一个代理接口,它的开发步骤如下:
- 1.定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
- 2.将InvocationHandler对象作为参数传入java.lang.reflect.Proxy的newProxyInstance方法中
- 3.通过调用java.lang.reflect.Proxy的newProxyInstance方法获得动态代理对象
- 4.通过代理对象调用目标方法
示例:
实现计算器功能的接口:
public interface ArithmeticDAO {
//+ 加
public void plus(int i, int j);
//- 减
public void sub(int i, int j);
//* 乘
public void mul(int i, int j);
//div 除
public void div(int i, int j);
//求余(取模)
public void mod(int i, int j);
}
实现计算器的实现类:
public class ArithmeticDAOImpl implements ArithmeticDAO {
@Override
public void plus(int i, int j) {
System.out.println(i+"+"+j+"="+(i+j));
}
@Override
public void sub(int i, int j) {
System.out.println(i+"-"+j+"="+(i-j));
}
@Override
public void mul(int i, int j) {
System.out.println(i+"*"+j+"="+(i*j));
}
@Override
public void div(int i, int j) {
System.out.println(i+"/"+j+"="+(i/j));
}
@Override
public void mod(int i, int j) {
System.out.println(i+"%"+j+"="+(i%j));
}
}
代理实现类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*使用JDK动态代理实现在方法前后加入日志信息
1. 用匿名内部类的方式实现InvocationHandler 接口
*/
public class ArithmeticLoggingProxy{
private ArithmeticDAO target; //需要被代理的对象
public ArithmeticLoggingProxy(ArithmeticDAO target) {
this.target = target;
}
public ArithmeticDAO getLoggingProxy(){
ArithmeticDAO proxy = null; //代理对象
//指定代理对象由哪一个类加载器加载
ClassLoader loader = target.getClass().getClassLoader();
//需要代理对象实现的数组,里面保存的是代理对象的类型,即其中有哪些方法
Class[] interfaces = new Class[]{ArithmeticDAO.class};
/*
调用代理对象中的方法时,应该执行的代码。InvocationHandler 调用处理器
Object proxy:代理对象
Method method:正在被调用的方法
Object[] args:调用方法时,传入的参数
*/
InvocationHandler invoke = new InvocationHandler () {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println(name+" start……"+ Arrays.asList(args));
Object result = method.invoke(target, args);
System.out.println(name+" end……");
return result;
}
};
proxy = (ArithmeticDAO) Proxy.newProxyInstance(target.getClass().getClassLoader(),interfaces ,invoke);
return proxy;
}
}
//==============================================================
/*2. 在类上实现InvocationHandler 接口*/
public class ArithmeticLoggingProxy implements InvocationHandler{
private ArithmeticDAO target; //需要被代理的对象
public ArithmeticLoggingProxy(ArithmeticDAO target) {
this.target = target;
}
/*
调用代理对象中的方法时,应该执行的代码。InvocationHandler 调用处理器
Object proxy:代理对象
Method method:正在被调用的方法
Object[] args:调用方法时,传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
String name = method.getName();
System.out.println(name+" start……"+ Arrays.asList(args));
try{
result = method.invoke(target, args);
}catch (Exception e){
System.out.println("异常信息:"+e.getMessage());
}finally {
System.out.println(name+" end……" );
}
return result;
}
}
测试:
//用匿名内部类的方式实现InvocationHandler 接口的测试类
@Test
public void test01(){
//需要被代理的对象
ArithmeticDAOImpl target = new ArithmeticDAOImpl();
ArithmeticDAO proxy = new ArithmeticLoggingProxy2(target).getLoggingProxy();
proxy.plus(10,20);
proxy.sub(10,5);
}
//=============================================================
//在类上实现InvocationHandler 接口的测试类
@Test
public void test01(){
//需要被代理的对象
ArithmeticDAO target = new ArithmeticDAOImpl();
//获取处理器
InvocationHandler handler = new ArithmeticLoggingProxy(target);
//代理对象
ArithmeticDAO proxy = (ArithmeticDAO)Proxy.newProxyInstance(Main.class.getClassLoader(), target.getClass().getInterfaces(), handler);
roxy.plus(10,20);
proxy.sub(10,5);
}
运行结果:
二、CgLib动态代理
CgLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理。它的开发步骤:
- 1.定义一个org.springframework.cglib.proxy.MethodInterceptor接口的实现类,重写intercept方法
- 2.获取org.springframework.cglib.proxy.Enhancer类的对象
- 3.分别调用Enhancer对象的setSuperclass和setCallback方法,使用create方法获取代理对象
- 4.通过代理对象调用目标方法
示例:
/* CGLib动态代理方式实现 */
public class ArithmeticInteceptor implements MethodInterceptor {
/*
Object o:代理对象
Method method:正在调用的方法
Object[] args:目标方法的形参
MethodProxy proxyMethod:代理方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy proxyMethod) throws Throwable {
Object result = null;
String name = method.getName();
System.out.println(name+" start……"+ Arrays.asList(args));
try{
result = proxyMethod.invokeSuper(proxy,args);
// result = method.invoke(proxy,args);
}catch (Exception e){
System.out.println("异常信息:"+e.getMessage());
}finally {
System.out.println(name+" end……");
}
return null;
}
}
测试类代码:
@Test
public void test01(){
/* 获取Enhance对象
Enhancer enhancer = new Enhancer();
设置目标类
enhancer.setSuperclass(ArithmeticDAOImpl.class);
调用回调方法
enhancer.setCallback(new ArithmeticInteceptor());
ArithmeticDAOImplproxy = (ArithmeticDAOImpl)enhancer.create();
*/
//============== 相当于以下一句=============
//获取代理对象
Arithmetic proxy = (ArithmeticDAOImpl)Enhancer.create(ArithmeticDAOIMpl.class, new ArithmeticInteceptor());
proxy.plus(10, 20);
proxy.sub(10,5);
}
运行结果:
三、JDK动态代理与CgLib动态代理的区别
- JDK动态代理针对实现了接口的类的接口方法进行代理
- CgLib动态代理是基于继承来实现代理,所以它无法对final类、private方法和static方法实现代理
Spring AOP中的代理使用的默认策略是:
- 如果目标对象实现了接口,则默认采用JDK动态代理
- 如果目标对象没有实现接口,则采用CgLib进行动态代理
- 如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理