0

0

03-Spring5 AOP

爱谁谁

爱谁谁

发布时间:2025-06-27 11:58:23

|

798人浏览过

|

来源于php中文网

原创

什么是aop面向切面编程?aop是一种编程范式,它通过将业务逻辑的各个部分隔离,从而降低了这些部分之间的耦合度,提升了程序的可重用性,并提高了开发效率。简单来说,aop允许你在不修改原始代码的情况下,向主干功能中添加新功能。例如,在登录系统中,aop可以用来添加日志记录、权限验证等功能,而无需改变登录逻辑本身。

03-Spring5 AOP

AOP的底层原理是使用动态代理技术实现的。动态代理有两种情况:

  1. JDK动态代理:适用于有接口的情况。通过创建接口实现类的代理对象来增强类的方法。

    03-Spring5 AOP

  2. CGLIB动态代理:适用于没有接口的情况。通过创建子类的代理对象来增强类的方法。

    03-Spring5 AOP

AOP(JDK动态代理)使用JDK动态代理,通过Proxy类中的方法创建代理对象。

03-Spring5 AOP

调用newProxyInstance方法,该方法有三个参数:

  • 第一参数:类加载器
  • 第二参数:增强方法所在的类,这个类实现的接口,支持多个接口
  • 第三参数:实现这个接口InvocationHandler,创建代理对象,写增强的部分

编写JDK动态代理代码,首先创建接口并定义方法:

package com.dance.spring.learn.proxy;
public interface UserDao {
    public int add(int a, int b);
    public String update(String id);
}

然后创建接口实现类并实现方法:

package com.dance.spring.learn.proxy.impl;
import com.dance.spring.learn.proxy.UserDao;
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        return a+b;
    }
    @Override
    public String update(String id) {
        return id;
    }
}

接着使用Proxy创建接口代理对象:

package com.dance.spring.learn.proxy;
import com.dance.spring.learn.proxy.impl.UserDaoImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
    public static void main(String[] args) {
        Class>[] classes = {UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao userDao1 = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), classes, new UserDaoProxy(userDao));
        int add = userDao1.add(1, 2);
        System.out.println(add);
    }
}
class UserDaoProxy implements InvocationHandler{
    private UserDao userDao;
    public UserDaoProxy(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法之前执行:"+method.getName()+",参数:"+ Arrays.toString(args));
        // 执行方法
        Object invoke = method.invoke(userDao, args);
        System.out.println("方法之后执行:返回值为:"+invoke);
        return invoke;
    }
}

执行结果:

方法之前执行:add,参数:[1, 2]
方法之后执行:返回值为:3

CGLIB动态代理(扩展知识点):CGLIB(Code Generation Library)是一个开源项目,是一个强大、高性能、高质量的代码生成类库。它可以在运行时扩展Java类和实现Java接口。Hibernate使用它来实现持久化对象的动态生成。CGLIB通过ASM字节码处理框架来转换字节码并生成新的类。它广泛用于许多AOP框架中,如Spring AOP,用于方法拦截。脚本语言如Groovy和BeanShell也使用ASM来生成Java字节码。

如果有兴趣尝试CGLIB动态代理,可以参考以下链接:

https://www.php.cn/link/5001e11e24d2f4e723a67feb678f4e27

AOP术语

  • 连接点:类中可以被增强的方法称为连接点。
  • 切入点:实际被增强的方法称为切入点。如果一个类中有四个方法可以被增强,但只对其中两个进行增强,这两个方法就是切入点。
  • 通知(增强):实际增强的逻辑部分称为通知(增强)。
  • 通知的类型:前置通知、后置通知、环绕通知、异常通知、最终通知。
  • 切面:把通知应用到切入点的过程称为切面。

AOP操作(准备工作):Spring框架通常基于AspectJ实现AOP操作。AspectJ不是Spring的组成部分,而是一个独立的AOP框架。通常将AspectJ和Spring框架一起使用来进行AOP操作。基于AspectJ的AOP操作可以通过XML配置文件或注解方式实现。

切点表达式:切点表达式的作用是知道对哪个类中的哪个方法进行增强。语法结构为:execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))。例如:

Boba.video
Boba.video

AI动漫视频生成器

下载
  • com.dance.spring.learn.BookDao类中的add方法进行增强:execution(* com.dance.spring.learn.BookDao.add(..))
  • com.dance.spring.learn.BookDao类中的所有方法进行增强:execution(* com.dance.spring.learn.BookDao.*(..))
  • com.dance.spring.learn.dao包中所有类、类中所有方法进行增强:execution(* com.dance.spring.learn.*.*(..))

AOP操作(AspectJ注解):首先创建User类:

package com.dance.spring.learn.aop;
@Component
public class User {
    public void add(){
        System.out.println("add......");
    }
}

然后创建增强类并编写增强逻辑:

package com.dance.spring.learn.aop;
public class UserProxy {
    /**
     * 前置通知
     */
    public void before() {
        System.out.println("before......");
    }
}

进行通知的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <component-scan base-package="com.dance.spring.learn.aop"></component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

修改增强类,配置前置通知:

/**
 * 前置通知
 */
@Before(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
public void before() {
    System.out.println("before......");
}

编写测试类:

@Test
public void test10(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Spring-aop.xml");
    User user = classPathXmlApplicationContext.getBean("user", User.class);
    user.add();
}

执行结果:

before......
add......

编写其他通知:

package com.dance.spring.learn.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserProxy {
    /**
     * 前置通知
     */
    @Before(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
    public void before() {
        System.out.println("before......");
    }
    /**
     * 后置通知(返回通知)
     */
    @AfterReturning(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    /**
     * 最终通知
     */
    @After(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
    /**
     * 异常通知
     */
    @AfterThrowing(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
        return proceed;
    }
}

执行结果(使用test10进行测试):

环绕之前.........before......add......环绕之后.........after.........afterReturning.........

异常通知只有在方法抛出异常时才会执行。手动修改代码模拟异常:

修改User类的add方法:

public void add(){
    int i = 5/0;
    System.out.println("add......");
}

再次调用test10:

环绕之前.........before......after.........afterThrowing.........

可以看到在报错后执行了异常通知,但后置通知和环绕之后的通知未执行。

注解总结

  • @Aspect:标识这是一个aspect类。
  • @Before:前置通知。
  • @AfterReturning:后置通知(返回通知)。
  • @After:最终通知(finally)。
  • @AfterThrowing:异常通知。
  • @Around:环绕通知。

相同的切入点抽取:

package com.dance.spring.learn.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserProxy {
    @Pointcut(value = "execution(* com.dance.spring.learn.aop.User.add(..))")
    public void pointcut(){}
    /**
     * 前置通知
     */
    @Before(value = "pointcut()")
    public void before() {
        System.out.println("before......");
    }
    /**
     * 后置通知(返回通知)
     */
    @AfterReturning(value = "pointcut()")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    /**
     * 最终通知
     */
    @After(value = "pointcut()")
    public void after() {
        System.out.println("after.........");
    }
    /**
     * 异常通知
     */
    @AfterThrowing(value = "pointcut()")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
        return proceed;
    }
}

如果有多个类对同一个方法进行增强,可以设置优先级。在增强类上添加注解@Order(数字类型值),数字越小优先级越高:

@Order(1)

完全注解开发,使用配置类替换XML:

package com.dance.spring.learn.aop.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.dance.spring.learn.aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {}

编写测试类:

@Test
public void test11(){
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(com.dance.spring.learn.aop.config.SpringConfig.class);
    User user = annotationConfigApplicationContext.getBean("user", User.class);
    user.add();
}

执行结果:

环绕之前.........before......add......环绕之后.........after.........afterReturning.........

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

154

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

88

2026.01.26

hibernate和mybatis有哪些区别
hibernate和mybatis有哪些区别

hibernate和mybatis的区别:1、实现方式;2、性能;3、对象管理的对比;4、缓存机制。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

157

2024.02.23

Hibernate框架介绍
Hibernate框架介绍

本专题整合了hibernate框架相关内容,阅读专题下面的文章了解更多详细内容。

91

2025.08.06

Java Hibernate框架
Java Hibernate框架

本专题聚焦 Java 主流 ORM 框架 Hibernate 的学习与应用,系统讲解对象关系映射、实体类与表映射、HQL 查询、事务管理、缓存机制与性能优化。通过电商平台、企业管理系统和博客项目等实战案例,帮助学员掌握 Hibernate 在持久层开发中的核心技能。

39

2025.09.02

Hibernate框架搭建
Hibernate框架搭建

本专题整合了Hibernate框架用法,阅读专题下面的文章了解更多详细内容。

72

2025.10.14

hibernate和mybatis有哪些区别
hibernate和mybatis有哪些区别

hibernate和mybatis的区别:1、实现方式;2、性能;3、对象管理的对比;4、缓存机制。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

157

2024.02.23

Hibernate框架介绍
Hibernate框架介绍

本专题整合了hibernate框架相关内容,阅读专题下面的文章了解更多详细内容。

91

2025.08.06

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

23

2026.03.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.6万人学习

YMP在线手册
YMP在线手册

共64课时 | 49万人学习

10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号