目录
一、 Spring AOP介绍
1.1 创建项目
1.1.1 层级代码结构
1.1.2 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.2 AOP
1.2.1 AOP介绍
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.
1.2.2 AOP入门案例
1.2.2.1 编辑配置类
@Configuration
@ComponentScan("com.jt") //包扫描
@EnableAspectJAutoProxy //开启AOP
public class SpringConfig {
}
1.2.2.2 编辑切面类
package com.jt.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component //将给类交给Spring容器管理 !!!
@Aspect //标识该类是一个切面
public class SpringAOP {
/**
* 知识回顾: AOP利用动态代理扩展目标方法.
* 公式: 切面 = 切入点表达式 + 通知方法
* 切入点表达式: 如果目标对象满足切入点表达式的判断(if),则spring自动为其创建代理对象
* 通知方法: 对目标方法进行扩展的封装方法.
* 目标对象的bean的ID: userServiceImpl
* 切入点表达式:
* 1. bean("bean的ID")
* AOP规则: 如果目标对象满足切入点表达式,则执行通知方法
*/
@Pointcut("bean(userServiceImpl)")
public void pointcut(){
}
//前置通知: 在目标方法执行之前执行.
@Before("pointcut()")
public void before(){
System.out.println("我是前置通知!!!!");
}
}
1.2.2.3 编辑测试类
package com.jt;
import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSpring {
@Test
public void demo1(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//本来是目标对象,只不过与切入点表达式匹配,则动态生成代理对象.
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getClass());
//由于是代理对象,所以方法可以扩展
userService.addUser();
}
}
1.3 常见通知类型
- 前置通知: 在目标方法执行之前执行.
@Before("pointcut()")
- 后置通知: 在目标方法执行之后执行
@AfterReturning("pointcut()")
- 异常通知: 目标方法执行报错时,执行该通知
@AfterThrowing("pointcut()")
- 最终通知: 目标方法之后都要执行的通知
@After("pointcut()")
- 重点掌握 环绕通知: 在目标方法执行前后都要执行. 控制目标方法
@Around("pointcut()")
package com.jt.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component //将给类交给Spring容器管理 !!!
@Aspect //标识该类是一个切面
public class SpringAOP {
/**
* 知识回顾: AOP利用动态代理扩展目标方法.
* 公式: 切面 = 切入点表达式 + 通知方法
* 切入点表达式: 如果目标对象满足切入点表达式的判断(if),则spring自动为其创建代理对象
* 通知方法: 对目标方法进行扩展的封装方法.
* 目标对象的bean的ID: userServiceImpl
* 切入点表达式:
* 1. bean("bean的ID")
* AOP规则: 如果目标对象满足切入点表达式,则执行通知方法
*/
@Pointcut("bean(userServiceImpl)")
public void pointcut(){
}
//1.前置通知: 在目标方法执行之前执行.
@Before("pointcut()")
public void before(){
System.out.println("我是前置通知!!!!");
}
//2.后置通知: 在目标方法执行之后执行
@AfterReturning("pointcut()")
public void afterReturn(){
System.out.println("我是后置通知!!!!");
}
//3.异常通知: 目标方法执行报错时,执行该通知
@AfterThrowing("pointcut()")
public void afterThrowing(){
System.out.println("我是异常通知!!!!");
}
//4.最终通知: 目标方法之后都要执行的通知
@After("pointcut()")
public void after(){
System.out.println("最终通知都要执行");
}
//5.重点掌握 环绕通知: 在目标方法执行前后都要执行. 控制目标方法
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知执行前!!!!");
//底层调用动态代理的invoke方法,执行目标方法
Object result = joinPoint.proceed();
System.out.println("环绕通知执行后!!!!");
return result;
}
}
1.3.1 正常运行结束展示
正常运行结束展示:
1.3.2 异常运行结果展示
定义 int a = 1/0;
使程序报错测试异常通知
异常运行结果展示:
1.4 切入点表达式
1.4.1 bean表达式
说明: 根据bean的ID拦截指定的对象.
@Pointcut("bean(userServiceImpl)")
public void pointcut(){
}
1.4.2 within表达式
说明: 按照类型匹配. 可以使用通配符*号
语法:
//1. 只拦截UserServiceImpl的类
@Pointcut("within(com.jt.service.UserServiceImpl)")
//2. 拦截com.jt.service下的一级的类.
@Pointcut("within(com.jt.service.*)")
//3. 拦截com.jt.service下所有包下的所有类
@Pointcut("within(com.jt.service..*)")
//4. 拦截com.任意包.service下所有包下的所有类
@Pointcut("within(com.*.service..*)")
说明: 上述的2种操作方法 粒度较粗, 一般情况下不用.
1.4.3 execution表达式
作用: 粒度比较细,可以按照方法参数进行匹配.
语法:
//语法: @Pointcut("execution(返回值类型 包名.类名.方法名(参数列表))")
//1. 按照类型方法匹配
@Pointcut("execution(* com.jt.service.UserServiceImpl.addUser())")
//2. 拦截Service层下一级的类,但不排除下一级子菜单
@Pointcut("within(com.jt.Service.*)")
//3. 要求返回值任意, com.jt.service包下的所有的子孙类中的任意方法的任意参数要求拦截.
@Pointcut("execution(* com.jt.service..*.*(..))")
//4. 要求返回值任意, com.jt.service包下的所有的子孙类中的add开头的方法并且参数1个是int类型 进行拦截
@Pointcut("execution(* com.jt.service..*.add*(int))")
//5. 拦截下一级所有类,包括下一级子菜单
/@Pointcut("within(com.chuci.springdemo009_aop.Service..*)")
//6. 拦截com 任以包 service下所有包下所有类
@Pointcut("within(com.*.springdemo009_aop.Service..*)")
1.4.4 @annotation表达式
作用: 可以根据用户的自定义注解进行拦截
package com.chuci.springdemo009_aop.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //注解运行时有效
@Target(ElementType.METHOD) //注解标识 方法
public @interface ChuCi {
}
使用环绕通知进行拦截测试
使用自定义注解进行标记
测试代码及结果
@Test
public void test004(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService4 userService = context.getBean(UserService4.class);
System.out.println(userService.getClass());;
userService.addUser();
userService.delUser();
}
1.5 关于通知的讲解
1.5.1 关于AOP 通知的用法
- 第一类:
- @Before(“pointcut()”)
- @AfterReturning(“pointcut()”)
- @AfterThrowing(“pointcut()”)
- @After(“pointcut()”)
可以记录程序执行的各个过程. 为日志提供记录.
- 第二类:
- @Around(“pointcut()”) 环绕通知可以控制目标方法是否执行. 环绕通知可以控制业务流转的过程!!!
例子:
- 权限的校验
- 缓存系统
- 异常的处理等
1.5.2 通知中常用API
1.5.2.1 前置通知用法
需求:
- 获取当前的目标对象的类型.
- 获取当前的方法名称
- 获取当前传递的参数.
常见报错: ProceedingJoinPoint is only supported for around advice ProceedingJoinPoint 只能用到环绕通知中.
//1.前置通知: 在目标方法执行之前执行.
@Before("pointcut()")
public void before(JoinPoint joinPoint){//连接点:获取方法中的数据
Class targetClass = joinPoint.getTarget().getClass();
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getSignature().getDeclaringTypeName();
Object[] objs = joinPoint.getArgs();
System.out.println("我是前置通知!!!!");
System.out.println("类型:"+ targetClass);
System.out.println("方法名称:"+methodName);
System.out.println("类的名称:"+className);
System.out.println("方法中携带的参数:"+ Arrays.toString(objs));
}
1.5.2.2 后置通知用法
需求: 记录一下用户目标方法的返回值
说明: 通过属性returning 获取方法的返回值
//2.后置通知: 在目标方法执行之后执行
// 通过returning = "result"属性,获取目标方法的返回值,当作参数传递给result
@AfterReturning(value = "pointcut()",returning = "result")
public void afterReturn(Object result){
System.out.println("我是后置通知!!!!");
System.out.println("用户的返回值为:"+result);
}
1.5.2.3 异常通知用法
说明: 如果用户执行业务方法时,报错了,则可以使用异常通知记录日志.
用法:
//3.异常通知: 目标方法执行报错时,执行该通知
@AfterThrowing(value = "pointcut()",throwing = "exception")
public void afterThrowing(Exception exception){
System.out.println("我是异常通知!!!!");
System.out.println("获取异常信息:"+exception.getMessage());
exception.printStackTrace();
}
1.6 切面排序
说明: 根据@Order注解 实现切面的排序
1.6.1 编辑SpringAOP1
//5.重点掌握 环绕通知: 在目标方法执行前后都要执行. 控制目标方法
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知A执行前!!!!");
//底层调用动态代理的invoke方法,执行目标方法
Object result = joinPoint.proceed();
System.out.println("环绕通知A执行后!!!!");
return result;
}
1.6.2 编辑SpringAOP2
package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component //将类交给spring容器管理
@Aspect //标识切面类
@Order(1) //AOP第一个执行 数字越小越靠前
public class SpringAOP2 {
//通过环绕通知 指定切入点表达式.
@Around("@annotation(com.jt.anno.Lyj)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//1.如果有下一个通知,则执行通知方法,没有通知,则执行目标方法
System.out.println("执行环绕通知B开始");
Object result = joinPoint.proceed();
System.out.println("执行环绕通知B结束");
return result;
}
}
1.7 关于代理对象生成策略说明
默认策略:
- Spring中默认采用的动态代理的规则是JDK代理,如果被代理者没有接口.则自动使用CGLIB
- 如果需要修改为cglib代理,则添加如下代码
@Configuration
@ComponentScan("com.jt") //包扫描
@EnableAspectJAutoProxy(proxyTargetClass = true) //开启AOP
public class SpringConfig {
}
SpringBoot中默认代理模式采用CGLIB代理.如果需要修改为JDK代理则需要修改配置文件即可
1.8 关于Spring总结
-
为什么学习spring框架 让程序设计实现松耦合.
-
什么是面向接口编程 以后对象中的属性一般写接口. java中多态的体现. 属性类型更加的灵活 松耦合.
-
什么是IOC Ioc全称Inversion of Control,即“控制反转”,这是一种设计思想。对象创建的权利由Spring框架完成.由容器管理对象的生命周期.
-
Spring容器启动方式 1. xml方式 2.注解方式
-
什么时候使用工厂模式:
(1). 对象不能直接实例化的时候.
(2). spring框架整合其它第三方框架时使用. FactoryBean -
单例多例问题: 默认条件下是单例模式 @Scope(“prototype”)
-
懒加载规则: 1. 默认条件下 懒加载无效 添加注解@Lazy 有效. 只对单例模式有效.
-
spring生命周期管理 4个过程:
(1). 对象创建
(2). 对象初始化 @PostConstruct
(3). 业务调用
(4). 对象销毁 @PreDestroy -
Spring中依赖注入的注解@Autowired 1.默认按照类型注入 2.可以按照名称注入 @Qualifier(“cat”) @Resource java中的注解.
-
MVC 设计思想 View 视图层 Model业务层 Control 控制层
-
根据MVC设计思想: 层级代码结构 Controller/Service/Mapper(Dao)
-
@Value spring为属性动态赋值 基本类型和String和集合(几乎不用)
-
动态代理 JDK动态代理/CGLib动态代理
-
AOP 面向切面编程 在不改变源码的条件下对方法进行扩展.
-
AOP常见注解
(1). @Aspect 标识切面
(2). @Pointcut 标识切入点表达式 4种写法 2种常用
(3). 五个通知注解
(4). @EnableAspectJAutoProxy(proxyTargetClass = true) //开启AOP
(5). 排序注解 @Order
至此,本节完~~~
上一节:CGB3-day4-JDKProxy&CGLibProxy
下一节:CGB3-day6-Spring MVC入门
此 系 列 以 完 整 记 录 自 己 在 J a v a 学 习 路 的 过 程 此系列以完整记录自己在Java学习路的过程 此系列以完整记录自己在Java学习路的过程