文档章节

AOP

张舒歌
 张舒歌
发布于 2016/11/07 15:11
字数 1446
阅读 48
收藏 3

AOP概念

AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程)。其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能。像日志记录,事务处理,权限控制等功能,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。AOP是OOP的一个强有力的补充。

AOP术语

AOP的术语不太直观,Spring文档中也没有给一个确切的定义,所以重在理解。

  • Join Point: Spring AOP中,join point就是一个方法。(通俗来讲就是起作用的那个方法)。

  • Pointcut: 用来指定join point(通俗来讲就是描述的一组符合某个条件的join point)。通常使用pointcut表达式来限定joint point,Spring默认使用AspectJ pointcut expression language。

  • Advice: 在join point上特定的时刻执行的操作,Advice有几种不同类型,下文将会讨论(通俗地来讲就是起作用的内容和时间点)。

  • Introduction:给对象增加方法或者属性。

  • Target object: Advice起作用的那个对象。

  • AOP proxy: 为实现AOP所生成的代理。在Spring中有两种方式生成代理:JDK代理和CGLIB代理。

  • Aspect: 组合了Pointcut与Advice,在Spring中有时候也称为Advisor。某些资料说Advisor是一种特殊的Aspect,其区别是Advisor只能包含一对pointcut和advice,但是aspect可以包含多对。AOP中的aspect可以类比于OOP中的class。

  • Weaving:将Advice织入join point的这个过程。

Advice的类型

  • Before advice:  执行在join point之前的advice,但是它不能阻止joint point的执行流程,除非抛出了一个异常(exception)。

  • After returning advice: 执行在join point这个方法返回之后的advice。

  • After throwing advice: 执行在join point抛出异常之后的advice。

  • After(finally) advice: 执行在join point返回之后或者抛出异常之后的advice,通常用来释放所使用的资源。

  • Around advice: 执行在join point这个方法执行之前与之后的advice。

实现机制

Spring AOP是基于代理机制的,通过JDK Proxy和CGLIB Proxy两种方法实现代理。

如果target object没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

如果target object实现了一个以上的接口,那么Spring将使用JDK Proxy来实现代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。当然,你也可以强制使用CGLIB来进行代理,但是这样可能会造成性能上的下降。

使用xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启切面注解-->
    <aop:aspectj-autoproxy/>
    <bean id="audience" class="com.zc.demo.aop.Audience"/>
    <bean id="magician" class="com.zc.demo.aop.Magician"></bean>
    <aop:config>
        <!--定义切面  引用audience Bean  将audience定义成一个切面-->
        <aop:aspect ref="audience">
            <!--用于匹配连接点的执行方法  execution在方法执行时触发
               以*开始代表我们不关心方法的返回类型,可以返回任意类型。
               com.zc.demo.di.interfaces.Performer.perform()  指定全限定类名和方法名
               (..)标识切点选择任意的play()方法,无论改方法的入参是什么。
            -->
            <!--可以单独定义一个命名切点,也可在每个通知元素中定义-->
            <aop:pointcut id="performance" expression="execution(* com.zc.demo.di.interfaces.Performer.perform(..))"/>

            <!--Performer.perform()做为切点的执行方法  在执行perform()方法之前先调用audience切面的前置通知方法takeSeats()和turnOffcellPhones()-->
            <aop:before pointcut-ref="performance" method="takeSeats"/>
            <aop:before pointcut-ref="performance" method="turnOffcellPhones"/>

            <!--在切点方法成功执行之后调用后置通知applaud()方法-->
            <aop:after-returning pointcut="execution(* com.zc.demo.di.interfaces.Performer.perform(..))"
                                 method="applaud"/>
            <!--在切点方法执行时并抛出异常后调用后置通知demandRefund()方法-->
            <aop:after-throwing pointcut="execution(* com.zc.demo.di.interfaces.Performer.perform(..))"
                                method="demandRefund"/>

            <!--环绕式通知-->
            <aop:around method="watchPerformance" pointcut-ref="performance"/>
        </aop:aspect>


        <!--定义切面 传递参数-->
        <aop:aspect ref="magician">
            <aop:pointcut id="thinking" expression="execution(* com.zc.demo.aop.Thinker.thinkOfSomething(String)) and args(thoughts)"/>
            <aop:before method="interceptThoughts" pointcut-ref="thinking" arg-names="thoughts"/>
        </aop:aspect>
    </aop:config>
</beans>

使用注解annotation实现aop

package com.zc.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 通过注解定义切面
 * Created by zhangchi9 on 2016/11/7.
 */
@Component
@Aspect
public class AudienceAnnotation {
    /**
     * 定义切点
     */
    @Pointcut("execution(* com.zc.demo.di.interfaces.Performer.perform(..))")
    public void performance(){}

    @Before("performance()")
    public void takeSeats(){
        System.out.println("表演之_前获得座位");
    }
    @Before("performance()")
    public void turnOffCellPhones(){
        System.out.println("表演之_前关闭手机");
    }
    @After("performance()")
    public void after(){
        System.out.println("无论成功失败表演之后都调用");
    }
    @AfterReturning("performance()")
    public void applaud(){
        System.out.println("表演成功之后要鼓掌");
    }
    @AfterThrowing("performance()")
    public void demandRefund(){
        System.out.println("表演失败之后要退票");
    }
    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint joinPoint){
        try {
            /**表演之前**/
            System.out.println("环绕通知——表演之前获得座位!");
            System.out.println("环绕通知——表演之前关闭手机!");
            long start = System.currentTimeMillis();

            /** 执行被通知的方法 **/
            joinPoint.proceed();

            /**表演之后*/
            long end = System.currentTimeMillis();
            System.out.println("环绕通知——表演之后要鼓掌!");
            System.out.println("环绕通知——"+(end-start));
        } catch (Throwable throwable) {
            System.out.println("环绕通知——表演失败后我们要求退票!");
            throwable.printStackTrace();
        }
    }
}

 源码demo可在https://git.oschina.net/ciyuan/ssm.git获取

© 著作权归作者所有

上一篇: 配置数据源druid
下一篇: IOC
张舒歌
粉丝 1
博文 108
码字总数 69886
作品 0
海淀
高级程序员
私信 提问

暂无文章

3_数组

3_数组

行者终成事
46分钟前
7
0
经典系统设计面试题解析:如何设计TinyURL(二)

原文链接:https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR 编者注:本文以一道经典的系统设计面试题:《如何设计TinyURL》的参考答案和解析为例,帮助...

APEMESH
今天
7
0
使用logstash同步MySQL数据到ES

概述   在生成业务常有将MySQL数据同步到ES的需求,如果需要很高的定制化,往往需要开发同步程序用于处理数据。但没有特殊业务需求,官方提供的logstash就很有优势了。   在使用logstas...

zxiaofan666
今天
10
0
X-MSG-IM-分布式信令跟踪能力

经过一周多的鏖战, X-MSG-IM的分布式信令跟踪能力已基本具备, 特点是: 实时. 只有要RX/TX就会实时产生信令跟踪事件, 先入kafka, 再入influxdb待查. 同时提供实时sub/pub接口. 完备. 可以完整...

dev5
今天
7
0
OpenJDK之CyclicBarrier

OpenJDK8,本人看的是openJDK。以前就看过,只是经常忘记,所以记录下 图1 CyclicBarrier是Doug Lea在JDK1.5中引入的,作用就不详细描述了,主要有如下俩个方法使用: await()方法,如果当前线...

克虏伯
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部