Spring(三)(spring三大特性)

  本篇文章为你整理了Spring(三)(spring三大特性)的详细内容,包含有spring三级缓存如何解决循环依赖 spring三大特性 spring三大核心思想 spring三剑客 Spring(三),希望能帮助你了解 Spring(三)。

   !-- spring-aspects begin --

   !-- maven项目中,使用aop的AspectJ框架,只需要增加此依赖,自动添加依赖aspectjweaver(包含了aspectjrt)--

   dependency

   groupId org.springframework /groupId

   artifactId spring-aspects /artifactId

   version ${spring.version} /version

   /dependency

   !-- spring-aspects end --

   /dependencies

  

 

 

  2.2 切入点

  通知需要在哪些方法上执行的表达式;(可以唯一匹配或模糊匹配);

  2.2.1 唯一匹配

  

execution(public int com.kgc.spring.aspectj.ArithmeticCalculator.add(int ,int ))

 

  

 

  execution(修饰符 返回值类型 方法全类名)

  2.2.2 模糊匹配

  

execution(* com.kgc.spring.aspectj.*.*(..)

 

  

 

  通用切入点表达式含义:

  
2.3.1 JoinPoint 对象

  JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象。
 

  常用api:

  
Signature getSignature();

  获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息

  
2.3.2 ProceedingJoinPoint对象

  ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中 添加了 两个方法.

  
//起别名,方便单元测试,根据别名,从容器中获取

  public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

   @Override

   public int add(int m, int n) {

   return m + n;

   @Override

   public int sub(int m, int n) {

   return m - n;

   @Override

   public int nul(int m, int n) {

   return m*n;

   @Override

   public int div(int m, int n) {

   System.out.println("====== 执行 div 方法 ======");

   return m/n;

  

 

 

  2.4.3 @Before 前置通知

  在目标方法执行前,自动执行此方法(通过代理实现);

  

@Component //声明为一个普通的组件,放入spring的容器中,才可以生效

 

  @Aspect //声明当前类是 一个切面

  public class LogAspect {

   //重用切入点表达式

   @Pointcut( "execution(* com.kgc.spring.aspectj.*.*(..))")

   public void joinPointcut(){}

   //前置通知 @Before

   @Before("joinPointcut()")

   public void logBeforeMethod(JoinPoint joinPoint){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   //获取通知作用的目标方法入参,返回的是参数值数组

   Object[] methodParams = joinPoint.getArgs();

   System.out.println("------ LogAspect "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");

  

 

  2.5 配置文件

  spring-aop.xml

  

 ?xml version="1.0" encoding="UTF-8"? 

 

   beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:aop="http://www.springframework.org/schema/aop"

   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"

   !-- 组件 --

   context:component-scan base-package="com.kgc.spring.aspectj" /context:component-scan

   !-- 基于注解方式实现Aspect切面 --

   !-- 作用:当spring的容器检测到此配置项,会自动将Aspect切面匹配的目标对象,放入容器,默认使用的是jdk的动态代理 --

   aop:aspectj-autoproxy /aop:aspectj-autoproxy

  
//从容器中获取计算器的实例对象

   ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);

   System.out.println(arithmeticCalculator.getClass());

   //调用切面作用的目标方法,执行操作,

   int result = arithmeticCalculator.div(20, 10);

   System.out.println("****** 通过单元测试,计算结果:"+result +" ******");

  

 

  测试结果

  

class com.sun.proxy.$Proxy15

 

  ------ LogAspect div 方法,入参:[20, 10] ------

  ====== 执行 div 方法 ======

  ****** 通过单元测试,计算结果:2 ******

  

 

  3、后置通知

  3.1 @After

  目标方法发执行之后,自动执行;

  特点:

  后置通知无法获取目标方法的返回值;

  它的执行跟目标方法是否抛出异常无关,不影响此方法的执行;

  

@After("joinPointcut()")

 

  public void logAfterMethod(JoinPoint joinPoint){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   //获取通知作用的目标方法入参,返回的是参数值数组

   Object[] methodParams = joinPoint.getArgs();

   System.out.println("------ LogAspect "+methodName+" 方法执行结束 ------");

  

 

  3.2 测试

  3.2.1 无异常

  

@Test

 

  public void testSpringAopAspectj(){

   //从容器中获取计算器的实例对象

   ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);

   //调用切面作用的目标方法,执行操作,

   int result = arithmeticCalculator.div(20, 10);

   System.out.println("****** 通过单元测试,计算结果:"+result +" ******");

  

 

  测试结果

  

====== 执行 div 方法 ======

 

  ------ LogAspect div 方法执行结束 ------

  ****** 通过单元测试,计算结果:2 ******

  

 

  3.2.2 有异常

  

@Test

 

  public void testSpringAopAspectj(){

   //从容器中获取计算器的实例对象

   ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);

   //调用切面作用的目标方法,执行操作,

   int result = arithmeticCalculator.div(20, 0);

   System.out.println("****** 通过单元测试,计算结果:"+result +" ******");

  

 

  测试结果

  

====== 执行 div 方法 ======

 

  ------ LogAspect div 方法执行结束 ------ //有异常也会执行后置通知

  java.lang.ArithmeticException: / by zero

  

 

  4、返回通知

  4.1 @AfterReturning

  目标方法返回结果后自动执行,可以获取目标方法的返回值;

  但是要求@AfterReturning必须增加属性returning,指定一个参数名;

  且此参数名必须跟通知方法的一个形参名一致,用于接收返回值;

  

@AfterReturning(value = "joinPointcut()",returning = "result")

 

  public void afterReturningMethod(JoinPoint joinPoint,Object result){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   System.out.println("------ LogAspect "+methodName+" 方法,执行结果:"+ result +" ------");

  

 

  4.2 测试

  测试结果

  

====== 执行 div 方法 ======

 

  ------ LogAspect div 方法,返回结果:2 ------

  ****** 通过单元测试,计算结果:2 ******

  

 

  5、异常抛出通知

  5.1 @AfterThrowing

  异常抛出通知 @AfterThrowing ,在目标方法抛出异常后,可以获取目标方法发生异常后抛出的异常信息;

  但是要求 @AfterThrowing 必须增加属性 throwing,指定一个参数名;

  且此参数名必须跟通知方法的一个形参名一致,用于接收异常;

  

@AfterThrowing(value = "joinPointcut()",throwing = "ex")

 

  public void logAfterThrowingMethod(JoinPoint joinPoint,Exception ex){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   System.out.println("------ LogAspect "+methodName+" 方法,执行异常信息:"+ ex.getMessage() +" ------");

  

 

  5.2 测试

  

@Test

 

  public void testSpringAopAspectj(){

   //从容器中获取计算器的实例对象

   ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);

   System.out.println(arithmeticCalculator.getClass());

   //调用切面作用的目标方法,执行操作,

   int result = arithmeticCalculator.div(20, 0);

   System.out.println("****** 通过单元测试,计算结果:"+result +" ******");

  

 

  测试结果

  

====== 执行 div 方法 ======

 

  ------ LogAspect div 方法,执行异常信息:/ by zero ------

  java.lang.ArithmeticException: / by zero

  

 

  6、环绕通知

  6.1 @Around

  环绕通知 @Around,可以看作是上面四种通知的结合体,一般不建议跟单个的通知共用(防止冲突失效);

  作用:可以让开发人员在环绕通知的处理方法中根据不同也业务逻辑,决定是否发起对目标方法的调用;

  

@Around(value = "joinPointcut()")

 

  public Object logAroundMethod(ProceedingJoinPoint joinPoint){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   //定义获取目标方法的返回值变量

   Object result = null;

   try{

   //实现前置通知功能

   System.out.println("------ LogAspect "+methodName+" 方法 Around通知,入参:"+ Arrays.toString(joinPoint.getArgs()) +" ------");

   //手动调用原目标方法(业务中决定,是否对核心方法方法发起调用)

   result = joinPoint.proceed();

   }catch (Throwable tx){

   //实现异常抛出通知功能

   System.out.println("------ LogAspect "+methodName+" 方法 Around通知,执行异常信息:"+ tx.getMessage() +" ------");

   }finally {

   //实现后置通知功能

   System.out.println("------ LogAspect "+methodName+" 方法 Around通知,执行结束 ------");

   //实现返回通知功能

   System.out.println("------ LogAspect "+methodName+" 方法 Around通知,执行结果:"+ result +" ------");

   return result;

  

 

  6.2 测试

  6.2.1 测试结果,无异常

  

//调用切面作用的目标方法,执行操作,

 

  int result = arithmeticCalculator.div(20, 10);

  

 

  

class com.sun.proxy.$Proxy13

 

  ------ LogAspect div 方法 Around通知,入参:[20, 10] ------

  ====== 执行 div 方法 ======

  ------ LogAspect div 方法 Around通知,执行结束 ------

  ------ LogAspect div 方法 Around通知,返回结果:2 ------

  ****** 通过单元测试,计算结果:2 ******

  

 

  6.2.2 测试结果,有异常

  

//调用切面作用的目标方法,执行操作,

 

  int result = arithmeticCalculator.div(20, 0);

  

 

  

class com.sun.proxy.$Proxy13

 

  ------ LogAspect div 方法 Around通知,入参:[20, 0] ------

  ====== 执行 div 方法 ======

  ------ LogAspect div 方法 Around通知,执行异常信息:/ by zero ------

  ------ LogAspect div 方法 Around通知,执行结束 ------

  ------ LogAspect div 方法 Around通知,返回结果:null ------

  

 

  6.2.3 测试结果 不调用 原方法

  

//调用切面作用的目标方法,执行操作,

 

  int result = arithmeticCalculator.div(20, 0);

  //(业务中决定,是否对核心方法发起调用)

  //不调用核心方法

  //result = joinPoint.proceed();

  

 

  

------ LogAspect div 方法 Around通知,入参:[20, 10] ------

 

  ------ LogAspect div 方法 Around通知,执行结束 ------

  ------ LogAspect div 方法 Around通知,返回结果:null ------

  

 

  7、切入点优先级

  当有多个前置通知时,我们想自定义前置通知顺序:使用@Order(int)

  指定切面优先级,一般都是int型整数,值越小,优先级越高**(默认值 2^31 - 1 最低优先级);

  7.1 多个前置通知

  logBeforeMethod

  

@Before("joinPointcut()")

 

  public void logBeforeMethod(JoinPoint joinPoint){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   //获取通知作用的目标方法入参,返回的是参数值数组

   Object[] methodParams = joinPoint.getArgs();

   System.out.println("------ LogAspectBeforeMethod "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");

  

 

  verifyBeforeMethod

  

@Component

 

  @Aspect

  public class VerifyParamAspect {

   @Before("com.kgc.spring.aspectj.LogAspect.joinPointcut()")

   public void verifyBeforeMethod( JoinPoint joinPoint){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   //获取通知作用的目标方法入参,返回的是参数值数组

   Object[] methodParams = joinPoint.getArgs();

   System.out.println("------ verifyBeforeMethod "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");

  

 

  7.2 测试(默认)

  

@Test

 

  public void testVerifyParamAspect(){

   //从容器中获取计算器的实例对象

   ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);

   System.out.println(arithmeticCalculator.getClass());

   //调用切面作用的目标方法,执行操作,

   int result = arithmeticCalculator.div(20, 10);

   System.out.println("****** 通过单元测试,计算结果:"+result +" ******");

  

 

  测试结果

  

------ LogAspectBeforeMethod div 方法,入参:[20, 10] ------ //LogAspectBeforeMethod 先执行

 

  ------ verifyBeforeMethod div 方法,入参:[20, 10] ------

  ====== 执行 div 方法 ======

  ****** 通过单元测试,计算结果:2 ******

  

 

  7.3 测试(自定义优先级)

  

@Component

 

  @Aspect

  @Order(1) //指定切面优先级,一般都是int型整数,值越小,优先级越高(默认值 2^31 - 1)

  public class VerifyParamAspect {

   @Before("com.kgc.spring.aspectj.LogAspect.joinPointcut()")

   public void verifyBeforeMethod( JoinPoint joinPoint){

   //获取通知作用的目标方法名

   String methodName = joinPoint.getSignature().getName();

   //获取通知作用的目标方法入参,返回的是参数值数组

   Object[] methodParams = joinPoint.getArgs();

   System.out.println("------ verifyBeforeMethod "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");

  

 

  测试结果

  

------ verifyBeforeMethod div 方法,入参:[20, 10] ------ //优先级高的切面中的verifyBeforeMethod,先执行

 

  ------ LogAspectBeforeMethod div 方法,入参:[20, 10] ------

  ====== 执行 div 方法 ======

  ****** 通过单元测试,计算结果:2 ******

  

 

  以上就是Spring(三)(spring三大特性)的详细内容,想要了解更多 Spring(三)的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: