【Java 初级】Spring核心之面向切面编程(AOP)

这篇具有很好参考价值的文章主要介绍了【Java 初级】Spring核心之面向切面编程(AOP)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

tip:作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。

💕💕 推荐:体系化学习Java(Java面试专题)

java 切面,Java 初级,面试,spring,java,aop,动态代理

1、什么是 AOP

1.1、概述

AOP(面向切面编程)是一种编程范式,用于将横切关注点(如日志记录、性能统计等)从主要业务逻辑中分离出来。通过将这些横切关注点与业务逻辑分离开来,可以提高代码的可重用性、可维护性和可扩展性。在AOP中,切面是一个模块化的单元,它封装了与横切关注点相关的行为,并可以在多个不同的应用程序中重用。切面可以通过一种称为“织入”的过程将其与主要业务逻辑相结合,从而创建一个完整的应用程序。

1.2、AOP 的作用

AOP 的作用主要有以下几个方面:

1. 代码复用:AOP 可以将一些通用的功能,如日志记录、安全控制等,抽象出来形成切面,这些切面可以被多个模块或应用程序共享,从而避免了代码重复。

2. 降低耦合度:AOP 可以将一些横跨多个模块的关注点从业务逻辑中解耦出来,使得应用程序更加模块化,降低了各个模块之间的耦合度。

3. 提高代码可维护性:AOP 可以将一些非核心的功能从主要业务逻辑中分离出来,使得代码更加清晰、易于维护。

4. 提高代码可扩展性:AOP 可以在不修改主要业务逻辑的情况下,通过增加新的切面来扩展应用程序的功能。

5. 提高代码的灵活性:AOP 可以在运行时动态地将切面织入到主要业务逻辑中,从而可以根据不同的需求对应用程序进行配置和定制。

1.3、AOP 的应用场景

AOP 的应用场景比较广泛,以下是一些常见的应用场景:

  1. 日志记录:通过 AOP 可以在方法执行前后记录日志,方便开发人员对系统进行调试和问题排查。

  2. 安全控制:通过 AOP 可以在方法执行前进行权限校验,从而保证系统的安全性。

  3. 缓存管理:通过 AOP 可以在方法执行前后对数据进行缓存,从而提高系统的性能。

  4. 事务管理:通过 AOP 可以在方法执行前后进行事务管理,从而保证系统的数据一致性。

  5. 性能统计:通过 AOP 可以在方法执行前后进行性能统计,从而方便开发人员对系统进行性能优化。

  6. 异常处理:通过 AOP 可以在方法执行过程中捕获异常,并进行统一的处理,从而提高系统的健壮性和稳定性。

  7. 分布式追踪:通过 AOP 可以在方法执行前后进行分布式追踪,从而方便开发人员对系统进行分布式调试和问题排查。

2、AOP 的配置方式

AOP 的配置方式主要有两种:基于 XML 的配置和基于注解的配置。

2.1、基于 XML 的配置方式

基于 XML 的配置方式需要在 Spring 的配置文件中定义切面、切点、通知等元素,并使用 aop:config 元素将它们组合起来。以下是一个基于 XML 的 AOP 配置的示例:

<!-- 定义切面 -->
<bean id="loggingAspect" class="com.example.LoggingAspect"/>
 <!-- 定义切点 -->
<aop:pointcut id="serviceMethod" expression="execution(* com.example.Service.*(..))"/>
 <!-- 定义通知 -->
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="serviceMethod"/>
 <!-- 定义通知实现类 -->
<bean id="loggingAdvice" class="org.springframework.aop.interceptor.CustomizableTraceInterceptor">
    <property name="enterMessage" value="Entering $[methodName]($[arguments])"/>
    <property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/>
</bean>
 <!-- 启用 AOP -->
<aop:config/>

上述配置文件定义了一个名为 loggingAspect 的切面,一个名为 serviceMethod 的切点,一个名为 loggingAdvice 的通知实现类,并将通知绑定到切点上。这个示例的作用是在 com.example.Service 包下的所有方法执行前后打印日志。

2.2、基于注解的配置方式

基于注解的配置方式需要在 Java 类中使用注解来标记切面、切点、通知等元素。以下是一个基于注解的 AOP 配置的示例:

@Aspect
@Component
public class LoggingAspect {
    
    @Pointcut("execution(* com.example.Service.*(..))")
    public void serviceMethod() {}
    
    @Before("serviceMethod()")
    public void beforeAdvice() {
        System.out.println("Entering method...");
    }
    
    @AfterReturning("serviceMethod()")
    public void afterAdvice() {
        System.out.println("Leaving method...");
    }
}

上述代码定义了一个名为 LoggingAspect 的切面,使用 @Pointcut 注解定义了一个名为 serviceMethod 的切点,并使用 @Before 和 @AfterReturning 注解定义了两个通知方法。这个示例的作用与前面的示例相同,都是在 com.example.Service 包下的所有方法执行前后打印日志。

3、AOP 实现原理

AOP 的实现原理主要是基于动态代理和字节码操作。Spring AOP 使用了 JDK 动态代理和 CGLIB 字节码操作两种方式来实现 AOP。

JDK 动态代理是通过反射机制在运行时动态地创建代理对象,代理对象与目标对象实现了相同的接口,并在代理对象中增加了切面逻辑。以下是一个使用 JDK 动态代理实现 AOP 的示例代码:

public interface UserService {
    void addUser(String name);
}

public class UserServiceImpl implements UserService {
    public void addUser(String name) {
        System.out.println("addUser: " + name);
    }
}

public class UserServiceProxy implements InvocationHandler {
    
    private Object target;
    
    public UserServiceProxy(Object target) {
        this.target = target;
    }
   
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before addUser");
        Object result = method.invoke(target, args);
        System.out.println("after addUser");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new UserServiceProxy(userService));
        proxy.addUser("John");
    }
}

上述代码中, UserService 接口定义了一个 addUser 方法, UserServiceImpl 类是 UserService 接口的实现类。 UserServiceProxy 类是代理类,实现了 InvocationHandler 接口,用于在代理对象的方法执行前后增加切面逻辑。 Main 类中使用 Proxy.newProxyInstance 方法创建代理对象,并调用 addUser 方法。

CGLIB 字节码操作是通过继承目标对象并重写目标方法的方式来实现 AOP,因此目标对象不需要实现接口。以下是一个使用 CGLIB 实现 AOP 的示例代码:

public class UserService {
    public void addUser(String name) {
        System.out.println("addUser: " + name);
    }
}

public class UserServiceInterceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before addUser");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after addUser");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new UserServiceInterceptor());
        UserService userService = (UserService) enhancer.create();
        userService.addUser("John");
    }
}

上述代码中, UserService 类是目标对象, UserServiceInterceptor 类是拦截器类,实现了 MethodInterceptor 接口,用于在目标方法执行前后增加切面逻辑。 Main 类中使用 Enhancer 类创建代理对象,并调用 addUser 方法。

无论是使用 JDK 动态代理还是 CGLIB 字节码操作,AOP 的实现原理都是基于代理模式和字节码操作。通过在代理对象中增加切面逻辑,实现了对目标对象的增强。

4、什么是动态代理

动态代理是一种在运行时动态生成代理类的技术,可以在不修改原始代码的情况下,为类或对象添加一些额外的功能。动态代理通常用于实现 AOP(面向切面编程)和远程方法调用等场景。

动态代理的实现原理是通过 Java 反射机制,在运行时动态生成代理类。代理类实现了与目标类相同的接口,并在代理类中增加了额外的逻辑,例如记录日志、性能监控等。当调用代理对象的方法时,实际上是调用了代理类中的方法,代理类再调用目标对象的方法,并在方法执行前后执行额外的逻辑。

Java 中有两种动态代理方式:JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是基于接口的代理,只能代理实现了接口的类。而 CGLIB 动态代理是基于继承的代理,可以代理任何类,但代理的类不能声明为 final 类型。

4.1、JDK 动态代理

JDK 动态代理是 Java 标准库中提供的一种动态代理实现方式,它可以在运行时动态生成代理类,并实现被代理接口的所有方法。JDK 动态代理主要依赖于 Java 的反射机制和 InvocationHandler 接口。

下面是一个简单的 JDK 动态代理的示例代码:

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

// 定义一个接口
interface Hello {
    void sayHello();
}

// 实现接口的类
class HelloImpl implements Hello {
    public void sayHello() {
        System.out.println("Hello, world!");
    }
}

// 实现 InvocationHandler 接口的代理类
class HelloProxy implements InvocationHandler {
    private Object target;
     public HelloProxy(Object target) {
        this.target = target;
    }
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建被代理对象
        Hello hello = new HelloImpl();
         // 创建代理对象
        Hello proxy = (Hello) Proxy.newProxyInstance(
            hello.getClass().getClassLoader(),
            hello.getClass().getInterfaces(),
            new HelloProxy(hello)
        );
         // 调用代理对象的方法
        proxy.sayHello();
    }
}

在上面的代码中,我们定义了一个接口 Hello 和一个实现该接口的类 HelloImpl 。接着我们定义了一个实现 InvocationHandler 接口的代理类 HelloProxy ,它的作用是在调用被代理对象的方法前后输出日志。最后我们在 main 函数中创建了被代理对象 HelloImpl 和代理对象 HelloProxy ,并通过 Proxy.newProxyInstance 方法生成了代理对象实例。当我们调用代理对象的 sayHello 方法时,实际上是调用了 HelloProxy 中的 invoke 方法,该方法会在调用被代理对象的 sayHello 方法前后输出日志。

JDK 动态代理的原理是通过反射机制在运行时动态生成代理类,并实现被代理接口的所有方法。当调用代理对象的方法时,实际上是调用了代理类中的方法,代理类再调用目标对象的方法,并在方法执行前后执行额外的逻辑。

4.2、CGLIB 动态代理

CGLIB 动态代理是一种基于继承的代理实现方式,它可以在运行时动态生成代理类,并继承被代理类。CGLIB 动态代理主要依赖于 ASM(一个 Java 字节码操作库)。

下面是一个简单的 CGLIB 动态代理的示例代码:

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

// 被代理类
class Hello {
    public void sayHello() {
        System.out.println("Hello, world!");
    }
}

// 实现 MethodInterceptor 接口的代理类
class HelloProxy implements MethodInterceptor {
     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        // 设置被代理类为父类
        enhancer.setSuperclass(Hello.class);
        // 设置回调函数
        enhancer.setCallback(new HelloProxy());
        // 创建代理对象
        Hello proxy = (Hello) enhancer.create();
        // 调用代理对象的方法
        proxy.sayHello();
    }
}

5、AOP 在项目中的应用

AOP(面向切面编程)是一种编程思想,它可以通过将横切关注点(如日志记录、性能统计、事务管理等)与业务逻辑分离,使得代码更加模块化、易于维护和扩展。在项目中,AOP 可以应用于很多场景,下面举例说明几个常见的应用场景。

  1. 日志记录
    在项目中,我们通常需要记录一些关键操作的日志,以便于后续排查问题。使用 AOP 可以很方便地实现日志记录,例如使用 Spring AOP,我们可以定义一个切面,通过在切面中定义一个方法,在方法中记录日志,并将该切面织入到需要记录日志的方法中。
  2. 安全控制
    在项目中,我们通常需要对一些敏感操作进行安全控制,例如需要登录才能访问某些页面或执行某些操作。使用 AOP 可以很方便地实现安全控制,例如使用 Spring Security,我们可以定义一个切面,在切面中判断用户是否已经登录,并根据需要进行权限控制。
  3. 性能统计
    在项目中,我们通常需要对一些关键操作进行性能统计,以便于优化系统性能。使用 AOP 可以很方便地实现性能统计,例如使用 Spring AOP,我们可以定义一个切面,在切面中记录方法的执行时间,并将该切面织入到需要进行性能统计的方法中。
  4. 事务管理
    在项目中,我们通常需要对一些关键操作进行事务管理,以保证数据的一致性和完整性。使用 AOP 可以很方便地实现事务管理,例如使用 Spring AOP,我们可以定义一个切面,在切面中开启事务、提交事务或回滚事务,并将该切面织入到需要进行事务管理的方法中。
    总之,AOP 在项目中的应用非常广泛,可以帮助我们更好地实现代码的分离和模块化,提高代码的可维护性和可扩展性。

6、实践-手写一个 AOP 的案例

以下是一个基于Spring AOP实现日志记录的示例代码:

  1. 创建一个切面类:
@Aspect
@Component
public class LogAspect {
     private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
     @Around("execution(* com.example.demo.controller.*.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        LOGGER.info("Method {} execution time: {}ms", joinPoint.getSignature().getName(), endTime - startTime);
        return result;
    }
     @Before("execution(* com.example.demo.controller.*.*(..)) && args(request,..)")
    public void logBefore(JoinPoint joinPoint, HttpServletRequest request) {
        LOGGER.info("Request URL: {} {}", request.getMethod(), request.getRequestURL());
        LOGGER.info("Request parameters: {}", request.getParameterMap());
    }
}

这里定义了两个切点,一个是 @Around ,用于记录方法的执行时间,另一个是 @Before ,用于记录请求的参数。

  1. 在Spring Boot主类中添加 @EnableAspectJAutoProxy 注解:
@SpringBootApplication
@EnableAspectJAutoProxy
public class DemoApplication {
     public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
 }
  1. 测试

编写一个简单的Controller:

@RestController
public class HelloController {
     @GetMapping("/hello")
    public String hello(@RequestParam("name") String name) {
        return "Hello, " + name;
    }
}

启动应用,访问 http://localhost:8080/hello?name=world,可以在控制台看到类似如下的日志:

2021-09-23 16:53:11.453  INFO 12345 --- [nio-8080-exec-1] c.e.d.aop.LogAspect                     : Request URL: GET http://localhost:8080/hello
2021-09-23 16:53:11.454  INFO 12345 --- [nio-8080-exec-1] c.e.d.aop.LogAspect                     : Request parameters: {name=[world]}
2021-09-23 16:53:11.454  INFO 12345 --- [nio-8080-exec-1] c.e.d.aop.LogAspect                     : Method hello execution time: 4ms

可以看到,请求的参数和方法的执行时间都被记录下来了。
java 切面,Java 初级,面试,spring,java,aop,动态代理

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊文章来源地址https://www.toymoban.com/news/detail-757847.html

到了这里,关于【Java 初级】Spring核心之面向切面编程(AOP)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [SSM]Spring面向切面编程AOP

    目录 十五、面向切面编程AOP 15.1AOP介绍 15.2AOP的七大术语 15.3切点表达式 15.4使用Spring的AOP 15.4.1准备工作 15.4.2基于AspectJ的AOP注解式开发 15.4.3基于XML配置方式的AOP(了解) 15.5AOP的实际案例:事务处理 15.6AOP的实际案例:安全日志 IoC使软件组件松耦合。AOP让你能够捕捉系统中经

    2024年02月15日
    浏览(46)
  • 认识 spring AOP (面向切面编程) - springboot

    本篇介绍什么是spring AOP, AOP的优点,使用场景,spring AOP的组成,简单实现AOP 并 了解它的通知;如有错误,请在评论区指正,让我们一起交流,共同进步! 本文开始 AOP: 面向切面编程,也就是面向某一类编程,对某一类事情进行统一处理; spring AOP: 是实现了AOP这种思想的一

    2024年02月14日
    浏览(48)
  • Spring AOP(面向切面编程)和方法拦截

    Spring AOP(面向切面编程)和方法拦截 Spring是一款广泛使用的Java开发框架,提供了丰富的功能和工具,用于简化企业级应用程序的开发。其中一个重要的特性是面向切面编程(AOP)和方法拦截。本文将介绍Spring AOP和方法拦截的概念、工作原理以及在实际开发中的应用。 在软

    2024年02月05日
    浏览(43)
  • 切面的魔力:解密Spring AOP 面向切面编程

    目录 一、AOP简介 1.1 什么是AOP ? 1.2 什么是面向切面编程 ? 1.3 AOP 的特点 二、 AOP的基本概念解读 2.1 AOP的基本概念 2.2 AOP 概念趣事解读 三、代码情景演示 3.1 编写目标对象(超级英雄们正常的行动) 3.2 编写通知类 3.2.1 前置通知 3.2.2 后置通知 3.2.3 异常通知 3.2.4 环绕通知

    2024年02月11日
    浏览(54)
  • 【Spring AOP】结合日志面向切面编程 两种写法

            这里需要提前了解什么是Spring的AOP(Aspect Oriented Programming)。是在OOP(面向对象)思想的一种拓展思想。 简单来说就是将某个代码块嵌入到其它的代码块中 。笔者先前学Spring也有学什么IoC啊AOP啊,但实际上没有用过、就那听过学过没啥用的。。没会儿就忘记了。

    2024年02月13日
    浏览(42)
  • spring6-AOP面向切面编程

    1、场景模拟 搭建子模块:spring6-aop 1.1、声明接口 声明计算器接口Calculator,包含加减乘除的抽象方法 1.2、创建实现类 1.3、创建带日志功能的实现类 1.4、提出问题 ①现有代码缺陷 针对带日志功能的实现类,我们发现有如下缺陷: 对核心业务功能有干扰,导致程序员在开发核

    2024年02月08日
    浏览(51)
  • Spring AOP使用指南: 强大的面向切面编程技术

    🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页 ——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐 🌊 《100天精通Golang(基础

    2024年02月09日
    浏览(51)
  • Spring AOP (面向切面编程)原理与代理模式—实例演示

    Spring 中文文档 (springdoc.cn) Spring | Home 官网         Java是一个面向对象(OOP)的语言,但它有一些弊端。虽然使用OOP可以通过组合或继承的方式来实现代码的重用。但当我们需要为多个不具有继承关系的对象(一般指的是两个不同的类,它们之间没有继承自同一个父类或接

    2024年02月15日
    浏览(54)
  • 【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用

    【JavaEE】 AOP(1) 1.1 AOP 与 Spring AOP AOP ( A spect O riented P rogramming),是一种思想,即 面向切面编程 Spring AOP 则是一个框架,Spring项目中需要引入依赖而使用 AOP和Spring AOP的关系就相当于IoC和DI Spring AOP让开发者能够半自动的开发AOP思想下实现的功能 1.2 没有AOP的世界是怎样的

    2024年02月11日
    浏览(46)
  • Spring AOP:面向切面编程在实际项目中的应用

    🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页 ——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐 🌊 《100天精通Golang(基础

    2024年02月09日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包