AOP奇技淫巧

作者: 空白格_5f7d | 来源:发表于2020-07-02 10:23 被阅读0次

    image

    一、AOP的基本概念:

    1、什么是aop:

    AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。

    在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。

    AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

    2、AOP的相关概念:

    (1)横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

    (2)Aspect(切面):通常是一个类,里面可以定义切入点和通知

    (3)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

    (4)Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)

    (5)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

    (6)weave(织入):将切面应用到目标对象并导致代理对象创建的过程

    (7)introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

    (8)AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

    (9)目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

    3、Advice通知类型介绍:

    (1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可

    (2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

    (3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名

    来访问目标方法中所抛出的异常对象

    (4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式

    (5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

    4、AOP使用场景:

    Authentication 权限

    Caching 缓存

    Context passing 内容传递

    Error handling 错误处理

    Lazy loading 懒加载

    Debugging  调试

    logging, tracing, profiling and monitoring 记录跟踪 优化 校准

    Performance optimization 性能优化

    Persistence  持久化

    Resource pooling 资源池

    Synchronization 同步

    Transactions 事务

    二、你们认为的AOP 应该是这样的

    对没错 ,这是AOP 最常用的方式,作为日志处理、异常处理、参数校验 。

    三、我要说的AOP 长这样

    先描述下业务场景:对于 业务类型的订单接口:我们可以通过数据校验避免,订单数据重复提交(例如:开始、结束、拒绝订单) 但是对于 新建订单 是无法通过 校验实现的。 为了避免重复数据,最好的就是引入锁机制处理。但问题来了,又不是所有的接口都需要锁,只是对特定的接口需要添加锁。添加锁肯定就要try、catch、finally每次写太费劲了,况且一旦修改锁,更换其他类型的锁很麻烦,工作量也很大。所以,AOP ,嗯 ,靠谱。 可以在指定方法上加自定义注解,让AOP 扫描带有自定义注解的方法就ok了。

    细心的同学会发现,红框内容有点不太一样 @annotation。execution(* com.x.x.x.x.contxroller..*.*(..))" 指的是过滤方法(里面是 [正则表表达式](https://blog.csdn.net/corbin_zhang/article/details/80576809) )。@annotation() 里面放的是注解的全类名,惊喜不?

    /**

    *该注解 是在方法上 添加锁的

    */

    @Target({java.lang.annotation.ElementType.PARAMETER, ElementType.METHOD})

    @Retention(RetentionPolicy.RUNTIME)

    public @interface MethodLock {

        String desc() default "";

    }

    /**

    * 元注解(meta-annotation):

    *

    *   元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

    *     1.@Target,

    *     2.@Retention,

    *     3.@Documented,

    *     4.@Inherited

    */

    /**

    * @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

    *

    *   作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

    *

    *   取值(ElementType)有:

    *

    *     1.CONSTRUCTOR:用于描述构造器

    *     2.FIELD:用于描述域

    *     3.LOCAL_VARIABLE:用于描述局部变量

    *     4.METHOD:用于描述方法

    *     5.PACKAGE:用于描述包

    *     6.PARAMETER:用于描述参数

    *     7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

    */

    @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })

    /**

    * @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

    *

    *   作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

    *

    *   取值(RetentionPoicy)有:

    *

    *     1.SOURCE:在源文件中有效(即源文件保留)

    *     2.CLASS:在class文件中有效(即class保留)

    *     3.RUNTIME:在运行时有效(即运行时保留)

    *

    *   Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。

    */

    @Retention(RetentionPolicy.RUNTIME)

    /**

    * @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

    * Documented是一个标记注解,没有成员。

    */

    @Documented

    /**

    * @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

    *

    *   注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

    *

    *   当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

    */

    @Inherited

    代码如下:

    /**

    * 锁注解 切面

    */

    @Component //作为一个bean 被spring 扫描到

    @Aspect

    @Order(100)

    1. //order越小越是最先执行,但更重要的是最先执行的最后结束。order默认值是2147483647

    public class MethodLockAspect {

        @Around("@annotation(com.x.x.x.x.annoxtation.MethodLock)")

        public Object around(ProceedingJoinPoint joinPoint) {

            return MethodLockAspectUtils.methodLock(joinPoint);

        }

    }

    * @Apsect:将当前类标识为一个切面;

    * @Pointcut:定义切点,这里使用的是条件表达式;

    * @Before:前置增强,就是在目标方法执行之前执行;

    * @AfterReturning:后置增强,方法退出时执行;

    * @AfterThrowing:有异常时该方法执行;

    * @After:最终增强,无论什么情况都会执行;

    * @Afround:环绕增强;

    //切面中 锁具体的实现

    public class MethodLockAspectUtils {

        /**

        * 互斥锁 参数默认false,不公平锁

        */

        private static Lock lock = new ReentrantLock(false);

        public static Object methodLock(ProceedingJoinPoint joinPoint){

            lock.lock();

            Object obj = null;

            try {

                obj = joinPoint.proceed();

            } catch (Throwable e) {

                e.printStackTrace();

            } finally {

                lock.unlock();

            }

            return obj;

        }

    }

    四、使用:

    在需要添加同步方法上使用注解即可

    五、注意

    这里只是简单的是锁机制,后期进行更新切换,zookeeper、redis,mysql等分布式锁。

    相关文章

      网友评论

        本文标题:AOP奇技淫巧

        本文链接:https://www.haomeiwen.com/subject/dgaztqtx.html