Java代理模式
java静态代理模式
首先关于java的代理模式我的理解是:分为四个部分
1 针对于客户端的部分
2 有一个公共的接口
3 有个被代理的类
4 有个代理类
而代理模式带来的优点是:我们可以对被代理类增加更多的处理,在Spring的AOP中就是使用的代理模式可以使得针对于切面进行编程。
静态代理的例子
一个公共的接口Person.class
public interface Person{
public void giveMoney();
}
一个被代理的类
public class Student implements Person{
private String name; public Student(String name){ this.name = name; } public void giveMoney(){ System.out.println(name+"给了50块钱");
}
}
一个代理类
public class Proxy implements Person{
private Person person;
public Proxy(Person person){
this.person = person;
}
public void giveMoney(){
System.out.println("作业完成的很好");//这个就是在我们被代理的对象前面添加的方法。
person.giveMoney();
System.out.println("老师再见");//这个使我们在被代理的对象后面添加的方法类似于Spring AOP中的切面前置通知和前面后置通知
}
}
一个客户端的类
public class Client{
Student stu = new Student("jhc"); Proxy proxy = new Proxy(stu); proxy.giveMoney();
}
结果可以看到在我们交钱的时候增加了新的方法。
但是静态代理会有一个缺点就是:我们可能不存在只有一个代理类的情况,也许我们会有很多代理类,而添加而过每个代理类都通过对代理类增加方法的话那样操作起来会很麻烦。所以还有一个方式是动态代理。
动态代理模式
通过JDK的动态代理模式实现代理。
在java.lang.reflect包下面有一个Proxy类和InvocationHandler接口,通过这两个类去实现java的动态代理。
创建一个动态代理对象的步骤
- 创建一个实现了InvocationHandler接口的对象
InvocationHandler stuHandler = new MyInvocationHandler(stu);
- 使用Proxy类的静态方法得到一个动态代理的类
Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(),new Class<?>[]{Person.class});
- 通过类然后由反射获得一个具有InvocationHandler类的构造函数
Constructor<?> cons = stuProxyClass.getConstructor(InvocationHandler.class);
- 通过构造器constructor来创建一个动态实例stuProxy
Person person = (Person)cons.newInstance();
上面四个步骤可以简化成为两个步骤
InvocationHandler stuHandler = new MyInvocationHandler(stu);
Person proxy = (Person)Proxy.newInstance(Person.class.getClassLoader(),new Class[]{Person.class},stuHandler);
关于动态代理的实例模式其实一样
- 首先是一个公共的接口Person
public interface Person {
public void giveMoney();
}
- 其次是被代理类的实现Student
private String name;
- 第三步是比较中的实现InvocationHandler的接口
public class StuInvocationHandler<T> implements InvocationHandler<T>{ private T target; public StuInvocationHandler(T target){ this.target = target; } public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{ System.out.println("在这插入我们想要的方法"); Object result = method.invoke(target,args); System.out.println("这里也可以插入我们想要的方法"); return result; } }
- 第四步是在客户端使用动态代理并测试
public class ProxyTest{ public static void main(String[] args){ Person jhc = new Student("jhc"); InvocationHandler stuHandler = new StuInvocationHandler<Person>(jhc); Person stuProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[?]{Person.class},stuHandler); stuProxy.giveMoney(); } }
结果如图
下面是Proxy.newInstance()的源码的内容。
/*首先解释一下三个参数,第一个参数是为了能够加载出来Person即公共接口这个类,第二个参数是找出实现的全部的接口,第三个参数是为了动态类能够调用方法
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
*这里产生了代理类.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
下面是大神关于怎么产生的代理类的分析,具体的做法我也不知道怎么回事,只知道他是生成了动态的代理类的文件并且对该文件进行了反编译。得到的内容是
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}```
这是把文件写入到硬盘上,利用javap可以把文件进行反编译
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
- protected Proxy(InvocationHandler h) {
- Objects.requireNonNull(h);
- this.h = h;
- }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static
{
try
{
//看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
- *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void giveMoney()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
}
这里大神写的很清楚,产生的动态代理类继承自Proxy类,并且构造参数自动的拥有了InvocationHandler的构造参数,并且实现了接口以及接口的方法m3,这里值得注意的是其实纵观全局来看的话,动态代理类只是一个中介类,他并没有实现被代理类的任何方法都是通过InvocationHandler里面具体的Target实现的,这样动态代理的优点在于便于修改动态代理类
###总结
---
这是动态代理的JDK实现,缺点在于实现公共接口的类才能实现动态代理,没有办法实现对于继承来的类的处理。
主要参考的是大神的博客
https://www.cnblogs.com/gonjan-blog/p/6685611.html