Java代理之静态、动态代理

代理

一般对象可以通过公共接口完成自己所需完成的工作。但有些对象由于某些原因无法履行自己的日常职责。大图像加载时间过长、有的对象运行在远程计算机上、拦截发送到对象的消息等等。这时可以使用代理对象,通过它来承担客户端的职责,再将相应请求合理的转发给底层目标对象。
代理模式就是为了提供一个代理(Proxy)来控制对目标对象的访问。代理对象通常拥有一个几乎和实际对象相同的接口。它通常控制访问,并将请求合理的转发给底层真实对象。可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

静态代理

这里写图片描述
商家(Business类)邀请明星唱歌(调用SingStar的sing()方法),需要联系经纪人(代理Proxy)谈妥费用,经纪人(AssitantProxy)安排(这个过程中,设备、场务人员协调全部由助理做,如果没有助理,明星自己就得额外做这些事,所以代理扩展目标对象功能)明星演出。其中经纪人和明星同属一个公司(RecordCompany接口,代理对象与实际对象实现几乎相同的接口)

接口(公司):
package com.willem.proxy;

/**
 * 唱片公司提供唱歌接口(舞台设备、工作人员)
 *
 * @author :wang_cgong
 *
 * Copyright (C) 2004-2017 All Rights Reserved.
 *
 * @Date 2017-08-21 15:08:12
 */
public interface RecordCompany {

    public void Sing();
}
实际对象(明星)
package com.willem.proxy; 
/**
 * Created by wang_cgong on 2017-08-21 下午4:14.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

/**
 * 类描述(description): 歌星演出经过公司同意(实现RecordCompany)
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-21 下午4:14
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class SingStar implements RecordCompany{
	@Override
	public void Sing() {
		System.out.println("《当你老了》\n《消愁》\n《穿越太平洋》\n《回忆那么伤》\n《告白气球》");
	}
}
代理对象(经纪人)
package com.willem.proxy; 
/**
 * Created by wang_cgong on 2017-08-21 下午4:31.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

/**
 * 类描述(description): 助理(代理),通知(歌星)唱歌、和商家商谈出场费等等
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-21 下午4:31
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class AssitantProxy implements RecordCompany {
	private RecordCompany target;
//用构造器注入,目标对象,只要目标对象实现了RecordCompany都可以
	public AssitantProxy(RecordCompany target){
		this.target=target;
	}
	@Override
	public void Sing() {
		System.out.println("南京演唱会准备就绪");
		target.Sing();//歌星唱歌(等会调用代理,通过向上转型,其实底层调用的是SingStar的Sing()方法)
		System.out.println("本站演唱会结束");
	}
}
调用(商家)
package com.willem.proxy; /**
 * Created by wang_cgong on 2017-08-21 下午4:46.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

/**
 * 类描述(description): 商家通过助理联系明星演出事宜
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-21 下午4:46
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class Business {
	public static void main(String [] args){
		SingStar target=new SingStar();
		//代理对象。将目标对象传给代理对象(商家将相关演出事宜告知助理)
		AssitantProxy proxy=new AssitantProxy(target);
		//执行代理方法(助理执行自己的sing()方法,它在底层又调用了目标对象的sing()方法)
		proxy.Sing();
	}
}

总结:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。

Aspectj

AspectJ是Eclipse基金组织的开源项目,多语法结构基本上已经成为AOP领域的标准。AspectJ是在编译时进行增强,所以它有一个专门的编译器来生成遵守Java字节码编码规范的Class文件。而Spring采用的是动态代理的方式,它并不需要有一个专门的编译器。故也称AspectJ为静态AOP实现,而Spring AOP为动态AOP实现。需要去官网下载aspectj-xxx.jar,双击安装。最后编译器安装支持aspectj的插件,项目关联aspectjrt.jar,选择Ajc编译器。编写java业务类,编写aspect切面

public aspect Logging{
	public void logging(){
        System.out.println("请输入验证码");
    }
}
public aspect LoggingAspect{
	void around():call(void logging()){
	System.out.println("验证通过");
	proceed;
	System.out.println("欢迎登录");
	}
}

动态代理

jdk自带动态代理

Java动态代理类位于java.lang.reflect包下:
1.java.lang.reflect.Proxy类
动态生产代理类和对象
2.java.lang.reflect.Proxy.InvocationHandler类
可以通过invoke()方法实现对真实角色的代理访问。
每次通过Proxy生成代理类对象都需要指定处理器对象。
抽象接口、真实对象类同上面的静态代理,不做改变。代理对象由Proxy类生成,所以不再需要代理类实现抽象接口(但真实对象还是需要实现接口),取而代之的是动态代理的处理程序接口(InvocationHandler)的实现。

类SinstarHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 类描述(description): 动态代理类对应的调用处理程序类 
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-23 上午10:48
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class SinstarHandler implements InvocationHandler {
	private SingStar target;
	//构造器注入,spring的bean也有构造器注入属性
	public SinstarHandler(SingStar target){
		super();
		this.target=target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("南京演唱会准备就绪");
		method.invoke(target,args);
		System.out.println("本站演唱会结束");
		return null;
	}
}
客户端调用
package com.willem.dynamic.proxy;
/**
 * Created by wang_cgong on 2017-08-23 下午2:28.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

import java.lang.reflect.Proxy;

/**
 * 类描述(description): 调用代理
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-23 下午2:28
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 */
public class Bussiness {
	public static void main(String [] args){
		SingStar target=new SingStar();
		//生成SingStar的调用控制器对象
		SinstarHandler handler=new SinstarHandler(target);
		RecordCompany proxy= (RecordCompany)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{RecordCompany.class},handler);
		//通过代理对象调用真正对象的方法
		proxy.Sing();
	}
	}

问题来了,静态代理类调用了实际对象的方法,动态代理怎么就最终调用了实际对象的方法?
根据传递的被代理类(SingStar)及其实现的接口(RecordCompany)生成代理类的字节码加载到缓存中,但是加载到缓存中只是一个.java文件也不能用,所以底层还有编译等操作。如何生成代理类我们没法看到底层代码,但JDK源码有一段是生成动态代理类字节码的:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

因此我们可以用这段代码生成商家邀请明星演出,经过的(代理)助理的字节码:

package com.willem.dynamic.proxy;
/**
 * Created by wang_cgong on 2017-08-24 上午10:55.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

/**
 * 类描述(description): JDK动态代理生成代理类字节码
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-24 上午10:55
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;
public class DynamicProxyDemo {
	public static void main(String[] args) throws Exception {
	//根据传递的被代理类(SingStar)及其实现的接口(RecordCompany)生成代理类的字节码,即这一串参数[SingStar.class.getInterfaces()]提供了被代理类及其接口
		byte[] classFile = ProxyGenerator.generateProxyClass("DynamicProxy", SingStar.class.getInterfaces());
		FileOutputStream out = null;
		try {
			//在当前工作目录下生成动态代理类的class文件
			out = new FileOutputStream(System.getProperty("user.dir") + "\\DynamicProxy.class");
			out.write(classFile);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

字节码如下反编译后:

import com.willem.dynamic.proxy.RecordCompany;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class DynamicProxy extends Proxy
  implements RecordCompany
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  public DynamicProxy(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void Sing()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static
  {
    try
    {
      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("com.willem.dynamic.proxy.RecordCompany").getMethod("Sing", 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());
  }
}

public final class DynamicProxy extends Proxy implements
RecordCompany

JDK动态代理类默认继承了Proxy类,由于Java的单继承,JDK动态代理无法实现继承式代理(无法继承实现类,只能去实现实现类的接口),所以只支持接口代理。
proxy对象调用sing()方法:

 public final void Sing()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }

其实就转发到了控制器的invoke方法,控制器中method.invoke(target,args);通过反射调用目标对象的sing()方法。从而实现代理。

代理工厂

在工厂中用匿名内部类代替控制器类:

package com.willem.dynamic.proxy;
/**
 * Created by wang_cgong on 2017-08-24 下午5:33.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 类描述(description): 匿名内部类代替调用处理器handler代理工厂
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-24 下午5:33
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class ProxyFactory {
	private Object target;
	//构造器注入目标对象
	public ProxyFactory(Object target){
		this.target=target;
	}
	//生成代理对象
	public Object getProxyInstance(){
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new
				InvocationHandler(){
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("开始事务");
						Object returnValue=method.invoke(target,args);
						System.out.println("事务结束");
						return returnValue;
					}
				});
	}
}

结果展示:
这里写图片描述
总结:JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

invoke方法分析

接口,同名方法,不同参数

public interface JdkProxyInterface {
    String doSomething(String thingsNeedParm);
    String doSomething(String thingsNeedParm1,String thingsNeedParm2);
}

接口实现

public class SubjectIpml implements JdkProxyInterface {
    @Override
    public String doSomething(String thingsNeedParm) {
        System.out.println("使用" + thingsNeedParm + "做了一些事情");
        return "调用成功";
    }

    @Override
    public String doSomething(String thingsNeedParm1, String thingsNeedParm2) {
        System.out.println("使用" + thingsNeedParm1+"&"+thingsNeedParm2 + "做了一些事情");
        return "调用成功";
    }
}

代理

public class SubjectProxy implements InvocationHandler {

    private JdkProxyInterface subject;

    SubjectProxy(JdkProxyInterface subject){
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //进行method过滤,如果是其他方法就不调用
        if (method.getName().equals("doSomething")){
            System.out.println("做某些事前的准备");
            Object object = method.invoke(subject,args);
            System.out.println("做某些事后期收尾");
            return object;
        }
        if (method.getName().equals("equals")){
            System.out.println("equals来啦");
            Object object = method.invoke(subject,args);
            System.out.println("equals走啦");
            return object;
        }
        return "调用失败";
    }
    public JdkProxyInterface getProxy() {
        return (JdkProxyInterface) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), this);
    }
}

结果
在这里插入图片描述

CGLIB代理

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承(重写父类(目标类)所有方法),final不能被重写,所以不能代理final方法。
目标类:

package com.willem.cglib; /**
 * Created by wang_cgong on 2017-08-21 下午4:14.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

/**
 * 类描述(description): cglib代理,代理目标类都不需要实现接口了
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-08-21 下午4:14
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class SingStar {
	
	public void sing() {
		
		System.out.println("《当你老了》\n《消愁》\n《穿越太平洋》\n《回忆那么伤》\n《告白气球》");
	}
	public boolean test(String s){
		System.out.println("测试Fastcalss如何根据下标定位方法:"+s);
		return true;
	}
}

代理工厂

package com.willem.cglib;
/**
 * Created by wang_cgong on 2017-09-11 下午9:28.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 类描述(description): Cglib代理工厂,在内存中动态生成被代理类的子类对象
 *
 * @作者(author): wang_cgong
 
 * 时间(time): 2017-09-11 下午9:28
 *
 * @version: v 1.0.0
 *
 * @since JDK1.6
 
 */
public class CglibProxyFactory implements MethodInterceptor {
	
	private Enhancer en=new Enhancer();
	
	private Object target;
	
	public CglibProxyFactory(Object target){
		this.target=target;
	}
	
	public Object getProxyInstance() {
		
		//通过增强类对象设置父类(目标对象类)
		en.setSuperclass(target.getClass());
		//设置回调函数(拦截器类)
		en.setCallback(this);
		//返回代理对象
		//System.out.println(en.create());
		return en.create();
	}
	
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println(method.getName()+"执行前做的工作!");
		//Object result=methodProxy.invokeSuper(obj,args);//相当于AOP的切点
		//①methodProxy.invokeSuper(target,args);//错误
		//②methodProxy.invoke(target,args);//正确
		//③methodProxy.invoke(obj,args);//堆栈溢出发生循环调用
		methodProxy.invokeSuper(obj,args);
		//④method.invoke(target,args);//正确,但走的是JDK的invoke方法,通过反射来调用目标方法,那用cglib就无意义了
		System.out.println(method.getName()+"执行后做的工作!");
		return null;
	}
}

客户端调用

package com.willem.cglib;
/**
 * Created by wang_cgong on 2017-09-12 下午4:57.
 * <p>
 * Copyright (c) 2016- 2017, willem517782125@qq.com All Rights Reserved.
 */

/**
 1. 类描述(description): Cglib代理工厂客户端调用
 2.  3. @作者(author): wang_cgong
 
 4. 时间(time): 2017-09-12 下午4:57
 5.  6. @version: v 1.0.0
 7.  8. @since JDK1.6
 
 */
public class Bussiness {
	public static void main(String [] args){
		SingStar target=new SingStar();
		//保存生成的class文件
		System.out.println(System.getProperty("user.dir"));


        /** 开启 保存cglib生成的动态代理类类文件*/
        try {
            saveGeneratedCGlibProxyFiles(System.getProperty("user.dir"));
        } catch (Exception e) {
            e.printStackTrace();
        }
		//代理对象
		SingStar proxy= (SingStar) new CglibProxyFactory(target).getProxyInstance();
		proxy.sing();
	}
	public static void saveGeneratedCGlibProxyFiles(String dir) throws Exception {
        Field field = System.class.getDeclaredField("props");
        field.setAccessible(true);
        Properties props = (Properties) field.get(null);
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, dir);//dir为保存文件路径
        props.put("net.sf.cglib.core.DebuggingClassWriter.traceEnabled", "true");
    }
}
cglib细节深入
invoke()和invokeSuper()区别

执行代理之前,Enhancer会通过creat()方法生成代理类【目标类(SingStar)的子类。经过增强的,它反编译后是extends目标类】SingStar$$EnhancerByCGLIB$$f7ff98ef.class字节码。在调用目标类的sing()方法时会生成两个FastClass字节码文件(这也是Cglib调用方法效率高于原生Jdk代理的原因,Fast文件会把各个方法哈希码转换为类似数组下标的Index,方法调用时直接找对应的“下标”,不需要反射调用。)
1.SingStar$$EnhancerByCGLIB$$f7ff98ef$$FastClassByCGLIB$$a7574022.class(代理类的FastClass,实例对象是fci.f2)
2SingStar$$FastClassByCGLIB$$a2922115.class(目标类的FastClass 实例对象是fci.f1)
引用不同调的方法也不同,下面是intercept中4种调用方法本质
在这里插入图片描述

methodProxy.invokeSuper(target,args)

代理工厂的①methodProxy.invokeSuper(target,args)会报转型异常
由图可知调用MethodProxy的invokeSuper。

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

最终调用fci.f2.invoke(fci.i2, obj, args),即我们需要看代理类子类的FastClass(SingStar$$EnhancerByCGLIB$$f7ff98ef$$FastClassByCGLIB$$a7574022)反编译后的java文件中的invoke方法:

public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final SingStar$$EnhancerByCGLIB$$f7ff98ef singStar$$EnhancerByCGLIB$$f7ff98ef = (SingStar$$EnhancerByCGLIB$$f7ff98ef)o;
 ...
 }      

方法参数传入的o,是target(父类),要转型为代理类:SingStar$$EnhancerByCGLIB$$f7ff98ef,而代理类是SingStar通过Enhancer 的creat方法创建的增强类(extends了目标类),也就是父类的扩展(子类),这样的向下转型肯定是不安全的,因为子类有的属性父类没有,java会自动检查抛出转型异常,关于向下转型可以参考Thinking in java这里不再赘述。

methodProxy.invokeSuper(obj,args)

③methodProxy.invokeSuper(obj,args);这是cglib提倡使用的方法。看看它的机制。
这里还有final int n

public int getIndex(final Signature signature) {
        final String string = signature.toString();
        switch (string.hashCode()) {
            case -1985748682: {
            //子类重写的sing方法下标21,hashCode和操作系统运算机制有关,不同的jdk生成的不一样。
                if (string.equals("CGLIB$sing$1()V")) {
                    return 21;
                }
                break;
            }

同样的再看fci.f2的invoke方法

public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final SingStar$$EnhancerByCGLIB$$f7ff98ef singStar$$EnhancerByCGLIB$$f7ff98ef = (SingStar$$EnhancerByCGLIB$$f7ff98ef)o;
        try {
            switch (n) {
                case 21: {
     singStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$sing$1();
                    return null;
                }
        catch (Throwable t) {
            throw new InvocationTargetException(t);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

好了看看singStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$sing$1()

 final void CGLIB$sing$1() {
        super.sing();
    }

调用了目标类方法,没有疑问。

methodProxy.invoke(target,args)

②methodProxy.invoke(target,args);由图可知调用MethodProxy的invoke:

//recursion will result if you use the object passed as the first argument to the MethodInterceptor (usually not what you want)
 public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (IllegalArgumentException e) {
            if (fastClassInfo.i1 < 0)
                throw new IllegalArgumentException("Protected method: " + sig1);
            throw e;
        }
    }

走的是fci.f1.invoke(fci.i1, obj, args),看看fci.f1的fastCalss:
先看final int n

public int getIndex(final Signature signature) {
        final String string = signature.toString();
        switch (string.hashCode()) {
            case 2094464646: {
            //父类sing方法
                if (string.equals("sing()V")) {
                    return 1;
                }
                break;
            }
        }
        return -1;
    }

fci.f1的invoke方法:

 public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final SingStar singStar = (SingStar)o;
        try {
            switch (n) {
                case 0: {
                    return new Boolean(singStar.test((String)array[0]));
                }
                case 1: {
                    singStar.sing();
                    return null;
                }
                case 2: {
                    return new Boolean(singStar.equals(array[0]));
                }
                case 3: {
                    return singStar.toString();
                }
                case 4: {
                    return new Integer(singStar.hashCode());
                }
            }
        }
        catch (Throwable t) {
            throw new InvocationTargetException(t);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

直接根据下标调用的singStar.sing()。此时的singStar是传入的父类对象target。直接调用目标类的方法。没有问题。但没有增强。

method.invoke(target,args)

method.invoke(target,args)由图看出调用的是jdk原生invoke方法,通过反射调用目标类方法,没问题。但这效率低下。

methodProxy.invoke(obj,args)

③methodProxy.invoke(obj,args);//发生循环调用,内存溢出。看一下步骤:

  1. 一开始 proxy.sing(),调用代理类sing(),SingStar$$EnhancerByCGLIB$$f7ff98ef.class的sing(),不是fci所以不会调fastclass里的方法。看CGLIB$BIND_CALLBACKS方法,CGLIB$CALLBACK_0,其实就是MethodIntercepter的对象,enhancer.setCallback(this)时传入的拦截器对象,我们设置了而且不为空。所以sing中走cglib$CALLBACK_0 != null逻辑,调用intercept
public final void sing() {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        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);
        }
        //拦截器对象设置了不为null
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept((Object)this, SingStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$sing$1$Method, SingStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$emptyArgs, SingStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$sing$1$Proxy);
            return;
        }
        super.sing();
    }
    /**cglib$CALLBACK_0是这么来的:最后一句也就是CGLIB$CALLBACK_0其实就是
    MethodIntercepter的对象,enhancer.setCallback(this)时传入的拦截器对象,我们设
    置了而且不为空。所以sing中走cglib$CALLBACK_0 != null逻辑
    */
    private static final void CGLIB$BIND_CALLBACKS(final Object o) {
        final SingStar$$EnhancerByCGLIB$$f7ff98ef singStar$$EnhancerByCGLIB$$f7ff98ef = (SingStar$$EnhancerByCGLIB$$f7ff98ef)o;
        if (!singStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$BOUND) {
            singStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$BOUND = true;
            Object o2;
            if ((o2 = SingStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = SingStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$STATIC_CALLBACKS) != null) {
                singStar$$EnhancerByCGLIB$$f7ff98ef.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
            }
        }
    }
  1. 拦截器intercept中我们调用了MethodProxy的invoke(obj,args)。从fci.f1的fastCalss获取final int n=1,然后走fci.f1.invoke(fci.i1, obj, args),调用singStar.sing()。final SingStar singStar = (SingStar)o,代理类向上转型,所以实际调的是代理类的sing().及proxy.sing()。

又去走intercept方法了,然后再执行proxy.sing();进入死循环,最终爆栈。
这里写图片描述

代理和装饰者模式区别

代理针对对象访问控制,比如开门这个功能影响了房子使用中的很多领域,所以我们把开门这一动作抽象出来做为代理。装饰模式,房间缺少椅子,但它不影响房子这个对象的基本属性,我们给他增加椅子、电视等等,扩展房子功能,这两个模式关注点不同,代理关注的是对象访问控制,装饰关注扩展对象功能。

public interface Room{
	//可以睡觉、洗澡、做饭
    public void action();
}
public class SleepRoom implements Room{
	@Override
	public void action(){
    sleep();
    };
    private void sleep(){
    	System.out.pringtln("我要睡觉了");
    };
}
public class RoomProxy implements Room{
	RoomProxy(Room room){
	this.room = room;
	}
	//睡觉、洗澡、做饭。都需要事先开门。它影响了房间多处功能。
	@Override
	public void action(){
    	System.out.pringtln("门打开了,进入房间");
    	room.action();
    }
}
//装饰房子
public interface Room{
    public void decorator();
}
//房间放椅子
public class ChairOfRoom implements Room{
	@Override
    public void decorator(){
		putChair();
	}
	//房间放椅子
	private void putChair(){
    	System.out.pringtln("放了八把椅子");
    };
}
//创建一个抽象装饰器
public abstract class RoomDecorator implements Room{
   protected Room decoratedRoom;
 
   public RoomDecorator(Room decoratedRoom){
      this.decoratedRoom= decoratedRoom;
   }
   public void decorator(){
      decoratedRoom.decorator();
   }  
}
//实际椅子装饰器。
public class ChairDecorator extends RoomDecorator {
 
   public ChairDecorator (Room decoratedRoom) {
      super(decoratedRoom);     
   }
 
   @Override
   public void decorator() {
      decoratedRoom.decorator();      
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值