代理模式与AOP实现原理:从静态代理到动态代理再到AOP

代理模式与AOP实现原理:从静态代理到动态代理再到AOP

在面向对象编程中,代理模式是一种常见的设计模式,用于在不改变目标对象的前提下对其功能进行扩展。而面向切面编程(AOP)进一步通过代理模式,将横切关注点(如日志记录、事务管理等)与业务逻辑分离,从而实现更为优雅和模块化的设计。本文将详细介绍静态代理、动态代理(包括JDK动态代理和CGLIB动态代理)的实现方式,并进一步探讨AOP的原理及其在Spring中的应用。


1. 静态代理

静态代理概述

静态代理是指在代码中显式地创建代理类,代理类与目标对象实现相同的接口,并通过代理类的方法对目标对象进行增强。静态代理的优点在于可以在不修改目标对象的前提下扩展其功能,然而其缺点是需要实现与目标对象相同的接口,导致代理类繁多,增加了维护成本。

静态代理示例

目标对象:

public class Girl implements Human {
    @Override
    public void eat() {
        System.out.println("Em mmm.. mm..");
    }
}

抽象接口:

interface Human {
    void eat();
}

代理类:

public class ProxyGirl implements Human {
    private Human human;

    public ProxyGirl(Human human) {
        this.human = human;
    }

    @Override
    public void eat() {
        System.out.println("chiqian");
        human.eat();  // 调用目标对象的方法
        System.out.println("chihou");
    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        Girl girl = new Girl();
        Human proxyGirl = new ProxyGirl(girl);
        proxyGirl.eat();  // 输出增强后的方法
    }
}

输出结果:

chiqian
Em mmm.. mm..
chihou
静态代理的优缺点
  • 优点:在不修改目标对象的前提下扩展目标对象的功能。
  • 缺点:需要实现与目标对象相同的接口,导致代理类繁多,维护成本高。

2. 动态代理

动态代理概述

动态代理与静态代理不同,它是在运行时动态生成代理对象,而不是在编译时静态生成。动态代理有两种主要实现方式:基于JDK的动态代理和基于CGLIB的动态代理。

  • JDK动态代理:适用于目标对象实现了接口的情况,通过InvocationHandlerProxy类实现。
  • CGLIB动态代理:适用于目标对象没有实现接口的情况,通过字节码生成工具库CGLIB实现。
2.1 JDK动态代理示例

接口及其实现类:

// 定义接口
public interface MyInterface {
    void doSomething();
}

// 实现接口
public class MyInterfaceImpl implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

InvocationHandler:

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

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method execution.");
        Object result = method.invoke(target, args);
        System.out.println("After method execution.");
        return result;
    }
}

动态代理测试类:

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        MyInterfaceImpl myInterfaceImpl = new MyInterfaceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(myInterfaceImpl);

        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class<?>[]{MyInterface.class},
                handler);

        proxy.doSomething();  // 调用代理对象的方法
    }
}

输出结果:

Before method execution.
Doing something...
After method execution.
2.2 CGLIB动态代理示例

目标类:

public class MyClass {
    public void doSomething() {
        System.out.println("Doing something in MyClass...");
    }
}

MethodInterceptor:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method execution.");
        Object result = proxy.invokeSuper(obj, args);  // 调用父类(被代理类)的方法
        System.out.println("After method execution.");
        return result;
    }
}

CGLIB动态代理测试类:

import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        MyMethodInterceptor interceptor = new MyMethodInterceptor();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyClass.class);  // 设置要代理的类
        enhancer.setCallback(interceptor);  // 设置回调函数
        MyClass proxy = (MyClass) enhancer.create();  // 创建代理对象

        proxy.doSomething();  // 调用代理对象的方法
    }
}

输出结果:

Before method execution.
Doing something in MyClass...
After method execution.
CGLIB动态代理的依赖

使用CGLIB时,需要在Maven项目中添加CGLIB依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version> <!-- 使用时请检查最新版本 -->
</dependency>

3. AOP实现原理

AOP概述

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将横切关注点(如日志记录、事务管理、安全检查等)与业务逻辑分离开来,从而实现模块化的代码设计。AOP通过将横切关注点定义为独立的切面,并在特定的切入点上织入这些切面,来实现对目标对象方法的增强。

AOP的基本概念
  • 切面(Aspect):封装横切关注点的模块,由切入点和通知组成。
  • 切入点(Pointcut):定义切面应该应用到程序的哪些部分,通过表达式来指定目标方法、类或包。
  • 通知(Advice):定义在切入点处执行的动作,可以在方法执行前、执行后、抛出异常时执行。
  • 织入(Weaving):将切面应用到目标对象的过程,分为编译时织入、类加载时织入和运行时织入。
AOP实现的三种方式
  1. 编译时织入:在编译过程中将切面代码织入目标类中,例如AspectJ。
  2. 类加载时织入:在类加载时由类加载器动态地将切面代码织入目标类中,使用Java的Instrumentation API。
  3. 运行时织入:在运行时通过代理对象将切面代码织入目标对象中,Spring AOP就是采用运行时织入。
AOP在Spring中的实现

Spring AOP主要使用动态代理来实现AOP功能:

  • JDK动态代理:当目标对象实现了接口时,使用JDK动态代理。
  • CGLIB动态代理:当目标对象没有实现接口时,使用CGLIB生成代理类。

Spring AOP示例:

定义切面类:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingAspect {

    // 切入点表达式,表示在com.example包下的所有方法执行前执行这个切面
    @Before("execution(* com.example.*.*(..))")
    public void logBefore() {
        System.out.println("Before method execution.");
    }
}

配置和使用切面:

将切面类配置到Spring应用上下文中,Spring会自动检测@Aspect注解并在运行时动态创建代理对象,从而在方法执行前自动调用logBefore()方法。

Spring AOP的优点:

  • 非侵入性:不需要修改现有的业务代码,通过配置切面即可实现增强。
  • 灵活性:可以通过简单的配置文件或注解轻松调整横切关注点的应用范围。
  • 可重用性:切面可以被多个不同的模块使用,提高了代码的可重用性

和模块化程度。


结论

通过本文对静态代理、动态代理以及AOP的详细介绍,可以看出代理模式在Java编程中的广泛应用。从静态代理的显式代码实现,到动态代理的运行时生成代理对象,再到AOP的高度模块化设计,代理模式不仅提升了代码的可维护性,还为横切关注点的处理提供了有效的手段。随着业务需求的复杂化,AOP在Spring等框架中的应用也愈发重要,使得代码更加优雅、灵活和可扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

heromps

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

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

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

打赏作者

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

抵扣说明:

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

余额充值