设计模式:代理模式

代理的概念:简单的理解就是通过某一个对象创建一个代理对象,不直接引用原本的对象,而是由创建的代理对象来控制对原对象的引用。即为原本的对象提供一种代理,以控制对这个对象的访问。

代理对象在客服端和目标对象之间起到中介作用,使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。

 

静态代理:显式声明被代理对象

由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.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、增加了系统的复杂度。

 

posted @ 2020-03-08 15:58  LPJのBLOG  Views(118)  Comments(0Edit  收藏  举报