1、AOP指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。aop底层是动态代理。
1 package com.bie.config; 2 3 import org.aspectj.lang.annotation.Aspect; 4 import org.springframework.context.annotation.Bean; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.context.annotation.EnableAspectJAutoProxy; 7 8 import com.bie.aop.LogAspect; 9 import com.bie.aop.MathCalculator; 10 11 /** 12 * 13 * 14 * @Title: SpringApplicationConfig.java 15 * @Package com.bie.config 16 * @Description: TODO 17 * @author biehl 18 * @date 2019年12月9日 19 * @version V1.0 20 * 21 * 22 * 1、AOP指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。aop底层是动态代理。 23 * 24 * 第一步、导入aop模块,spring-aspects。 25 * 第二步、定义业务逻辑类,MathCalculator。在业务逻辑运行的时候打印日志(方法运行之前,方法运行结束,方法出现异常打印日志。)。 26 * 第三步、定义一个日志切面类LogAspect,切面类里面的方法需要动态感知到类的方法MathCalculator.div运行到那一步了。然后开始执行。 27 * 通知方法介绍: 28 * 前置通知:logStart,在目标方法运行之前运行。注解@Before 29 * 后置通知:logEnd,在目标方法运行结束之后运行,无论方法正常结束还是异常结束。注解@After 30 * 返回通知:logReturn,在目标方法运行正常返回之后运行。注解@AfterRetruning 31 * 异常通知:logException,在目标方法运行异常返回之后运行。注解@AfterThrowing 32 * 环绕通知:动态代理。手动推进目标方法运行。 注解@Around 33 * 第四步、根据通知方法给切面类的目标方法标注何时何地运行。标注通知注解。 34 * 第五步、将切面类,和业务逻辑层(目标方法所在的类)都加如到容器中。使用@Bean注解或者@Component注解即可。 35 * 第六步、告诉Spring那个类是切面类。给切面类加一个@Aspect注解。 36 * 第七步、开启基于注解版的aop模式切面功能。启用该注解@EnableAspectJAutoProxy,启动AspectJ自动代理。 37 * @EnableXXX开启某项功能。 38 * 39 * AOP切面编程重要的三步走: 40 * 第一步、将业务逻辑组件和切面类都加入到容器中。告诉spring容器那个是切面类(@Aspect)。 41 * 第二步、在切面类上的每个通知方法上标注通知注解,告诉spring何时何地运行(注意切入点表达式的书写)。 42 * 第三步、开启基于注解版的aop模式切面功能。启用该注解@EnableAspectJAutoProxy,启动AspectJ自动代理。 43 */ 44 // @Configuration告诉Spring这是一个配置类,相当于bean.xml配置文件。 45 @Configuration 46 @EnableAspectJAutoProxy 47 public class SpringApplicationConfig15 { 48 49 /** 50 * 将目标类加入到容器中 51 * 52 * @return 53 */ 54 @Bean 55 public MathCalculator mathCalculator() { 56 return new MathCalculator(); 57 } 58 59 60 /** 61 * 将切面类加入到容器中 62 * 63 * @return 64 */ 65 @Bean 66 public LogAspect logAspect() { 67 return new LogAspect(); 68 } 69 70 }
开发业务逻辑层类,如下所示:
1 package com.bie.aop; 2 3 /** 4 * 5 * 6 * @Title: MathCalculator.java 7 * @Package com.bie.aop 8 * @Description: TODO 9 * @author biehl 10 * @date 2019年12月12日 11 * @version V1.0 12 * 13 */ 14 public class MathCalculator { 15 16 /** 17 * 18 * @param i 19 * @param j 20 * @return 21 */ 22 public int div(int i, int j) { 23 System.out.println(i + " / " + j + " = " + i / j); 24 return i / j; 25 } 26 27 }
开发切面类,如下所示:
1 package com.bie.aop; 2 3 import java.util.Arrays; 4 5 import org.aspectj.lang.JoinPoint; 6 import org.aspectj.lang.annotation.After; 7 import org.aspectj.lang.annotation.AfterReturning; 8 import org.aspectj.lang.annotation.AfterThrowing; 9 import org.aspectj.lang.annotation.Aspect; 10 import org.aspectj.lang.annotation.Before; 11 import org.aspectj.lang.annotation.Pointcut; 12 13 /** 14 * 15 * 16 * @Title: LogAspect.java 17 * @Package com.bie.aop 18 * @Description: TODO 19 * @author biehl 20 * @date 2019年12月12日 21 * @version V1.0 22 * 23 * 日志切面类 24 */ 25 @Aspect // 告诉Spring容器,当前类是切面类 26 public class LogAspect { 27 28 /** 29 * 抽取公共的切入点表达式 30 * 31 * 方式一,如果是本类引用,直接使用即可,@Before("pointCut()") 32 * 方式二,其他的切面类引入,加上类路径即可@Before("com.bie.aop.LogAspect.pointCut()") 33 */ 34 @Pointcut(value = "execution(* com.bie.aop.MathCalculator.*(..))") 35 public void pointCut() { 36 } 37 38 /** 39 * JoinPoint参数必须出现在参数的第一位 40 * 41 * 方法运行前执行该方法。@Before目标方法之前切入。 42 * 43 * 切入点表达式,public int com.bie.aop.MathCalculator.div(int int)。 指定在那个方法切入。 44 * 45 * Signature签名是方法的签名。 46 */ 47 @Before(value = "pointCut()") 48 public void logStart(JoinPoint joinPoint) { 49 // 获取到方法参数列表 50 Object[] args = joinPoint.getArgs(); 51 // 获取到方法名称 52 String methodName = joinPoint.getSignature().getName(); 53 System.out.println(methodName + "()方法名称, @Before除法方法执行了......参数列表是{" + Arrays.asList(args) + "}"); 54 } 55 56 /** 57 * JoinPoint参数必须出现在参数的第一位 58 * 59 * 方法运行后执行该方法 60 * 61 * @After(value = "public int com.bie.aop.MathCalculator.*(..)") 62 * 代表了MathCalculator该类的所有方法。所有方法的任意参数。 63 * 64 */ 65 @After(value = "pointCut()") 66 public void logEnd(JoinPoint joinPoint) { 67 // 获取到方法参数列表 68 Object[] args = joinPoint.getArgs(); 69 // 获取到方法名称 70 String methodName = joinPoint.getSignature().getName(); 71 System.out.println(methodName + "()方法名称, @After除法方法结束了......参数列表是{" + Arrays.asList(args) + "}"); 72 } 73 74 /** 75 * JoinPoint参数必须出现在参数的第一位 76 * 77 * returning指定谁来封装返回值。 78 * 79 * @param result 80 * 参数封装返回值 81 */ 82 @AfterReturning(value = "pointCut()", returning = "result") 83 public void logReturn(JoinPoint joinPoint, Object result) { 84 // 获取到方法名称 85 String methodName = joinPoint.getSignature().getName(); 86 System.out.println(methodName + "()方法名称, @AfterReturning除法方法正常返回了......运行结果是{" + result + "}"); 87 } 88 89 /** 90 * JoinPoint参数必须出现在参数的第一位 91 * 92 * throwing指定谁来封装返回值。 93 * 94 * @param exception 95 * 参数封装返回值 96 */ 97 @AfterThrowing(value = "pointCut()", throwing = "exception") 98 public void logException(JoinPoint joinPoint, Exception exception) { 99 // 获取到方法名称 100 String methodName = joinPoint.getSignature().getName(); 101 System.out.println(methodName + "()方法名称, @AfterThrowing除法方法异常了......异常信息是{" + exception + "}"); 102 } 103 104 }
测试主类,如下所示:
1 package com.bie.main; 2 3 import org.springframework.beans.BeansException; 4 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 5 6 import com.bie.aop.MathCalculator; 7 import com.bie.config.SpringApplicationConfig15; 8 9 /** 10 * 11 * 12 * @Title: SpringApplication.java 13 * @Package com.bie.main 14 * @Description: TODO 15 * @author biehl 16 * @date 2019年12月9日 17 * @version V1.0 18 * 19 */ 20 public class SpringApplication { 21 22 public static void main(String[] args) { 23 // 获取到注解配置类 24 AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringApplicationConfig15.class); 25 26 try { 27 // 使用容器中获取到目标对象,而不是自己创建对象。 28 MathCalculator mathCalculator = ac.getBean(MathCalculator.class); 29 mathCalculator.div(42, 2); 30 } catch (BeansException e) { 31 e.printStackTrace(); 32 } 33 34 // 调用关闭的时候,调用destory销毁方法。 35 ac.close(); 36 } 37 38 }