Spring AOP 通知和切入点表达式增强(PROJECT04_DAY01_02)

3. Spring AOP编程增强

3.1 切面通知应用增强

3.1.1 通知类型

在基于Spring AOP编程的过程中,基于AspectJ框架标准,Spring中定义了五种类型的通知(通知描述的是一种扩展业务),它们分别是:

  • 前置通知 (@Before)
  • 返回通知 (@AfterReturning)
  • 异常通知 (@AfterThrowing)
  • 后置通知 (@After)
  • 环绕通知 (@Around) :重点掌握(优先级最高)

3.1.2 通知执行顺序

假如这些通知全部写到一个切面对象中,其执行顺序及过程,如图-8所示:
在这里插入图片描述
图-8

说明:实际项目中可能不会在切面中定义所有的通知,具体定义哪些通知要结合业务进行实现。Spring Boot 2.3.0 版本之后(包含),通知的执行顺序是@Around–>@Before–>@AfterReturning–>@After–>@Around(出异常时是:@Around–>@Before–>@AfterThrowing–>@After–>@Around),不同版本中通知的执行顺序以项目实际运行结果为准。

3.1.3 通知实践过程分析

代码实践分析如下:

@Component
@Aspect
public class SysTimeAspect {
        @Pointcut("bean(sysUserServiceImpl)")
        public void doTime(){}
        
        @Before("doTime()")
        public void doBefore(JoinPoint jp){
                System.out.println("time doBefore()");
        }

        @After("doTime()")
        public void doAfter(){
                System.out.println("time doAfter()");
        }

        /**核心业务正常结束时执行 执行returning*/
        @AfterReturning("doTime()")
        public void doAfterReturning(){
                System.out.println("time doAfterReturning");
        }

        /**核心业务出现异常时执行 执行Throwing*/
        @AfterThrowing("doTime()")
        public void doAfterThrowing(){
                System.out.println("time doAfterThrowing");
        }

        @Around("doTime()")
        public Object doAround(ProceedingJoinPoint jp) throws Throwable{
                System.out.println("doAround.before");
				Object obj=jp.proceed();
				System.out.println("doAround.after");
				return obj;
        }
}

说明:对于@AfterThrowing通知只有在出现异常时才会执行,所以当做一些异常监控时可在此方法中进行代码实现。

课堂练习:定义一个异常监控切面,对目标页面方法进行异常监控,并以日志信息形式输出异常

package com.cy.pj.common.aspect;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class SysExceptionAspect {

        @AfterThrowing(pointcut="bean(*ServiceImpl)",throwing = "e")
        public void doHandleException(JoinPoint jp,Throwable e) {
                MethodSignature ms=(MethodSignature)jp.getSignature();
                log.error("{}'exception msg is {}",ms.getName(),e.getMessage());
        }
}

说明:AfterThrowing中throwing属性的值,需要与它描述的方法的异常参数名相同。

3.2 切入点表达式增强

Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:

在这里插入图片描述
表-1 Spring AOP 中切入点表达式说明

3.2.1 bean表达式(重点)

bean表达式一般应用于类级别,实现粗粒度的切入点定义,案例分析:

  • bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法
  • bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法

说明:bean表达式内部的对象是由 Spring 容器管理的一个bean对象,表达式内部的名字应该是 Spring 容器中某个bean的name。

3.2.2 within表达式(了解)

within表达式应用于类级别,实现粗粒度的切入点表达式定义,案例分析:

  • within(“aop.service.UserServiceImpl”)指定当前包中这个类内部的所有方法
  • within(“aop.service.*”) 指定当前目录下的所有类的所有方法
  • within(“aop.service. .*”) 指定当前目录以及子目录中类的所有方法

3.2.3 execution表达式(了解)

execution表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析:
语法:execution(返回值类型 包名.类名.方法名(参数列表))。

  • execution(void aop.service.UserServiceImpl.addUser())匹配addUser方法
  • execution(void aop.service.PersonServiceImpl.addUser(String))方法参数必须为String的addUser方法
  • execution(* aop.service. .* .*(. .)) 万能配置

3.2.4 @annotation表达式(重点)

@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析

  • @annotation(anno.RequiredLog) 匹配有此注解描述的方法
  • @annotation(anno.RequiredCache) 匹配有此注解描述的方法

其中:@RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日志扩展操作。

课堂练习:定义Cache相关切面,使用注解表达式定义切入点,并使用此注解对需要使用Cache的业务方法进行描述,代码分析如下:

第一步:定义注解@RequiredCache和@ClearCache

package com.cy.pj.common.annotation;
/**
 * 自定义注解,一个特殊的类,所有注解都默认继承Annotation接口
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredCache {
   //...
}

package com.cy.pj.common.annotation;
/**
 * 自定义注解,一个特殊的类,所有注解都默认继承Annotation接口
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClearCache {
   //...
}

第二步:定义SysCacheAspect切面对象

package com.cy.pj.common.aspect;
@Aspect
@Component
public class SysCacheAspect {
           
	private Map<Object,Object> cache = new ConcurrentHashMap<>();
	
	@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")
	public void doCache() {}
	
	@Pointcut("@annotation(com.cy.pj.common.annotation.ClearCache)")
	public void doClear() {}
	
	@AfterReturning("doClear()")
	public void doAfterReturning() {
		cache.clear();
	}
	
	@Around("doCache()")
	public Object doAround(ProceedingJoinPoint jp) throws Throwable {
		System.out.println("Get Data from Cache");
		Object result = cache.get("dataKey");
		if(result!=null) return result;
		result = jp.proceed();
		System.out.println("Put Data from Cache");
		cache.put("dataKey", result);
		return result;
	}
}

第三步:使用@RequiredCache注解对特定业务目标对象中的查询方法进行描述。

@RequiredCache
    @Override
    public List<Map<String, Object>> findObjects() {.
            return list;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值