2026/4/5 13:27:29
网站建设
项目流程
内蒙古优途国际旅行社,如何做优化网站排alexa优化,帝国cms7.0网站搬家换域名换空间等安装教程,wordpress去除相册样式在软件开发的世界里#xff0c;我们不断追求着代码的高内聚、低耦合和可维护性。当系统变得复杂时#xff0c;一些与核心业务逻辑无关但又必不可少的功能#xff0c;如日志记录、事务管理、权限校验等#xff0c;会不可避免地散落在各个业务模块中#xff0c;形成大量重复…在软件开发的世界里我们不断追求着代码的高内聚、低耦合和可维护性。当系统变得复杂时一些与核心业务逻辑无关但又必不可少的功能如日志记录、事务管理、权限校验等会不可避免地散落在各个业务模块中形成大量重复代码这给维护带来了巨大挑战。Spring AOP面向切面编程正是为解决这一问题而生的利器。它如同一位技艺高超的裁缝能够在不改动衣服业务代码本身的情况下为其巧妙地缝上精美的补丁横切逻辑。本文将带你深入理解 AOP 的概念、原理并通过 XML 和注解两种方式手把手教你如何在 Spring 中应用 AOP。一、AOP概念的引入首先我们来看一下登录的原理如上图所示这是一个基本的登录原理图但是如果我们想要在这个登录之上添加一些新的功能比如权限校验那么我们能想到的就有两种方法①通过对源代码的修改实现。②不通过修改源代码方式添加新的功能 AOPAOP 的解决方案横向抽取AOP 提供了一种全新的思路在不修改 源代码的前提下动态地为其添加功能。我们将日志和权限校验逻辑抽离出来形成独立的 “切面”然后在程序运行时由框架将这些切面 “织入” 到目标方法的执行流程中。这就是 AOP 的核心思想将横切关注点Cross-cutting Concerns与业务逻辑分离。AOP的优势运行期间不修改源代码的情况下对已有的方法进行增强1. 减少重复的代码2. 提供开发的效率3. 维护方便二、AOP的核心概念在软件业AOP为Aspect Oriented Programming的缩写意为面向切面编程AOP是一种编程范式隶属于软工范畴指导开发者如何组织程序结构AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术AOP是OOP的延续是软件开发中的一个热点也是Spring框架中的一个重要内容是函数式编程的一种衍生范型利用AOP可以对业务逻辑的各个部分进行隔离从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性同时提高了开发的效率AOP采取横向抽取机制取代了传统纵向继承体系重复性代码事务管理、安全检查、缓存为什么要学习AOP可以在不修改源代码的前提下对程序进行增强三、AOP的底层逻辑3.1 JDK的动态代理技术1、为接口创建代理类的字节码文件2、使用ClassLoader将字节码文件加载到JVM3、创建代理类实例对象执行对象的目标方法3.2CGLIB 代理技术为类生成代理对象被代理类有没有接口都无所谓底层是生成子类继承被代理类四、Spring AOP 的两种实现方式4.1 基于 XML 的配置4.1.1 AOP相关的术语Joinpoint(连接点) 类里面有哪些方法可以增强这些方法称为连接点Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)Aspect(切面)-- 是 切入点通知 的结合以后咱们自己来编写和配置的4.1.2 基本准备工作AspectJ是一个面向切面的框架它扩展了Java语言。AspectJ定义了AOP语法,AspectJ实际上是对AOP编程思想的一个实践。4.1.3 AOP配置文件方式的入门创建maven项目坐标依赖、dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.0.2.RELEASE/version /dependency dependency groupIdcommons-logging/groupId artifactIdcommons-logging/artifactId version1.2/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-test/artifactId version5.0.2.RELEASE/version /dependency dependency groupIdlog4j/groupId artifactIdlog4j/artifactId version1.2.12/version /dependency dependency groupIdjunit/groupId artifactIdjunit/artifactId version4.12/version scopetest/scope /dependency !--AOP联盟-- dependency groupIdaopalliance/groupId artifactIdaopalliance/artifactId version1.0/version /dependency !--Spring Aspects-- dependency groupIdorg.springframework/groupId artifactIdspring-aspects/artifactId version5.0.2.RELEASE/version /dependency !--aspectj-- dependency groupIdorg.aspectj/groupId artifactIdaspectjweaver/artifactId version1.8.3/version /dependency /dependencies创建被增强的类// 被增强的类 public class User { //连接点/切入点 public void add(){ System.out.println(add......); } public void update(){ System.out.println(update......); } }将目标类配置到Spring中bean iduser classcom.aopImpl.User/bean定义切面类public class UserProxy { //增强/通知 ---》前置通知 public void before(){ System.out.println(before.............); } }在配置文件中定义切面类bean iduserProxy classcom.aopImpl.UserProxy/bean在配置文件中完成AOP的配置!--配置切面-- aop:config !--配置切面 切入点 通知组成-- aop:aspect refuserProxy !--前置通知UserServiceImpl的save方法执行前会增强-- !--pointcut后边是切入点表达式作用是知道对对面的那个方法进行增强-- aop:before methodbefore pointcutexecution(public void com.aopImpl.User.add())/ /aop:aspect /aop:config完成测试public class DemoTest { Test public void aopTest1(){ ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml); User user (User) applicationContext.getBean(user); user.add(); } }4.1.4 切入点的表达式再配置切入点的时候需要定义表达式具体展开如下切入点表达式的格式如下execution([修饰符] [返回值类型] [类全路径] [方法名 ( [参数] )])修饰符可以省略不写不是必须要出现的。返回值类型是不能省略不写的根据你的方法来编写返回值可以使用 * 代替。包名类名方法名参数的规则如下例如com.qcby.demo3.BookDaoImpl.save()首先包名类名方法名是不能省略不写的但是可以使用 * 代替中间的包名可以使用 * 号代替类名也可以使用 * 号代替也有类似的写法*DaoImpl方法也可以使用 * 号代替参数如果是一个参数可以使用 * 号代替如果想代表任意参数使用 ..比较通用的表达式execution(* com.qcby.*.ServiceImpl.save(..))举例2com.qcby.demo3.BookDaoImpl当中所有的方法进行增强execution(* com.qcby.*.ServiceImpl.*(..))举例3com.qcby.demo3包当中所有的方法进行增强execution(* com.qcby.*.*.*(..))!--配置切面-- aop:config !--配置切面 切入点 通知组成-- aop:aspect refuserProxy !--切入点的表达式 execution() 固定的写法 public 是可以省略不写的 方法的返回值 int String 通用的写法可以编写 * 不能省略不写的 包名类名 不能省略不写的编写 * com.* 方法名称 add() 可以写 * 参数列表 (..) 表示任意类型和个数的参数 比较通用的表达式execution(* com.*.User.add(..))-- aop:before methodbefore pointcutexecution(* com.*.User.add(..))/ /aop:aspect /aop:config4.1.5 AOP的通知类型1. 前置通知目标方法执行前进行增强。如上配置案例就是前置通知2. 环绕通知目标方法执行前后都可以进行增强。目标对象的方法需要手动执行。// 环绕通知 public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println(before.............); // 执行被增强的方法 proceedingJoinPoint.proceed(); System.out.println(after.............); }xml配置aop:around methodaround pointcutexecution(* com.*.User.add(..))/3. 最终通知目标方法执行成功或者失败进行增强。// 最终通知 public void after() { System.out.println(after.............); }xml配置aop:after methodafter pointcutexecution(* com.*.User.add(..))/4. 后置通知目标方法执行成功后进行增强。//后置通知 public void afterReturning() { System.out.println(afterReturning.............); }xml配置aop:after-returning methodafterReturning pointcutexecution(public void com.aopImpl.User.add())/5. 异常通知目标方法执行失败后进行增强。(发生异常的时候才会执行否则不执行)//异常通知 public void afterThrowing() { System.out.println(afterThrowing.............); }需要改动一下切点//连接点/切入点 public void add(){ int a 10 / 0; System.out.println(add......); }xml配置aop:after-throwing methodafterThrowing pointcutexecution(public void com.aopImpl.User.add())/4.2 基于注解的配置创建maven工程导入坐标。编写接口完成IOC的操作。步骤略。编写切面类给切面类添加注解 Aspect编写增强的方法使用通知类型注解声明1.配置xml扫描注解?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://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 !--开启注解扫描-- context:component-scan base-packagecom.aopImpl/context:component-scan /beans2.配置注解Component public class User { //连接点/切入点 public void add(){ System.out.println(add......); } }给切面类添加注解 Aspect编写增强的方法使用通知类型注解声明Component Aspect //生成代理对象 public class UserProxy { }3.配置文件中开启自动代理?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://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 !--开启注解扫描-- context:component-scan base-packagecom.aopImpl/context:component-scan !--开启Aspect生成代理对象-- aop:aspectj-autoproxy/aop:aspectj-autoproxy /beans4.通知类型注解Before -- 前置通知AfterReturing -- 后置通知Around -- 环绕通知目标对象方法默认不执行的需要手动执行After -- 最终通知AfterThrowing -- 异常抛出通知Component Aspect //生成代理对象 public class UserProxy { //增强/通知 ---》前置通知 Before(value execution(* com.*.User.add(..))) public void before(){ System.out.println(before.............); } // 环绕通知 Around(value execution(* com.*.User.add(..))) public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println(before.............); // 执行被增强的方法 proceedingJoinPoint.proceed(); System.out.println(after.............); } // 最终通知 After(value execution(* com.*.User.add(..))) public void after() { System.out.println(after.............); } //异常通知 AfterThrowing(value execution(* com.*.User.add(..))) public void afterThrowing() { System.out.println(afterThrowing.............); } //后置通知 AfterReturning(value execution(* com.*.User.add(..))) public void afterReturning() { System.out.println(afterReturning.............); } }5.测试类Test public void aopTest1(){ ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml); user.add(); }