代理
代理就是通过代理对象去访问实际的目标对象,比如我们在生活中租房,可以直接找房东,也可以通过某些租房平台去租房,通过租房平台的这种方式就是代理。在java中这种租房平台就被叫做代理类,代理类不仅能实现目标对象,还能增加一些额外的功能。
静态代理
什么叫做静态代理?
静态代理就是在代码运行之前,这个代理类就已经存在了。
例子
首先,需要创建一个租房的通用接口
package com.sxl.staticProxy;
//租房接口
public interface Rent {
public void rent();
}
然后,我们需要一个被代理类,也就是需要出租房子的房东
package com.sxl.staticProxy;
//房东出租房屋
public class Landlord implements Rent{
/**
* 声明一个被代理类,需要重写接口中的rent方法
*/
@Override
public void rent() {
System.out.println("房东已经出租房屋!");
}
}
随即,我们需要写一个代理类,也就是中介
package com.sxl.staticProxy;
//代理,中介帮助房东出租房屋
/**
* 声明一个代理类,要和被代理类继承同一个接口
* 且要重写接口中的rent方法
*/
public class Proxy implements Rent{
private Rent r;
public Proxy(Rent r) {
this.r = r;
}
@Override
public void rent() {
System.out.println("中介正在寻找房源...");
kanfang();
r.rent();
System.out.println("中介已经帮助完成租房!");
shoufei();
}
public void kanfang(){
System.out.println("中介带领前去看房...");
}
public void shoufei(){
System.out.println("中介收取费用!");
}
}
最后,租客前来寻找中介租房
package com.sxl.staticProxy;
//租客前来租房
public class Test {
public static void main(String[] args) {
Landlord l=new Landlord();
Proxy p=new Proxy(l);
p.rent();
}
}
运行结果
这样,我们就可以通过代理来完成租房子了
静态代理的优点
- 真实角色的操作可以更加纯粹,而不用过多的关注公共业务
- 代理角色可以去完成公共业务,实现了业务的分工
- 公共业务发生拓展的时候,更加便于拓展和管理
静态代理的缺点
- 由于静态代理在代码运行之前就已经存在代理类,因此对于每一个代理对象都需要建一个代理类去代理,当需要代理的对象很多时就需要创建很多的代理类,严重降低程序的可维护性,开发效率也会大大降低。
动态代理
动态代理是指代理类不是写在代码中,而是在运行过程中产生的,
基于Jdk的动态代理
底层实现
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
基于Cglib的动态代理
cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。
在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
基于javassist的动态代理(不做了解)
总结
静态代理比较容易理解, 需要被代理的类和代理类实现自同一个接口, 然后在代理类中调用真正实现类, 并且静态代理的关系在编译期间就已经确定了。而动态代理的关系是在运行期间确定的。静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性。
JDK 动态代理所用到的代理类在程序调用到代理类对象时才由 JVM 真正创建,JVM 根据传进来的业务实现类对象以及方法名 ,动态地创建了一个代理类的 class 文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。
静态代理和动态代理都是基于接口实现的, 而对于那些没有提供接口只是提供了实现类的而言, 就只能选择 CGLIB 动态代理了。
JDK 动态代理和 CGLIB 动态代理的区别
- JDK 动态代理基于 Java 反射机制实现, 必须要实现了接口的业务类才能用这种方法生成代理对象。
- CGLIB 动态代理基于 ASM 框架通过生成业务类的子类来实现。
- JDK 动态代理的优势是最小化依赖关系,减少依赖意味着简化开发和维护并且有 JDK 自身支持。还可以平滑进行 JDK 版本升级,代码实现简单。
- 基于 CGLIB 框架的优势是无须实现接口,达到代理类无侵入,我们只需操作我们关系的类,不必为其它相关类增加工作量,性能比较高。
描述代理的几种实现方式? 分别说出优缺点?
代理可以分为 “静态代理” 和 “动态代理”,动态代理又分为 “JDK 动态代理” 和 “CGLIB 动态代理” 实现。
1.静态代理:
代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.
优点:可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性。
缺点:不同的接口要有不同的代理类实现,会很冗余
2.JDK 动态代理:
为了解决静态代理中,生成大量的代理类造成的冗余;
JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现.
jdk 的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象
jdk 动态代理之所以只能代理接口是因为代理类本身已经 extends 了 Proxy,而 java 是不允许多重继承的,但是允许实现多个接口
优点:解决了静态代理中冗余的代理实现类问题。
缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
3.CGLIB 代理:
由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK 方式解决不了;
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLib 合适,反之,使用 JDK 方式要更为合适一些。
由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。
优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。
缺点:技术实现相对难理解些。