本文部分原创,大部分为黑马程序员课程的提炼
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强,在此过程中会建立一个新的类对象。
分类:基于接口的动态代理、基于子类的动态代理
涉及的类:Proxy
提供者:JDK官方
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用,新建的对象必须为接口。
如何创建代理对象:
Object Proxy.newProxyInstance(ClassLoader,Class[],InvocationHandler)
方法的参数:
ClassLoader:类加载器
它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]:字节码数组
它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。
例如 Producer 为被代理对象,IProducer 为其接口
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用,注意,不要使用method.invoke(proxy),这样程序会陷入死循环,invoke()方法传入的对象应该由外部给出
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f);
由第三方包提供
cglib cglib 2.1_3 涉及的类:Enhancer 提供者:第三方cglib库 创建代理对象的要求: 被代理类不能是最终类 如何创建代理对象: Object Enhancer.create(Class , Callback) 方法的参数: Class:字节码 它是用于指定被代理对象的字节码。 Callback:用于提供增强的代码。它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。
我们一般写的都是该接口的子接口实现类:MethodInterceptor
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* @param proxy 代理对象的引用,注意,不要使用method.invoke(proxy),这样程序会陷入死循环,invoke()方法传入的对象应该由外部给出
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
* @param methodProxy :当前执行方法的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(12000f);
动态代理有一个很大的缺陷。这在基于子类和基于接口的动态代理方法中都存在。以下以基于接口的动态代理方法举例。
1)建立一个接口
public interface Fruit {
public void eat();
public void run();
}
2)建立一个将要被代理的类
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("我吃了一个苹果");
}
public void run(){
System.out.println("苹果跑起来了!!!");
eat();
}
}
3)对类中的方法进行增强
我想在吃苹果前洗个手。
我想在跑步前热个身。
public class Demo1 {
@Test
public void demo1(){
final Apple apple = new Apple();
final Fruit apple1 = (Fruit)Proxy.newProxyInstance(apple.getClass().getClassLoader(), apple.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("run".equals(method.getName()))
System.out.println("苹果热身一下");
if("eat".equals(method.getName()))
System.out.println("洗一下手");
method.invoke(apple);
return null;
}
});
System.out.println("apple1的方法:");
apple1.run();
}
}
运行结果
apple1的方法:
苹果热身一下
苹果跑起来了!!!
我吃了一个苹果
发现:我并没有在吃苹果前洗手!!!!
这点很关键。
因为有时候我写的类中,许多内部的方法会相互之间调用,但如果我想对它们进行同时增强的话,用动态代理是做不到的。因为动态代理只会增强最先调用的方法,后续内部之间的相互调用的方法,是不会被增强的。