SpringAop的使用
AOP是什么
与OOP对比,面向切面,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。
AOP的应用场景
- 日志记录
- 权限验证
- 效率检查
- 事务管理
- exception
springAop的底层技术
JDK动态代理 | CGLIB代理 | |
---|---|---|
编译时期的织入还是运行时期的织入? | 运行时期织入 | 运行时期织入 |
初始化时期织入还是获取对象时期织入? | 初始化时期织入 | 初始化时期织入 |
springAop和AspectJ的关系
Aop是一种概念springAop
AspectJ都是Aop的实现,SpringAop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是自己的
1、pring AOP提供两种编程风格@AspectJ support ------------>利用aspectj的注解Schema-based 2、AOP support ----------->xml aop:config 命名空间
证明:spring,通过源 码分析了,我们可以知道spring底层使用的是JDK或者CGLIB来完成的代理,并且在官网上spring给出了aspectj的文档,和springAOP是不同的
spring Aop的概念
aspect:一定要给spring去管理 抽象 aspectj->类
pointcut:切点表示连接点的集合 -------------------> 表 (我的理解:PointCut是JoinPoint的谓语,这是一个动作,主要是告诉通知连接点在哪里,切点表达式决定 JoinPoint 的数量)
Joinpoint:连接点 目标对象中的方法 ----------------> 记录 (我的理解:JoinPoint是要关注和增强的方法,也就是我们要作用的点)
Weaving :把代理逻辑加入到目标对象上的过程叫做织入
target 目标对象 原始对象aop
Proxy 代理对象 包含了原始对象的代码和增加后的代码的那个对象
advice:通知 (位置 + logic)
advice通知类型:
Before 连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常
After 连接点正常执行之后,执行过程中正常执行返回退出,非异常退出
After throwing 执行抛出异常的时候
After (finally) 无论连接点是正常退出还是异常退出,都会执行Around advice: 围绕连接点执行,例如方法调用。这是最有用的切面方式。
around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。
Proceedingjoinpoint 和JoinPoint的区别:
Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。JoinPoint的方法
1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表; 2.Signature getSignature() :获取连接点的方法签名对象; 3.java.lang.Object getTarget() :获取连接点所在的目标对象; 4.java.lang.Object getThis() :获取代理对象本身;proceed()有重载,有个带参数的方法,可以修改目标方法的的参数Introductionsperthis使用方式如下:@Aspect("perthis(this(com.chenss.dao.IndexDaoImpl))")要求:1. AspectJ对象的注入类型为prototype2. 目标对象也必须是prototype的原因为:只有目标对象是原型模式的,每次getBean得到的对象才是不一样的,由此针对每个对象就会产生新的切面对象,才能产生不同的切面结果。
SpringAop案例
1、键一个切面加上@Aspect(切面注解)和@Component(交给spring管理)
2、创建切点( @Pointcut("execution(* com.tl.dao.Dao.(…))"))
execution( com.tl.dao.Dao.*(…))这个是连接点(就是目标方法) 可以是类也可以是方法 可以是一个也可以是一批
3.创建方法比如前置、后置、环绕等。方法上加上对应注解 注解里面的参数是切点
@Aspect
@Component
public class TlAspect {
/**
* 申明切入点,匹配UserDao所有方法调用
* execution匹配方法执行连接点
* within:将匹配限制为特定类型中的连接点
* args:参数
* target:目标对象
* this:代理对象
*/
@Pointcut("execution(* com.tl.dao.Dao.*(..))")// 切入点表达式
public void pointCutExecution(){// 切入点签名
}
@Pointcut("args(java.lang.String)")// 切入点表达式
public void pointCutArgs(){
}
@Pointcut("within(com.tl.dao.IndexDao)")// 切入点表达式
public void pointCutWithin(){
}
@Pointcut("@within(com.tl.anno.Tuling)")// 切入点表达式
public void pointCutWithinAnno(){
}
@Pointcut("target(com.tl.dao.IndexDao)")// 切入点表达式
public void pointCutTarget(){
}
@Pointcut("this(com.tl.dao.IndexDao)")// 切入点表达式
public void pointCutThis(){
}
@Pointcut("@annotation(com.tl.dao.TulingTest)")// 切入点表达式
public void pointCutAnno(){
}
@Around("pointCutExecution()&&pointCutAnno()")
public Object pointCutAround(ProceedingJoinPoint pjp) throws Throwable {
return retVal;
}
// @After("within(@com.tl.anno.Tuling *)")
// public void after(){
// System.out.println("after-e");
// }
// @Before("com.tl.config.TlAspect.pointCutWithin()")
// public void before(){
// System.out.println("before-w");
// }
@Before("pointCutExecution()&&!pointCutAnno()")//后置Before里面的值的切点
public void before(JoinPoint joinPoint){
//目标方法执行后的
}
}
各种连接点joinPoint的意义:
1. execution
用于匹配方法执行 join points连接点,最小粒度方法,在aop中主要使用。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
example:
//匹配com.chenss.dao包下的任意接口和类的任意方法
@Pointcut("execution(* com.chenss.dao.*.*(..))")
//匹配com.chenss.dao包下的任意接口和类的public方法
@Pointcut("execution(public * com.chenss.dao.*.*(..))")
//匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法
@Pointcut("execution(public * com.chenss.dao.*.*())")
//匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")
//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")
//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")
//匹配任意的以te开头的方法
@Pointcut("execution(public * *(..))")@Pointcut("execution(* te*(..))")
//匹配com.chenss.dao.IndexDao接口中任意的方法
@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")
//匹配com.chenss.dao包及其子包中任意的方法
@Pointcut("execution(* com.chenss.dao..*.*(..))")
关于这个表达式的详细写法,可以脑补也可以参考官网很容易的,可以作为一个看spring官网文档的入门,打破你害怕看官方文档的心理,其实你会发觉官方文档也是很容易的
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples
由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参
数名等与方法相关的信息,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的
2.within
™表达式的最小粒度为类// ------------// within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法
3.args
args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关
/**
* args同execution不同的地方在于:
* * args匹配的是运行时传递给方法的参数类型
* execution(* *(java.io.Serializable))匹配的是方法在声明时指定的方法参数类型。 */ @Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
@Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified
4.this JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)
5.target 指向接口和子类
/** * 此处需要注意的是,如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用的是CGLIB代理
* JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口。
* 而CGLIB继承被代理的类来实现。
* 所以使用target会保证目标不变,关联对象不会受到这个设置的影响。
* 但是使用this对象时,会根据该选项的设置,判断是否能找到对象。
*/
@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法@Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ