【Java高级】一篇文章带你弄懂动态代理

目录

| 动态代理概述

| 静态代理

| 动态代理

动态代理 VS 静态代理

动态代理的实现步骤示例

动态代理中 Proxy类 newProxyInstance源码

动态代理中的 invoke( )方法

| CGLIB代理

| 结语




| 动态代理概述

代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

| 静态代理

  • 静态代理就是程序员在编写代码的时候就已经把代理类的源码写好了,编译后就会生成.class文件。

  • 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

  • 举一个租客通过中介租房子的例子。

(1)首先创建一个person接口,这个接口就是租客和中介的公共接口,这个接口有一个rentHouse()方法。

public interface Person {
	//租房
	public void rentHouse();
}

(2)创建租客Renter类,实现上述接口

public class Renter implements Person{
	@Override
	public void rentHouse() {
		System.out.println("租客租房成功!");	
	}
}

(3)创建中介类RenterProxy,同样实现Person接口,但是还另外持有一个租客类对象

public class RenterProxy implements Person{
	private Person renter;
	public RenterProxy(Person renter){
		this.renter = renter;
	}
	@Override
	public void rentHouse() {
		System.out.println("中介找房东租房,转租给租客!");
		renter.rentHouse();
		System.out.println("中介给租客钥匙,租客入住!");	
	}
}

(4)新建测试类测试

public class StaticProxyTest {
	public static void main(String[] args) {
		Person renter = new Renter();
		RenterProxy proxy = new RenterProxy(renter);
		proxy.rentHouse();
	}
}

 

| 动态代理

动态代理 VS 静态代理

  • 类似上面 RenterProxy这样的代理类 ,若是在程序运行时创建的,则这种代理方式被成为动态代理

  • 在静态代理中,代理类(RenterProxy)是自己已经定义好了的,在程序运行之前就已经编译完成。

  • 而动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,如果我们想在每个代理方法前都加一个方法,如果代理方法很多,我们需要在每个代理方法都要写一遍,很麻烦。而动态代理则不需要。

    换言之,动态代理就是把静态代理已经确定好的类内容,换成了【通过反射机制获取】,实际上完成的功能适合静态代理一模一样的

  • 代码实现:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。


动态代理的实现步骤示例

(1)和静态代理相同,首先定义一个person接口

public interface Person {
	//租房
	public void rentHouse();
}

(2)创建被代理的类

public class Renter implements Person{
	@Override
	public void rentHouse() {
		System.out.println("租客租房成功!");
	}
}

(3)创建RenterInvocationHandler类,这个类实现了InvocationHandler接口,并持有一个被代理类的对象,InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后通过反射在invoke方法中执行代理类的方法。在代理过程中,在执行代理类的方法前或者后可以执行自己的操作,这就是spring aop的主要原理。

public class RenterInvocationHandler<T> implements InvocationHandler{
	//被代理类的对象
	private T target;
	
	public RenterInvocationHandler(T target){
		this.target = target;
	}

	/**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//代理过程中插入其他操作
		System.out.println("租客和中介交流");
		Object result = method.invoke(target, args);
		return result;
	}
}

(4)创建动态代理对象

public class ProxyTest {

	public static void main(String[] args) {

		//创建被代理的实例对象
		Person renter = new Renter();
		//创建InvocationHandler对象
		InvocationHandler renterHandler = new RenterInvocationHandler<Person>(renter);
		
		
		//创建代理对象,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
		Person renterProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class<?>[]{Person.class}, renterHandler);
		renterProxy.rentHouse();
		
		//也可以使用下面的方式创建代理类对象,Proxy.newProxyInstance其实就是对下面代码的封装
		/*try {
			//使用Proxy类的getProxyClass静态方法生成一个动态代理类renterProxy 
			Class<?> renterProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[]{Person.class});
			//获取代理类renterProxy的构造器,参数为InvocationHandler
			Constructor<?> constructor = renterProxyClass.getConstructor(InvocationHandler.class);
			//使用构造器创建一个代理类实例对象
			Person renterProxy = (Person)constructor.newInstance(renterHandler);
			renterProxy.rentHouse();
			//
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
	}

}

动态代理中 Proxy类 newProxyInstance源码

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        /*
         * Look up or generate the designated proxy class.
         */
         //生成动态代理类
        Class<?> cl = getProxyClass0(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //获取构造器参数是InvocationHandler类,constructorParams是Proxy的静态成员变量
            // private final static Class[] constructorParams ={ InvocationHandler.class };
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
        try {
           //使用构造器创建一个代理类实例对象
            return cons.newInstance(new Object[] {h} );
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString());
            }
        }
    }

Class<?> cl = getProxyClass0(loader, interfaces);这行代码生成了一个代理类,这个类缓存在java虚拟机中。可以通过下列代码将其打印出来。

        byte[] classByte = ProxyGenerator.generateProxyClass("$Proxy0",
				new Class<?>[] { Person.class });
		String path = "D:/Eclipse/Workspace/Demo/bin/$Proxy0.class";
		try (FileOutputStream fos = new FileOutputStream(path)) {
			fos.write(classByte);
			fos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

将class文件反编译:

public final class $Proxy0 extends Proxy
  implements Person
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
//通过带InvocationHandler参数的构造器生成代理类对象时,将我们写的RenterInvocationHandler对象传进来
//父类持有:protected InvocationHandler h
  public $Proxy0(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 (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void rentHouse()
    throws 
  {
    try
    {
    //执行代理类的rentHouse方法时,实际上是执行InvocationHandler类的invoke方法,也就是我们自己写的
    //RenterInvocationHandler类的invoke方法,在此方法中我们通过method.invoke(target, args)反射,再
    //执行具体类的rentHouse方法      
      this.h.invoke(this, m3, null);
      return;
    }
    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);
    }
  }

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

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { 
      Class.forName("java.lang.Object") });
      //获取到了rentHouse方法的方法名
      m3 = Class.forName("test.father.Person").getMethod("rentHouse", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

java自动生成了一个$Proxy0代理类,这个类在内存中,所以可以通过反射获取这个类的构造方法,然后创建的代理类实例对象。分析这个类源码,可以知道当调用代理类对象的rentHouse方法时的大概流程为:调用RenterInvocationHandler类的invoke方法,而RenterInvocationHandler类的invoke方法中又用反射调用了被代理类的rentHouse方法。 RenterInvocationHandler可以看成是中间类,它持有被代理对象,把外部对invoke的调用转为对被代理对象的调用。而代理类通过持有中间类,调用中间类的invoke方法,来达到调用被代理类的方法的目的


动态代理中的 invoke( )方法

invoke 与 反射

invoke方法来自Method类,可能不会像我们经常用到的基础类型包装类,以及集合类还有他们的扩展和工具类使用的那么多。

但是Method类所在的包可是大名鼎鼎的反射Reflact,不是有一句话Java没有反射,那么很多框架都不会存在。

我们经常new 对象出来,但是new的前提是你知道你到底需要什么对象,你才能new,然而不管是代码还是现实生活都有未知性,也就是直到程序运行时受条件限制才知道什么类,什么方法。

反射解决的这个问题,运行时获取对象结构,调用方法。 Method这个类就是关于反射调用方法的

大概意思就是说提供类或者接口的方法信息,就可以访问调用对应的方法。

invoke的意思上就有调用的意思,也就是说我们可以通过反射包下的Method类调用invoke方法,调用我们所提供的方法以及调用方法的参数来完成动态调用。

也就是根据你给的对象/实例,方法名,以及参数来调用。找了个“替身”来帮你调用方法。

invoke

实际上invoke方法的使用,和我们常见的有所区别。

我们经常创建一个对象A,A对象里面的方法getA()方法,然后A.getA()

我们采用新的方式调用 (1)弄一个方法的“替身”(其实就是构建一个Method对象,让这个Method对象来代替你现在要用的方法) (2)然后给替身需要的对象和参数,让替身去替你调用(像JOJO的替身替你去战斗)

具体代码演示如下:

public class InvokeTest {
    public void test(String[] arg){
        for (String string : arg) {
            System.out.println("zp is " + string);
        }
    }
    @Test
    public void invokeDemo() throws Exception {
        //获取字节码对象,这里要填好你对应对象的包的路径
        Class<InvokeTest> clazz = (Class<InvokeTest>) Class.forName("com.example.zp.demo.testDemo.InvokeTest");
        //形式一:获取一个对象
//        Constructor con =  clazz.getConstructor();
//        InvokeTest m = (InvokeTest) con.newInstance();
        //形式二:直接new对象,实际上不是框架的话,自己写代码直接指定某个对象创建并调用也可以
        InvokeTest m = new InvokeTest();
        String[] s = new String[]{"handsome","smart"};
        //获取Method对象
        Method method = clazz.getMethod("test", String[].class);
        //调用invoke方法来调用
        method.invoke(m, (Object) s);
    }

所以使用invoke方法要比别的方法多做一步,就是构建一个Method对象,这个对象替代的是现在程序要调用方法的替代品。

而且除了参数以外,invoke还会多要一个对象,因为方法调用需要对象,所以invoke要想调用的目标方法,就需要目标方法的需要的对象。

看起来invoke方法不仅比平常方法直接调用要麻烦很多,但是你有想过吗,我只需要输入参数,我可以调用替代各种方法,在未知的情况下,根据条件决定去调用什么对象,什么方法,一下子就让代码变得灵活,这不仅是invoke的妙处,也是整个反射的妙处,在程序运行时根据条件灵活使用。


| CGLIB代理

  • 动态代理需要被代理类实现接口,如果被代理类没有实现接口,那么这么实现动态代理?这时候就需要用到CGLib了。这种代理方式就叫做CGlib代理。

  • Cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。

  • CGLIB代理所需的前期准备:导入cglib的jar包,另外Spring的核心包中已经包括了Cglib功能,也可以导入spring-core-3.2.5.jar

(1)创建被代理类UserService(不能为final)

public class UserService {
	public void getName(){
		System.out.println("张三!");
	}
}

(2)创建代理工厂类ProxyFactory

public class ProxyFactory<T> implements MethodInterceptor {

	private T target;

	public ProxyFactory(T target) {
		this.target = target;
	}

	// 创建代理对象

	public Object getProxyInstance() {

		// 1.cglib工具类
		Enhancer en = new Enhancer();
		// 2.设置父类
		en.setSuperclass(this.target.getClass());
		// 3.设置回调函数
		en.setCallback(this);

		return en.create();
	}

   //拦截方法
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		System.out.println("开始事务...");

		// 执行目标对象的方法
		Object result = method.invoke(target, args);

		System.out.println("提交事务...");
		return result;
	}

}


| 结语

动态代理与CGLib动态代理都是实现Spring AOP的基础。如果加入容器的目标对象有实现接口,用动态代理,如果目标对象没有实现接口,用Cglib代理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graskli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值