Java静态代理与动态代理

这几天看了一点关于Java里比较流行的设计模式-代理模式,该设计模式很多框架里都会使用,同时也体现了Java的AOP思想,在此做一个总结。

代理模式里有三个概念,接口、实现该接口的被代理对象以及代理类。

  • 静态代理

首先设计一个接口IPerson,该接口规范了被代理对象的行为:

public interface IPerson {
    String sing(String name);

    String dance(String name);
}

接下来写一下被代理对象Person,也就是实际干活的对象:

public class Person implements IPerson {
    @Override
    public String sing(String name) {
        System.out.println("Person sing " + name);
        return "sing over.";
    }

    @Override
    public String dance(String name) {
        System.out.println("Person dance " + name);
        return "dance over.";
    }
}

所谓代理模式,那当然不能自己直接去干活,不然和非代理模式有啥区别,所以,对于用户来说,Person是不可被调用的。但实际干活的是Person,不去调用它我们怎么干活?那就中间套一层代理类,而给代理类传入Person对象,我们只要去调用代理类就行,具体的Person由代理类去调用。这里是代理类PersonProxy的实现:

public class PersonProxy implements IPerson {
    private IPerson person;

    public PersonProxy(IPerson person) {
        this.person = person;
    }

    @Override
    public String sing(String name) {
        System.out.println("Proxy working...singing...");
        return person.sing(name);
    }

    @Override
    public String dance(String name) {
        System.out.println("Proxy working...dancing...");
        return person.dance(name);
    }
}

我们可以看到,这里Person的sing和dance方法由PersonProxy去调用,对用户只需要开放PersonProxy,就可以由PersonProxy调用Person的方法去做实际的事,Person对于用户来说是隐蔽的,这就做到了代码业务方面的解耦;同时,由于外面嵌套了一层PersonProxy,在实际调用Person对象的sing或dance之前,我们可以加上一些自己的其他逻辑,在这里就是System.out.println(“Proxy working…singing…”),这个就是Java框架上常说的面向切片编程,也就是AOP思想。

最有就是业务调用类ProxyUtil:

public class ProxyUtil {
    static public void doTest() {
        PersonProxy person = new PersonProxy(new Person());
        person.sing("song01");
    }
}

这里我们看到我们只需要调用PersonProxy,就可以让我们的Person干活,Person对于用户来说是不需要见到的。(这边看似我们传入了new Person(),但实际上new Person()完全可以放到代理类PersonProxy的构造方法中实现,我这儿只是没有改)静态代理就是这样,一个规范的接口(IPerson),一个实现接口的被代理对象(Person,真正干活的主),一个代理类(PersonProxy,指示他人干活的,可以看作领导),但是静态代理的弊端也在这里体现了,那就是我们需要在代理类里调用被代理对象,当我们有多个被代理对象的时候,我们就需要多个代理类,项目一大这反而更加不方便,如此,动态代理就应运而生了。

  • 动态代理
    其实动态代理的本质和静态代理一样,也是需要三样东西,接口、实现该接口的被代理对象以及代理类,只是这个代理类不由我们去实现了,原因如上所说,每个被代理对象都需要一个代理类,由我们去实现岂不是累死?那么代理类由谁去生成呢?答案就是jdk,我们只需要规范好生成的代理类应该是个什么样子,传入一些参数,Proxy.newProxyInstance方法就会给我们返回我们需要的代理类,所以,这个类有点类似代理类工厂,专门给我们生产相应的代理类。(了解动态代理需要一些关于反射的知识,这边不详细说,只在用到时提一句)

接口IPerson和被代理对象Person和之前一样,就不重复写了,这儿主要说明一下代理类工厂PersonProxyMaker,也就是“生成代理类的类”的实现:

public class PersonProxyMaker {
    private IPerson proxied = new Person();

    public IPerson getProxy() {
        //ClassLoader loader = Person.class.getClassLoader();
        //Class[] intfs = proxied.getClass().getInterfaces();
//        在newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法中,首先通过loader, interfaces获取类对象cl, 之后通过c1获取该对象的构造函数Constructor: Proxy(InvocationHandler h), 然后通过cons.newInstance(new Object[]{h})返回动态代理对象
        return (IPerson) Proxy.newProxyInstance(Person.class.getClassLoader(), Person.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sing")) {
                    System.out.println("Proxy working...singing...");
                    return method.invoke(proxied, args);
                }
                if (method.getName().equals("dance")) {
                    System.out.println("Proxy working...dancing...");
                    return method.invoke(proxied, args);
                }
                return null;
            }
        });
    }
}

反射在这儿主要体现在两个地方,一就是在Proxy.newProxyInstance中,也是通过反射getProxyClass0获取代理类的Class类型对象,之后获取它的构造函数,然后通过cons.newInstance生成代理类实例返回,也就是我们getProxy返回的对象,它也就是我们真正需要的代理类。另外一个需要用到反射的地方是Proxy.newProxyInstance方法要求我们传入一个继承InvocationHandler接口的类,我们这儿用的是匿名函数,本质上是一样的。在我们继承InvocationHandler的类中,自己需要重写invoke方法,在这儿通过反射(method.invoke)去调用相应的实际被代理对象,由此可见,当有多个被代理对象时,我们只需要实现我们的invoke方法,就可以实现正真被代理类的逻辑转发。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值