在具体讨论动态代理之前,我们需要先了解Java中的反射技术,动态代理就是反射技术的重要应用场景之一。
一、什么是Java反射?
先说什么是Java反射技术
Java反射技术就是允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。
1.为什么需要反射技术
任何一项技术都不是凭空出现的,都是为了解决某些特定问题而产生的,反射技术也不例外。
举个简单的例子,公司有一个项目老板让你负责,某天需要进行某个功能模块的更新,你直接把项目停了进行更新;不久又需要对这个模块更新,你又把项目停了进行更新,造成了公司的损失,第二天老板就告诉你,你可以卷铺盖回家种地了。
那么这个时候就需要用到反射技术了,使用反射技术,你就可以实现增量更新,只需要更新这个功能模块,通过反射机制项目就可以在运行中读取到你的更新,也就不需要停止项目的运行来进行更新了,这个机制是不是很棒呢?
2.反射技术有哪些应用?
反射的应用场景有很多,比如:
- 开发通用框架:比如我们常见的Spring框架,还有ORM框架像Mybatis框架之中都使用到了反射技术,用以根据配置文件加载不同的类或者对象、调用不同的方法,而这一切都可以在程序运行时来实现。
- 动态代理:动态代理也同样使用反射技术来实现的,可以在方法前后加上自己的内容,后文中会详细讲解。
- 注解:注解的实现也是通过反射技术实现的,注解本身只是一种注释手段,但是有了反射机制,就可以通过注解解释器根据注解内容去执行不同的方法或者行为。
关于反射的内容我们就大概了解这么多,下面我们一起来学习一下Java中的动态代理吧!
二、动态代理
首先,我们需要搞清楚什么是动态代理。
所谓动态代理,是和静态代理相对的,举个简单的例子来看一下
情境:618购物节要到了,你想要买东西送给你女朋友(没有的话男朋友也可以),那么这个时候作为一条合格的lick dog,你有如下两个选择
静态代理:提前问你女朋友需要什么,等到了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()函数中,不知道这一点的话可能不太好理解整个流程
运行效果如下:
但是,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");
}
}
在主函数中运行进行简单的测试,运行结果如下
三、总结
总的来说,在Java中动态代理分为JDK动态代理和CGLIB动态代理两种方式,主要区别在于JDK动态代理要求提供接口进行代理,而CGLIB动态代理可以对任意类进行代理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。