1. spring AOP
1.1 AOP介绍
面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,利用aop可以对业务逻辑的各个部分之间进行隔离,使得业务逻辑个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
总结: spring中AOP利用代理对象在不修改源代码条件下,对方法进行扩展.
1.2 aop入门案例
1.2.1 导入jar包
<!--引入AOPjar包文件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.2.2 编辑切面类
注解:
1.@Aspect 标识当前类是切面类
2.@Pointcut 标识切入点表达式
3.@Before 通知注解
4.@EnableAspectAutoProxy
package com.jt.demo2.aop;
//AOP 面向切面编程 1年开发
//知识铺垫: 切面 = 动态代理+方法的扩展 后期被AOP API封装
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 TxAspect{
//编码: 切面=切入点表达式+通知方法
@Pointcut("bean(userServiceImpl)")
public void pointcut(){}
@Before("pointcut()")
public void before(){
System.out.println("aop入门案例");
}
}
1.2.3 编辑配置类
@Configuration//标识这个类是配置类
@ComponentScan("com.jt.demo2") //包扫描注解
@EnableAspectJAutoProxy //让aop有效
public class SpringConfig{
}
1.2.4 编辑测试类
package com.jt.demo2;
import com.jt.demo2.config.SpringConfig;
import com.jt.demo2.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestAspect {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//看上去: 是目标对象 AOP:内部已经封装动态代理规则
//实际上: 已经被AOP封装,是一个代理对象
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getClass());
//代理机制:可以扩展方法
userService.addUser();
}
}
1.3 切入点表达式
作用: 配置aop为谁创建代理对象
1.3.1 bean标签
语法:@Pointcut(“bean(对象的id)”) 只能匹配固定的类/对象 一个
@Pointcut("bean(userServiceImpl)")
public void pointCut(){
}
1.3.2 within表达式
语法:@Pointcut(“within(包名.类名)”) 可以匹配多个类
@Pointcut("within(com.jt.demo2.service.UserServiceImpl)")
public void pointCut(){
}
@Pointcut("within(com.jt.demo2.service.*)") //多个
public void pointCut(){
}
说明: bean和within 都是粗粒度的匹配, 匹配的是类(类中还有方法/方法可以重载)
1.3.3 execution表达式
语法:@Pointcut(“execution(返回值类型 包名.类名.方法名(参数列表))”)
@Pointcut("execution(void com.jt.demo2.service.UserServiceImpl.addUser())")
public void pointCut(){
}
//要求: 拦截service.包下的所有的类,任意方法, 任意参数 返回值类型任意!!
@Pointcut("execution(* com.jt.demo2.service..*.*(..))")
public void pointCut(){
}
1.3.4 @annotation
1.自定义注解
package com.jt.demo2.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 TX {
}
2.使用注解
3.配置aop切面
@Pointcut("@annotation(com.jt.demo2.anno.TX)")
public void pointCut(){
}
1.4 通知方法
作用:当用户满足切入点表达式,才会执行扩展方法
注意事项:通知必须与切入点表达式绑定
1.4.1 通知类型 ----- 前置通知 @Before
作用:在目标方法运行前执行
//通知方法: 对原有方法的扩展
@Before("pointCut()")
public void before(){
System.out.println("AOP入门案例");
}
1.4.2 通知类型—后置通知 @AfterReturning
1.作用:在目标方法执行之后执行
@AfterReturning("pointCut()")
public void afterReturning(){
System.out.println("AOP后置通知");
}
2.动态获取返回值
/**
* 记录目标方法的返回值结果
* returning: 后置通知获取返回值的属性
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void afterReturning(Object result){
System.out.println("方法的返回值:"+result);
}
1.4.3 通知类型—异常通知 @AfterThrowing
1.作用:抛出异常时,异常通知执行
@AfterThrowing("pointCut()")
public void afterThrowing(){
System.out.println("抛出异常!!!");
}
2.获取异常信息
/**
* 说明: 如果程序执行抛出异常,则可以由异常通知进行记录
* throwing:抛出异常的属性
*/
@AfterThrowing(value = "pointCut()",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint,Exception exception){
//exception.printStackTrace();
System.out.println("异常信息"+exception.getMessage());
}
1.4.4 通知类型—最终通知 @After
作用:不管目标方法执行是否正确,都要执行,一般不用
@After("pointCut()")
public void after(){
System.out.println("我是最终通知!!");
}
1.4.5 通知类型— 环绕通知 @Around
说明:
1.前四大通知类型不能控制目标方法的运行,所以一般在使用时一般记录程序的运行状态
2.咋目标方法执行前后都要执行,只有环绕通知才可以控制目标方法是否执行,使用最多的通知方法
/**
* 注意事项: 环绕通知中必须添加参数,并且必须位于第一位
* 用法:
* Proceed with the next advice or target method invocation
* 1.如果有下一个通知,则执行下一个通知,
* 2.如果没有下一个通知则执行目标方法
* @return
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知开始");
Object result = joinPoint.proceed();
System.out.println("环绕通知结束");
return result;
}
1.5 关于连接点joinPoint的用法
说明: 当用户调用方法时,如果该方法被切入点表达式匹配,则spring会为其对象创建代理 则将这个方法成为 连接点
1.5.1 连接点的作用
通过joinPoint对象可以获取当前方法的所有参数,可以供用户进行记录
//通知方法: 对原有方法的扩展
@Before("pointCut()")
public void before(JoinPoint joinPoint){
//1.获取目标对象的类型
Class targetClass = joinPoint.getTarget().getClass();
//2.获取目标对象名称
String targetName = joinPoint.getSignature().getDeclaringTypeName();
//3.获取目标方法的名称
String methodName = joinPoint.getSignature().getName();
//4.获取参数数据
Object[] args = joinPoint.getArgs();
System.out.println("目标对象类型:"+targetClass);
System.out.println("目标对象名称:"+targetName);
System.out.println("目标方法名称:"+methodName);
System.out.println("参数名称:"+ Arrays.toString(args));
}
1.5.2 关于joinPoint和ProceedingJoinPoint区别
1.ProceedingJoinPoint只适用于环绕通知,因为只有环绕通知,才能控制目标方法的运行
2.JoinPoint 适用于其他的四大通知类型,可以用来记录运行的数据
3.ProceedingJoinPoint 中有特殊的方法proceed();
4.如果使用"JoinPoint" 则必须位于参数的第一位
1.6 关于切面顺序研究
spring中的aop如果有多个环绕通知参与,则采用嵌套结构进行调用,执行的顺序与代码的顺序有关
调用流程示意图:
控制Aop执行顺序: @Order 注解
1.7 spring中默认代理策略
1.7.1 知识回顾:
1. 动态代理方式
1.JDK动态代理 被代理者必须有接口
2.CGLib代理 不管有无接口都可以为其创建代理对象. 代理对象是被代理者的子类(继承)
1.7.2 spring创建代理的规则
如果目标对象有接口,默认采用 jdk动态代理模式
如果目标对象没有接口. 默认采用 cdlib动态代理
1.7.3 代理模式的修改
如果需要改变默认的模式,则只能强制使用cglib动态代理模式
1.8 AOP企业中使用说明
工作中几乎不写AOP原始代码! 都会有更高级的API进行扩展
使用场景:
1.面试
2.开发源代码框架时使用
3.进行业务优化时,可能会用到AOP