设计模式之代理模式详解和应用

JS设计模式之代理模式 Star这个类就是明星,StarProxy这个类就是明星经纪人,假如我们是商家,现在我们想请这个明星来参加我们的活动,我们开始和经纪人谈价格,talk出价1w,此时经纪人会做一个判断,如果价格大于等于1w,这个活动就比较适合,可以参加,代理就会去通知明星演出,如果小于1w的话,就不会,这个就是简单的代理模式,我们要访问明星,需要经过经纪人(也就是代理),才能去访问。其实就是给一个对象提供一个代理,如果要访问这个对象必须经过这个代理,先说,这个模式的优点是什么。 阅读详情


1 代理模式定义

都知道 SpringAOP 是用代理模式实现,到底是怎么实现的?我们来一探究竟,并且自己仿真手写 还原部分细节。

代理模式(ProxyPattern)是指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型模式。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标 对象之间起到中介的作用。

官方原文:Provide a surrogate or placeholder for another object to control access to it.

首先来看代理模式的通用UML类图:

img

代理模式一般包含三种角色:

抽象主题角色(Subject):抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以是抽象类;

真实主题角色(RealSubject):该类也被称为被代理类,该类定义了代理所表示的真实对象,是负责执行系统真正的逻辑业务对象;

代理主题角色(Proxy):也被称为代理类,其内部持有 RealSubject 的引用,因此具备完全的对 RealSubject的代理权。客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对 象前后增加一些处理代码。

在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。代理模式属于结构型模式,分为静态代理和动态代理。

2 代理模式的应用场景

生活中的租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,都是代理 模式的实际体现。当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过也给代理对象 来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。

3 代理模式的通用写法

下面是代理模式的通用代码展示。

首先创建代理主题角色ISubject类:

public interface ISubject {
    void request();
}

创建真实主题角色RealSubject类:

public class RealSubject implements ISubject {
    public void request() {
        System.out.println("real service is called.");
    }
}

创建代理主题角色Proxy类:

public class Proxy implements ISubject {

    private ISubject subject;

    public Proxy(ISubject subject){
        this.subject = subject;
    }

    public void request() {
        before();
        subject.request();
        after();
    }

    public void before(){
        System.out.println("called before request().");
    }

    public void after(){
        System.out.println("called after request().");
    }
}

客户端调用代码:

public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.request();
    }
}

运行结果

called before request().
real service is called.
called after request().

4 从静态代理到动态代理

举个例子,有些人到了适婚年龄,其父母总是迫不及待地希望早点抱孙子。而现在在各种压力之下, 很多人都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲,比子女自己还着急。下面来看代码实现。

静态代理:

创建顶层接口IPerson的代码如下:

public interface IPerson {
    void findLove();
}

儿子张三要找对象,实现ZhangSan类:

public class ZhangSan implements IPerson {
    public void findLove() {
        System.out.println("儿子要求:肤白貌美大长腿");
    }
}

父亲张老三要帮儿子张三相亲,实现Father类:

public class ZhangLaosan implements IPerson {

    private ZhangSan zhangsan;

    public ZhangLaosan(ZhangSan zhangsan) {
        this.zhangsan = zhangsan;
    }

    public void findLove() {
        // before
        System.out.println("张老三开始物色");
        zhangsan.findLove();
        // after
        System.out.println("开始交往");
    }
}

来看测试代码:

public class Test {
    public static void main(String[] args) {
        ZhangLaosan zhangLaosan = new ZhangLaosan(new ZhangSan());
        zhangLaosan.findLove();
    }
}

运行结果:

张老三开始物色
儿子要求:肤白貌美大长腿
开始交往

但上面的场景有个弊端,就是自己父亲只会给自己的子女去物色对象,别人家的孩子是不会管的。

但社会上这项业务发展成了一个产业,出现了媒婆、婚介所等,还有各种各样的定制套餐。如果还使用静态代理成本就太高了,需要一个更加通用的解决方案,满足任何单身人士找对象的需求。

这就是由静态代理升级到了动态代理。

采用动态代理基本上只要是人(IPerson)就可以提供相亲服务。

动态代理的底层实现一般不用我们自己亲自去实现,已经有很多现成的API。

在Java生态中,目前最普遍使用的是JDK自带的代理和Cglib提供的类库。

下面我们首先基于JDK的动态代理支持如来升级一下代码。

首先,创建媒婆(婚介所)类JdkMeipo:

public class JdkMeipo implements InvocationHandler {
    
    private IPerson target;
    
    // 反射获取
    public IPerson getInstance(IPerson target){
        this.target = target;
        Class<?> clazz =  target.getClass();
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void after() {
        System.out.println("双方同意,开始交往");
    }

    private void before() {
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
}

再创建一个类ZhaoLiu:

public class ZhaoLiu implements IPerson {

    public void findLove() {
        System.out.println("赵六要求:有车有房学历高");
    }

    public void buyInsure() {
    }
}

测试代码如下:

public class Test {
    public static void main(String[] args) {
        JdkMeipo jdkMeipo = new JdkMeipo();
        IPerson zhangsan = jdkMeipo.getInstance(new Zhangsan());
        zhangsan.findLove();
        
        IPerson zhaoliu = jdkMeipo.getInstance(new ZhaoLiu());
        zhaoliu.findLove();
    }
}

运行效果如下:

我是媒婆,已经收集到你的需求,开始物色
张三要求:肤白貌美大长腿
双方同意,开始交往
我是媒婆,已经收集到你的需求,开始物色
赵六要求:有车有房学历高
双方同意,开始交往

5 静态模式在业务中的应用

这里“小伙伴们”可能会觉得还是不知道如何将代理模式应用到业务场景中,我们来看一个实际的业务场景。

在分布式业务场景中,通常会对数据库进行分库分表,分库分表之后使用 Java操作时就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。

先创建Order订单类:

@Data
public class Order {
    private Object orderInfo;
    //订单创建时间进行按年分库
    private Long createTime;
    private String id;
}

复制

创建OrderDao持久层操作类:

public class OrderDao {
    public int insert(Order order){
        System.out.println("OrderDao创建Order成功!");
        return 1;
    }
}

创建IOrderService接口:

public interface IOrderService {
    int createOrder(Order order);
}

创建OrderService实现类:

public class OrderService implements IOrderService {
    private OrderDao orderDao;

    public OrderService(){
        //如果使用Spring应该是自动注入的
        //我们为了使用方便,在构造方法中将orderDao直接初始化了
        orderDao = new OrderDao();
    }

    public int createOrder(Order order) {
        System.out.println("OrderService调用orderDao创建订单");
        return orderDao.insert(order);
    }
}

接下来使用静态代理,主要完成的功能是:根据订单创建时间自动按年进行分库。

根据开闭原则,我们修改原来写好的代码逻辑,通过代理对象来完成。

先创建数据源路由对象,使用ThreadLocal的单例实现DynamicDataSourceEntity类:

public class DynamicDataSourceEntity {

    public final static String DEFAULE_SOURCE = null;

    private final static ThreadLocal<String> local = new ThreadLocal<String>();

    private DynamicDataSourceEntity(){}

    public static String get(){return  local.get();}

    public static void restore(){
         local.set(DEFAULE_SOURCE);
    }

    //DB_2018
    //DB_2019
    public static void set(String source){local.set(source);}

    public static void set(int year){local.set("DB_" + year);}
}

创建切换数据源的代理类OrderServiceSaticProxy:

public class OrderServiceStaticProxy implements IOrderService {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");

    private IOrderService orderService;
    public OrderServiceStaticProxy(IOrderService orderService) {
        this.orderService = orderService;
    }

    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到【DB_" +  dbRouter + "】数据源处理数据" );
        DynamicDataSourceEntity.set(dbRouter);
        this.orderService.createOrder(order);
        DynamicDataSourceEntity.restore();
        after();
        return 0;
    }

    private void before(){ System.out.println("Proxy before method."); }
    private void after(){ System.out.println("Proxy after method."); }
}

来看测试代码:

public class DbRouteProxyTest {
    public static void main(String[] args) {
        try {
            Order order = new Order();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
            Date date = sdf.parse("2020/03/01");
            order.setCreateTime(date.getTime());
            IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
            orderService.createOrder(order);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

Proxy before method.
静态代理类自动分配到【DB_2020】数据源处理数据
OrderService调用orderDao创建订单
OrderDao创建Order成功!
Proxy after method.

结果符合我们的预期。现在再来回顾一下类图,看是不是和我们最先画的一致,如下图所示。

img

动态代理和静态代理的基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。

6 动态代理在业务中的应用

上面的案例理解了,我们再来看数据源动态路由业务,帮助“小伙伴们”加深对动态代理的印象。

创建动态代理的类OrderServiceDynamicProxy:

package com.oldluedu.vip.pattern.proxy.dbroute.proxy;

import com.oldluedu.vip.pattern.proxy.dbroute.db.DynamicDataSourceEntity;

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

public class OrderServiceDynamicProxy implements InvocationHandler {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args[0]);
        Object object = method.invoke(target, args);
        after();
        return object;
    }

    private void before(Object target) {
        try {
            System.out.println("Proxy before method.");
            Long time = (Long) target.getClass().getMethod("getCreateTime").invoke(target);
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("动态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据");
            DynamicDataSourceEntity.set(dbRouter);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void after() {
        System.out.println("Proxy after method.");
    }
}

测试代码如下:

public class DbRouteProxyTest {
    public static void main(String[] args) {
        try {
            Order order = new Order();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
            Date date = sdf.parse("2020/03/01");
            order.setCreateTime(date.getTime());
            IOrderService orderService = (IOrderService) new OrderServiceDynamicProxy().getInstance(new OrderService());
            orderService.createOrder(order);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行效果如下:

Proxy before method.
静态代理类自动分配到【DB_2020】数据源处理数据
OrderService调用orderDao创建订单
OrderDao创建Order成功!
Proxy after method.

依然能够达到相同运行效果。但是,使用动态代理实现之后,我们不仅能实现 Order的数据源动态 路由,还可以实现其他任何类的数据源路由。当然,有个比较重要的约定,必须实现getCreateTime() 方法,因为路由规则是根据时间来运算的。我们可以通过接口规范来达到约束的目的,在此就不再举例。

7 手写JDK动态代理实现原理

不仅知其然,还得知其所以然。既然JDK动态代理功能如此强大,那么它是如何实现的呢?我们现 在来探究一下原理,并模仿JDK动态代理动手写一个属于自己的动态代理。

我们都知道JDK动态代理采用字节重组,重新生成对象来替代原始对象,以达到动态代理的目的。

7.1 JDK动态代理的实现原理

  1. 获取被代理对象的引用,并且获取它的所有接口(反射获取)。
  2. JDK Proxy类重新生成一个新的类,实现了被代理类所有接口的方法。
  3. 动态生成Java代码,把增强逻辑加入到新生成代码中。
  4. 编译生成新的Java代码的class文件。
  5. 加载并重新运行新的class,得到类就是全新类。

7.2 CGLib动态代理容易踩的坑

  1. 无法代理final修饰的方法。

以上过程就叫字节码重组。JDK中有一个规范,在ClassPath下只要是$开头的.class文件,一般都是自动生成的。那么我们有没有办法看到代替后的对象的“真容”呢?做一个这样测试,我们将内存中 的对象字节码通过文件流输出到一个新的.class文件,然后利用反编译工具查看其源代码。

public class Test {
    public static void main(String[] args) {
        JdkMeipo jdkMeipo = new JdkMeipo();
        IPerson zhangsan = jdkMeipo.getInstance(new Zhangsan());
        zhangsan.findLove();
        // 通过反编译工具可以查看源代码
        try {
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class});
            FileOutputStream os = new FileOutputStream("F://$Proxy0.class");
            os.write(bytes);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行以上代码,我们能在E盘下找到一个Proxy0.class文件。使用Jad反编译,得到Proxy0.jad 文件,打开它可以看到如下内容:

import com.oldluedu.vip.pattern.proxy.dynamicproxy.jdkproxy.IPerson;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IPerson {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void findLove() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buyInsure() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.oldluedu.vip.pattern.proxy.dynamicproxy.jdkproxy.IPerson").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.oldluedu.vip.pattern.proxy.dynamicproxy.jdkproxy.IPerson").getMethod("buyInsure");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们发现,$Proxy0继承了Proxy类,同时还实现了Person接口,而且重写了findLove()等方法。 在静态块中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用 目标对象的方法。“小伙伴们”此时一定会好奇:这些代码是哪里来的呢?其实是JDK帮我们自动生成 的。现在我们不依赖JDK,自己来动态生成源代码、动态完成编译,然后替代目标对象并执行。

创建GPInvocationHandler接口:

public interface GPInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}

创建GPProxy类:

/**
 * 用来生成源代码的工具类
 */
public class GPProxy {

    public static final String ln = "\r\n";

    public static Object newProxyInstance(GPClassLoader classLoader, Class<?> [] interfaces, GPInvocationHandler h){
       try {
           //1、动态生成源代码.java文件
           String src = generateSrc(interfaces);

		   //System.out.println(src);
           //2、Java文件输出磁盘
           String filePath = GPProxy.class.getResource("").getPath();
		   //System.out.println(filePath);
           File f = new File(filePath + "$Proxy0.java");
           FileWriter fw = new FileWriter(f);
           fw.write(src);
           fw.flush();
           fw.close();

           //3、把生成的.java文件编译成.class文件
           JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
           StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
           Iterable iterable = manage.getJavaFileObjects(f);

          JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
          task.call();
          manage.close();

           //4、编译生成的.class文件加载到JVM中来
          Class proxyClass =  classLoader.findClass("$Proxy0");
          Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
          f.delete();

           //5、返回字节码重组以后的新的代理对象
           return c.newInstance(h);
       }catch (Exception e){
           e.printStackTrace();
       }
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces){
            StringBuffer sb = new StringBuffer();
            sb.append(GPProxy.class.getPackage() + ";" + ln);
            sb.append("import " + interfaces[0].getName() + ";" + ln);
            sb.append("import java.lang.reflect.*;" + ln);
            sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
                sb.append("GPInvocationHandler h;" + ln);
                sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
                    sb.append("this.h = h;");
                sb.append("}" + ln);
                for (Method m : interfaces[0].getMethods()){
                    Class<?>[] params = m.getParameterTypes();

                    StringBuffer paramNames = new StringBuffer();
                    StringBuffer paramValues = new StringBuffer();
                    StringBuffer paramClasses = new StringBuffer();

                    for (int i = 0; i < params.length; i++) {
                        Class clazz = params[i];
                        String type = clazz.getName();
                        String paramName = toLowerFirstCase(clazz.getSimpleName());
                        paramNames.append(type + " " +  paramName);
                        paramValues.append(paramName);
                        paramClasses.append(clazz.getName() + ".class");
                        if(i > 0 && i < params.length-1){
                            paramNames.append(",");
                            paramClasses.append(",");
                            paramValues.append(",");
                        }
                    }

                    sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln);
                        sb.append("try{" + ln);
                            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
                            sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln);
                        sb.append("}catch(Error _ex) { }");
                        sb.append("catch(Throwable e){" + ln);
                        sb.append("throw new UndeclaredThrowableException(e);" + ln);
                        sb.append("}");
                        sb.append(getReturnEmptyCode(m.getReturnType()));
                    sb.append("}");
                }
            sb.append("}" + ln);
            return sb.toString();
    }


    private static Map<Class,Class> mappings = new HashMap<Class, Class>();
    static {
        mappings.put(int.class,Integer.class);
    }

    private static String getReturnEmptyCode(Class<?> returnClass){
        if(mappings.containsKey(returnClass)){
            return "return 0;";
        }else if(returnClass == void.class){
            return "";
        }else {
            return "return null;";
        }
    }

    private static String getCaseCode(String code,Class<?> returnClass){
        if(mappings.containsKey(returnClass)){
            return "((" + mappings.get(returnClass).getName() +  ")" + code + ")." + returnClass.getSimpleName() + "Value()";
        }
        return code;
    }

    private static boolean hasReturnValue(Class<?> clazz){
        return clazz != void.class;
    }

    private static String toLowerFirstCase(String src){
        char [] chars = src.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

}

创建GPClassLoader类:

public class GPClassLoader extends ClassLoader {

    private File classPathFile;
    public GPClassLoader(){
        String classPath = GPClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        String className = GPClassLoader.class.getPackage().getName() + "." + name;
        if(classPathFile  != null){
            File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
            if(classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try{
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1){
                        out.write(buff,0,len);
                    }
                    return defineClass(className,out.toByteArray(),0,out.size());
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

创建GPMeipo类:

public class GpMeipo implements GPInvocationHandler {
    private IPerson target;
    public IPerson getInstance(IPerson target){
        this.target = target;
        Class<?> clazz =  target.getClass();
        return (IPerson) GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void after() {
        System.out.println("双方同意,开始交往");
    }

    private void before() {
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
}

客户端测试代码如下:

public class Test {
    public static void main(String[] args) {
        GpMeipo gpMeipo = new GpMeipo();
        IPerson zhangsan = gpMeipo.getInstance(new Zhangsan());
        zhangsan.findLove();
    }
}

运行效果如下:

我是媒婆,已经收集到你的需求,开始物色
张三要求:肤白貌美大长腿
双方同意,开始交往

到此,手写JDK动态代理就完成了。“小伙伴们”是不是又多了一个面试用的“撒手锏”呢?

8 CGLib代理调用API及原理分析

简单看一下CGLib代理的使用,还是以媒婆为例,创建CglibMeipo类:

public class CGlibMeipo implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception{
        //相当于Proxy,代理的工具类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
        System.out.println("开始物色");
    }

    private void after(){
        System.out.println("OK的话,准备办事");
    }
}

创建单身客户类Customer:

public class Customer {
    public void findLove(){
        System.out.println("儿子要求:肤白貌美大长腿");
    }
}

有个小细节,CGLib代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代 理的。来看测试代码:

public class CglibTest {
    public static void main(String[] args) {
        try {
            Customer obj = (Customer)new CGlibMeipo().getInstance(Customer.class);
            obj.findLove();         
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

CGLib 代理的实现原理又是怎样的呢?我们可以在测试代码中加上一句代码,将 CGLib 代理后 的.class文件写入磁盘,然后反编译来一探究竟,代码如下:

public class CglibTest {
    public static void main(String[] args) {
        try {
            //JDK是采用读取接口的信息
            //CGLib覆盖父类方法
            //目的:都是生成一个新的类,去实现增强代码逻辑的功能

            //JDK Proxy 对于用户而言,必须要有一个接口实现,目标类相对来说复杂
            //CGLib 可以代理任意一个普通的类,没有任何要求

            //CGLib 生成代理逻辑更复杂,效率,调用效率更高,生成一个包含了所有的逻辑的FastClass,不再需要反射调用
            //JDK Proxy生成代理的逻辑简单,执行效率相对要低,每次都要反射动态调用

            //CGLib 有个坑,CGLib不能代理final的方法
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://cglib_proxy_classes");
            Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class);
            System.out.println(obj);
            obj.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

重新执行代码,我们会发现在E://cglib_proxy_class目录下多了三个.class文件,如下图所示。

在这里插入图片描述

通过调试跟踪发现,Customer

EnhancerByCGLIB

3feeb52a.class 就是 CGLib 代理生成的代 理类,继承了Customer类。

package com.oldluedu.vip.pattern.proxy.dynamicproxy.cglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Customer$$EnhancerByCGLIB$$6d99cfc2 extends Customer implements Factory {
    ...
    final void CGLIB$findLove$0() {
        super.findLove();
    }

    public final void findLove() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);
        } else {
            super.findLove();
        }
    }
    ...
}

复制

我们重写了Customer类的所有方法,通过代理类的源码可以看到,代理类会获得所有从父类继承 来的方法,并且会有 MethodProxy 与之对应,比如 Method CGLIBfindLove0Method、 MethodProxy CGLIBfindLove0

//代理方法(methodProxy.invokeSuper()方法会调用) 
final void CGLIB$findLove$0() {
    super.findLove();
}

//被代理方法(methodProxy.invoke()方法会调用,这就是为什么在拦截器中调用 methodProxy.invoke 会发生死循环,一直在调用拦截器) 
public final void findLove() {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if (var10000 != null) {
        var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);
    } else {
        super.findLove();
    }
}

调用过程为:代理对象调用 this.findLove()方法→调用拦截器→methodProxy.invokeSuper→ CGLIBfindLove0→被代理对象findLove()方法。

此时,我们发现拦截器 MethodInterceptor中就是由 MethodProxy的invokeSuper()方法调用代 理方法的,MethodProxy非常关键,我们分析一下它具体做了什么。

package net.sf.cglib.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.sf.cglib.core.AbstractClassGenerator;
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastClass.Generator;

public class MethodProxy {
    private Signature sig1;
    private Signature sig2;
    private MethodProxy.CreateInfo createInfo;
    private final Object initLock = new Object();
    private volatile MethodProxy.FastClassInfo fastClassInfo;

    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
        return proxy;
    }
    ...
    private static class CreateInfo {
        Class c1;
        Class c2;
        NamingPolicy namingPolicy;
        GeneratorStrategy strategy;
        boolean attemptLoad;

        public CreateInfo(Class c1, Class c2) {
            this.c1 = c1;
            this.c2 = c2;
            AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
            if (fromEnhancer != null) {
                this.namingPolicy = fromEnhancer.getNamingPolicy();
                this.strategy = fromEnhancer.getStrategy();
                this.attemptLoad = fromEnhancer.getAttemptLoad();
            }

        }
    }
	...
}

继续看invokeSuper()方法:

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

    private static class FastClassInfo
    {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;
    }

上面的代码调用就是获取代理类对应的FastClass,并执行代理方法。还记得之前生成的三个.class 文件吗?Customer

EnhancerByCGLIB

3feeb52a

FastClassByCGLIB

6aad62f1.class 就是代理类的FastClass,Customer

FastClassByCGLIB

2669574a.class 就是被代理类的FastClass。

CGLib代理执行代理方法的效率之所以比JDK的高,是因为CGlib采用了FastClass机制,它的原 理简单来说就是:为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个 index(int类型);这个index当作一个入参,FastClass就可以直接定位要调用的方法并直接进行调 用,省去了反射调用,所以调用效率比JDK代理通过反射调用高。下面我们反编译一个FastClass看看:

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

    private static class FastClassInfo
    {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;
    }

CGLib代理执行代理方法的效率之所以比JDK的高,是因为CGlib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个 index(int类型);这个index当作一个入参,FastClass就可以直接定位要调用的方法并直接进行调 用,省去了反射调用,所以调用效率比JDK代理通过反射调用高。下面我们反编译一个FastClass看看:

public class test {
    public int getIndex(Signature signature) {
        String s = signature.toString();
        s;
        s.hashCode();
        JVM INSTR lookupswitch 11:default 223
        …
        JVM INSTR pop;
        return -1;
    }

    //部分代码省略
    //根据 index 直接定位执行方法
    public Object invoke(int i, Object obj, Object[] aobj) throws InvocationTargetException {
        (Customer) obj;
        i;
        JVM INSTR tableswitch 0 10:default
        161 goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12 _L2:
        eat();
        return null;
        _L3:
        findLove();
        return null; …throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
}

FastClass 并不是跟代理类一起生成的,而是在第一次执行 MethodProxy 的 invoke()或 invokeSuper()方法时生成的,并放在了缓存中。

//MethodProxy 的 invoke()或 invokeSuper()方法都调用了 init()方法
private void init()
{
    /* 
     * Using a volatile invariant allows us to initialize the FastClass and
     * method index pairs atomically.
     * 
     * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this 
     * code could allow fastClassInfo to be instantiated more than once, which
     * appears to be benign.
     */
    if (fastClassInfo == null)
    {
        synchronized (initLock)
        {
            if (fastClassInfo == null)
            {
                CreateInfo ci = createInfo;

                FastClassInfo fci = new FastClassInfo();
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(sig1);
                fci.i2 = fci.f2.getIndex(sig2);
                fastClassInfo = fci;
            }
        }
    }
}

至此,CGLib代理的原理我们就基本搞清楚了,对代码细节有兴趣的“小伙伴”可以自行深入研究。

9 CGLib和JDK动态代理对比

(1)JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象。

(2) JDK动态代理和CGLib代理都在运行期生成字节码, JDK动态代理直接写Class字节码, CGLib 代理使用ASM框架写Class字节码,CGlib代理实现更复杂,生成代理类比JDK动态代理效率低。

(3)JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率更高。

10 代理模式与Spring生态

1、代理模式在Spring中的应用

先看ProxyFactoryBean核心方法getObject(),源码如下:

@Nullable
public Object getObject() throws BeansException {
    this.initializeAdvisorChain();
    if (this.isSingleton()) {
        return this.getSingletonInstance();
    } else {
        if (this.targetName == null) {
            this.logger.info("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
        }

        return this.newPrototypeInstance();
    }
}

在getObject()方法中,主要调用 getSingletonInstance()和 newPrototypeInstance()。在 Spring 的配置中如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。如果修改 scope,则每次 创建一个新的原型对象。newPrototypeInstance()里面的逻辑比较复杂,我们后面再做深入研究,这里 先做简单了解。

Spring 利用动态代理实现 AOP 时有两个非常重要的类:JdkDynamicAopProxy 类和CglibAopProxy类,来看一下类图,如下图所示。

img

10 静态代理和动态代理的本质区别

(1)静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加, 违背开闭原则。

(2)动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。

(3)若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。

11 代理模式的优缺点

代理模式具有以下优点:

(1)代理模式能将代理对象与真实被调用目标对象分离。

(2)在一定程度上降低了系统的耦合性,扩展性好。

(3)可以起到保护目标对象的作用。

(4)可以增强目标对象的功能。

当然,代理模式也有缺点:

(1)代理模式会造成系统设计中类的数量增加。

(2)在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢。

(3)增加了系统的复杂度。

12 Spring中的代理选择原则

(1)当Bean有实现接口时,Spring就会用JDK动态代理。

(2)当Bean没有实现接口时,Spring会选择CGLib代理。

(3)Spring可以通过配置强制使用CGLib代理,只需在Spring的配置文件中加入如下代码:

<aop:aspectj-autoproxy proxy-target-class="true"/>
23种设计模式-代理(Proxy)设计模式 软考中级-软件设计师 代理设计模式 阅读详情

相关推荐

DIY天文摄影:如何用NMP溶剂安全去除佳能CMOS传感器的拜耳滤镜(附详细步骤)

本文为天文摄影爱好者提供了一份详尽的DIY指南,介绍如何使用NMP溶剂安全去除佳能CMOS传感器的拜耳滤镜,将其改造为高灵敏度单色相机。文章深入剖析了NMP溶剂的作用原理与关键风险,并系统性地拆解了从材料准备、分步操作到后期验证的完整实践流程,旨在帮助具备动手能力的爱好者实现深空天体摄影的进阶目标。

w3x4y的博客 301

代理模式:一文彻底搞懂静态代理和动态代理

代理模式(Proxy Pattern)是一种结构型设计模式,它的概念很简单,它通过创建一个代理对象来控制对原始对象的访问。代理模式主要涉及两个角色:代理角色和真实角色。代理类负责代理真实类,为真实类提供控制访问的功能,真实类则完成具体的业务逻辑。这样,当我们不方便或者不能直接访问真实对象时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。代理模式的原理是将真实类的功能封装在代理类中,最基本的方式就是创建一个代理类,代理类实现和真实类相同的接口,并且在代理类中引用真

qq_36756227的博客 1万+

【Hyper-V】虚拟机网络配置全解析:从端口映射到固定IP实战

本文全面解析Hyper-V虚拟机网络配置,从理解虚拟交换机三种模式(外部、内部、专用)的底层逻辑入手,指导用户创建稳定的内部/NAT网络,并详细演示如何在Linux与Windows虚拟机中配置固定IP。文章进一步深入讲解端口映射原理与netsh命令实战,以及双网卡配置和PowerShell自动化脚本等高级技巧,帮助用户彻底解决虚拟机网络“失联”问题,实现灵活可控的网络环境。

weixin_29290947的博客 265

代理模式(详解)

代理模式是一种结构型设计模式,它为对象提供了一个代理,并由代理对象控制对原对象的访问。这可以是出于安全、性能、事务等多种原因。简单来说,代理模式就是通过代理对象来控制对实际对象的访问。代理对象在客户端和目标对象之间起到了中介的作用。代理模式通过引入代理对象,在不修改原始对象的情况下,为其添加额外的功能或控制。无论是静态代理、动态代理还是CGLIB代理,它们都有各自的优缺点,开发者可以根据具体的需求选择合适的代理方式。希望本文能帮助你更好地理解代理模式,并在实际项目中灵活应用。

weixin_67417798的博客 8144

VS Code ESP-IDF无法找到源文件、头文件波浪线解决

背景:想要编译小智AI项目,可以正常编译。但是一些头文件,例如#include <nvs.h>等无法找到。

Hot_Ant的博客 1484

AI应用架构师实战:高等教育智能课程推荐系统开发

性能:选课季并发量达1000QPS,推荐响应时间≤500ms(否则学生不耐烦)。数据隐私:符合《教育数据安全管理规范》,学生姓名、学号等敏感信息需匿名化存储。可扩展性:支持新增课程类型(如MOOC、实践课)、新增数据源(如竞赛获奖记录)。教育AI要“以教为中心”:不能纯技术驱动,要和教育工作者深度合作(如教师参与定义课程的前置关系)。数据是核心:数据质量决定推荐效果,要重视数据治理和隐私保护。持续迭代:教育需求是动态的(如每年新增课程、学生目标变化),要定期收集用户反馈,更新模型和策略。张三。

AI大模型应用之禅 1345

一文搞懂代理模式

代理模式是常见的设计模式之一,顾名思义,代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)以下是具体理解!........................

weixin_43953283的博客 3万+

电磁场仿真软件:Sonnet_(1).Sonnet软件概述

根据特定需求自定义仿真流程,如多频率点仿真、多边界条件仿真等。

kkchenjj的博客 1441

设计模式第4讲——代理模式(Proxy)

代理模式属于结构型设计模式。一、什么是代理模式二、分类 三、特点 四、应用场景4.1 生活场景4.2 Java场景 五、代码实现5.0 代码结构5.1 静态代理5.2 JDK动态代理5.3 Cglib代理 六、总结

让~学习~成为一种习惯(橡 皮 人の技术博客) 8973

Spring AI 实战:第七章、Spring AI Advisor机制之记忆大师

从金鱼到最强大脑通过 Spring AI 的记忆功能,我们成功让 AI:记住了用户身份实现了多用户隔离做到了持久化存储最终效果用户:“和上次一样” → AI 秒懂用户:“你知道我的名字吗?” → AI:“当然,张三!AI的进化路线🐟金鱼脑 →🧠最强大脑 →🏛️记忆宫殿管理员。

liaokailin的专栏 1511

node: /lib64/libstdc++.so.6: version `CXXABI_1.3.11‘ not found (required by node) 解决方案

本文主要介绍了node: /lib64/libstdc++.so.6: version `CXXABI_1.3.11’ not found (required by node) ,希望能对使用nodejs的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 1万+

设计模式之代理模式ProxyPattern(六)

1、什么是代理模式?代理模式是23种设计模式中的一种。代理模式是一种结构型设计模式,它允许为其他对象提供一个替代品或占位符,以控制对这个对象的访问。2、代理模式的角色构成抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理类可以通过实现该接口来代理真实主题。真实主题(Real Subject):定义了代理所代表的真实对象。代理(Proxy):持有对真实主题的引用,并实现了与真实主题一样的接口,客户端通过代理来访问真实主题,同时可以在访问真实主题前后进行一些额外操作。

徐泗空的博客 1457

Unity的粒子系统使用shadergraph(内置管线)制作的一个顶点偏移shader,但是粒子模型移动时,顶点也会偏移?

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!

**My Coding Family** 1165

设计模式之代理模式详解

本文深入解析了代理设计模式,通过生活实例(电商平台作为购物代理)类比说明其核心概念。代理模式通过中介对象控制对真实对象的访问,提供延迟加载、访问控制等功能。文章详细介绍了两种实现方式:静态代理(编译时确定关系,代码示例展示图片加载场景)和动态代理(运行时生成,含JDK实现)。类图和序列图直观展示了结构和工作流程,帮助理解代理模式在实际开发中的应用价值。

从不水文,坚持创作,加油 1613

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

设计模式之代理模式详解(附应用举例实现)

设计模式之代理模式详解(附应用举例实现)

pursuit的博客 6803

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

java设计模式之 代理模式【Proxy Pattern】 详解

首先我们举个例子说明一下什么是策略模式: 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道 被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代理人虽然不能干活,但是被 代理的人能干活呀。 比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时这样的: 先定义一种类型的女人: package com.cbf4lif...

蛋蛋淡淡定的庄园 297

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

设计模式之代理模式(ProxyPattern)

设计模式之代理模式(ProxyPattern) 文章目录设计模式之代理模式(ProxyPattern)一.代理模式(ProxyPattern)二.编程实例1.图片读取案例2.String类访问修饰符[]读写操作区分案例 一.代理模式(ProxyPattern) 代理模式是创建一个代理类,来帮助我们处理真实类,一般来说,这种代理类有点像明星的经纪人,如果你想接触到明星,必须先和他们的经纪人打交道。...

Worthy_Wang的博客 475

【设计模式】代理模式的知识详解

设计模式 -- 代理的知识详解,场景+案例+面试

WizLC 1469

详解设计模式:代理模式

代理模式(Proxy Pattern),Java 常见的设计模式之一,是 GoF 的 23 种设计模式中的一种结构型设计模式。 代理模式 是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。代理对象 具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理(为真实对象提供代理,然后供其他对象通过代理访问真实对象)。 现 Demo、代理(CGLIB 动态代理)实现Demo

栗筝i的博客 3683

[DesignPattern]ProxyPattern

代理模式玩过扮白脸、扮黑脸的游戏吗?这就是代理要做的:控制和管理访问。还记得状态模式的那台糖果机吗?现在我们需要远程监控糖果机。(在开始编码之前,需要收集需求)。远程代理 远程代理就好比“远程对象的本地代表”。何谓“远程对象“?这是一种对象,活在不同的Java虚拟机(JVM)堆中。 你的客户对象所做的就像是在做远程方法调用,但其实只是调用本地堆中的”代理“上的方法,再由代理处理所有网络通信的低层细节

xy_cpp的博客 414

代理模式详解 (附java语言源码)

代理模式(Proxy Pattern): 为其他对象提供一个代理以控制对这个对象的访问。(Provide a surrogate or placeholder foranther object to control access to it.) 可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。 分类: 静态代理(静态定义代理类) 动态代理(动态生...

数据知道的博客 1万+

设计模式之【代理模式】详解

动态代理(Dynamic Proxy)是一种在运行时动态创建代理对象的技术。与静态代理相比,动态代理不需要显式地编写代理类,而是在程序运行过程中由 JVM 自动生成代理对象。

Shine19的博客 1189

《设计模式》代理模式

定义代理模式就是为被访问的目标对象提供一个代理,此时代理对象充当访问对象和目标对象之间的媒介,通过代理对象实现对目标对象的访问。被代理的对象可以是远程对象、创建开销大的对象以及需要安全控制的对象,Java 中的代理按照代理的生成时机不同分为静态代理和动态代理,静态代理就是在编译期就生成代理对象,而动态代理是在 Java 运行时动态生成,而动态代理又分为 JDK 动态代理和 Cglib 动态代理两种。代理模式的角色组成抽象主题类(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。

华仔仔的博客 3997

设计模式-代理模式详解

代理模式摘要 代理模式是一种结构型设计模式,通过代理对象控制对目标对象的访问,在不修改目标对象的情况下增强其功能。分为静态代理和动态代理两种实现方式: 静态代理:手动创建实现相同接口的代理类,在方法调用前后添加额外处理逻辑 动态代理: JDK动态代理:基于接口,通过Proxy类和InvocationHandler接口实现 CGLIB动态代理:基于继承,适用于无接口的类 核心应用场景包括远程调用、性能监控、权限控制等。静态代理实现简单但灵活性差,动态代理更灵活但性能略低。

new_daimond的博客 1309
上一篇: 设计模式之原型模式与建造者模式详解和应用
下一篇: 设计模式之门面模式与装饰器模式详解和应用
赵广陆
赵广陆 优质创作者: Java技术领域 优质创作者: Java技术领域 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄10年 16万+粉丝 1349原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵广陆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值