最近从网上学习了动态代理,写篇博客来加深一下理解,学习了https://www.zhihu.com/question/20794107 这篇文章
从静态代理开始说起
假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志。
这个可以通过静态代理来实现
1.为现有的每一个类都编写一个对应的代理类,并且让它实现和目标类相同的接口(假设都有)
2.在创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。也就是说,代理对象 = 增强代码 + 目标对象(原对象)。有了代理对象后,就不用原对象了
接口

public interface UserService {
    //新增用户,为了简化不体现参数
    void addUser(); //接口方法不能体现{},即body(类体)
    //修改用户,为了简化不体现参数
    void editUser();
}

实现类

public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("新增一个用户");
    }

    @Override
    public void editUser() {
        System.out.println("对用户进行修改");
    }

代理类

public class UserServiceProxy implements UserService{
    private UserService realSubject;
    @Override
    public void addUser() {
        System.out.println("代理类UserServiceProxy addUser方法的调用前执行...");
        realSubject.addUser();
        System.out.println("代理类UserServiceProxy addUser方法的调用后执行...");

    }

    @Override
    public void editUser() {
        System.out.println("代理类UserServiceProxy editUser方法的调用前执行...");
        realSubject.editUser();
        System.out.println("代理类UserServiceProxy editUser方法的调用后执行...");
    }

    public UserServiceProxy() {
    }

    public UserServiceProxy(UserService realSubject) {
        this.realSubject = realSubject;
    }
}

但是我们会发现静态代理有他的缺陷,一个是要为每一个方法编写他的代理类,工作量太大,另外后续再接口和实现类中如果添加了方法,也要在代理中添加,太麻烦,于是我们就要想办法来解决

我们知道通过反射能够通过类的class对象,创建出对应的实例,那我们能否不写代理类,而是通过代理类的class对象直接创建代理实例呢,这时候有人可能就会提出疑问了,如果不写代理类,哪里来的class对象呢,那些构造器,方法去哪里找呢?
实际上,代理类和目标类理应实现同一组接口。之所以实现相同接口,是为了尽可能保证代理对象的内部结构和目标对象一致,这样我们对代理对象的操作最终都可以转移到目标对象身上,代理对象只需专注于增强代码的编写。所以我们可以从接口那里获取到本来应该由代理类提供的信息,而构造方面则可以通过java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类互相配合来实现
先说说Proxy,Proxy有个静态方法:getProxyClass(ClassLoader, interfaces),只要你给它传入类加载器和一组接口,它就给你返回对应接口的代理Class对象。简单来说就是在获得接口的属性的同时,可以通过反射实例化为对象了.
图片说明
再来说说InvocationHandler,根据代理Class的构造器创建对象时,需要传入InvocationHandler。每次调用代理对象的方法,最终都会调用InvocationHandler的invoke()方法:
图片说明

 class ProxyTest {
    public static void main(String[] args) throws Throwable {
        //目标对象
        CalculatorImpl target = new CalculatorImpl();
        //传入目标对象
        //目的:1.根据它实现的接口生成代理对象 2.代理对象调用目标对象方法
        Calculator calculatorProxy = (Calculator) getProxy(target);
        calculatorProxy.add(1, 2);
        calculatorProxy.subtract(2, 1);
    }

    //用来返回代理对象的方法
    private static Object getProxy(final Object target) throws Exception {
        //参数1:随便找个类加载器给它, 参数2:目标对象实现的接口,让代理对象实现相同接口
        //得到的是代理对象的class
        Class proxyClazz = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces());
        //传入InvocationHandler,得到有参构造器
        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
        //通过构造器反射获得代理对象
        Object proxy = constructor.newInstance(new InvocationHandler() {
            //InvocationHandler接口的invoke方法每次调用代理对象方法,最终都会执行invoke
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName() + "方法开始执行...");
                //通过反射执行目标对象方法,两个参数一个是目标对象,一个是本来方法的参数,来应对重构参数不一样的同名方法
                Object result = method.invoke(target, args);
                System.out.println(result);
                System.out.println(method.getName() + "方法执行结束...");
                //返回方法执行结果
                return result;
            }
        });
        //返回代理对象
        return proxy;
    }
}

不过实际编程中,一般不用getProxyClass(),而是使用Proxy类的另一个静态方法:Proxy.newProxyInstance(),直接返回代理实例,连中间得到代理Class对象的过程都帮你隐藏.
最后来回顾一下学到的
图片说明
最后动态代理生成的代理对象,最终都可以用接口接收,和目标对象一起形成了多态,可以随意切换展示不同的功能。但是切换的同时,与用实现类接受相比只能使用该接口定义的方法。