AOP和spring AOP学习记录

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

AOP和spring AOP学习记录

opticor   2020-03-09 我要评论
### AOP基本概念的理解 面向切面AOP主要是在编译期或运行时,对程序进行织入,实现代理, 对原代码毫无侵入性,不破坏主要业务逻辑,减少程序的耦合度。 - 主要应用范围: 日志记录,性能统计,安全控制,事务处理,异常处理等等 #### 名词性概念 - **切面(Aspect)** 通常是一个类,在里面可以定义切入点和通知,即`切面=切入点+通知`。 - **连接点(Joint Point)** 被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中连接点指的就是被拦截的到的方法,实际上连接点还可以是字段或者构造器。 - **切入点(Pointcut)** 对连接点进行拦截的定义。 - **通知(Advice)** 拦截到连接点之后所要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。 - **AOP 代理** AOP 框架创建的对象,代理就是目标对象的加强。Spring 中的 AOP 代理可以使 JDK 动态代理,也可以是 CGLIB 代理,前者基于接口,后者基于子类。 - **织入**(Weaving) 把切面加入到对象,并创建出代理对象的过程。 #### 动态代理 在运行期间生成对象进行代理, spring AOP就是动态代理。 #### 静态代理 自己编写代理对象,在编译器织入, AspectJ就是在编译期间进行织入,从而减少对运行时效率的影响。 # SpringAOP 根据对象是否是实现类选择代理的方法 - 如果要代理的对象实现了接口,spring AOP会根据接口,使用JDK Proxy创建代理 - 如果没有实现接口,则通过CGLIB代理 当然也可以指定都使用CGLIB进行切入,而JDK方法不适用于没有接口实现的目标类 #### 对于JDK proxy Jdk proxy会实现目标类的接口,代理逻辑就在新的实现类中 只能对实现类进行代理,性能也优于CGLIB,所以平时都是分为"接口-实现iml"来设计。 #### 对于CGLIB cglib会创建一个代理目标类的子类,而代理逻辑就在这一子类中添加, 然后得到这一子类的对象,也就是代理增强后的对象,一切都是动态的, 具体实现有待学习。 ## 切面类 使用@Aspect注解定义切面类 比如这样 ```java @Component @Aspect @Oder(99) public class WebLogAspect {} ``` 另外对同一个方法有多个切面进行代理的时候,难免需要区分执行顺序, 这时候可以使用@Order注解定义优先级,数字越低,级别越高。 ## 切面Advice 执行顺序around -> before -> around -> after -> afterRuternning #### @After 第一个参数都必须是JoinPoint类型 在目标方法完成后通知, 无论方法是以何种方式完成,异常也是如此 #### @After-returning 第一个参数都必须是JoinPoint类型 结束并安全返回时通知,异常不通知 #### @After-throwing 第一个参数都必须是JoinPoint类型 异常时通知 #### @Before 第一个参数都必须是JoinPoint类型 顾名思义 ####@Around 最强大的通知 第一个形参必须是ProceedingJoinPoint类型, ```java public interface ProceedingJoinPoint extends JoinPoint { void set$AroundClosure(AroundClosure var1); default void stack$AroundClosure(AroundClosure arc) { throw new UnsupportedOperationException(); } Object proceed() throws Throwable; Object proceed(Object[] var1) throws Throwable; } ``` 可以看到它继承自JoinPoint,多了proceed方法 注解的方法体内调用ProceedingJoinPoint参数的**proceed()**方法才会执行目标。 调用这个方法时,还可以传入一个Object[]对象作为参数 可以通过这个切面**决定方法是否执行,改变传入参数,改变返回值,检查异常等**。 #### 参数方法 - getArgs(): Returns the method arguments. - getThis(): Returns the proxy object. - getTarget(): Returns the target object. - getSignature(): Returns a description of the method that is being advised. - toString(): Prints a useful description of the method being advised. ## 切点表达式 有关切点表达式,建议阅读 [传送门](https://www.jianshu.com/p/a0b9c53ac019) 可以定义一个切点,之后就不用一一指定,直接带入value即可,例如: ```java @Pointcut("execution(* com.hyg.app.controller..*.*(..))") public void webLog(){} @Before(value = "webLog()") public void doBefore (JoinPoint jp){ String name = jp.getSignature().getName(); System.out.println(name+"开始执行"); } ``` ### execution 表达式中最常用的方法是execution,粒度最小 对于`execution(* com.hyg.app.controller..*.*(..))` - `execution `表达式的类型指定 - 第一个`* `代表 任意的**返回值类型** - com.jiuxian aop所切的**包名** - 包后面`.. `表示**当前包**及**其子包**,一个`.`是当前包,两个`.`就包括所有子包 - 第二个`*` 表示类名,代表所有类 - `.*(..) `表示任何方法, 括号代表参数 .. 表示任意参数 后面那串东西,可以记为每一个`.`都是更深的一个粒度 来个精确点的示例: `execution(* com.hyg.app.service.*Service.add*(String))` 表示`所有类型`的,`com.hyg.app.service`包下的, 所有以`Service`结尾的类, 以`add`开头的,参数类型为`String`的方法 ### 例子 在官方文档中有很多示例,如下 ``` 所有public方法 execution(public * *(..)) 所有名字以set开头的方法 execution(* set*(..)) 所有AccountService中实现的接口的方法 execution(* com.xyz.service.AccountService.*(..)) 所有service包下的方法 execution(* com.xyz.service.*.*(..)) 所有在service包以及子包下的方法 execution(* com.xyz.service..*.*(..)) within(com.xyz.service.*) 所有service包下方法 within(com.xyz.service..*) 所有service包和子包下方法 this(com.xyz.service.AccountService) 匹配accountservice代理的对象 target(com.xyz.service.AccountService) 实现了AccountService接口的对象 拥有transactional注解的 @annotation(org.springframework.transaction.annotation.Transactional) 传递的参数是Serializable的 args(java.io.Serializable): ``` ### 关于execution和within的区别 execution可以指定方法返回类型,类名,方法名和参数名等与方法相关的部件, 而within的最小粒度是类。 ### 关于this和target的区别 this匹配的是代理类,即目标对象被代理后的代理对象 target则是匹配普通的目标对象 以下情况外,二者的效果都是一致的。 this(SomeObject)或target(SomeObject),这里SomeObject实现了某个接口:对于这种情况,虽然表达式中指定的是一种具体的对象类型,但由于其实现了某个接口,因而Spring默认会使用Jdk代理为其生成代理对象,Jdk代理生成的代理对象与目标对象实现的是同一个接口,但代理对象与目标对象还是不同的对象,由于代理对象不是SomeObject类型的,因而此时是不符合this语义的,而由于目标对象就是SomeObject类型,因而target语义是符合的,此时this和target的效果就产生了区别;这里如果强制Spring使用Cglib代理,因而生成的代理对象都是SomeObject子类的对象,其是SomeObject类型的,因而this和target的语义都符合,其效果就是一致的。 ### args 用于指定参数类型,类型必须是全路径的 ### 官网解释 - `execution`: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP. - `within`: Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP). - `this`: Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type. - `target`: Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type. - `args`: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types. - `@target`: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type. - `@args`: Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types. - `@within`: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP). - `@annotation`: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation. [---官方文档---](https:/https://img.qb5200.com/download-x/docs.spring.io/springhttps://img.qb5200.com/download-x/docs/current/spring-framework-reference/core.html#aop) 我的博客:[https://www.seyana.life/post/12](https://www.seyana.life/post/12)

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们