Java动态代理

Java的代理机制在很多常用的框架中都有应用,如Spring AOP。Java的代理主要作用是在不修改别人的代码基础上,完成功能的扩展。Java中包含三种代理模式,分别是静态代理、JDK动态代理、Cglib代理

静态代理

静态代理的特点是:委托类和代理类实现实现相同的接口,代理类包含委托类类型的属性。实际例子如下:

接口:

public interface Interface {

    public void test();
}

委托类:

public class Subject implements Interface{


    @Override
    public void test() {
        System.out.println("代理中.....");
    }
}

代理类:

public class MyProxy implements Interface{

    private Subject subject;

    public MyProxy(Subject subject){
        this.subject = subject;
    }

    @Override
    public void test() {
        System.out.println("代理之前......");
        subject.test();
        System.out.println("代理之后......");
    }
}

测试类:

public class Client {

    public static void main(String[] args) {
        Subject subject = new Subject();

        MyProxy proxy = new MyProxy(subject);

        proxy.test();//实际调用委托类的test方法
    }
}

静态代理的缺点:

  • 代理类和委托类需要实现相同的接口,这种一对一的关系导致当委托类很多的时候,代理类也会很多;
  • 如果接口类增加或减少方法时,委托类和代理类都需要修改

JDK动态代理

JDK动态代理的特点:我们不需要定义上面的代理类,即MyProxy类,只需调用JDK中的API来动态的生成该类。API是java.lang.reflect.Proxy的方法:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)

  • ClassLoader loader:委托类对象的类加载器,即上面的Subject.class.getClassLoader()
  • Class<?>[] interfaces:委托类对象实现的接口类型,即上面的Interface.class.getInterfaces(),如果委托类实现多个接口,可以穿入多个接口类型(new Class[]{InterfaceA.class,InterfaceB.class}),这样可代理多个接口中的任何一个方法
  • InvocationHandler handler:事件处理,调用委托类的方法时,会执行该对象中的invoke(Object proxy,Method method,Object[] args)方法,并将委托类方法的参数传入

示例如下:

接口:

public interface Interface {

    public void test();
}

委托类:

public class Subject implements Interface{

    @Override
    public void test() {
        System.out.println("代理中.....");
    }
}

处理类:

public class MyInvocationHandler implements InvocationHandler {

    private Subject subject;

    public MyInvocationHandler(Subject subject){
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("代理执行方法------" + method.getName());

        Object result = method.invoke(subject,args);

        System.out.println("执行结束....");

        return result;
    }
}

测试类:

public class Client {

    public static void main(String[] args) {
        Subject subject = new Subject();

        MyInvocationHandler handler = new MyInvocationHandler(subject);

        Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
                new Class[]{Interface.class},handler);

        proxy.test();//实际调用hangler的invoke方法,proxy对象是类型是com.sun.proxy.Proxy$0
    }
}

优点:代理对象动态生成,不需要实现接口;只需委托类实现接口即可;不许定义那么多的代理类

动态代理原理

先来看看Proxy.newProxyInstance方法源码:

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);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);//关键,这句会产生动态代理类

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            
            //constructorParams是Proxy中的静态变量Class[] constructorParams = {InvocationHandler.class}
            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);
        }
    }

代理类是动态,可以用过工具类打印出来看看动态生成的代理类是怎样的:

import com.heyu.shiro.proxy.Interface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Interface
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);//调用java.lang.reflect.Proxy的构造方法,并初始化属性
  }
  
  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void test()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);//继承父类java.lang.reflect.Proxy的属性InvocationHandler,并调用InvocationHandler的invoke方法
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //该静态块完成代理类属性的初始化,发现除了java.lang,Object中的方法,还重写了Interface接口中的test方法
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.heyu.shiro.proxy.Interface").getMethod("test", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
    }
  }
}

动态的生成$Proxy0这个类,并通过反射机制创建 该类的对象,并调用该类重写接口的test()方法,方法内部实际调用InvocationHandler的invoke方法

Cglib代理

JDK的动态代理要求委托类要实现接口,但是很多时候我们并不需要接口,这个时候JDK动态代理就没法使用了。Cglib就派上用场了。先上实例:

委托类:

public class Subject {

    public String test() {
        System.out.println("代理中.....");

        return "hello";
    }
}

处理类:

public class CgPorxy implements MethodInterceptor {

    public Object getProxy(Class clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);//设置父类
        enhancer.setCallback(this);//设置回调
        return enhancer.create();//生成代理类对象
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理开始....");
        Object result = methodProxy.invokeSuper(o,objects);//会调用代理类对象中的方法,如果方法有返回值,会返回
        System.out.println("代理结束....");
        return result;
    }
}

测试类:

public class Client {

    public static void main(String[] args) {

        CgPorxy porxy = new CgPorxy();//创建处理类

        Subject subject = (Subject) porxy.getProxy(Subject.class);
        System.out.println(subject.test());
    }
}

cglib生成的代理类的内容:

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Subject$$EnhancerByCGLIB$$a539bb91
  extends Subject
  implements Factory
{
  private boolean CGLIB$BOUND;
  public static Object CGLIB$FACTORY_DATA;
  private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final Callback[] CGLIB$STATIC_CALLBACKS;
  private MethodInterceptor CGLIB$CALLBACK_0;//拦截器
  private static Object CGLIB$CALLBACK_FILTER;//被代理方法
  private static final Method CGLIB$test$0$Method;//被代理方法
  private static final MethodProxy CGLIB$test$0$Proxy;//代理方法
  private static final Object[] CGLIB$emptyArgs;
  private static final Method CGLIB$equals$1$Method;
  private static final MethodProxy CGLIB$equals$1$Proxy;
  private static final Method CGLIB$toString$2$Method;
  private static final MethodProxy CGLIB$toString$2$Proxy;
  private static final Method CGLIB$hashCode$3$Method;
  private static final MethodProxy CGLIB$hashCode$3$Proxy;
  private static final Method CGLIB$clone$4$Method;
  private static final MethodProxy CGLIB$clone$4$Proxy;
  
  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class localClass1 = Class.forName("com.heyu.shiro.proxy.Subject$$EnhancerByCGLIB$$a539bb91");//代理类
    Class localClass2;//被代理类
    Method[] tmp83_80 = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$equals$1$Method = tmp83_80[0];
    CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
    Method[] tmp103_83 = tmp83_80;
    CGLIB$toString$2$Method = tmp103_83[1];
    CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
    Method[] tmp123_103 = tmp103_83;
    CGLIB$hashCode$3$Method = tmp123_103[2];
    CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");
    Method[] tmp143_123 = tmp123_103;
    CGLIB$clone$4$Method = tmp143_123[3];
    CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    tmp143_123;
    Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "test", "()Ljava/lang/String;" }, (localClass2 = Class.forName("com.heyu.shiro.proxy.Subject")).getDeclaredMethods());
    CGLIB$test$0$Method = tmp191_188[0];//被代理方法
    CGLIB$test$0$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "test", "CGLIB$test$0");
    tmp191_188;
  }

通过源码发现,道理类所有从父类继承的方法,都会有MethodProxy与之对应。例如:CGLIB$test$0$Method、CGLIB$test$0$Proxy,再来看看代理类的test()方法

final String CGLIB$test$0()
  {
    return super.test();
  }
  
  public final String test()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;//这个变量实质是我们的CgProxy类
    if (tmp17_14 != null) {
      return (String)tmp17_14.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy);//执行CgProxy中的intercept方法
    }
    return super.test();
  }

methodInterceptor.intercept()方法会调用上面的CGLIB$test$0()方法,及调用父类的test方法。

JDK动态代理和Cglib代理的区别:

  • JDK动态代理是动态生成实现接口的类,并实现接口中的方法,方法内容是调用InvocationHandler的invoke方法。
  • Cglib代理是动态生成委托类的子类,并重写父类方法,内容是调用自定义的实现接口MethodInterceptor的intercept方法,methodProxy.invokeSuper()方法会调用父类的方法。

注意:因为Cglib代理会重写父类的方法,因此代理方法不能是static 或final的,会导致无法代理;Cglib会动态生成委托类的子类,因此委托类不能是final的


Spring Aop

spring中的切面编程使我们常用的功能,spring aop支持JDK动态代理,支持Cglib代理。如果委托类实现了接口,那么采用JDK动态代理;如果没有实现接口,那么采用Cglib代理模式。

Spring aop Cglib动态代理

测试Spring Aop,先看委托类不实现接口

定义切面:

@Aspect
@Component
public class TestAspect {

    @Pointcut("execution(* com.example.demo.aspact.Proxy.*(..))")
    public void point(){}

    @Before("point()")
    public void test(){
        System.out.println("执行方法之前");
    }
}

定义委托类:

@Component
public class Proxy {

    public void test(){

        System.out.println("========");
    }

}

定义测试类:

@Component
public class BaseService {

    @Autowired
    private Proxy proxy;

    public void service(){
        System.out.println(proxy.getClass());
        proxy.test();
    }
}

定义单元测试启动项目:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @Autowired
    private BaseService baseService;

    @Test
    public void contextLoads() {
        baseService.service();
    }

}

运行单元测试结果如下:

class com.example.demo.aspact.Proxy$$EnhancerBySpringCGLIB$$599e29ff
执行方法之前
========

从结果看proxy.getClass()输出的是Cglib生成的动态子类;证明委托类没有实现接口时采用的是Cglib动态代理

Spring aop JDK动态代理 

如果使用springboot开发,会默认使用Cglib动态代理,我们需要先修改配置。在appplication.properties中:

spring.aop.proxy-target-class=false //默认为true,为true时会使用Cglib动态代理

在增加接口:

public interface Interface {

    public void test();
}

修改委托类如下:

@Component
public class Proxy implements Interface{

    @Override
    public void test(){

        System.out.println("========");
    }

}

测试类修改如下:

@Component
public class BaseService {

    @Autowired
    private Interface proxy;//修改为Interface类型,否则会报错

    public void service(){
        System.out.println(proxy.getClass());
        proxy.test();
    }
}

测试结果如下:

class com.sun.proxy.$Proxy61
执行方法之前
========
发现此时使用动态代理


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值