java静态代理和动态代理的区别和联系

6 篇文章 0 订阅
3 篇文章 0 订阅

代理的作用在不修改原有代码的情况下,去增强跟核心业务没有关系的公共功能的代码。

例如,加日志,增加权限控制,加事物管理。

AOP 底层原理 - 代理 

首先什么是代理 ? ----代理就是由委托人与代理人签订代理协议,授权代理人在一定范围内代表其向第三者进行处理有关事务。

静态代理

定义一个游戏玩家的接口

public interface Player {
    void play();
}

一个普通游戏玩家,实现了玩家的接口

public class NormalPlayler implements Player {
    public String name;
    public NormalPlayler(String name){
        this.name = name;
    }
    public void play() {
        login();
        System.out.println(this.name + "游戏中...");
        logout();
    }
    private void login(){
        System.out.println(this.name + "登录游戏成功!");
    }
    private void logout(){
        System.out.println(this.name + "退出游戏成功!");
    }
}

普通玩家玩游戏:

public class ProxyTest {

    public static void main(String[] args) {
        Player player = new NormalPlayler("Bill");
        player.play();
    }
}

打印结果:

Bill登录游戏成功!
Bill游戏中...
Bill退出游戏成功!

Process finished with exit code 0

现在我想在不改变普通玩家类的前提下做一些功能的增强,在每次登录前判断下是否在8:00-10:00时间区间吗,只有这段时间才可以玩游戏

用静态代理的方式实现:

定义一个代理类,在每次你要玩游戏的时候就不能用普通玩家了,只能用代理。

public class PlayerProxy implements Player {
    Player player ;
    public PlayerProxy(Player player){
        this.player = player;
    }
    public void play() {
        System.out.println("现在处于8:00-10:00时间窗口中,可以玩游戏");
        this.player.play();
        System.out.println("游戏空闲中");
    }
}

用代理玩家代替普通玩家:

public class ProxyTest {

    public static void main(String[] args) {
        Player player = new NormalPlayler("Bill");
        Player player1 = new PlayerProxy(player);
        player1.play();
    }
}

执行结果:

现在处于8:00-10:00时间窗口中,可以玩游戏
Bill登录游戏成功!
Bill游戏中...
Bill退出游戏成功!
游戏空闲中

Process finished with exit code 0

上面例子用静态代理实现了我们想要等功能,既没有改变我原来的类,又增加了新功能,

但是我们总不能为每个需要代理的类都创建一个代理类吧,这样开发成本太高了。

所以就让动态代理排上用场了:

JDK 动态代理

public class ProxyTest {

    public static void main(String[] args) {
        Player player = new NormalPlayler("Bill");

        Player proxPlay = (Player) Proxy.newProxyInstance(player.getClass().getClassLoader(), player.getClass().getInterfaces(),(proxy,  method, arg)->{
            System.out.println("现在处于8:00-10:00时间窗口中,可以玩游戏");
            Object obj = method.invoke(player,arg);
            System.out.println("游戏空闲中");
           return  obj;
        });
        proxPlay.play();
    }
}

执行结果: 

现在处于8:00-10:00时间窗口中,可以玩游戏
Bill登录游戏成功!
Bill游戏中...
Bill退出游戏成功!
游戏空闲中

Process finished with exit code 0

如上代码, 每次只要传入不同的player,或者各种实例都可以,Proxy利用反射机制实现对我们自己的方法的调用,我们可以在调用自己的方法前后都加需要的代码。

下面详细介绍下invoke方法的参数

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
ClassLoader loader -- 一般用我们自己接口的classLoader类加载器

Class<?>[] interfaces一般使用接口的getInterface获取接口数组

InvocationHandler h -- 最后一个参数,是一个接口,我们需要实现InvocationHandler接口的invoke方法

public Object invoke(Object proxy, Method method, Object[] args)
method.invoke-- 这个方法就是调用我们自己需要代理的方法。第一个参数是我们的实例,第二个参数是传进来的参数

args -- 被代理方法的参数列表

CGLIB 动态代理:

用cglib之前需要加入cglib包:

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
public class ProxyTest {

    public static void main(String[] args) {
        NormalPlayler player = new NormalPlayler("Bill");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(NormalPlayler.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("现在处于8:00-10:00时间窗口中,可以玩游戏");
            Object obj = method.invoke(player,objects);
            System.out.println("游戏空闲中");
            return obj;
        });
        NormalPlayler cgPlayler = (NormalPlayler) enhancer.create();
        cgPlayler.play();
    }
}

执行结果:

现在处于8:00-10:00时间窗口中,可以玩游戏
Bill登录游戏成功!
Bill游戏中...
Bill退出游戏成功!
游戏空闲中

Process finished with exit code 0

使用cglib 的时候可以看出, 我们没有实现任何接口也没有继承任何类也可以实现代理。

这个接口只有一个intercept()方法,这个方法有4个参数:

1)obj表示增强的对象,即实现这个接口类的一个对象;

2)method表示要被拦截的方法;

3)args表示要被拦截方法的参数;

4)proxy表示要触发父类的方法对象;

JDK 动态代理也有局限性:

1. 代理的类不能加到spirng ioc里面去

2. 每个被代理的对象都必须实现接口。

CGLIB 动态代理的局限性,没法把代理类加到spring ioc中去。

所以用Spring的AOP 比较好。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值