中企动力科技股份有限公司做网站,甘肃网站建设,wordpress4.3 撰写设置,网站建设和维护岗位的职责前言 前面荔枝已经梳理了Spring框架中的IOC部分的知识#xff0c;接下来荔枝继续梳理Spring框架的另一大重点#xff1a;AOP面向切面编程。在这篇文章中#xff0c;荔枝会着重弄清楚AOP的概念并对实现AOP的两种方式进行梳理#xff0c;同时荔枝也会相应给出代码样例。毕竟荔…前言 前面荔枝已经梳理了Spring框架中的IOC部分的知识接下来荔枝继续梳理Spring框架的另一大重点AOP面向切面编程。在这篇文章中荔枝会着重弄清楚AOP的概念并对实现AOP的两种方式进行梳理同时荔枝也会相应给出代码样例。毕竟荔枝始终觉得只有文字的描述是苍白无力的有代码其实理解起来会更快哈哈哈。 文章目录
前言
一、代理模式和AOP概念
1.1 代理模式
1.2 AOP概念
二、基于注解方式实现AOP
2.1 动态代理分类和依赖引入
2.2 切入点表达式以及五种通知类型
2.3 重用切入点表达式
2.4 切面的优先级
三、基于XML方式实现AOP
总结 一、代理模式和AOP概念
在开发中我们常常考虑将不同功能的程序分离出来降低系统的耦合度也就是解耦操作。也就是说通常在针对一些附加功能的时候我们需要一种方法把子类中重复的代码注入到父类中。
1.1 代理模式 代理模式是设计模式中的一种属于结构型模式。它的作用就是通过提供一个代理类让我们在调用目标方法的时候不再是直接对目标方法进行调用而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来一一解耦。调用目标方法时先调用代理对象的方法减少对目标方法的调用同时让附加功能能够集中在一起也有利于统一维护。代理主要分为两种静态代理和动态代理。
静态代理
静态代理解决了高耦合的问题通过一个代理类构造方法获取执行类的对象并调用其add方法实现接口功能并在前后加上附加功能比如日志文件。
package com.crj.aop;public class CalculatorStaticProxy implements Calaulator{//将代理目标对象传递进来private Calaulator calaulator;public CalculatorStaticProxy(Calaulator calaulator) {this.calaulator calaulator;}Overridepublic int add(int i, int j) {//输出日志System.out.println([日志]add方法开始参数是ii、jj);calaulator.add(i,j);//输出日志System.out.println([日志]add方法结束);return 0;}
}静态代理确实实现了解耦但是由于代码都写死了完全不具备任何的灵活性。就拿日志功能来说将来其他地方也需要附加日志那还得声明多个静态代理类那就产生了大量重复的代码日志功能还是分散的没有统一管理。 进一步的需求将日志功能集中到一个代理类中将来有任何日志需求都通过这一个代理类来实现这就需要使用动态代理技术了。 动态代理
Java种支持使用Proxy.newProxyInstance(classLoader,interfaces,invocationHandler)来实现接口的动态代理需要三个参数需代理类的类加载器、需代理类的实现接口数组和重写的代理方法。其中第三个参数实现目标的代理方法借助InvocationHandler类种的invoke方法来实现。
动态代理类
package com.crj.spring6.aop;import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;//实现动态代理
public class ProxyFactory {//目标对象private Object target;public ProxyFactory(Object target) {this.target target;}//返回代理对象public Object getProxy(){/*** 参数说明* Proxy.newProxyInstance()方法* 1.类加载器ClassLoader loader 加载动态生成代理类的类加载器* 2.Class[] interfaces 目标对象实现的所有接口的class类型数组* 3.IncocationHandler 设置代理对象实现目标对象方法的过程*///类加载器ClassLoader classLoader target.getClass().getClassLoader();//目标对象实现的所有接口的class类型数组Class?[] interfaces target.getClass().getInterfaces();//使用匿名内部类来实现接口InvocationHandler invocationHandler new InvocationHandler(){Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** 参数* 第一个参数代理对象* 第二个参数需要重写的目标对象的方法* 第三个参数method方法里面的参数*///方法调用之前输出日志System.out.println([动态代理日志]method.getName()参数 Arrays.toString(args));//调用目标方法Object result method.invoke(target, args);//方法调用之后输出日志System.out.println([动态代理日志]method.getName()结果 result);return result;}};return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);}}测试类
package com.crj.spring6.aop;public class TestCal {public static void main(String[] args) {//创建代理对象ProxyFactory proxyFactory new ProxyFactory(new CalculatorImpl());//获取代理对象Calaulator proxy (Calaulator)proxyFactory.getProxy();proxy.add(1,2);}
}1.2 AOP概念 AOP(Aspect Oriented Programming)是一种设计思想是软件设计领域中的面向切面编程它是面向对象编程的一种补充和完善它以预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能。利用AOP可以对业务逻辑的各个部分进行隔离从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性同时提高了开发的效率使程序是松耦合的。
相关术语
横切关注点
分散在各个模块中解决同一类问题的或者说是从每个方法种抽取出来的同类非核心也无就乘坐横切关注点比如日志管理、用户验证等。换句话说有几个附加功能就有几个横切关注点。
通知增强
每一个横切关注点上要做的事情都需要写一个方法来实现这样的方法就叫通知方法有五种通知类型
前置通知在被代理的目标方法前执行返回通知在被代理的目标方法成功结束后执行异常通知在被代理的目标方法异常结束后执行后置通知在被代理的目标方法最终结束后执行环绕通知使用try..catch.finally结构围绕整个被代理的目标方法包括上面四种通知对应的所有位置
切面
封装通知方法的类
目标
被代理的目标对象
代理
向目标对象应用通知之后创建的代理对象
连接点
允许使用通知的地方
切入点 定位连接点的方式。每个类的方法中都包含多个连接点所以连接点是类中客观存在的事物从逻辑上来说。如果把连接点看作数据库中的记录那么切入点就是查询记录的SQL语句。Spring的AOP技术可以通过切入点定位到特定的连接点。通俗说要实际去增强的方法切点通过org.springframework.aop.Pointcut接口进行描述它使用类和方法作为连接点的查询条件。 二、基于注解方式实现AOP
我们知晓AOP面向切面编程的底层也是通过动态代理来实现的所以对于动态代理我们除了了解基本的代理分类之外还需要明确AOP种如何使用动态代理的方式来实现AOP。
2.1 动态代理分类和依赖引入 AOP动态代理有两种JDK动态代理和CGlib动态代理分别对应有接口和无接口的动态代理。JDK动态代理会生成接口实现类的代理对象而CGlib动态代理是通过继承被代理对象并重写的方式实现的。 pom.xml文件
项目的环境中即pom.xml文件中需要引入aop和aspects依赖。
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.crj/groupIdartifactIdspring6/artifactIdversion1.0-SNAPSHOT/versionpackagingpom/packagingmodulesmodulespring-first/modulemodulespring6-ioc-xml/modulemodulespring6-ioc-annotation/modulemodulespring6-aop/module/modulespropertiesmaven.compiler.source17/maven.compiler.sourcemaven.compiler.target17/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/properties!-- 此处引入spring依赖--dependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion6.0.2/version/dependency!-- 引入junit依赖--dependencygroupIdorg.junit.jupiter/groupIdartifactIdjunit-jupiter-api/artifactIdversion5.6.3/version/dependency!-- 引入log4j2的依赖--dependencygroupIdorg.apache.logging.log4j/groupIdartifactIdlog4j-core/artifactIdversion2.19.0/version/dependencydependencygroupIdorg.apache.logging.log4j/groupIdartifactIdlog4j-slf4j2-impl/artifactIdversion2.19.0/version/dependencydependencygroupIdjakarta.annotation/groupIdartifactIdjakarta.annotation-api/artifactIdversion2.1.1/version/dependency!--spring aop依赖--dependencygroupIdorg.springframework/groupIdartifactIdspring-aop/artifactIdversion6.0.2/version/dependency!--spring aspects依赖--dependencygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion6.0.2/version/dependency/dependencies
/project
除了环境总配置之外还需要在项目中resources目录下的xml文件中配置开启组件扫描和aspectj自动代理。
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!--开启组件扫描--context:component-scan base-packagecom.crj.spring6.aop.annotationAop/context:component-scan!--开启aspectj自动代理为目标对象生成代理--aop:aspectj-autoproxy/aop:aspectj-autoproxy
/beans
2.2 切入点表达式以及五种通知类型
在配置完环境依赖后我们需要在切面类中定义通知的类型并设置切入点Java中同个定义了几个注解来实现设置通知类型
前置通知 Before(value “切入点表达式”)返回通知 AfterReturning异常通知 AfterThrowing后置通知 After()环绕通知 Around()
需要注意切入点表达式的结构execution(权限修饰符 方法返回值类型 切入点方法的全类名.方法名方法参数列表))
前置通知
package com.crj.spring6.aop.annotationAop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;//切面类
Aspect //切面类
Component //IOC容器
public class LogAspect {//设置切入点和通知类型//通知类型// 前置通知 Before(value “切入点表达式”)// 返回通知 AfterReturning// 异常通知 AfterThrowing// 后置通知 After()// 环绕通知 Around()//前置通知Before(value execution(public int com.crj.spring6.aop.annotationAop.CalculatorImpl.add(..)))public void beforeMethod(JoinPoint joinPoint){String methodName joinPoint.getSignature().getName();Object[] args joinPoint.getArgs();System.out.println(Logger-.-前置通知方法名methodName,参数 Arrays.toString(args));}
}返回通知
返回通知中我们在注解中可以通过returning属性获取执行目标增强方法后的返回值。
package com.crj.spring6.aop.annotationAop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;//切面类
Aspect //切面类
Component //IOC容器
public class LogAspect {//返回通知AfterReturning(value execution(* com.crj.spring6.aop.annotationAop.CalculatorImpl.*(..)),returning result)public void afterReturnMethods(JoinPoint joinPoint,Object result){//增强方法的名字String methodName joinPoint.getSignature().getName();//增强方法的参数Object[] args joinPoint.getArgs();//增强方法的返回值 resultSystem.out.println(Logger-.-返回通知方法名methodName,参数 Arrays.toString(args)目标方法的返回值result);}}异常通知 在异常通知中可以通过throwing属性获得目标增强方法执行异常抛出的信息。
package com.crj.spring6.aop.annotationAop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;//切面类
Aspect //切面类
Component //IOC容器
public class LogAspect {//异常通知AfterThrowing(value execution(* com.crj.spring6.aop.annotationAop.CalculatorImpl.*(..)),throwing ex)public void afterThrowingMethod(JoinPoint joinPoint,Throwable ex){String methodName joinPoint.getSignature().getName();Object[] args joinPoint.getArgs();System.out.println(Logger-.-异常通知方法名methodName,参数 Arrays.toString(args)异常方法的信息ex);}
}环绕通知
环绕通知需要注意的是Around注解下的方法参数类型选择的是ProceedingJoinPoint借助该类型对象的proceed的方法来调用目标函数。
package com.crj.spring6.aop.annotationAop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;//切面类
Aspect //切面类
Component //IOC容器
public class LogAspect {//环绕通知Around(value execution(* com.crj.spring6.aop.annotationAop.CalculatorImpl.*(..)))public Object aroundMethod(ProceedingJoinPoint joinPoint){String methodName joinPoint.getSignature().getName();Object[] args joinPoint.getArgs();String argString Arrays.toString(args);Object result null;try{System.out.println(环绕通知——目标方法之前执行);//调用目标方法result joinPoint.proceed();System.out.println(环绕通知——目标方法返回值之后执行);}catch (Throwable throwable){System.out.println(环绕通知——目标方法出现异常之后执行);}finally {System.out.println(环绕通知——后置通知);}return result;}
}2.3 重用切入点表达式
通过上述的四个通知例子我们发现切入点表达式除了在一些特殊的场景需求下其余的情况总是一样的为了避免代码的冗余我们可以通过重用切入点表达式来提高程序的可复用性。
//前置通知
Before(value Pointcut())
public void beforeMethod(JoinPoint joinPoint){String methodName joinPoint.getSignature().getName();Object[] args joinPoint.getArgs();System.out.println(Logger-.-前置通知方法名methodName,参数 Arrays.toString(args));
} Pointcut(value execution(* com.crj.spring6.aop.annotationAop.CalculatorImpl.*(..)))
public void pointCut(){} 注意上述是在同一个切面中的定义在不同的切面中的话需要在通知的切入点表达式加上重用函数的全类名 2.4 切面的优先级
相同目标方法上同时存在多个切面时可以通过切面的优先级控制切面的内外嵌套顺序
优先级高的切面外面优先级低的切面里面
主要是通过使用Order注解来设定切面的优先级
Order(较小的数)优先级高Order(较大的数)优先级低 三、基于XML方式实现AOP
这里只需要将前面的注解部分去掉并通过一个xml文件来配置好切面类的设置即可。
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!--开启组件扫描--context:component-scan base-packagecom.crj.spring6.aop.xmlaop/context:component-scan!--配置AOP的五种通知类型--aop:config!--配置切面类--aop:aspect reflogAspect!--配置切入点--aop:pointcut idpointcut expressionexecution(* com.crj.spring6.aop.annotationAop.CalculatorImpl.*(..))/!--配置五种通知类型--!--前置--aop:before methodbeforeMethod pointcut-refpointcut/aop:before!--后置--aop:after methodaroundMethod pointcut-refpointcut/aop:after!--返回--aop:after-returning methodafterReturnMethods returningresult pointcut-refpointcut/aop:after-returning!--异常--aop:after-throwing methodafterThrowingMethod throwingex pointcut-refpointcut/aop:after-throwing!--环绕--aop:around methodaroundMethod pointcut-refpointcut/aop:around/aop:aspect/aop:config
/beans 总结 哈哈学到这里荔枝总算把最最重要的部分学完了马上就可以上手项目实操了真实有点迫不及待呢哈哈哈哈哈哈~~~希望荔枝梳理的会对有需要的小伙伴有帮助接下来荔枝可能会继续学习和梳理并总结产出在暑假结束后荔枝也会对项目进行总结和复盘希望那时的荔枝会离大佬更进一步哈哈哈哈哈哈。 今朝已然成为过去明日依然向往未来我是小荔枝在技术成长的路上与你相伴码文不易麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~