Spring之AOP解析应用

AOP(Aspect Oriented Programming):面向切面编程

  • 可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术
  • 利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性。
  • 在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
  • 将公共功能提取出来可以极大的提升开发效率 同时提高了开发的效率。
  • 以往是通过继承体系来达到代码重用,如果将继承体系看做一种自上而下的树状结构,那么继承是一种纵向的代码重用,随着时间推移软件行业又提出了aop的概念及面向切片编程,aop可以看做是横向的代码重用,

 

主要功能:

日志记录,性能统计,安全控制,事务处理,异常处理,数据验证,数据加解密,为了保证对象在并发下只有一个对象访问的加锁和解锁等等。

首先日志、数据验证、加解密这些代码不应该写在需求中,这样代码重复混乱臃肿不灵活,也会带来很大的工作量。比如日志需求发生变化,就要修改所有的模块。

 

主要意图:

将共用性系统功能(日志、性能、安全、异常等等)和其他共用功能(数据验证,加解密)和业务逻辑分离;

 

AOP和OOP(Object Oriented Programming)的区别:

OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合的隔离效果。

如果说面向对象编程是关注将需求功能划分为不同的并且相对独立,封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系的话;那么面向切面编程则是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。

面向对象编程主要用于为同一对象层次的公用行为建模。它的弱点是将公共行为应用于多个无关对象模型之间。而这恰恰是面向切面编程适合的地方。有了 AOP,我们可以定义交叉的关系,并将这些关系应用于跨模块的、彼此不同的对象模型。AOP 同时还可以让我们层次化功能性而不是嵌入功能性,从而使得代码有更好的可读性和易于维护。它会和面向对象编程合作得很好。 oop对象层面建模,AOP业务层面建模

 

AOP示意图:

原理

AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理和CGLIB动态代理,AOP功能的使用还是比较简单的,把相关bean注入到Spring容器中,编写好相应的Aspect类即可,以下两点需要记住:

1、AOP基于动态代理模式
2、AOP是方法级别

概念:

切面(Aspect):  

横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象,Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现

切点(pointcut):

匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时),切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法

连接点(Joinpoint):

在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在SpringAOP中,一个连接点总是表示一个方法的执行

通知(Advice): 

在切面的某个特定的连接点上执行的动作。其中包括了AroundBeforeAfter等不同类型的通知。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链

 

引入(Intorduction)

用来给一个类型声明额外的方法或属性(也被称为连接类型声明)。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现接口,以便简化缓存机制

 

目标对象(Target Object)

被一个或者多个切面所通知的对象。也被称做被通知对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理对象

AOP代理(Aop proxy)

AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理

植入(weaving)

把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入

 

每个出口都是连接点,但是我们使用的那个出口才是切点。每个应用有多个位置适合织入通知,这些位置都是连接点。但是只有我们选择的那个具体的位置才是切点


二、常用切入点指示符

1、execution表达式

用于匹配方法执行的连接点,属于方法级别

语法
execution(修饰符 返回值类型 方法名(参数)异常)

语法参数描述
修饰符可选,如public,protected,写在返回值前,任意修饰符填*号就可以
返回值类型必选,可以使用*来代表任意返回值
方法名必选,可以用*来代表任意方法
参数()代表是没有参数,(..)代表是匹配任意数量,任意类型的参数,当然也可以指定类型的参数进行匹配,如要接受一个String类型的参数,则(java.lang.String), 任意数量的String类型参数:(java.lang.String..)等等。。。
异常可选,语法:throws 异常,异常是完整带包名,可以是多个,用逗号分隔

符号

符号描述
*匹配任意字符
..匹配多个包或者多个参数
+表示类及其子类

条件符

符号描述
&&、and
||
!

案例

  • 拦截com.gj.web包下的所有子包里的任意类的任意方法
    execution(* com.gj.web..*.*(..))
  • 拦截com.gj.web.api.Test2Controller下的任意方法
    execution(* com.gj.web.api.Test2Controller.*(..))
  • 拦截任何修饰符为public的方法
    execution(public * * (..))
  • 拦截com.gj.web下的所有子包里的以ok开头的方法
    execution(* com.gj.web..*.ok*(..))
    更多用法大家可以根据语法自行设计,本文不在进行举例

2、@annotation

根据所应用的注解对方法进行过滤
语法
@annotation(注解全路径)
实例
对用了com.gj.annotations.Test注解的所有方法进行拦截
@annotation(com.gj.annotations.Test)

3、Within

根据类型(比如接口、类名或者包名过滤方法)进行拦截
语法
within(typeName)
示例

  • 对com.gj.web下的所有子包的所有方法进行拦截
    within(com.gj.web..*)
    更多用法可以根据语法自行设计

4、@Within

用于匹配所有持有指定注解类型内的方法,与within是有区别的,within是用于匹配指定类型内的方法执行,而@within是指定注解类型内的方法

5、bean

Spring AOP扩展的,AspectJ没有对于它的指示符,用于匹配特定名称的bean对象的执行方法
语法
bean(beanName)
示例
bean(testController)

三、AOP通知

在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ支持5种类型的通知注解

注解描述
@Before前置通知, 在方法执行之前执行
@After后置通知, 在方法执行之后执行
@AfterReturn返回通知, 在方法返回结果之后执行
@AfterThrowing异常通知, 在方法抛出异常之后
@Around环绕通知,围绕方法的执行

1、@Before

1、@Before

示例

 

    @Before("testCut()")
    public void cutProcess(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP开始拦截, 当前拦截的方法名: " + method.getName());
    }

2、@After

示例

 

    @After("testCut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP执行的方法 :"+method.getName()+" 执行完了");
    }

3、@AfterReturn

其中value表示切点方法,returning表示返回的结果放到result这个变量中
示例

 

    /**
     * returning属性指定连接点方法返回的结果放置在result变量中
     * @param joinPoint 连接点
     * @param result 返回结果
     */
    @AfterReturning(value = "testCut()",returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP拦截的方法执行成功, 进入返回通知拦截, 方法名为: "+method.getName()+", 返回结果为: "+result.toString());
    }

4、@AfterThrowing

其中value表示切点方法,throwing表示异常放到e这个变量
示例

    @AfterThrowing(value = "testCut()", throwing = "e")
    public void afterThrow(JoinPoint joinPoint, Exception e) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP进入方法异常拦截, 方法名为: " + method.getName() + ", 异常信息为: " + e.getMessage());
    }

5、Around

示例

    @Around("testCut()")
    public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解方式AOP拦截开始进入环绕通知.......");
            Object proceed = joinPoint.proceed();
            System.out.println("准备退出环绕......");
            return proceed;
    }

四、SpringBoot中使用

1、创建注解

注解用于进行AOP拦截

 

/**
 * @author Gjing
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

2、创建一个接口

 

/**
 * @author Gjing
 **/
@RestController
@RequestMapping("test1")
public class TestController {

    @GetMapping("/ok")
    @ApiOperation(value = "测试1", httpMethod = "GET")
    @ApiImplicitParam(name = "id", value = "id值", dataType = "int", paramType = "query")
    @Test
    @NotNull
    public String test2(Integer id) {
        return "ok";
    }

}

3、创建切面类

 

/**
 * @author Gjing
 **/
@Aspect
@Component
public class TestAspect {

    @Pointcut("@annotation(com.gj.annotations.Test)")
    public void testCut() {

    }

    @Before("testCut()")
    public void cutProcess(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP开始拦截, 当前拦截的方法名: " + method.getName());
    }

    @After("testCut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP执行的方法 :"+method.getName()+" 执行完了");
    }


    @Around("testCut()")
    public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解方式AOP拦截开始进入环绕通知.......");
            Object proceed = joinPoint.proceed();
            System.out.println("准备退出环绕......");
            return proceed;
    }

    /**
     * returning属性指定连接点方法返回的结果放置在result变量中
     * @param joinPoint 连接点
     * @param result 返回结果
     */
    @AfterReturning(value = "testCut()",returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP拦截的方法执行成功, 进入返回通知拦截, 方法名为: "+method.getName()+", 返回结果为: "+result.toString());
    }

    @AfterThrowing(value = "testCut()", throwing = "e")
    public void afterThrow(JoinPoint joinPoint, Exception e) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP进入方法异常拦截, 方法名为: " + method.getName() + ", 异常信息为: " + e.getMessage());
    }

}

 


AOP实现并发对共享数据访问

1.乐观锁,如果是数据库中的数据添加版本号比较;

2.提供一些必备的功能,对被访问对象实现加锁或解锁功能。以保证所有在修改数据对象的操作之前能够调用lock()加锁,在它使用完成后,调用unlock()解锁。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值