• 11 - Spring AOP介绍与使用2 - 简单配置


    2、Spring AOP 的简单配置 

    (1)添加pom依赖

     完整:

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0modelVersion>
    6. <groupId>com.zhoulzgroupId>
    7. <artifactId>spring_aop_studyartifactId>
    8. <version>1.0-SNAPSHOTversion>
    9. <dependencies>
    10. <dependency>
    11. <groupId>org.springframeworkgroupId>
    12. <artifactId>spring-contextartifactId>
    13. <version>5.2.3.RELEASEversion>
    14. dependency>
    15. <dependency>
    16. <groupId>junitgroupId>
    17. <artifactId>junitartifactId>
    18. <version>4.12version>
    19. <scope>testscope>
    20. dependency>
    21. <dependency>
    22. <groupId>org.springframeworkgroupId>
    23. <artifactId>spring-aopartifactId>
    24. <version>5.2.3.RELEASEversion>
    25. dependency>
    26. <dependency>
    27. <groupId>cglibgroupId>
    28. <artifactId>cglibartifactId>
    29. <version>3.3.0version>
    30. dependency>
    31. <dependency>
    32. <groupId>org.aspectjgroupId>
    33. <artifactId>aspectjweaverartifactId>
    34. <version>1.9.5version>
    35. dependency>
    36. <dependency>
    37. <groupId>aopalliancegroupId>
    38. <artifactId>aopallianceartifactId>
    39. <version>1.0version>
    40. dependency>
    41. <dependency>
    42. <groupId>org.springframeworkgroupId>
    43. <artifactId>spring-aspectsartifactId>
    44. <version>5.2.3.RELEASEversion>
    45. dependency>
    46. dependencies>
    47. project>

    (2)编写配置

    首先进行pom依赖的添加,然后是xml配置文件的配置:applicatonContext.xml

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xmlns:aop="http://www.springframework.org/schema/aop"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans
    7. http://www.springframework.org/schema/beans/spring-beans.xsd
    8. http://www.springframework.org/schema/context
    9. http://www.springframework.org/schema/context/spring-context.xsd
    10. http://www.springframework.org/schema/aop
    11. http://www.springframework.org/schema/aop/spring-aop.xsd
    12. ">
    13. <context:component-scan base-package="com.zhoulz">context:component-scan>
    14. <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    15. beans>

    添加注解,见 LogUtil:

    1. package com.zhoulz.util;
    2. import org.aspectj.lang.annotation.*;
    3. import org.springframework.stereotype.Component;
    4. import java.lang.reflect.Method;
    5. import java.util.Arrays;
    6. @Aspect
    7. @Component
    8. public class LogUtil {
    9. /**
    10. * 通知注解有以下几种类型:
    11. * @Before:前置通知,在方法执行之前完成
    12. * @After:后置通知,在方法执行之后执行
    13. * @AfterReturning:返回通知,在返回结果之后运行
    14. * @AfterThrowing:异常通知,出现异常的时候使用
    15. * @Around:环绕通知
    16. *
    17. * 注意:在方法 的参数列表中不要随便添加参数值,会有异常信息
    18. * */
    19. @Before("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    20. public static void start(){ //方法里面暂时不用传入参数
    21. System.out.println("方法开始执行,参数是:" );
    22. }
    23. @AfterReturning("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    24. public static void stop(){
    25. System.out.println("方法执行结束,结果是:" );
    26. }
    27. @AfterThrowing("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    28. public static void logException(){
    29. System.out.println("方法抛出异常:");
    30. }
    31. @After("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    32. public static void logFinally(){
    33. System.out.println("方法执行结束。。。。over");
    34. }
    35. }
    /**
     * 通知注解有以下几种类型:
     * @Before:前置通知,在方法执行之前完成
     * @After:后置通知,在方法执行之后执行
     * @AfterReturning:返回通知,在返回结果之后运行
     * @AfterThrowing:异常通知,出现异常的时候使用
     * @Around:环绕通知
     *
     * 注意:在方法 的参数列表中不要随便添加参数值,会有异常信息
     *
     * 切入点表达式:
     *  最精确的匹配方式: —— 但是太死板了(别的方法就用不了了)
     *      execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))
     * 在实际的生产环境中,更多的时候是使用通配符的方式: “*”、“..”
     *   * :
     *      1、可以用来匹配一个或多个字符:
     *          execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,Integer))
     *          execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,Integer))
     *      2、匹配任意类型的参数:
     *          execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))
     *      3、* 在进行匹配的时候,只能匹配一层路径,不能匹配多层
     *          execution(public Integer com.zhoulz.service.*.*.M*Calculator*.*(Integer,*))
     *      4、* 不能够匹配访问修饰符,如果不确定访问修饰符是什么,可以直接省略不写
     *            execution(Integer com.zhoulz.service.M*Calculator.*(Integer,*)) —— 省略了public
     *      5、返回值可以使用*来代替,但是不能空着不写
     *           execution(* com.zhoulz.service.M*Calculator.*(Integer,*))
     *  .. :
     *      1、可以匹配多个参数,任意类型
     *          execution(public Integer com.zhoulz..M*Calculator*.*(..))
     *      2、可以匹配多层路径,
     *          execution(* com.zhoulz..M*Calculator.*(..))
     *
     *  最偷懒的方式:
     *           execution(* *(..))
     *
     *   如果表达式是以*开头,那么可以代替所有:
     *                      execution(* com.*(..)) —— 不可以
     *                      execution(* com..*(..)) —— 可以
     *                      execution(* com.*.*(..)) —— 可以
     *
     *  使用通配符的时候不是越简洁越好,更多的是要选择符合要求或者符合规则的匹配方式,
     *  此时就要求在定义标识符的时候必须要遵守项目规范
     *
     *  在使用 表达式的时候还支持逻辑运算符:
     *      && : 多个条件必须同时满足;
     *      || : 多个条件只要满足其中一个即可;
     *      !  : 取反,处理这种情况的其他都满足;
     *          如:execution(public Integer com.zhoulz.service.MyCalculator.*(..)) && execution(* *(..))
     *              !execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))
     *
     * 通知的正常执行顺序:
     * 如果正常执行:@Before —> @After —> @AfterReturning
     * 如果异常结束:@Before —> @After —> @AfterThrowing
     *
     * 如果想要在方法中获取对应的参数或者方法参数等信息的时候,必须要使用JoinPoint对象,并且此参数必须是第一个
     *  getSignature()、getArgs()、signature.getName()
     *         如果方法中有返回值,那么必须要在注解中添加returning = "result",这个result必须要和参数列表中的参数名称保持一致
     *         如果需要添加异常信息,那么在注解中要添加throwing = "e",这个e必须要和参数列表中的参数名称保持一致
     *     如果想要添加其他参数,必须要添加args(参数列表),ArgName(参数列表)
     *
     *     通知方法(即下面的start、stop等方法)在定义的时候有没有什么特殊的要求?
     *          通知方法在定义的时候对访问修饰符、返回值类型都没有明确的要求,
     *          但是要注意,参数不能随便添加
     *
     *  环绕通知:
     *      环绕通知在执行的时候是优于普通通知的
     *      如果是正常结束,那么执行的顺序是:
     *               环绕前置通知— @Before—环绕后置通知—环绕返回通知—@After—@AfterReturning
     *      如果是异常结束,那么执行的顺序是:
     *              环绕前置通知— @Before—环绕异常通知—环绕返回通知—@After—@AfterReturning
     *              (注意:异常时,外部(即环绕通知外的通知)没有异常通知@AfterThrowing了,即,没有异常捕获了)
     *      即;如果出现异常的时候,在环绕通知中解决了,那么普通通知是接受不到异常的,
     *         如果想让普通通知接收到,需要进行抛出:在环绕通知中加上throw throwable
     *         然后执行顺序是:
     *              环绕前置通知— @Before—环绕异常通知—环绕返回通知—@After—@AfterThrowing
     *
     *  当应用程序中包含多个切面的时候,具体的执行顺序是怎样的?
     *      :按照切面类的首字母进行排序操作,按照字典序A->Z
     *       如果需要人为的规定顺序,可以在切面上添加@Order注解,同时可以添加具体的值
     *       值越小越优先 1 —> 2147483647
     * */

    代码示例: 代码说明见上面

    LogUti:

    1. package com.zhoulz.util;
    2. import org.aspectj.lang.JoinPoint;
    3. import org.aspectj.lang.ProceedingJoinPoint;
    4. import org.aspectj.lang.Signature;
    5. import org.aspectj.lang.annotation.*;
    6. import org.springframework.core.annotation.Order;
    7. import org.springframework.stereotype.Component;
    8. import java.lang.reflect.Method;
    9. import java.util.Arrays;
    10. @Aspect
    11. @Component
    12. @Order(100)
    13. public class LogUtil {
    14. /**
    15. * 代码说明见上面
    16. *
    17. * */
    18. //如果有多个匹配的表达式相同,能否做抽象?可以
    19. // : 定义一个没有返回值的空方法,给该方法添加@Pointcut注解,后续在使用(匹配表达式)的时候直接调用该方法名称
    20. // 此处的方法只是起一个声明的作用,能够供内部的其他通知方法进行调用
    21. @Pointcut("execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))")
    22. public void myPointCut(){} // —— 然后后面用的时候直接写:myPointCut() 即可
    23. //还可以定义多个
    24. @Pointcut("execution(* *(..))")
    25. public void myPointCut2(){}
    26. @Before("execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))")
    27. //public static void start(Method method,Object ... args){ //原来的写法 //方法里面暂时不用传入参数
    28. public static void start(JoinPoint joinPoint){
    29. //获取方法签名
    30. Signature signature = joinPoint.getSignature();
    31. //获取方法参数信息
    32. Object[] args = joinPoint.getArgs();
    33. //获取方法名称
    34. //System.out.println(signature.getName());
    35. System.out.println("Log----"+signature.getName()+"方法开始执行,参数是:" +Arrays.asList(args));
    36. }
    37. //@AfterReturning(value = "execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))",returning = "result")
    38. //匹配表达式进行了抽象,见上面的 myPointCut()方法
    39. @AfterReturning(value = "myPointCut()",returning = "result")
    40. public static void stop(JoinPoint joinPoint,Object result){ // —— 怎么加结果result?:上面的通知注解要改动
    41. Signature signature = joinPoint.getSignature();
    42. System.out.println("Log----"+signature.getName()+"方法执行结束,结果是:" +result);
    43. }
    44. @AfterThrowing(value = "myPointCut()",throwing = "e")
    45. public static void logException(JoinPoint joinPoint,Exception e){
    46. Signature signature = joinPoint.getSignature();
    47. System.out.println("Log----"+signature.getName()+"方法抛出异常:" + e.getMessage());
    48. }
    49. @After("myPointCut2()")
    50. public static void logFinally(JoinPoint joinPoint){
    51. Signature signature = joinPoint.getSignature();
    52. System.out.println("Log----"+signature.getName()+"方法执行结束。。。。over");
    53. }
    54. /* //环绕通知 —— 把上面4个通知任意拿过来执行
    55. @Around("myPointCut2()")
    56. public Object around(ProceedingJoinPoint pjp) throws Throwable { //需要传参
    57. Signature signature = pjp.getSignature();
    58. Object[] args = pjp.getArgs();
    59. Object result = null;
    60. try {
    61. System.out.println("环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
    62. //通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
    63. result = pjp.proceed(args);
    64. result = 100;
    65. System.out.println("环绕通知stop:"+signature.getName()+"方法执行结束了");
    66. } catch (Throwable throwable) {
    67. //throwable.printStackTrace();
    68. System.out.println("环绕异常通知:"+signature.getName()+"出现异常了");
    69. throw throwable;
    70. }finally {
    71. System.out.println("环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
    72. }
    73. return result;
    74. }*/
    75. }

     再创建一个切面类:SecurityUtil

    1. package com.zhoulz.util;
    2. import org.aspectj.lang.JoinPoint;
    3. import org.aspectj.lang.ProceedingJoinPoint;
    4. import org.aspectj.lang.Signature;
    5. import org.aspectj.lang.annotation.*;
    6. import org.springframework.core.annotation.Order;
    7. import org.springframework.stereotype.Component;
    8. import java.util.Arrays;
    9. @Aspect
    10. @Component
    11. @Order(100)
    12. public class SecurityUtil {
    13. //如果有多个匹配的表达式相同,能否做抽象?可以
    14. // : 定义一个没有返回值的空方法,给该方法添加@Pointcut注解,后续在使用(匹配表达式)的时候直接调用该方法名称
    15. // 此处的方法只是起一个声明的作用,能够供内部的其他通知方法进行调用
    16. @Pointcut("execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))")
    17. public void myPointCut(){} // —— 然后后面用的时候直接写:myPointCut() 即可
    18. //还可以定义多个
    19. @Pointcut("execution(* *(..))")
    20. public void myPointCut2(){}
    21. @Before("execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))")
    22. //public static void start(Method method,Object ... args){ //原来的写法 //方法里面暂时不用传入参数
    23. public static void start(JoinPoint joinPoint){
    24. //获取方法签名
    25. Signature signature = joinPoint.getSignature();
    26. //获取方法参数信息
    27. Object[] args = joinPoint.getArgs();
    28. //获取方法名称
    29. //System.out.println(signature.getName());
    30. System.out.println("Security----"+signature.getName()+"方法开始执行,参数是:" +Arrays.asList(args));
    31. }
    32. //@AfterReturning(value = "execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))",returning = "result")
    33. //匹配表达式进行了抽象,见上面的 myPointCut()方法
    34. @AfterReturning(value = "myPointCut()",returning = "result")
    35. public static void stop(JoinPoint joinPoint, Object result){ // —— 怎么加结果result?:上面的通知注解要改动
    36. Signature signature = joinPoint.getSignature();
    37. System.out.println("Security----"+signature.getName()+"方法执行结束,结果是:" +result);
    38. }
    39. @AfterThrowing(value = "myPointCut()",throwing = "e")
    40. public static void logException(JoinPoint joinPoint,Exception e){
    41. Signature signature = joinPoint.getSignature();
    42. System.out.println("Security----"+signature.getName()+"方法抛出异常:" + e.getMessage());
    43. }
    44. @After("myPointCut2()")
    45. public static void logFinally(JoinPoint joinPoint){
    46. Signature signature = joinPoint.getSignature();
    47. System.out.println("Security----"+signature.getName()+"方法执行结束。。。。over");
    48. }
    49. /*//环绕通知 —— 把上面4个通知任意拿过来执行
    50. @Around("myPointCut2()")
    51. public Object around(ProceedingJoinPoint pjp) throws Throwable { //需要传参
    52. Signature signature = pjp.getSignature();
    53. Object[] args = pjp.getArgs();
    54. Object result = null;
    55. try {
    56. System.out.println("环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
    57. //通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
    58. result = pjp.proceed(args);
    59. result = 100;
    60. System.out.println("环绕通知stop:"+signature.getName()+"方法执行结束了");
    61. } catch (Throwable throwable) {
    62. //throwable.printStackTrace();
    63. System.out.println("环绕异常通知:"+signature.getName()+"出现异常了");
    64. throw throwable;
    65. }finally {
    66. System.out.println("环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
    67. }
    68. return result;
    69. }*/
    70. }

    Calculator 接口:

    1. package com.zhoulz.service;
    2. import org.springframework.stereotype.Service;
    3. //@Service
    4. public interface Calculator {
    5. public Integer add(Integer i,Integer j) throws NoSuchMethodException;
    6. public Integer sub(Integer i,Integer j) throws NoSuchMethodException;
    7. public Integer mul(Integer i,Integer j) throws NoSuchMethodException;
    8. public Integer div(Integer i,Integer j) throws NoSuchMethodException;
    9. }

    实现类:MyCalculator ——  注意,这里取消了对Calculator 接口 的继承(所以不是其实现类了)

    1. package com.zhoulz.service;
    2. import com.zhoulz.util.LogUtil;
    3. import org.springframework.stereotype.Service;
    4. import java.lang.reflect.Method;
    5. @Service
    6. public class MyCalculator /*implements Calculator */{
    7. public Integer add(Integer i, Integer j) throws NoSuchMethodException {
    8. //现在,想添加日志的输出功能,怎么做?
    9. //最简单的做法:在每行代码里面都做一个最基本的输出 —— 但是这样做效率太低了
    10. //System.out.prIntegerln("add方法开始执行,参数是:" + i + "----" + j );
    11. //改进: —— 定义一个LogUtil类(包含start()方法和stop()方法)
    12. //通过反射
    13. /*Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
    14. //然后add传入到下面的方法中 —— 下面的都同理
    15. LogUtil.start(add,i,j);*/
    16. Integer result = i + j;
    17. //System.out.prIntegerln("add方法执行结束,结果是:" + result);
    18. //LogUtil.stop(add,result);
    19. return result;
    20. }
    21. public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
    22. //System.out.prIntegerln("sub方法开始执行,参数是:" + i + "----" + j );
    23. /*Method sub = MyCalculator.class.getMethod("sub", Integer.class, Integer.class);
    24. LogUtil.start(sub,i,j);*/
    25. Integer result = i - j;
    26. //System.out.prIntegerln("sub方法执行结束,结果是:" + result);
    27. //LogUtil.stop(sub,result);
    28. return result;
    29. }
    30. public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
    31. //System.out.prIntegerln("mul方法开始执行,参数是:" + i + "----" + j );
    32. //Method mul = MyCalculator.class.getMethod("mul", Integer.class, Integer.class);
    33. //LogUtil.start(mul,i,j);
    34. Integer result = i * j;
    35. //System.out.prIntegerln("mul方法执行结束,结果是:" + result);
    36. //LogUtil.stop(mul,result);
    37. return result;
    38. }
    39. public Integer div(Integer i, Integer j) throws NoSuchMethodException {
    40. //System.out.prIntegerln("div方法开始执行,参数是:" + i + "----" + j );
    41. //Method div = MyCalculator.class.getMethod("div", Integer.class, Integer.class);
    42. //LogUtil.start(div,i,j);
    43. Integer result = i / j;
    44. //System.out.prIntegerln("div方法执行结束,结果是:" + result);
    45. //LogUtil.stop(div,result);
    46. return result;
    47. }
    48. //再加一个方法
    49. public Integer show(Integer i, Double j){
    50. System.out.println("show ........");
    51. return i;
    52. }
    53. }

    测试类:

    1. import com.zhoulz.service.Calculator;
    2. import com.zhoulz.service.MyCalculator;
    3. import org.junit.Test;
    4. import org.springframework.context.support.ClassPathXmlApplicationContext;
    5. public class MyTest {
    6. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    7. @Test
    8. public void test01() throws NoSuchMethodException {
    9. //ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    10. //Calculator calculator = context.getBean("myCalculator", Calculator.class);
    11. //或者直接写
    12. //Calculator calculator = context.getBean(Calculator.class);
    13. //取消MyCalculator对Calculator的继承,再测试 —— 结果正常(即没有接口实现的话,则用的是cglib进行动态代理)
    14. MyCalculator calculator = context.getBean(MyCalculator.class);
    15. calculator.add(10,2);
    16. // calculator.sub(10,2);
    17. calculator.div(10,0);
    18. calculator.show(10,2.5);
    19. //System.out.println(calculator.getClass()); //class com.sun.proxy.$Proxy24
    20. //取消继承后,结果为:class com.zhoulz.service.MyCalculator$$EnhancerBySpringCGLIB$$77df863d
    21. }
    22. @Test
    23. public void test02() throws NoSuchMethodException {
    24. MyCalculator calculator = context.getBean(MyCalculator.class);
    25. //calculator.add(10,2);
    26. calculator.div(10,0);
    27. }
    28. }

    结果:

    Security----div方法开始执行,参数是:[10, 2]
    Log----div方法开始执行,参数是:[10, 2]
    Log----div方法执行结束。。。。over
    Log----div方法执行结束,结果是:5
    Security----div方法执行结束。。。。over
    Security----div方法执行结束,结果是:5

  • 相关阅读:
    SQL Server对象类型(5)——4.5. 同义词(Synonym)
    PLC攻击(一):应用层攻击
    WEB网络渗透的基础知识
    【系统架构设计师考试大纲】
    链路状态路由协议 OSPF (三)
    css3-超链接伪类、列表、背景渐变
    “华为杯”研究生数学建模竞赛2015年-【华为杯】B题:数据的多流形结构分析(续)(附python代码实现))
    七、矩阵的初等变换
    行业追踪,2023-09-07
    配置MyBatis(不用Maven)
  • 原文地址:https://blog.csdn.net/zhoulizhengZ/article/details/127668569