java 动态代理的方法及其缺陷

本文部分原创,大部分为黑马程序员课程的提炼

1 动态代理简介

特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强,在此过程中会建立一个新的类对象。
分类:基于接口的动态代理、基于子类的动态代理

2 基于接口的动态代理:

  涉及的类: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);

3 基于子类的动态代理

由第三方包提供

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);

4 缺陷

动态代理有一个很大的缺陷。这在基于子类和基于接口的动态代理方法中都存在。以下以基于接口的动态代理方法举例。
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的方法:
苹果热身一下
苹果跑起来了!!!
我吃了一个苹果

发现:我并没有在吃苹果前洗手!!!!
这点很关键。
因为有时候我写的类中,许多内部的方法会相互之间调用,但如果我想对它们进行同时增强的话,用动态代理是做不到的。因为动态代理只会增强最先调用的方法,后续内部之间的相互调用的方法,是不会被增强的。

你可能感兴趣的:(Java,java,proxy,aop)