2

在具体讨论动态代理之前,我们需要先了解Java中的反射技术,动态代理就是反射技术的重要应用场景之一。

一、什么是Java反射?

先说什么是Java反射技术

Java反射技术就是允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。

1.为什么需要反射技术

任何一项技术都不是凭空出现的,都是为了解决某些特定问题而产生的,反射技术也不例外。

举个简单的例子,公司有一个项目老板让你负责,某天需要进行某个功能模块的更新,你直接把项目停了进行更新;不久又需要对这个模块更新,你又把项目停了进行更新,造成了公司的损失,第二天老板就告诉你,你可以卷铺盖回家种地了。

unnamed.jpg

那么这个时候就需要用到反射技术了,使用反射技术,你就可以实现增量更新,只需要更新这个功能模块,通过反射机制项目就可以在运行中读取到你的更新,也就不需要停止项目的运行来进行更新了,这个机制是不是很棒呢?

2.反射技术有哪些应用?

反射的应用场景有很多,比如:

  • 开发通用框架:比如我们常见的Spring框架,还有ORM框架像Mybatis框架之中都使用到了反射技术,用以根据配置文件加载不同的类或者对象、调用不同的方法,而这一切都可以在程序运行时来实现。
  • 动态代理:动态代理也同样使用反射技术来实现的,可以在方法前后加上自己的内容,后文中会详细讲解。
  • 注解:注解的实现也是通过反射技术实现的,注解本身只是一种注释手段,但是有了反射机制,就可以通过注解解释器根据注解内容去执行不同的方法或者行为。

关于反射的内容我们就大概了解这么多,下面我们一起来学习一下Java中的动态代理吧!

二、动态代理

首先,我们需要搞清楚什么是动态代理。

所谓动态代理,是和静态代理相对的,举个简单的例子来看一下

情境:618购物节要到了,你想要买东西送给你女朋友(没有的话男朋友也可以),那么这个时候作为一条合格的lick dog,你有如下两个选择

20190130103657_51238.jpeg

静态代理:提前问你女朋友需要什么,等到了618直接买

动态代理:不需要提前知道,618那天直接给去她购物车看看就知道了

例子可能不是特别合适,但是还是能够大概看出来区别

静态代理需要提前知道代理的内容,但是动态代理不需要也不关心,可以在运行时通过反射技术来获得

Java中主要有两种动态代理,JDK动态代理和CGLIB 动态代理

1.JDK动态代理

JDK动态代理是针对接口的动态代理,我们来看一个例子

public interface SayHelloService {  
 void sayHello(String s);  
}

我们在SayHelloService接口中定义了一个方法,然后我们需要写一个实现类

public class SayHelloServiceImpl implements SayHelloService {  
 @Override  
 public void sayHello(String s) {  
 System.out.println("my name is "+s);  
 }  
}

这个实现类也就是后面被代理的对象,接下来我们来完成代理类的实现

public class SayHelloProxy implements InvocationHandler {  
 private Object target;  
 //绑定被代理对象  
 public Object bind(Object target) {  
 this.target = target;  
 return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
 target.getClass().getInterfaces(), this);  
 }  
​  
 @Override  
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
 Object result = null;  
 System.out.println("进入jdk动态代理");  
 System.out.println("方法调用前");  
 method.invoke(target, args);  
 System.out.println("方法调用结束");  
 return null;  
 }  
}

其中bind()函数是用来绑定被代理对象的,invoke是重载函数,实现的是接口InvocationHandler中的方法

到此为止,我们的JDK动态代理就完成啦,下面我们来测试一下

public class Main {  
 public static void main(String[] args) {  
 SayHelloProxy handler=new SayHelloProxy();  
 SayHelloService proxy=(SayHelloService) handler.bind(new SayHelloServiceImpl());  
 proxy.sayHello("Jack");  
 }  
}

需要说明的一点时,在上述代码中,调用proxy.sayHello()时会先进入到上面代理类的invoke()函数中,不知道这一点的话可能不太好理解整个流程

运行效果如下:

image.png

但是,JDK动态代理有个问题就是只能对接口实现代理,这就是为什么需要CGLIB动态代理的原因

2.CGLIB 动态代理

CGLIB 动态代理与JDK动态代理不同,不需要提供接口就可以实现动态代理,它是通过继承的方式来实现的。

我们来看一个例子吧,直接看代码就行

我们首先定义一个被代理的类

public class HelloService {  
 public void sayHello(String s){  
 System.out.println("my name is "+s);  
 }  
}

然后来实现CGLIB的动态代理

public class MyMethodInterceptor implements MethodInterceptor {  
 @Override  
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  
 System.out.println("进入CGLIB 动态代理");  
 System.out.println("进入前");  
 Object object=methodProxy.invokeSuper(o,objects);  
 System.out.println("进入后");  
 return object;  
 }  
}

实现CGLIB的动态代理,需要实现MethodInterceptor接口,并重载其intercept函数,来在被代理都对象的方法前后加上自己的逻辑

public class Main {  
 public static void main(String[] args) {  
 Enhancer enhancer=new Enhancer();  
 enhancer.setSuperclass(HelloService.class);  
 enhancer.setCallback(new MyMethodInterceptor());  
 HelloService proxy=(HelloService)enhancer.create();  
 proxy.sayHello("yangtao");  
 }  
}

在主函数中运行进行简单的测试,运行结果如下

image.png

三、总结

总的来说,在Java中动态代理分为JDK动态代理和CGLIB动态代理两种方式,主要区别在于JDK动态代理要求提供接口进行代理,而CGLIB动态代理可以对任意类进行代理。


后端技术小黑板
14 声望1 粉丝