设计模式之代理模式

引言

代理模式:给目标对象提供一个代理(中间商),并通过代理对象实现对目标对象的调用,这样客户端通过代理对象,间接实现对目标对象的引用,这样就可以在代理对象中实现除具体业务之外的额外功能。

代理模式的组成结构

Subject(目标对象): 原始对象和代理对象都需要实现的目标接口

RealSubject(具体目标对象): 需要代理的目标对象

Proxy(代理对象): 代理目标对象的代理类

静态代理

静态代理:代理类和目标类需要实现同样的目标方法,同时在代理对象中维护一个目标对象的引用,通过代理对象的方法调用目标对象的方法

静态代理的示例

java 复制代码
//目标接口
public interface TargetService {
    public void request();
}
// 具体目标实现
public class TargetServiceImpl implements TargetService{
    @Override
    public void request() {
        System.out.println("TargetServiceImpl.request()");
    }
}
// 代理对象
public class ProxyServiceImpl implements TargetService{
    private TargetService targetService;

    public ProxyServiceImpl(TargetService targetService) {
        this.targetService = targetService;
    }
    @Override
    public void request() {
        System.out.println("before request ...");
        targetService.request();
        System.out.println("after request ...");
    }

    public static void main(String[] args) {
        TargetService targetService = new TargetServiceImpl();
        ProxyServiceImpl proxyService = new ProxyServiceImpl(targetService);
        proxyService.request();
    }
}
// 期望实现
//before request ...
//TargetServiceImpl.request()
//after request ...

静态代理实现相对比较简单,但是相对的比较繁琐,如果有许多或者函数需要实现代理功能如果都去实现接口会产生类爆炸,同时也增加了大量重复代码

动态代理

动态代理:与静态代理不同,动态代理不需要像静态代理那样去实现相应的目标接口,也就是我们并不会去创建相应的Java代理对象,而是在程序运行时直接将代理对象创建并通过反射API运行目标函数

jdk的动态代理

jdk动态代理是通过动态字节码技术,在程序运行的时候直接将代理对象生成,而不是和正常的程序执行一样,正常的执行需要先将class字节码通过classloader加载到jvm中创建class对象,然后通过代理类的class对象创建代理对象,动态字节码省去了classloader加载字节码的过程。

java 复制代码
// classloader 代理对象的类加载器,但是动态代理并不会创建代理对象,也就没有class文件也就没有相对应的类加载器(jvm会为每个class文件创建一个与之相对应的类加载器),此时就可以借用一个类加载器
// interfaces 目标类实现的接口
// 调用处理器,主要是实现代理对象的额外功能以及是实现对目标对象的调用,并返回目标对象返回值
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                                      
                                      
                                      
//调用处理器
public interface InvocationHandler {
    // proxy代理对象
    // method 目标对象方法
    //args 目标对象方法参数
    // 返回目标对应调用返回值
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}                                   

jdk动态代理示例

java 复制代码
public class JdkProxy {
    public static void main(String[] args) {
        TargetService targetService =new TargetServiceImpl();
        TargetHandler targetHandler = new TargetHandler(targetService);
        TargetService proxy = (TargetService) Proxy.newProxyInstance(TargetService.class.getClassLoader(), TargetServiceImpl.class.getInterfaces(), targetHandler);
        proxy.request();
    }

    static class TargetHandler implements InvocationHandler{
        private TargetService target;
        public TargetHandler(TargetService target){
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理对象:"+proxy.getClass().getName());
            System.out.println("before request");
            Object obj =  method.invoke(target,args);
            System.out.println("after request");
            return  obj;
        }
    }
}

//预期结果
//代理对象:com.sun.proxy.$Proxy0
//before request
//TargetServiceImpl.request()
//after request

cglib动态代理

jdk动态代理的缺点就是代理对象必须要实现目标对象的接口,着有很大的局限性,因为目标对象可能压根就没有实现接口,此时jdk动态代理就无法实现代理操作,cglib是通过继承的方式来实现的,代理对象继承目标对象,从而实现子类调用父类的方法来实现代理

cglib动态代理示例

java 复制代码
public class CglibProxy {
    public static void main(String[] args) {
        // 创建代理对象
        TargetServiceImpl targetServiceImpl = new TargetServiceImpl();
        // 实现额外功能
        MyMethodInterceptor interceptor =new MyMethodInterceptor(targetServiceImpl);
        // 创建代理类
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(targetServiceImpl.getClass().getClassLoader());
        enhancer.setSuperclass(targetServiceImpl.getClass());
        enhancer.setCallback(interceptor);
        TargetServiceImpl targetServiceImplProxy = (TargetServiceImpl) enhancer.create();
        targetServiceImplProxy.request();

    }
    static class  MyMethodInterceptor implements MethodInterceptor{
        private TargetServiceImpl targetService;

        public MyMethodInterceptor(TargetServiceImpl targetService){
            this.targetService= targetService;
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before request");
            Object obj = method.invoke(targetService, objects);
            System.out.println("after request");
            return obj;
        }
    }
}

jdk和cglib总结

  1. 实现代理的方式是类似的,首先创建目标对象,其次实现额外的功能,最后生成代理对象调用目标方法
  2. jdk是通过接口来实现代理,cglib是通过继承目标类来实现代理
  3. 二者都是采用动态字节码技术实现

基于Aspect的AOP编程

Aspect是基于注解来实现AOP切面编程,这样可以极大的简化开发工作量

Aspect aop编程示例

java 复制代码
// 表明这是一个切面类
@Aspect
@Component
public class MyLogAspect {
    //定义一个切入点
    @Pointcut("execution(* org.wh.proxy.TargetService.*(..))")
    public void logPointCut(){}

    //创建额外功能
    @Around("logPointCut()")
    public Object logAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("================join log====================");
        Object result = joinPoint.proceed();
        return result;
    }
}
//目标接口
public interface TargetService {
    public void request();
}
//具体目标实现
@Service
public class TargetServiceImpl implements TargetService{
    @Override
    public void request() {
        System.out.println("TargetServiceImpl.request()");
    }
}
//单元测试
@SpringBootTest
public class TargetServiceTest {
    @Autowired
    private TargetService targetService;

    @Test
    public void test1(){
        targetService.request();
    }
}
//预期结果
//================join log====================
//TargetServiceImpl.request()

springboot现在默认的jdk动态代理已经是cglib,之前是jdk动态代理

相关推荐
海绵波波10712 分钟前
flask后端开发(1):第一个Flask项目
后端·python·flask
zh路西法29 分钟前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
小k_不小1 小时前
C++面试八股文:指针与引用的区别
c++·面试
夏旭泽1 小时前
设计模式-备忘录模式
设计模式·备忘录模式
蓝染-惣右介1 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
AI人H哥会Java3 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
凡人的AI工具箱3 小时前
每天40分玩转Django:Django表单集
开发语言·数据库·后端·python·缓存·django
奔跑草-3 小时前
【数据库】SQL应该如何针对数据倾斜问题进行优化
数据库·后端·sql·ubuntu
中國移动丶移不动3 小时前
Java 并发编程:原子类(Atomic Classes)核心技术的深度解析
java·后端