代理是一种设计模式,提供了对目标对象的间接访问,即通过代理访问目标对象的方法,那么就可以很方便的在目标方法的基础上添加额外的操作,前拦截,后拦截等。
按照代理的创建时期可以分为两种:
静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态代理:在程序运行时运用反射机制动态创建而成
静态代理的原理比较简单:
①创建代理类实现代理接口
②在类中维护一个代理对象,通过构造器初始化代理对象
③实现接口方法,并在接口方法中使用代理对象调用同一接口方法,并实现前后拦截等功能。
//Persion接口
public interface Persion {
void pay();
}
//Student.java
public class Student implements Persion {
String name;
public Student(String name) {
this.name = name;
}
@Override
public void pay() {
System.out.println(name + " pay!");
}
}
//PersionProxy.java 创建代理类实现代理接口
public class PersionProxy implements Persion {
// 在类中维护一个代理对象,通过构造器初始化代理对象
Persion persion;
public PersionProxy(Persion persion) {
this.persion = persion;
}
// 实现接口方法,并在接口方法中使用代理对象调用同一接口方法,并实现前后拦截等功能。
@Override
public void pay() {
System.out.println("before");
persion.pay();
System.out.println("after");
}
}
//主函数
public class Main {
public static void main(String[] args) {
Student student = new Student("tom");
//获取student对象的代理
PersionProxy proxy = new PersionProxy(student);
//代理执行方法
proxy.pay();
}
}
输出结果
before
tom pay!
after
由代码可知静态代理是写死的代码,每个代理只能代理一个类型的指定方法。那么它的缺点也显而易见了,如果需要对大量的接口进行代理,就会有大量的代理类,不利于维护;如果要对代理的接口修改/添加/删除方法,同时也需要维护实现类和代理类,不利于扩展。
//Persion接口
public interface Persion {
void pay();
}
//Student.java
public class Student implements Persion {
String name;
public Student(String name) {
this.name = name;
}
@Override
public void pay() {
System.out.println(name + " pay!");
}
}
//DynamicProxy .java
public class DynamicProxy {
//返回一个代理对象
public static Object agent(Class interfaceClazz, Object proxy) {
return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz},
new MyHandler(proxy));
}
static class MyHandler implements InvocationHandler {
//代理对象
private Object proxyInstance;
public MyHandler(Object proxyInstance) {
this.proxyInstance = proxyInstance;
}
/**
proxy:被代理的类实例
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
//执行指定方法
Object invoke = method.invoke(proxyInstance, args);
System.out.println("after");
return invoke;
}
}
}
// main方法
public class Main {
public static void main(String[] args) {
//这里必须转成接口,包括参数中的Persion.class否则会报错
Persion proxy = (Persion) DynamicProxy.agent(Persion.class, new Student("tom"));
proxy.pay();
}
}
输出结果
before
tom pay!
after
优点:代理无需实现被代理的接口,编写的代理类可以代理任何实现了接口的对象(通过反射机制实现),具有较高的复用性。
缺点:被代理的目标对象必须要实现接口,否则无法使用JDK动态代理。
原理在于生成代理类中调用的方法实际指向invoke。
现在来研究研究它的原理,由于源码分析较为复杂,能力有限,只看会了大神分析的大概思路,后序再补充。
首先是在newProxyInstance方法中,它生成了代理类,此代理类是动态生成的类文件,看代码:
(摘自博客:https://www.cnblogs.com/gonjan-blog/p/6685611.html)
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类,并实现了代理的interface接口,类中持有自定义的InvocationHandler 类h,而h中又拥有真实对象。
那么思路经很清晰了,代理类实现了接口,对其中的接口的方法的实现中,实际上调用的是h中的invoke方法。通过反射来获取每一个方法对象。
总结一下步骤
//Student.java
public class Student implements Persion {
String name;
public Student(String name) {
this.name = name;
}
@Override
public void pay() {
System.out.println(name + " pay!");
}
}
//CGLIBProxy.java
public class CGLIBProxy implements MethodInterceptor {
Object proxy;
public Object getInstance(Object proxy) {
this.proxy = proxy;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.proxy.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("beforeg");
//真正调用
Object obj= methodProxy.invokeSuper(o, objects);
System.out.println("after");
return obj;
}
}
//main方法
public static void main(String[] args) {
CGLIBProxy proxy = new CGLIBProxy ();
Student student= (Student) proxy.getInstance(new Student("tom"));
student.pay();
}
输出结果
before
tom pay!
after
使用方式和JDK的动态代理类似,但是它可以直接对没有实现接口的类进行操作,会更加方便。
简单介绍两句:
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。(来自百度百科)
简单说它是用来生成字节码的库
CGLIB可以代理没有实现接口的类,相比JDK的动态代理更加灵活,性能也更好。
下面通过反编译CGLIB生成的代理类进行分析。
(此源码来源网络)
import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
//
// Decompiled by Procyon v0.5.30
//
public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
//我们自定义的拦截器对象会被这个指针指向
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$add$0$Method;
private static final MethodProxy CGLIB$add$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
static void CGLIB$STATICHOOK2() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb");
final Class<?> forName3;
CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0];
CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");
}
final void CGLIB$add$0() {
super.add();
}
public final void add() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
//判断自定义的拦截器是否为空,为空则调用CGLIB$BIND_CALLBACKS(),这个方法没有研究,大概是初始化拦截器
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
// 如果拦截器不为空则调用拦截器中重写的intercept()方法,并返回
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy);
return;
}
super.add();
}
static {
CGLIB$STATICHOOK2();
}
}
emmm,代码中的命名有点迷,重点看注释部分就可以。
简单总结一下:
代理逻辑
与业务逻辑
相互独立互不影响,降低了耦合,有利于对原方法的增强与扩展。