设计模式-代理模式

代理模式UML类图

代理模式,旨在代理某个对象的职责,并且可以在职责代理过程中,新增其他辅助方法,代理模式也叫作委托模式。

**Subject抽象主题角色:**被代理角色的抽象角色

**RealSubject具体主题角色:**被代理角色

**Proxy代理角色:**对被代理角色实现方法增强

一、JDK动态代理

JDK动态代理是基于反射原理实现,可以查看JDK提供的InvocationHandler接口和Proxy类

1、被代理对象抽象接口

java 复制代码
package com.example.demo.proxy;

public interface SubjectInterface {
	void coreMethod();
}

2、具体被代理对象

java 复制代码
package com.example.demo.proxy;

public class Subject implements SubjectInterface {
	@Override
	public void coreMethod() {
		System.out.println("具体角色");
	}
}

3、增强被代理对象

java 复制代码
package com.example.demo.proxy;

public class Advice {
	public void before(){
		System.out.println("前置处理");
	}

	public void after(){
		System.out.println("后置处理");
	}
}

4、JDK动态代理测试

java 复制代码
package com.example.demo.proxy;

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

public class JdkTest {
	public static void main(String[] args) {
		// 被代理对象
		Subject subject = new Subject();
		// 增强对象
		Advice advice = new Advice();
		InvocationHandler handler = new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				advice.before();
				Object invoke = method.invoke(subject, args);
				advice.after();
				return invoke;
			}
		};
		SubjectInterface proxy = (SubjectInterface) Proxy.newProxyInstance(
				subject.getClass().getClassLoader(), // 传入ClassLoader
				subject.getClass().getInterfaces(), // 传入要实现的接口
				handler); // 传入处理调用方法的InvocationHandler
		// 执行方法
		proxy.coreMethod();
	}
}

在运行期动态创建一个interface实例的方法如下:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader
    2. 需要实现的接口数组,至少需要传入一个接口进去
    3. 用来处理接口方法调用的InvocationHandler实例
  3. 将返回的Object强制转型为接口

动态代理实际上是JVM在运行期动态创建class字节码并加载的过程

二、CGLIB动态代理

CGLIB是一个强大的高性能的代码生成包,通过CGLIB的依赖包,可以在程序运行的过程中动态地生成对象和接口,CGLIB底层基于字节码处理框架ASM来转换字节码并生成新的类

1、引入依赖

bash 复制代码
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.3.0</version>
</dependency>

2、具体被代理对象

java 复制代码
package com.example.demo.proxy;

public class CglibSubject {
	public void coreMethod() {
		System.out.println("具体角色");
	}
}

3、增强被代理对象

java 复制代码
package com.example.demo.proxy;

public class Advice {
	public void before(){
		System.out.println("前置处理");
	}

	public void after(){
		System.out.println("后置处理");
	}
}

4、CGLIB动态代理测试

java 复制代码
package com.example.demo.proxy;

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

import java.lang.reflect.Method;

public class CglibTest {
	public static void main(String[] args) {
		// 被代理对象
		CglibSubject cglibSubject = new CglibSubject();
		// 增强对象
		Advice advice = new Advice();
		// 创建增强器
		Enhancer enhancer = new Enhancer();
		// 设置增强目标类
		enhancer.setSuperclass(cglibSubject.getClass());
		// 设置回调
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
					throws Throwable {
				advice.before();
				Object invoke = method.invoke(cglibSubject, args);
				advice.after();
				return invoke;
			}
		});
		// 创建代理,调用核心方法
		CglibSubject targetProxy = (CglibSubject) enhancer.create();
		targetProxy.coreMethod();
	}
}

三、Spring AOP动态代理

Spring 会根据当前被代理类是否实现了接口进行JDK动态代理和CGLIB动态代理的选择

详见DefaultAopProxyFactory类源码:

java 复制代码
package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
import org.springframework.core.NativeDetector;
import org.springframework.util.ClassUtils;

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    private static final long serialVersionUID = 7930414337282325166L;

    public DefaultAopProxyFactory() {
    }

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) && !ClassUtils.isLambdaClass(targetClass) ? 
                new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
    }
}
相关推荐
fakerth6 小时前
【OpenHarmony】设计模式模块详解
c++·单例模式·设计模式·openharmony
alibli9 小时前
一文学会设计模式之创建型模式及最佳实现
c++·设计模式
1024肥宅11 小时前
前端常用模式:提升代码质量的四大核心模式
前端·javascript·设计模式
郝学胜-神的一滴15 小时前
设计模式依赖于多态特性
java·开发语言·c++·python·程序人生·设计模式·软件工程
帅次15 小时前
系统分析师:软件需求工程的软件需求概述、需求获取、需求分析
设计模式·重构·软件工程·团队开发·软件构建·需求分析·规格说明书
EXtreme3516 小时前
【数据结构】算法艺术:如何用两个栈(LIFO)优雅地模拟队列(FIFO)?
c语言·数据结构·算法·设计模式·栈与队列·摊还分析·算法艺术
1024肥宅1 天前
JavaScript常用设计模式完整指南
前端·javascript·设计模式
特立独行的猫a2 天前
C++观察者模式设计及实现:玩转设计模式的发布-订阅机制
c++·观察者模式·设计模式
Wnq100722 天前
鸿蒙 OS 与 CORBA+DDS+QOS+SOA 在工业控制领域的核心技术对比研究
物联网·性能优化·wpf·代理模式·信号处理·harmonyos·嵌入式实时数据库
better_liang2 天前
每日Java面试场景题知识点之-单例模式
java·单例模式·设计模式·面试·企业级开发