AOP和springAOP

AOP:面向切面编程
SpringAop底层是动态代理技术(Proxy)

下面讲解在Spring中如何使用AOP

1.添加AOP起步依赖

        <!--AOP起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.写AOP类(AOP类中用Around注释记录的方法一定要有返回值,因为方法本身就应该有返回值)
 

package com.itheima.aop;
/*
* 该类用于统计Service中个方法的运行时间 切面类
* */

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j //日志
@Component // 将当前组件放入spring容器中
@Aspect // 当前类为切面类
public class TimeAspect {

	@Around("execution(* com.itheima.service.impl.*.*(..))") // execution()表示匹配方法
	//Aroud()表示环绕通知
	public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		//1. 获取开始时间
		long beginTime = System.currentTimeMillis();
		//2.运行原方法
		/*
		* ProceedingJoinPoint是Spring AOP提供的一个接口 用来获取原方法信息
		* proceed()运行原方法 返回值为原方法的返回值 因此用Object来接并返回
		* */
		Object result = proceedingJoinPoint.proceed();
		//3.获取结束时间 并计算运行时间
		long end = System.currentTimeMillis();
		log.info("\n"+proceedingJoinPoint.getSignature()+"运行时间:" + (end - beginTime)+"ms");
		return result ;
	}
}

 AOP核心

1.通知类型:

  • @Around:环绕通知 此类注解标注的通知方法在目标方法开始前结束后都被执行 报错最后不会执行 被此类注解标注的通知方法一定要有返回值 一定!
  • @Before:前置通知 此类注解标注的通知方法在目标方法前执行
  • @After:后置通知 此类注解标注的通知方法在目标方法后被执行 无论是否有异常都会执行
  • @AfterReturning:返回后通知 此类注解标注的通知方法在目标方法后被执行,有异常不执行
  • @AfterThrowing:异常后通知 此类注解标注的通知方法在目标方法发生异常后执行

2.@PointCut:该注解的作用是将公共的切点表达式抽取出来 需要用到时引用该切点即可

	// 定义切点 切点为 com.itheima.service.impl包下所有类的所有方法
	@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
	public void pc(){};

3.其他通知类型使用该切点

@Around("pc()") // execution()表示匹配方法

 4.通知执行顺序:当有多个切面的切入点都匹配到了目标方法,目标方法运行时 通知方法的执行顺序:默认按照类名的字符顺序执行
另一实现方法:使用@Order()注解
order中数字越小的在执行前越先执行 在执行后越后执行

5.切入点表达式

切入点表达式有两种形式,第一种是基于 execution()根据方法的签名来匹配,第二种是@annotation基于接口的形式进行匹配。
下面讲解第一种方式

/*
	 * execution()表示匹配方法 要在@pointcut中声明
	 * execution中表达式的基本形式是
	 * 访问修饰符 返回值类型 包名.类名.方法名(参数列表) throws 异常类型
	 * 其中访问修饰符可以省略,实际开发中也省略或者使用通配符来匹配
	 * 包名.类名可以省略,实际开发中不建议省略会导致匹配范围过大
	 * 参数列表中的值要填写清楚 比如Integer要写为java.lang.Integer
	 * throws 异常类型可以省略,实际开发中并不会要求写throws
	 * 例子		访问修饰符  返回值  包名.   			类名    .方法名 (参数列表)  此方法没有抛出异常不需要声明
	 * execution(public void com.itheima.service.DeptService.deleteDept(java.lang.Integer))
	 * */

	/*
	 * execution中可以使用通配符来描述切入点 有两种通配符
	 *  *可以统配单个独立的任意符号
	 * 例子 :execution(* com.*。service.*.update*(*))
	 * 第一个*通配任意返回值类型    第二个*通配任意一个包名
	 * 第三个*通配任意一个类名     第四个*通配任意一个以update开头的方法名称
	 * 第五个*通配任意一个参数且只能是一个
	 *   ..可以通配多个连续的任意符号 可以匹配任意层级的包 或者任意类型 任意个数的参数
	 * 例子 :execution(* com.itheima..Deptservice.*(..))
	 * 第一个*通配任意返回值类型 第一个..通配任意层级的包
	 * 第二个*通配Deptservice下的任意一个方法  第二个..通配任意类型的任意个参数
	 *
	 *
	 * 为满足更繁琐的业务需求也可以使用&& ||  ! 等来描述比较复杂的切入点
	 *  */
	/*
	* 切入点表达式的书写规范
	* 1.所有方法的命名尽可能规范 使可以使用*通配符来更好匹配
	* 2.描述切入点尽量描述接口方法 方便后续修改而不是特定方法
	* 3.在满足业务需求的情况下尽可能缩小切入点的范围 减少不必要的切入点而降低切入点带来的性能损失
	* */
	//切入点的返回类型为任意类型 包含com.itheima.service.impl包下所有类的所有方法 参数形式任意
	@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
	public void pc(){};
	@Around("pc()") // execution()表示匹配方法
	//Aroud()表示环绕通知
	public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		//1. 获取开始时间
		long beginTime = System.currentTimeMillis();
		//2.运行原方法
		/*
		* ProceedingJoinPoint是Spring AOP提供的一个接口 用来获取原方法信息
		* proceed()运行原方法 返回值为原方法的返回值 因此用Object来接并返回
		* */
		Object result = proceedingJoinPoint.proceed();
		//3.获取结束时间 并计算运行时间
		long end = System.currentTimeMillis();
		log.info("\n"+proceedingJoinPoint.getSignature()+"运行时间:" + (end - beginTime)+"ms");
		return result ;
	}

下面讲解第二种 基于接口的切入点表达式  使用方法
1.首先声明一个接口 (接口的名称任意)

package com.itheima.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* Target元注解描述的是该注解能注解的对象为 METHOD类型
* Retention元注解描述的是该注解的生命周期为RUNTIME也就是运行时
* */
@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}

2.在目标方法上注明该注解  一点是方法上

@MyLog
	@Select("select * from emp where username = #{username} and password = #{password}")
	Emp selectByUsernameAndPassword(Emp emp);

3.在切入方法中的Pointcut中声明注解的全类名即可

	@Pointcut("@annotation(com.itheima.aop.MyLog)")
	public void pc(){};

 6.连接点:连接点就是能够被切面的方法

/*
	 * 在Spring中用JoinPoint抽象了连接点
	 * 用JoinPoint可以获取该方法执行的相信息 如目标类名 方法名 方法参数
	 *对于@Around环绕通知 获取连接点只能通过ProceedingJoinPoint类来获取
	 *对于其他四种通知方法 只可以通过JoinPoint来获取抽象连接点
	 *  JoinPoint类的getSignature方法可以获取目标方法签名信息
	 * 	JoinPoint类的getArgs方法可以获取目标方法参数
	 *  JoinPoint类的getTarget方法可以获取目标对象
	 *  JoinPoint类的proceed方法可以令原方法执行
	 *JoinPoint是ProceedingJoinPoint的父类
	 * */
	@Around("pc()")
	public Object recordTime2(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		//1.获取目标类名
		String className = proceedingJoinPoint.getTarget().getClass().getName();
		//2.获取目标方法名
		String MethodName = proceedingJoinPoint.getSignature().getName();
		//3.获取目标方法参数 并封装在了Object[]中
		Object[] args = proceedingJoinPoint.getArgs();
		//4.运行原方法 并将该方法的返回值封装在Object中
		Object result = proceedingJoinPoint.proceed();
		return result ;
	}

	/*
	* 对于其他四种通知方法 只可以通过JoinPoint来获取抽象连接点
	* */
	@Before("pc()")
	public void AspectTest (JoinPoint joinPoint){
		//1.获取目标类名
        String className = JoinPoint.getTarget().getClass().getName();
		//2.获取目标方法名
		String MethodName = joinPoint.getSignature().getName();
		//3.获取目标方法参数 并封装在了Object[]中
		Object[] args = joinPoint.getArgs();
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值