Java 代理模式讲解

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 代理模式讲解相关的知识,希望对你有一定的参考价值。

文章目录

代理模式概念

概念

代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用

代理就是 一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一 个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主体对象与真实主体对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入

代理的本质

在不改变目标类方法的代码的情况下对目标类的方法进行增强.

动态、静态代理

代理是一个抽象的概念,简单理解就可以理解为在一个java类上去给它增加一些新的功能,但是却不用动原来的代码,在Java中分为静态代理和动态代理,然而动态代理又分为 jdk 动态代理和 cglib动态代理

静态代理:静态代理相当于是多写了一个代理类,在调用的时候调用的是代理类,在代理类中的处理还是原生的处理逻辑,不过在前后添加上需要添加的代码。

动态代理:在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术

这么说有点抽象,举个生活中的例子:

举例说明

比如说:

  • 母亲:代理客户端,本应该自己完成买菜业务,现在交由孩子作为代理代劳

  • 孩子:代理实例,虽然跑腿付出了体力,但是会得到好吃的

在上例中,如果母亲只有一个儿子,母亲每次都让他去代为买菜,形成了客户 端和代理的一一绑定关系,我们称为这种代理为静态代理,这种代理方式实现非常简单

如果母亲有好几个儿子,每次让谁买菜都得临时确认,所以会经常更换代为买菜的目标,那么客户端和代理实例之间会进行动态关联,这种代理我们称为动态代理

静态代理:

静态代理由程序员创建代理类在程序运行前代理类的 .class文件就已经存在了.

静态代理需要以下角色

a、接口(买菜这个行为)

  • 目标类和代理类都要实现该接口

b、目标类(儿子实现买菜这个行为)

  • 被代理的类

c、代理类(儿子)

  • 代理目标类的类

d、测试类(母亲)

  • 创建目标对象

  • 创建代理对象(注入目标对象)

  • 调用代理类的方法

代理流程如下图所示

代码示例

接口

package com.tjetc.proxy.static_proxy;

public interface UserOperation 
    /**
     * 添加
     */
    void add();

    /**
     * 删除
     */
    void delete();


目标类

package com.tjetc.proxy.static_proxy;

/**
 * 目标类(项目上线之后已经存在)
 */
public class UserOperationImpl implements UserOperation 
    @Override
    public void add() 
        System.out.println("UserOperationImpl.add()....");
    

    @Override
    public void delete() 
        System.out.println("UserOperationImpl.delete()....");
    

代理类

package com.tjetc.proxy.static_proxy;

/**
 * 新增一个代理类,为满足新业务,用到老的UserOperationImpl的业务逻辑
 */
public class UserOperationProxy implements UserOperation 
    /**
     * 老业务的实现类
     */
    private UserOperation userOperation;

    /**
     * 构造方法
     *
     * @param userOperation 老业务的实现类
     */
    public UserOperationProxy(UserOperation userOperation) 
        this.userOperation = userOperation;
    

    /**
     * 新业务新增方法
     */
    @Override
    public void add() 
        //调用方法之前要增强
        System.out.println("UserOperationProxy在add方法之前增强");
        //调用老业务的add方法
        this.userOperation.add();
        //调用方法之后要增强
        System.out.println("UserOperationProxy在add方法之后增强");
    

    @Override
    public void delete() 
        //调用方法之前要增强
        System.out.println("UserOperationProxy在delete方法之前增强");
        //代用老业务的delete方法
        this.userOperation.delete();
        //调用方法之后要增强
        System.out.println("UserOperationProxy在delete方法之后增强");
    

测试类

package com.tjetc.proxy.static_proxy;

public class Test 
    public static void main(String[] args) 
        //创建目标类
        UserOperation target = new UserOperationImpl();
        //创建代理类
        UserOperation proxy= new UserOperationProxy(target);
        //调用代理类的方法
        proxy.add();
        proxy.delete();
    


运行结果如下:

静态代理缺点:

1.接口增加方法, 目标类和代理类都要实现方法,增加了代码维护的复杂度

2.如果有很多方法需要方法(增强),会有大量重复代码.

动态代理

概念

在程序运行时运用反射机制动态创建而成,不是提前写好的,是后期动态生成的.

java中动态代理主要有JDK和CGLIB两种方式。

二者的区别主要是jdk是代理接口,而cglib是代理类。

  • JDK动态代理是代理模式的一种实现方式,其只能代理接口。如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。

  • 若该目标对象没有实现任何接口,则创建一个CGLIB代理。

JDK动态代理

由下图可以简单的知道调用代理类中的所有方法实际上都是调用中间类的invoke方法,而在invoke方 法中才是真正去调用对应的目标类的目标方法;这个比静态代理多了一层结构

流程如下图所示:

JDK动态代理使用步骤:

a、 新建一个接口

b、 为接口创建一个实现类(就是被代理的类)

c、 创建中间类实现java.lang.reflect.InvocationHandler接口

d、 测试(利用jdk创建动态代理类对象,调用代理对象的方法实现对被代理对象方法的增强)

代码示例

API :Proxy.newProxyInstance

函数原型:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

jdk的方法需要三个参数,

  • ClassLoader loader:类加载器
  • Class<?>[] interfaces:接口信息
  • InvocationHandler h:自定义的InvocationHandler

代码如下

package com.shixun.proxy.jdk_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


/**
 * 用户操作接口
 */
interface UserOperation 
    /**
     * 添加方法
     */
    void add();

    /**
     * 删除方法
     */
    void delete();


/**
 * 用户操作实现类
 */
class UserOperationImpl implements UserOperation 

    @Override
    public void add() 
        System.out.println("UserImpl.add()");
    

    @Override
    public void delete() 
        System.out.println("UserImpl.delete()");
    


/**
 * 自定义InvocationHandler
 */
class UserInvocationHandler implements InvocationHandler 

    //被代理对象 ( 目标类对象)
    private Object obj;

    public UserInvocationHandler(Object obj) 
        this.obj = obj;
    

    /**
     * @param proxy  代理对象
     * @param method 传进来的方法对象
     * @param args   传进来的方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        System.out.println("调用方法前增强");
        //反射,触发目标类对象的方法
        Object result = method.invoke(obj, args);
        System.out.println("调用方法后增强");
        return result;
    


public class JDKProxy 
    public static void main(String[] args) 
        //1、创建目标对象
        UserOperation userOperation = new UserOperationImpl();
        //2、创建代理对象
        //创建自定义的UserInvocationHandler对象
        UserInvocationHandler userInvocationHandler = new
                UserInvocationHandler(userOperation);
        //jdk的Proxy.newProxyInstance方法需要三个参数,一个classloader 一个是接口信息 一个自定义的InvocationHandler
        UserOperation userOperationProxy = (UserOperation)
                Proxy.newProxyInstance(userOperation.getClass().getClassLoader(),
                        userOperation.getClass().getInterfaces(),
                        userInvocationHandler);
        //3、调用代理对象
        userOperationProxy.add();
        userOperationProxy.delete();
    

运行结果如下所示:

CGLIB动态代理

CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法,这样也可以保证代理类拥有目标类的同名方法

代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑

代理流程如下图所示:

开发步骤

a、创建目标类:没有接口

b、在pom.xml添加cglib的依赖,写方法的拦截器

c、测试类

代码如下:

pom添加依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

代码:

package com.tjetc.proxy.jdk_proxy;

//package com.shixun.proxy.cglib_proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;



/**
 * 目标类
 **/
class PersonOperation 
    public void add()
        System.out.println("PersonOperation.add()");
    

    public void delete()
        System.out.println("PersonOperation.delete()");
    


/**
 * 方法拦截器类
 **/
class MyMethodInterceptor implements MethodInterceptor 
    /**
     * 拦截方法
     *
     * @param o
     * @param method
     * @param objects
     * @param methodProxy

    CGLib动态生成的代理类实例
    被代理(目标对象)的方法
    参数列表
    代理类方法对象 ,对method进行增强 ,性能好
     * @return
     * @throws Throwable */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 
        System.out.println("调用方法前增强");
        //调用代理类实例上的父类方法 (父类指的是目标类)
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("调用方法后增强");
        return result;
    


public class CGLIBProxy 
    public static void main(String[] args) 
        //创建目标对象
        PersonOperation personOperation = new PersonOperation();
        //创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //设置enhancer的父类
        enhancer.setSuperclass(PersonOperation.class);
        //设置回调对象 (相当于jdk的中间类)
        enhancer.setCallback(new MyMethodInterceptor());
        //通过字节码技术动态创建目标类的子类对象就是代理对象
        PersonOperation personOperationProxy = (PersonOperation)
                enhancer.create();
        personOperationProxy.add();
        personOperationProxy.delete();
    

运行结果如下:

以上是关于Java 代理模式讲解的主要内容,如果未能解决你的问题,请参考以下文章

Java Dome(AOP模式回顾小Dome)

JAVA SCRIPT设计模式--结构型--设计模式之Proxy代理模式(12)

包装模式就是这么简单啦

保护代理模式-Access Proxy(Java实现)

代理模式 vs 装饰模式

代理模式 vs 装饰模式