设计模式:代理模式
代理的概念:简单的理解就是通过为某一个对象创建一个代理对象,不直接引用原本的对象,而是由创建的代理对象来控制对原对象的引用。即为原本的对象提供一种代理,以控制对这个对象的访问。
代理对象在客服端和目标对象之间起到中介作用,使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。
静态代理:显式声明被代理对象
由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
在分布式业务场景中,我们通常会对数据库进行分库分表,分库分表之后使用 Java 操作时,就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。(静态代理)
动态代理:动态配置和替换被代理对象
在程序运行时,运用反射机制动态创建而成,无需手动编写代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。
接口类
委托类
代理类
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。利用了java的反射机制。
测试类:编制与调用
JDK动态代理
a. 面向接口的,必须提供一个委托类和代理类都要实现的接口,只有接口中的方法才能够被代理。
b. JDK动态代理的实现主要使用java.lang.reflect包里的Proxy类和InvocationHandler接口。
InvocationHandler接口:invoke()
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到
了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler这个接口的 invoke 方法来进行调用。同时在invoke的方法里 我们可以对被代理 对象的方法调用做增强处理(如添加事务、日志、权限验证等操作)。
public interface InvocationHandler {
//Object proxy:指被代理的对象。
//Method method:要调用的方法。(指代的是我们所要调用代理对象的某个方法的Method对象)
//Object[] args:方法调用时所需要的参数。(指代的是调用真实对象某个方法时接受的参数)
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
before();//增强
method.invoke(this.target,args);
after();//增强
}
Proxy类: newProxyInstance()
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
写法:Jdk动态代理实现了被代理对象的接口,代理类没有同时实现接口重写其中的findlove方法,利用反射可以动态实现多个方法(接口中的方法才能被代理),代理类需要实现InvocationHandler接口(invoke()方法)和调用Proxy类的 newProxyInstance()方法(反射)。 JDKMepo类背下。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
高仿真 JDK Proxy 手写实现
JDK Proxy 采用字节重组,重新生的对象来替代原始的对象以达到动态代理 的目的。JDK Proxy 生成对象的步骤如下: 字节码重组
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口。
3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)。
4、编译新生成的 Java 代码.class。
5、再重新加载到 JVM 中运行
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高。
代理模式与 Spring
代理模式在 Spring 源码中的应用先看 ProxyFactoryBean 核心的方法就是 getObject()方法,我们来看一下源码:
在 getObject()方法中,主要调用 getSingletonInstance()和 newPrototypeInstance();在 Spring 的配置中,如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。如果修改 scope 则每次创建一个新的原型对象。Spring 利用动态代理实现 AOP 有两个非常重要的类,一个是 JdkDynamicAopProxy 类 和 CglibAopProxy 类,来看一下类图:
Spring 中的代理选择原则
1、当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
2、当 Bean 没有实现接口时,Spring 选择 CGLib。
3、Spring 可以通过配置强制使用 CGLib,只需在 Spring 的配置文件中加入如下代码: <aop:aspectj-autoproxy proxy-target-class="true"/>
静态代理和动态的本质区别
1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
代理模式的优缺点
使用代理模式具有以下几个优点:
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。
当然,代理模式也是有缺点的:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。