前言
在说代理模式之前,我先说一下日常我们说的代理和被代理人,这东西就是我们常听到的律师帮人打官司,当发生纠纷的时候,自己也不懂法律啊,这咋办,总不能硬着头皮上吧,这时候就需要一个律师做你的代理,帮你打一波官司,而你就成为了被代理人。
在了解了代理和被代理人之后再来了解代理模式那就很方便了,只要记住代理就是做事的,被代理人只需要知道结果(其实就是过着懒人的生活,什么都不用操心)。
Java中的代理模式分为两种:
静态代理
这种代理模式从名字上来看就知道,静态静态,就是一开始规定好了,在这里就是代表是我们程序员自己根据需要被代理的类来创建的代理类
,可能看着比较绕,上代码就很清楚了:
首先先来个IClassMethod接口,里面主要是抽象了代理类和被代理类共同的方法(你要让别人代理你做,别人总要会这个东西吧)
public interface IClassMethod {
void playing(String something);
void goHome(String goHome);
}
再来个Person
类,这个类就是我们的被代理类啦,实现了IClassMethod
这个接口
public class Person implements IClassMethod {
public Person() {
System.out.println("init person");
}
@Override
public void playing(String something) {
System.out.println(something);
}
@Override
public void goHome(String goHome) {
System.out.println(goHome);
}
}
这时候有了被代理类,那么就来一个代理类PersonProxy
,它需要有被代理类的实例对象,不然怎么知道要代理谁呢,当然也要实现了IClassMethod
这个接口啦,不然怎么代替被代理的类做事呢:
public class PersonProxy implements IClassMethod {
private Person mPerson;
public PersonProxy(Person person) {
mPerson = person;
}
@Override
public void playing(String something) {
mPerson.playing(something);
}
@Override
public void goHome(String goHome) {
mPerson.goHome(goHome);
}
}
运行一下:
public class Test {
public static void main(String[] args) {
PersonProxy proxy=new PersonProxy(new Person());
proxy.goHome("我回家了");
proxy.playing("我在玩");
}
}
这样一整个静态代理就完成了,重新理一下静态代理:需要一个代理类(PersonProxy),一个被代理类(Person),还有一个两个都要实现的接口(IClassMethod)
,看PersonProxy
代码就知道,其实就是在代理类(PersonProxy
)里面使用Person来做操作,但是真正用的却是PersonProxy
的方法。
使用静态代理的话,也能实现普通的需求,但是如果有很多个被代理类呢,那不也要很多个代理类了么,那多麻烦,这时候就需要动态代理出场了。
动态代理
Java中有两种实现动态代理方式:一种是使用jdk的反射机制,一种是使用cglib。我们这就介绍下使用反射这种。
动态代理只能对接口代理,其中有两个很重要的东西,一个就是Proxy
类和InvocationHandler
接口,我们逐一介绍,Proxy就是Java提供生成代理类的类,里面有个newProxyInstance
方法生成代理类,这方法有三个参数:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
第一个参数:ClassLoader类加载器
第二个参数:接口类
第三个参数:重要的InvocationHandler
前两个没啥好说的,最后一个才是重点,这个InvocationHandler
里面就是一个方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
这方法主要是用来调用代理类的方法
的,第一个参数就是被代理类,第二个参数就是被代理类调用的方法,第三个就是方法的参数。
创建一个MyInvocationHandler
里面做一些自己的操作:
public class MyInvocationHandler implements InvocationHandler {
private Person mPerson;
public MyInvocationHandler(Person person) {
mPerson = person;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (int i = 0; i < args.length; i++) {
if (method.getName().equals("playing"))
args[i] = "我没在玩游戏";
}
return method.invoke(mPerson, args);
}
}
然后再使用Proxy
public class Test {
public static void main(String[] args) {
IClassMethod personProxy = (IClassMethod) Proxy.newProxyInstance(Person.class.getClassLoader(),
Person.class.getInterfaces(),
new MyInvocationHandler(new Person()));
personProxy.goHome("我回家了");
personProxy.playing("我在玩游戏");
}
}
可以看到,其实跟静态代理长的差不多,只不过这个代理类是jdk通过反射来生成的,这样只要是实现相同接口的类,都能使用动态代理,返回一个代理类。
看一下这个动态代理运行结果:
细心的大佬可能发现了,明明我在代理类传进去的是
我在玩游戏
,为何打印出来的却是我没在玩游戏
,这就是在MyInvocationHandler
这个类里面做了操作了:所以最后结果就成了
我没在玩游戏
。
总结
静态代理:代理类和被代理类都是实现相同的接口,代理类持有被代理类实例,然后代理类来进行操作。
动态代理:只能代理接口,使用jdk的反射机制自动创建代理类,并且自己实现InvocationHandler接口,在里面做一些自己的操作
主要用途:当不想向外暴露类的内部实现或者想要扩展下类功能就可以使用代理方式。