Java23种设计模式之代理模式Proxy

目录

前言 

一、代理模式的概念

二、静态代理

三、动态代理

    3.1 JDK代理

    3.2 Cglib代理

    3.3 JDK代理与Cglib代理的比较

四、总结


前言 

  这里可以添加本文要记录的大概内容:简单的讲解了一下设计模式中的代理模式

文章为学习笔记,不足之处,欢迎补充....

一、代理模式的概念

代理模式在 Java 开发中是一种比较常见的设计模式。设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用。

那什么是代理呢?举个生活中的例子,就好比某商品公司要请明星来代言,但不会亲自去和明星去交接,而是明星的经纪人来谈,明星只需要负责代言就好了,其他业务都交由经纪人来交接,此时经纪人就起到一个代理的作用。

二、静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

(一个代理类只能代理一个目标对象)接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy

package com.javaxl.design.proxy.staticproxy;

/**
 * @author 代码世界里的小李
 * @site www.javaxl.com
 * @company
 * <p>
 * 目标类
 */
public class TeacherDAO implements ITeacherDao {
    public void teach() {
        System.out.println("老师传授知识");
    }
}

//目标接口
interface ITeacherDao {
    void teach();
}

//代理类
class TeacherDAOProxy implements ITeacherDao {
    private ITeacherDao teacherDAO;

    public TeacherDAOProxy(ITeacherDao teacherDAO) {
        this.teacherDAO = teacherDAO;
    }

    @Override
    public void teach() {
        System.out.println("老师正式授课前的准备工作,如学生全部签到...");
        teacherDAO.teach();
        System.out.println("老师结束授课,如下课铃声响起...");
    }
}


public class Client {
    public static void main(String[] args) {
        TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
        proxy.teach();
    }
}

细节 代理对象与目标对象要实现相同的接口 调用的时候通过调用代理对象的方法来调用目标对象

静态代理的特点

1、目标角色固定

2、在应用程序执行前就得到目标角色

3、代理对象会增强目标对象的行为

4、有可能存在多个代理 引起"类爆炸"(缺点)

三、动态代理

相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法即满足生产需要的同时又达到代码通用的目的

动态代理的两种实现方式:JDK代理和Cglib代理

    3.1 JDK代理

接口 ​ ITeacherDao ​ 目标对象 ​ TeacherDAO ​ 代理类 ​ TeacherDAOProxy

package com.javaxl.design.proxy.dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 目标接口
 */
interface ITeacherDao {
    String teach();

    ITeacherDao sleep(int minutes);
}

class TeacherDao implements ITeacherDao{
    @Override
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date())+":老师传授知识";
    }

    @Override
    public ITeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}

//真实代理类的外衣
class TeacherDaoProxy{
    private ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    public Object xxx(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object obj = null;
                        String methodName = method.getName();
                        System.out.println("目标方法" + methodName + ":jdk代理开始...");
                        System.out.println("真实代理对象:"+proxy.getClass());
                        System.out.println("目标对象:"+target.getClass());
                        if("sleep".equals(methodName)){
//                            method.invoke(target, args);
//                            obj = proxy;
                            obj = method.invoke(target, args);
                        }else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                            obj = method.invoke(target, args);
                        }
                        System.out.println("目标方法" + methodName + ":jdk代理结束...");
                        return obj;
                    }
                });
    }
}


public class Client {
    public static void main(String[] args) {
        TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
        ITeacherDao ins = (ITeacherDao) proxy.xxx();
        System.out.println("===========代理类实例被使用   begin=============");
        System.out.println(ins);
        System.out.println("===========代理类实例被使用   end=============");
        System.out.println(ins.teach());
//        System.out.println(proxy.execute());
        System.out.println("===========代理类实例被使用   begin=============");
        ins.sleep(10);
        System.out.println("===========代理类实例被使用   end=============");
        ins.sleep(20).sleep(60);
    }
}

  特点:不需要实现接口,但是目标对象要实现接口,否则不能用动态代理 ​ ;代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象;代理类所在包:java.lang.reflect.Proxy

    3.2 Cglib代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理,cglib是针对类来实现代理的,它的原理是对指定的目标类动态生成代理类的子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

注:在pom.xml文件中加入cglib的相关依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
package com.javaxl.design.proxy.dynamic.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;


class TeacherDao {
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date()) + ":老师传授知识";
    }

    public TeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}

//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
    private Object target;

    public TeacherDaoProxy(Object target) {
        this.target = target;
    }

    //返回一个代理对象:	是 target  对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * @param proxyIns  由CGLib动态生成的代理类实例
     * @param method    上文中实体类所调用的被代理的方法引用
     * @param args      参数值列表
     * @param methodProxy   生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        Object res;
        System.out.println("目标方法" + methodName + ":cglib代理开始...");
        System.out.println("真实代理对象:" + proxyIns.getClass());
        System.out.println("目标对象:" + target.getClass());
        if ("sleep".equals(methodName)) {
//                            method.invoke(target, args);
//                            obj = proxy;
            res = method.invoke(target, args);
//            res = methodProxy.invokeSuper(proxyIns,args);
            res = proxyIns;
        } else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
            res = method.invoke(target, args);
        }
        System.out.println("目标方法" + methodName + ":cglib代理结束...");
        return res;
    }
}

public class Client {
    public static void main(String[] args) {
        TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
        proxy.sleep(111).sleep(222);
    }
}

细节 ​ 目标对象与代理对象都不需要实现接口 ​ Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展 ​ Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用 ​ Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类 ​

注意:①需要引入 cglib 的 jar 文件 ②在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException: ③目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

    3.3 JDK代理与Cglib代理的比较

JDK代理中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现的接口中定义的方法进行代理 这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高

Cglib代理实现动态代理完全不受代理类必须要实现接口的限制,而且cglib底层采用ASM字节码生成,使用字节码技术生成代理类,比使用反射效率要高 唯一需要注意的是cglib不能声明为final的方法进行代理  原理是动态生成被代理类的子类

四、总结

    应用  Spring框架的AOP就是Cglib代理的体现

    个人对代理模式的一个简单了解就到这里.........

     敖丙说过:你知道的越多,不知道的越多

 作者:代码世界里的小李

  • 12
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值