SpringBoot下的Spring——DAY04——动态代理总结、AOP、自定义注解进行拦截、动态获取注解参数、通知方法(内含源代码)
创始人
2024-05-31 13:12:32
0

SpringBoot下的Spring——DAY04——动态代理总结、AOP、自定义注解进行拦截、动态获取注解参数、通知方法(内含源代码)

源代码下载链接地址:https://download.csdn.net/download/weixin_46411355/87549575

目录

  • SpringBoot下的Spring——DAY04——动态代理总结、AOP、自定义注解进行拦截、动态获取注解参数、通知方法(内含源代码)
  • `源代码下载链接地址:`[https://download.csdn.net/download/weixin_46411355/87549575](https://download.csdn.net/download/weixin_46411355/87549575)
  • 1.动态代理总结
    • 1.1 JDK动态代理特点
    • 1.2 CGlib动态代理
      • 1.2.1 CGLib特点说明
    • 1.3 动态代理的作用
  • 2 Spring中的AOP
    • 2.1 AOP介绍
    • 2.2 AOP中专业术语(难点)
    • 2.3 AOP 入门案例
      • 2.3.1 创建一个SpringBoot的module
      • 2.3.1 导入jar包
      • 2.3.2 项目工程结构
      • 2.3.3 配置类
      • 2.3.4 Service层
        • 2.3.4.1 接口
        • 2.3.4.2 实现类
      • 2.3.5 切入点表达式
      • 2.3.6 定义切面类
      • 2.3.7 让AOP生效
      • 2.3.8 编辑测试类
    • 2.4 AOP形象化的比喻
    • 2.5 关于切入点表达式解析
      • 2.5.1 bean标签写法
      • 2.5.2 within表达式
      • 2.5.3 execution表达式
    • 2.6 按照自定义注解进行拦截
      • 2.6.1 自定义注解
      • 2.6.2 切入点表达式写法
      • 2.6.3 在service层实现类UserServiceImpl的addUser()方法上添加自定义的注解
    • 2.7 动态获取注解参数
      • 2.7.1 自定义注解
      • 2.7.2 使用注解
      • 2.8.3 需求
      • 2.8.4 编辑切面类
    • 2.8 通知方法
      • 2.8.1 关于通知方法解析
      • 2.8.2 前置通知案例
      • 2.8.3 后置通知案例
        • 2.8.3.1 添加接口方法
          • 1.编辑接口
          • 2.编辑实现类
        • 2.8.3.2 编辑AOP切面类SpringAOP
        • 2.8.3.3 编辑测试案例
        • 2.8.3.4 测试效果
      • 2.8.4 异常通知案例
        • 2.8.4.1 让service层实现类代码报错
        • 2.8.4.2 异常通知案例
        • 2.8.4.3 测试结果
  • 常用注解

1.动态代理总结

1.1 JDK动态代理特点

  1. 类型名称: class com.sun.proxy.$Proxy9
  2. 要求: 要求被代理者,必须是接口或者是实现类.
  3. JDK代理是java原生提供的API 无需导包.
  4. JDK动态代理在框架的源码中经常使用.
  5. 代理类和被代理类继承相同的接口,所以两者为兄弟关系

1.2 CGlib动态代理

1.2.1 CGLib特点说明

历史原因: JDK动态代理要求必须"有接口",但是某些类它没有接口,则无法使用JDK代理生成代理对象. 所以为了填补知识的空缺,则引入cglib代理.

问题说明: cglib动态代理 要求有无接口都可以创建代理对象. 问题? 如何保证和被代理者"相同"
答案(特点): 要求cglib动态代理继承被代理者.代理对象是被代理者的子类.

代理类和被代理类(目标类)两者是父子关系,代理对象是目标类的子类。

1.3 动态代理的作用

说明1: 一般我们将业务层中的耦合性高的代码,采用动态代理的方式进行解耦.使得程序更加具有扩展性. (业务逻辑的解耦)
说明2: Spring专门针对动态代理的规则.封装了一套API 起名 AOP

2 Spring中的AOP

2.1 AOP介绍

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.

2.2 AOP中专业术语(难点)

1).连接点: 用户可以被扩展的方法
2).切入点: 用户实际扩展的方法
3).通知: 扩展方法的具体实现
4).切面: 将通知应用到切入点的过程

2.3 AOP 入门案例

2.3.1 创建一个SpringBoot的module

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.3.1 导入jar包

org.springframework.bootspring-boot-starter-aop       

2.3.2 项目工程结构

在这里插入图片描述

2.3.3 配置类

SpringConfig.java

package com.jt.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@ComponentScan("com.jt")
@Configuration
public class SpringConfig {}

2.3.4 Service层

2.3.4.1 接口

package com.jt.service;public interface UserService {void addUser();void deleteUser();
}

2.3.4.2 实现类

package com.jt.service;import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Overridepublic void addUser() {System.out.println("完成用户新增");}@Overridepublic void deleteUser() {System.out.println("完成用户删除操作");}
}

2.3.5 切入点表达式

切入点表达式

  • bean(“对象的Id”) 每次拦截,只拦截1个
  • within(“包名.类名”)
  • execution(返回值类型 包名.类名.方法名(参数列表))
  • @annotation(注解的路径)

2.3.6 定义切面类

package com.jt;import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring_AOP {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = applicationContext.getBean(UserService.class);//如果是实现类对象,则方法没有被扩展//如果是代理对象,则方法被扩展 aop有效的System.out.println(userService.getClass());//class com.jt.service.UserServiceImpl$$EnhancerBySpringCGLIB$$baeada27userService.addUser();}
}

2.3.7 让AOP生效

说明: 编辑配置类,添加@EnableAspectJAutoProxy,让AOP机制有效
在这里插入图片描述

package com.jt.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@ComponentScan("com.jt")
@Configuration
@EnableAspectJAutoProxy//让spring中的AOP生效
public class SpringConfig {}

2.3.8 编辑测试类

package com.jt;import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring_AOP {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);//理论值:根据接口获取实现类对象 但是与切入点表达式匹配,为了后续扩展方便,为其创建代理对象UserService userService = applicationContext.getBean(UserService.class);//如果是实现类对象,则方法没有被扩展//如果是代理对象,则方法被扩展 aop有效的(是代理对象)/*getClass()是Object中的方法,不拦截*/System.out.println(userService.getClass());//class com.jt.service.UserServiceImpl$$EnhancerBySpringCGLIB$$baeada27userService.addUser();}
}

2.4 AOP形象化的比喻

说明: AOP是一种抽象的一种概念,看不见/摸不着.所以需要大家对概念有自己的认知.
在这里插入图片描述

2.5 关于切入点表达式解析

2.5.1 bean标签写法

@Pointcut(“bean(userServiceImpl)”) 只匹配ID为userServiceImpl的对象

2.5.2 within表达式

@Pointcut(“within(com.jt.service.*)”) 匹配xx.xx.service下的所有对象

2.5.3 execution表达式

@Pointcut("execution(* com.jt.service..*.*(..))")
拦截返回值类型任意 xx.xx.service包下所有子孙包的所有类的任意方法
@Pointcut("execution(* com.jt.service..*.add*(..))")
拦截返回值类型任意 xx.xx.service包下所有子孙包的所有类.以add开头的方法

在这里插入图片描述

2.6 按照自定义注解进行拦截

2.6.1 自定义注解

package com.jt.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)             //注解对方法有效
@Retention(RetentionPolicy.RUNTIME)     //运行期有效
public @interface MyAnnotation {//注解起标记作用}

2.6.2 切入点表达式写法

在这里插入图片描述

@Pointcut("@annotation(com.jt.anno.MyAnnotation)")public void pointCutMethod(){}

2.6.3 在service层实现类UserServiceImpl的addUser()方法上添加自定义的注解

在这里插入图片描述
测试类运行
在这里插入图片描述

2.7 动态获取注解参数

2.7.1 自定义注解

package com.jt.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Find {int id() default 0;
}

2.7.2 使用注解

在service层的实现类UserServiceImp的addUser()方法上面添加自定义注解@Find(id=101)
在这里插入图片描述

2.8.3 需求

利用前置通知,打印注解中的id值!!!

2.8.4 编辑切面类

/*** 知识点:* 如果切入点表达式只对当前通知有效,则可以按照如下方式编辑* 要求:动态的拦截Find注解,并且要获取Find注解中的参数Id* 难点:动态获取注解的对象!!* 代码解释:*      1.@annoattion(find) 拦截find变量名称对应类型的注解*      2.当匹配该注解后,将注解对象当做参数传递给find*      优势:可以一步到位获取注解的内容,避免了反射的代码*/@Before("@annotation(find)")public void before2(Find find){System.out.println("ID的值为:"+find.id());}

2.8 通知方法

2.8.1 关于通知方法解析

1.前置通知 在目标方法执行之前执行.
2.后置通知 在目标方法执行之后执行.
3.异常通知 在目标方法执行之后抛出异常时执行.
4.最终通知 都要执行的通知
说明: 上述的四大通知一般用于记录程序的运行状态.只做记录.
5.环绕通知 在目标方法执行前后都要执行的通知

2.8.2 前置通知案例

切面类
SpringAOP.java

 /*** 定义通知方法:*  1.前置通知 在目标方法执行之前执行*  2.后置通知 在目标方法执行之后执行*  3.异常通知 在目标方法执行之后抛出异常时执行*  4.最终通知 都要执行的通知*  5.环绕通知 在目标方法执行前后都要执行的通知**记录程序的状态* 1.目标对象的class/类路径 com.jt.xx.xxx.UserServiceImpl* 2.目标对象的方法名* 3.目标对象的方法的参数信息* 4.获取目标对象方法的返回值* 5.获取目标对象执行报错的异常信息*/@Before("pointCutMethod()")public void before(JoinPoint joinPoint){//1.获取目标对象的类型Class targetClass = joinPoint.getTarget().getClass();//2.获取目标对象的路径String path = joinPoint.getSignature().getDeclaringTypeName();//3.获取目标对象的方法名称String methodName = joinPoint.getSignature().getName();//4.获取方法的参数Object[] args = joinPoint.getArgs();System.out.println("类型" + targetClass);System.out.println("类的路径:" + path);System.out.println("方法名:" + methodName);System.out.println("参数:" + Arrays.toString(args));}

运行结果
在这里插入图片描述

2.8.3 后置通知案例

2.8.3.1 添加接口方法

1.编辑接口

在这里插入图片描述

package com.jt.service;public interface UserService {void addUser();void deleteUser();int findCount();//查询总数
}
2.编辑实现类

在这里插入图片描述

package com.jt.service;import com.jt.anno.Find;
import com.jt.anno.MyAnnotation;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Find(id = 101)@MyAnnotation//标记作用@Overridepublic void addUser() {System.out.println("完成用户新增");}@Overridepublic void deleteUser() {System.out.println("完成用户删除操作");}/*** 测试获取返回值的!!!* @return*/@MyAnnotation@Overridepublic int findCount() {return 1000;}
}

2.8.3.2 编辑AOP切面类SpringAOP

在这里插入图片描述

//注意事项:如果多个参数,joinPoint必须位于第一位!!!@AfterReturning(value="pointCutMethod()",returning = "result")public void afterReturn(JoinPoint joinPoint,Object result){//如果需要获取当前的方法信息,则可以通过joinPoint获取
//        System.out.println("我是后置通知");System.out.println("我是后置通知,获取方法的返回值"+result);}

2.8.3.3 编辑测试案例

package com.jt;import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring_AOP02 {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);//理论值:根据接口获取实现类对象 但是与切入点表达式匹配,为了后续扩展方便,为其创建代理对象UserService userService = applicationContext.getBean(UserService.class);//如果是实现类对象,则方法没有被扩展//如果是代理对象,则方法被扩展 aop有效的(是代理对象)/*getClass()是Object中的方法,不拦截*/System.out.println(userService.getClass());//class com.sun.proxy.$Proxy19userService.addUser();userService.findCount();//测试代返回值的方法}
}

2.8.3.4 测试效果

在这里插入图片描述

2.8.4 异常通知案例

2.8.4.1 让service层实现类代码报错

在这里插入图片描述

2.8.4.2 异常通知案例

throwing:获取异常信息,之后进行传递

 //后置通知与异常通知是互斥的,只能有一个@AfterThrowing(value = "pointCutMethod()",throwing = "exception")public void afterThrow(JoinPoint joinPoint,Exception exception){//打印异常//exception.printStackTrace();System.out.println("我是异常通知:"+exception.getMessage());}

2.8.4.3 测试结果

在这里插入图片描述

常用注解

@Configuration 标识当前类是配置类
@ComponentScan 包扫描注解 扫描注解
@Bean 标识该方法的返回值交给Spring容器管理
@Scope 控制多例和单例
@Lazy 懒加载
@PostConstruct 初始化方法
@PreDestroy 销毁方法
@Component 将当前类未来的对象交给容器管理
@Autowired 按照类型进行注入
@Qualifier 按照名称进行注入
@Repository 标识持久层注解
@Service 标识Service层
@Controller 标识Controller层
@Value 为属性赋值 @Value(“${key}”)
@PropertySource 加载指定路径的配置文件properties
@Aspect 标识当前类是一个切面类
@Pointcut 用于定义切入点表达式 表达式写法4种
@EnableAspectJAutoProxy 让AOP的注解有效果
@Before AOP-前置通知
@AfterReturning AOP-后置通知
@AfterThrowing AOP-异常通知
@After AOP-最终通知
@Around AOP-环绕通知

相关内容

热门资讯

122.(leaflet篇)l... 听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
Vue使用pdf-lib为文件... 之前也写过两篇预览pdf的,但是没有加水印,这是链接:Vu...
PyQt5数据库开发1 4.1... 文章目录 前言 步骤/方法 1 使用windows身份登录 2 启用混合登录模式 3 允许远程连接服...
Android studio ... 解决 Android studio 出现“The emulator process for AVD ...
Linux基础命令大全(上) ♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维...
再谈解决“因为文件包含病毒或潜... 前面出了一篇博文专门来解决“因为文件包含病毒或潜在的垃圾软件”的问题,其中第二种方法有...
南京邮电大学通达学院2023c... 题目展示 一.问题描述 实验题目1 定义一个学生类,其中包括如下内容: (1)私有数据成员 ①年龄 ...
PageObject 六大原则 PageObject六大原则: 1.封装服务的方法 2.不要暴露页面的细节 3.通过r...
【Linux网络编程】01:S... Socket多进程 OVERVIEWSocket多进程1.Server2.Client3.bug&...
数据结构刷题(二十五):122... 1.122. 买卖股票的最佳时机 II思路:贪心。把利润分解为每天为单位的维度,然后收...
浏览器事件循环 事件循环 浏览器的进程模型 何为进程? 程序运行需要有它自己专属的内存空间࿰...
8个免费图片/照片压缩工具帮您... 继续查看一些最好的图像压缩工具,以提升用户体验和存储空间以及网站使用支持。 无数图像压...
计算机二级Python备考(2... 目录  一、选择题 1.在Python语言中: 2.知识点 二、基本操作题 1. j...
端电压 相电压 线电压 记得刚接触矢量控制的时候,拿到板子,就赶紧去测各种波形,结...
如何使用Python检测和识别... 车牌检测与识别技术用途广泛,可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计...
带环链表详解 目录 一、什么是环形链表 二、判断是否为环形链表 2.1 具体题目 2.2 具体思路 2.3 思路的...
【C语言进阶:刨根究底字符串函... 本节重点内容: 深入理解strcpy函数的使用学会strcpy函数的模拟实现⚡strc...
Django web开发(一)... 文章目录前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和s...