一、什么是动态代理

  假设在Student类中有一个吃饭方法,当我需要给这个方法添加功能时,只能直接修改源代码(侵入式修改),但是在成熟的项目中,我们一般不会去这么干,可能导致更大的问题。

  那如何在不修改原有代码的基础上增加功能呢?
  此时我们就需要代理,由代理去完成新增的功能。
(特点:无侵入式的给代码增加额外的功能)

  程序为什么需要代理?

只因哥是一个大明星,能唱能跳,在代码中反应为有唱歌方法个跳舞方法。
但是只因哥不是一个随便的人,在真正开始唱歌,跳舞之前需要进行准备工作
而只因哥作为一个大明星,这些准备工作不应该让他自己来完成(不应该在原有代码中新增功能)
这时候就需要一个中介公司,而公司会派出一个经纪人(代理),经纪人会帮助只因哥完成这些琐事。
简单来说,如果一个对象的成员方法中做的事情太多,就可以通过代理来转移部分职责。

  代理长什么样?

当一个人来邀请只因哥唱歌跳舞的时候,只因哥会说去找他的代理
所以在代理人中也需要实现唱歌和跳舞方法。
如果想要代理只因哥,那代理人也必须实现唱歌跳舞方法,但是代理人不一定会唱歌跳舞。
代理人在干完琐事之后可以调用只因哥的唱歌跳舞方法。
所以对象有什么方法想被代理,代理就一定有对应的方法。

  代理怎么知道?

  代理人是怎么知道只因哥需要代理唱歌跳舞,而不需要代理打篮球呢
  这时候就需要通过接口,在接口中有唱歌跳舞方法。
  然后只因哥和代理人都去实现这个接口。


二、动态代理的代码实现

  如何获取代理对象

"newProxyInstance(ClassLoader loader, Class&lt?&gt[] interface, InvocationHandler h)"

  动态代理的代码实现

测试类

package com.qcqc.javaStudy;  
  
public class dynamicProxyDemo1 {  
    public static void main(String[] args) {  
        BigStar star = new BigStar("只因哥");  
        Star starProxy = ProxyUtil.createProxy(star);  
        System.out.println(starProxy.sing("只因你太美"));  
        starProxy.dance();  
  
    }  
}

BigStar

package com.qcqc.javaStudy;  
  
public class BigStar implements Star{  
    private String name;  
  
    //唱歌  
    @Override  
    public String sing(String name) {  
        System.out.println(this.name + "正在唱" + name);  
        return "谢谢";  
    }  
  
    //跳舞  
    @Override  
    public void dance() {  
        System.out.println(this.name + "正在跳舞");  
    }  
  
    public BigStar() {  
    }  
    public BigStar(String name) {  
        this.name = name;  
    }  
  
    /**  
     * 获取     
     *     
     * @return name  
     */    public String getName() {  
        return name;  
    }  
  
    /**  
     * 设置     
     *     
     * @param name  
     */  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public String toString() {  
        return "BigStar{name = " + name + "}";  
    }  
}

ProxyUtil

package com.qcqc.javaStudy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
/*  
* 类的作用:创建一个代理  
* */  
public class ProxyUtil {  
    /*  
    * 作用:给明星的对象创建代理    
    *    
    * 形参:需要代理的对象    
    *    
    * 返回值:给明星创建的代理    
    *         可以直接用接口名,因为需要实现这个接口    
    *    * 需求:外面的人想要大明星唱歌。    
    *       1.获取代理的对象    
    *           代理对象 = ProxyUtil.createProxy(大明星的对象);    
    *       2.再调用代理的方法    
    *           代理对象.唱歌方法("只因你太美");    
    * */    
    public static Star createProxy(BigStar bigStar){  
        Star star = (Star) Proxy.newProxyInstance(  
                ProxyUtil.class.getClassLoader(),//类的加载器,固定格式。  
                new Class[]{Star.class},//接口类的数组,表示需要代理的方法,可以多个接口,但是要保证实现类也实现这些接口  
                new InvocationHandler() {  
                    @Override  
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                        //代理需要干什么事  
                        /*  
                        * 第一个参数:代理的对象                        
                        * 第二个参数:要运行的方法,假如外界调用sing方法,则这里的method就是sing                        
                        * 第三个参数:调用sing方法时调用的实参                        
                        *                        
                        * */                        
                        //准备工作  
                        if ("sing".equals(method.getName())) {  
                            System.out.println("准备话筒");  
                        } else if ("dance".equals(method.getName())) {  
                            System.out.println("准备场地");  
                        }  
                        return method.invoke(bigStar,args);  
                    }  
                }  
        );  
        return star;  
  
    }  
}

Star

package com.qcqc.javaStudy;  
  
public interface Star {  
    //写需要被代理的方法  
    //  1.唱歌    
    public abstract String sing(String name);  
    //  2.跳舞  
    public abstract void dance();  
  
}

测试结果


三、代码的运行过程

  ①在测试类中首先创建大明星的对象,然后将这个对象传递给createProxy方法。
  ②在ProxyUtil中产生了大明星的代理对象。
  ③当在测试类中调用sing方法,会自动调用匿名内部类中的invoke方法,其有三个参数
   第一个参数表示代理对象,第二个参数就是调用的成员方法的对象,第三个参数是调用时的参数
  ④在invoke方法中执行完其他的代码后,返回值为method调用invoke的执行返回结果。