- JAVA 三大代理
- 静态代理
- 动态代理
- cglib代理
一.什么是代理
?可能一开始听到代理这个词语,觉得地是很复杂的东西,其实不是的,所有的创造源自于我们的生活,举个例子,在很多年以前,那个时候还没有线上的售票系统,每次我们去购买车票都得到车站,那个时候诞生了一种站点,叫做代售点,即可以在代售点购买到车站的车票,这种模式就是代理模式。因此,设计模式中的代理模式借鉴于此。以下是代理模式的大致UML图:
二.JAVA中的代理
- 静态代理
- 动态代理
- cglib代理
正如上述所列,java中的代理分为以上三种:静态、动态、cglib代理。接下来,我们将对这三种代理一一进行讲述并且归纳他们的优缺点。
1.静态代理
听到这个名字,我们应该是这个表情吧,静态代理,应该是最为简单的代理了?。那么,就由我给大家说说到底什么是静态代理。
- 顾名思义,静态代理就是静态的,即不可改变的代理,即通过编写特定的业务代理代码和额外的功能代码去实现代理业务。
以下我列出了静态代理所需要的条件:
静态代理所需条件(以java为例) |
---|
需要代理对象和目标对象(被代理对象)实现一样的接口 |
代理对象对目标对象的方法进行逻辑的修改 |
以下列出一份静态代理的实例代码:
/**
* @author linxu
* @date 2019/3/20
* 被代理的对象;
*/
public class StaticProxyed implements StaticProxyInterface{
@Override
public void service(){
System.out.println("I am a waiters");
}
}
public class StaticProxyer implements StaticProxyInterface {
private StaticProxyInterface staticProxyed;
public StaticProxyer(StaticProxyInterface staticProxyed) {
this.staticProxyed = staticProxyed;
}
@Override
public void service() {
System.out.println("i am proxy!");
staticProxyed.service();
}
}
public interface StaticProxyInterface {
void service();
}
public class ProxyModeTest {
@Test
public void testStaticProxy(){
StaticProxyInterface target=new StaticProxyed();
new StaticProxyer(target).service();
}
}
通过上面对静态代理的介绍,我的表情是这样的?,原来静态代理就是这么简单。好了,那么接下来就是动态代理了,我在想,动态代理会很难么?
2.动态代理
相比静态代理,我们可以猜猜动态代理不一样的地方,其实就一个词,动态,在上面我们可以看到,静态代理需要实现相同的接口并且对原有的业务逻辑进行修改并代理,这种硬编码的方式不灵活,且代码量会成倍增长?。
- 那么,动态代理是怎样的一回事呢?
- answer:动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能,因而动态代理又被称为JDK代理或接口代理
说说动态代理的几个前提:
- 动态代理对象不需要实现(目标对象实现)接口,但是要求目标对象必须实现接口,否则不能使用动态代理
- 需要实现的类:
InvocationHandler.class
并且在invoke()方法中进行重写
下面是代码:
public class ProxyModeTest {
@Test
public void testAutoProxy() {
AutoProxyInterface autoProxyInterface = new AutoProxyed();
System.out.println("被代理的类是:" + autoProxyInterface.getClass());
AutoProxyInterface proxyFactory = (AutoProxyInterface) new AutoProxyFactory(autoProxyInterface).getProxyInstance();
System.out.println("代理类是:"+proxyFactory.getClass());
proxyFactory.service();
}
}
public interface AutoProxyInterface {
void service();
}
public class AutoProxyFactory implements InvocationHandler {
private Object target;
public AutoProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this::invoke);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("auto proxy starting!");
Object returnVal=method.invoke(target,args);
System.out.println("commit!");
return returnVal;
}
}
public class AutoProxyed implements AutoProxyInterface {
@Override
public void service() {
System.out.println("i am service in auto proxy ed!");
}
}
运行之后的输出结果如下:
这个时候我的表情是这样?,原来动态代理是利用反射机制进行的。好了,又到了需要总结的时候了,下面我们总结一下静态代理与动态代理的区别:
静态代理 | 动态代理 |
---|---|
通过硬编码进行代理,编译时会出现.class文件 | 通过反射机制的动态代理,运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中 |
降低业务逻辑的耦合程度 | 同样可以降低业务逻辑的耦合程度 |
不够灵活,假如接口定义一个新的方法,那么就需要修改代理类进行新的代理 | 可以代理多个类的多个方法,灵活 |
以上就是两者的比较,相比之下,肯定是选择更好的动态代理了。
三.cglib代理
- 什么是cglib?
- answer:cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展
?听起来好像很厉害的样子,它确实很厉害,接下来先说说它的特点吧!
cglib代理的特点 |
---|
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现 |
CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截) |
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉 |
与动态代理差别,总结就是两点:
- 使用动态代理的对象必须实现一个或多个接口
- 使用cglib代理的对象则无需实现接口,达到代理类无侵入
好了,接下来就上demo吧!?
首先,maven下可以导入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
以下就是代码:
public class ProxyModeTest {
@Test
public void testCglib() {
CglibProxyed proxyed = new CglibProxyed();
System.out.println("被代理的类是:" + proxyed.getClass());
CglibProxyed proxy=(CglibProxyed) new CglibProxyFactory(proxyed).getProxyInstance();
System.out.println("代理类是:"+proxy);
proxy.servie();
}
}
public class CglibProxyed {
public void servie(){
System.out.println("i am cglib!");
}
}
public class CglibProxyFactory implements MethodInterceptor {
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("starting proxy...");
//method.invoke(o, objects);
//这里要注意一下,在我的电脑上,采用以上方法,会发生死循环最后栈溢出。
//采用如下方法则不会,归纳为:invoke需要代理的是上文的target,即真正的需要代理的对象,而invokeSuper()则是代理被代理过的o对象。
methodProxy.invokeSuper(o, objects);
System.out.println("stop proxy...");
return null;
}
}
好了,最后做一个总结,然后就go bed!
总结
- 静态代理实现较简单,只要代理对象对目标对象进行包装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。
- JDK动态代理需要目标对象实现业务接口,代理类只需实现InvocationHandler接口。
- 动态代理生成的类为 lass com.sun.proxy. P r o x y 4 , c g l i b 代 理 生 成 的 类 为 c l a s s c o m . c g l i b . U s e r D a o Proxy4,cglib代理生成的类为class com.cglib.UserDao Proxy4,cglib代理生成的类为classcom.cglib.UserDao E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB$552188b6。
- 静态代理在编译时产生class字节码文件,可以直接使用,效率高。
- 动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
- cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。
补充
在朋友阅读过这篇文章之后,我需要对文章作出点补充:
- 这篇文章并不是专门用于讲述代理模式的作用;更面向学习过代理模式的人。
- 代理模式的作用:
- 降低耦合程度,例如分离日志、验证业务等逻辑层。
- 用于拓展如:缓存等架构。
- 只暴露代理对象,而不暴露被代理的对象。