java三种代理方式总结

最近在学习java代理,总结输出如下。

java代理分为三种实现方式JDK静态代理,JDK动态代理和CGLIB代理,三种代理的特点及比较如下表

代理方式实现优点缺点其他
JDK静态代理代理类与委托类实现同一接口,并且在代理类中需要硬编码接口实现简单,容易理解代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低实现简单
JDK动态代理代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理不需要硬编码接口,代码复用率高只能够代理实现了接口的委托类底层使用反射机制进行方法的调用
CGLIB动态代理代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口不能对final类以及final方法进行代理底层将方法全部存入一个数组中,通过数组索引直接进行方法调用
  • JDK静态代理实现

如上表,静态代理需要代理类与目标类实现同一接口。
统一接口类Person,Man类实现Person接口;静态代理类StaticManProxy对Man类进行增强;即代理。

Person.java

public interface Person{
    String eat(String food);    
    String sleep(String where); 
}

Man.java

public class Man implements Person{
    public String sleep(String name){
        System.out.println(name+"睡觉了");
        return "";
    }

    public String eat(String name){
        System.out.println(name+"吃饭了");
        return "";
    }
}

静态代理类需要与目标类的接口,即StaticManProxy同样需要实现Person

StaticManProxy.java

public class StaticManProxy implements Person{

    private Person target;

    public StaticManProxy(Person target){
        this.target=target;
    }

    public String eat(String name) {
        System.out.println("静态代理 do something~");
        target.eat(name);
        return null;
    }

    public String sleep(String name) {
        return null;
    }
}

测试代码

public class ProxyTest {

    public static void main(String[] args){     
        //1、静态代理
        Person person=new Man();
        StaticManProxy manProxy=new StaticManProxy(person);
        manProxy.eat("静态代理");   
    }
}

结果

静态代理 do something~
静态代理吃饭了

如上StaticManProxy 即为Man的代理类,同时为其他实现Person接口的代理类;

  • JDK动态代理实现

JDK动态代理通过反射实现,直接使用JDK的java.lang.reflect.Proxy.newProxyInstance方法生成代理;

实现代码如下

public class ProxyFactory {

    // 代理目标
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxy() {

        return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(),
                target.getClass().getInterfaces(),
                /**
                 * InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,
                 * 我们不用单独去定义一个类来实现该接口, 而是直接使用一个匿名内部类来实现该接口,new
                 * InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类
                 */
                /**
                 * 在invoke方法编码指定返回的代理对象干的工作 proxy : 把代理对象自己传递进来 method:
                 * 把代理对象当前调用的方法传递进来 args: 把方法参数传递进来
                 * 
                 * 当调用代理对象的方法时,
                 * 实际上执行的都是invoke方法里面的代码,
                 * 因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法
                 */
                new InvocationHandler() {

                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        System.out.println("动态代理do something");

                        return method.invoke(target, args);

                    }
                });
    }

    public Object getProxyByLambda() {

        return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(),
                target.getClass().getInterfaces(), (proxy, method, args) -> {

                    System.out.println("Lambda动态代理do something");
                    return method.invoke(target, args);

                });
    }

}

测试代码段:

ProxyFactory proxy=new ProxyFactory(man);

        //2、匿名函数 模式
        Person p1=(Person)proxy.getProxy();
        System.out.println(p1.eat("匿名函数"));

        //3、Lambda 模式
        Person p=(Person)proxy.getProxyByLambda();

        System.out.println(p.eat("Lambda模式"));

结果

动态代理do something
匿名函数吃饭了

Lambda动态代理do something
Lambda模式吃饭了

如上,JDK动态代理通过Proxy.newProxyInstance直接生成代理对象,如需通过代理实现事物处理或者日志添加,则可通过以上代理类直接在代理Factory中增加对应需要处理的逻辑即可,代码调用时只需要将目标类传入则实现了对应的增强功能。针对所有接口实现类通用。

  • CGLIB动态代理

Spring中继承了CGLIB代理相关的代码,所以实现CGLIB代理只需要导入spring-core的jar包即可。

CGLIB代理的目标类不需要实现任何接口;

public class CgLibMan{

    public String eat(String name){
        System.out.println(name+"吃饭了");
        return "";
    }

    public String sleep(String name){
        System.out.println(name+"睡觉了");
        return "";
    }   
}

CGLIB代理实现类需要实现MethodInterceptor接口。如下:

public class CgLibProxyFactory implements MethodInterceptor{

    //维护目标对象
    private Object target;

    public CgLibProxyFactory(Object target){
        this.target=target;
    }

    //给目标对象创建一个代理对象
    public Object getCgLibProxy(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {

        System.out.println("开始事务...");

        //通过反射 执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        proxy.invokeSuper(o, args);

        System.out.println("提交事务...");

        return returnValue;

    }

}

测试代码段:

        //4、Cglib实现代理
        //目标对象
        CgLibMan ldhCg=new CgLibMan();
        //代理对象
        CgLibMan proxyCg=(CgLibMan) new CgLibProxyFactory(ldhCg).getCgLibProxy();
        proxyCg.eat("Cglib");
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值