Java中静态代理和动态代理的学习

Posted 程序逸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中静态代理和动态代理的学习相关的知识,希望对你有一定的参考价值。

最近才想起来动态代理模式的学习,惭愧惭愧,Spring中的AOP就是基于代理模式的,之前却一直没有去学习,十分后悔,今天来学习总结一下自己的心得和体会,如果有不对的地方,虚心学习,一起进步。

代理模式分为静态代理和动态代理,我们慢慢来。

静态代理

先说一下实际的情况:公司领导需要开会和对员工进行评价,领导类有两个方法:开会和根据员工名进行评价。但实际上领导有秘书,有的时候开会前需要秘书去准备材料,当秘书材料准备好了就开会。
通过上面这个小案例,我们再来看静态代理:就是代理类(秘书)和被代理类(领导)实现相同的接口,这样当我们调用代理类的方法时和被代理类调用方法相同,其核心就是代理类里面定义一个私有的被代理类对象属性。然后我们通过代码来具体看看:

  • 首先定义一个接口,原来规范功能,这里就是开会和给员工评价,接口IWork代码如下:
package com.ctvit.service;

/**
 * 该接口定义领导的工作内容,开会和员工评价
 */
public interface IWork {
    /**
     * 开会功能
     */
    void meeting();

    /**
     * 领导对员工进行评价
     * @param name 传入员工姓名领导进行评价等级
     * @return 返回等级,数字越大评价越好
     */
    int evaluate(String name);
}

  • 第二不:我们定义一个领导类来实现该接口:
package com.ctvit.service;

import java.util.Random;

/**
 * 领导类,实现IWork接口,实现自己的职责
 */
public class Leader implements IWork{
    @Override
    public void meeting() {
        System.out.println("领导进行公司开会了,十分辛苦");
    }

    @Override
    public int evaluate(String name) {
        Random random = new Random();
        int i = random.nextInt(10);
        System.out.println("领导思考了很久,对"+name+"做出了评价:"+i);
        return i;
    }
}

  • 第三步:书写秘书类,领导类让秘书去准备资料:
package com.ctvit.service;

/**
 * 领导秘书,有时候领导很忙,就让秘书去做一些工作
 */
public class Secretary implements IWork{
    private Leader leader;

    public Secretary(Leader leader){
        this.leader=leader;
    }

    @Override
    public void meeting() {
        System.out.println("小秘书收到领导通知,开始准备开会材料...");
        leader.meeting();
    }

    @Override
    public int evaluate(String name) {
        return leader.evaluate(name);
    }
}

  • 最后查看运行效果:
    在这里插入图片描述
  • 总结:可以看到,这个代理模式类似套娃,就是我们都实现统一的接口,然后我们让代理类定义一个私有被代理类属性,通过构造方法传入一个被代理类对象然后赋值给这个私有属性,然后来调用方法,这样就保证了被代理类的方法正常执行,但代理类可以在这些方法执行前执行自己的方法,这点就和AOP思想吻合。

动态代理

在这里插入图片描述

JDK动态代理:

  • 我们接着上面的静态代理案例继续写,我们首先定义一个类WorkInvocationHandler来实现InvocationHandler接口的invoke方法,该类代码如下:
package com.ctvit.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class WorkInvocationHandler implements InvocationHandler {

    private Object object;

    public WorkInvocationHandler(Object object){
        this.object=object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //打印一下object类和proxy的类对象信息
        System.out.println("object:"+object.getClass().getSimpleName());
        System.out.println("proxy:"+proxy.getClass().getSimpleName());

        //判断当前方法是否是meeting开会方法,如果是就代理,让秘书去准备材料
        if ("meeting".equals(method.getName())){
            System.out.println("代理小秘书先准备会材料...");
            return method.invoke(object,args);
        }else if("evaluate".equals(method.getName())){//判断是否是评价方法
            System.out.println("老板小秘书在准备员工资料给领导看");
            if (args[0] instanceof String){
                if ("sun".equals(args[0])){//判断传入的参数是否是sun这个员工,如果是则执行下面的代码
                    System.out.println("sun因为犯过错,评分为30分");
                    return 30;
                }
            }
            return method.invoke(object,args);
        }
        return null;
    }
}

  • 从上面的代码我们看出来,和静态代理不同的是,我们实现了一个官方提供的接口:InvocationHandler 并且实现了该接口的invoke方法,然后我们可以对所有的方法进行代理。这样我们就可以重写静态代理代码中的测试部分了,代码如下:
public class Test {
    public static void main(String[] args) {
       /* Leader leader = new Leader();
        Secretary secretary = new Secretary(leader);
        secretary.meeting();
        secretary.evaluate("程序逸");*/

        Leader leader = new Leader();
        IWork proxy=(IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),new Class[]{IWork.class},new WorkInvocationHandler(leader));
        proxy.meeting();
        proxy.evaluate("程序逸");
        proxy.evaluate("sun");


    }
}
  • 运行结构如下:

在这里插入图片描述

  • 我们还可以通过在invoke方法返回代理对象proxy来实现连续调用,我们来看一下这个案例:
package com.ctvit.service2;

public interface IWork {
    IWork iwork(String method);
}

package com.ctvit.jdk2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class WorkInvocationHandler implements InvocationHandler {
    private Object object;
    public WorkInvocationHandler(Object object){
        this.object=object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("iwork".equals(method.getName())){
            System.out.println("work----"+args[0]);
            return proxy;
        }
        return null;
    }
}
package com.ctvit.service2;

import com.ctvit.jdk2.WorkInvocationHandler;

import java.lang.reflect.Proxy;

public class TestDemo {
    public static void main(String[] args) {
        IWork iWork=(IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(),new Class[]{IWork.class},new WorkInvocationHandler(new IWork() {
            @Override
            public IWork iwork(String method) {
                return null;
            }
        }));
        iWork.iwork("AAA").iwork("BBB").iwork("CCC");
    }
}

在这里插入图片描述

CGLIB动态代理

  • 如果使用Maven项目,首先导入CGlib的依赖:
 	 <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
  • 我们使用的案例是第一个静态代理的案例,可以继续接着写。使用CGlib代理模式依旧是需要创建一个实现类来实现MethodInterceptor接口,代码如下:
package com.ctvit.jdk2;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LeaderMethodInterceptor implements MethodInterceptor {


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if ("meeting".equals(method.getName())) {
            System.out.println("代理先准备会议材料...");
            return methodProxy.invokeSuper(o, objects);
        } else if ("evaluate".equals(method.getName())) {
            if(objects[0] instanceof String) {
                if ("sun".equals(objects[0])) {
                    System.out.println("sun 犯过错误,所以考评分数较低...");
                    return 70;
                }
            }
            return methodProxy.invokeSuper(o, objects);
        }
        return null;
    }
}

  • 然后我们就可以写测试类:
package com.ctvit.service2;

import com.ctvit.jdk2.LeaderMethodInterceptor;
import com.ctvit.service.Leader;
import net.sf.cglib.proxy.Enhancer;

public class TestDemo2 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();//通过CGLIB动态代理获取代理对象过程
        enhancer.setSuperclass(Leader.class);//设置enhancer对象的父类
        enhancer.setCallback(new LeaderMethodInterceptor());//设置enhancer的回调对象
        Leader leader=(Leader)enhancer.create();
        leader.meeting();
        leader.evaluate("程序逸");
        leader.evaluate("sun");

    }
}
  • 运行结果:
    在这里插入图片描述
  • 有关CGlib动态代理我们需要注意实现的MethodInterceptor接口的方法的四个参数:
参数名作用
Object o表示需要增强的对象
Method method表示需要拦截的方法
Object[] objects表示方法的参数列表
MethodProxy methodProxy触发父类的方法对象

以上是关于Java中静态代理和动态代理的学习的主要内容,如果未能解决你的问题,请参考以下文章

JAVA动态代理

java项目实战代理模式(Proxy Pattern),静态代理 VS 动态代理

java--动态代理学习笔记

Spring之静态/动态代理模式

Java设计模式学习06——静态代理与动态代理(转)

代理模式(动态)