关于aop知识的记录

关于AOP,主要参考http://www.cnblogs.com/xrq730/p/4919025.html
顺便说一句,这位大神的博客真是厉害。

一些基本概念罗列如下:
AOP核心概念

1、横切关注点

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

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

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

实际上我们在使用时,简单的实现只用关注以下几点

1.要切入什么功能——这就是切面的组件
2.切入的时机
这里写图片描述
3.在哪里切入–切入点pointcut

下面就举一个简单的例子说明

<?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:context="http://www.springframework.org/schema/context" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
    xmlns:jee="http://www.springframework.org/schema/jee" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">


     <!--将TestAop中的hello方法切入到cn.note包以及他的子包下-->
    <bean name="testAop" class="com.note.aop.TestAop"></bean>-->
    <aop:config>
    <!-- 将testaop定义为切面组件-->
        <aop:aspect ref="testAop"> 
            <!-- 什么时候,向哪些方法切入 method是要切入的方法,pointcut:向哪些地方切入-->
            <aop:before method="hello"
            pointcut="within(cn.note..*)"/>
        </aop:aspect>
    </aop:config>


</beans>



将TestAop中的hello方法切入到cn.note包以及他的子包下,这样cn.note包下及子包下程序在运行前都会执行hello方法。

大致的意思都明白了,但是切入点pointcut里面究竟该怎么写还是有点费解。
这里我参考了
https://zhuanlan.zhihu.com/p/23356489

首先 是关于within的写法
大神们的对within的解释为:within 表示方法属于一个特定的类
我个人觉得 within是一种类型的匹配,只要在winthin所定义的范围之内的类都会被切入
例如

within(org.service.*)

表示的是org.service这个包下所有的类

within(org.service..*)

表示的是org.service这个包以及他的子包下所有的类

在日常编码中,我们碰到最多的是其实是execution
所以对于这个pointcut我们一定要熟悉
excution 的具体格式如下

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

翻译为

execution(访问修饰符?    返回值类型   方法所在的类的路径?  方法名(方法的参数) 方法抛出的异常?)

带问号的是可以省去的
例如对于下面的这样的一个方法

public void demo1(String str) throws Exception{}

我们可以将其写为

execution(void demo1(String str))

如果返回类型也不做要求的话
可以写成

execution(* demo1(String str))

如果参数也不重要的话
可以写成(..),表示参数个数任意,类型任意

execution(* demo1(..))

更过分的是,你连方法名也不怎么在意
你更可以这样

execution(* demo*(..))

不过 一般来说 我们都希望在某个包下某个类中切入
一般 我们都会写成这样

execution(* org.service.UserService.*(..))

表示org.service.UserService下所有方法。。
切记,execution必须要表示到方法级别
例如,你要是写成

execution(* org.service.*)

只到类这一级别,spring容器就会无法识别。

更为详细的pointcut,我们可以看
https://zhuanlan.zhihu.com/p/23356489

定义了切点之后,我们需要定义何时调用方法,即需要定义通知。

AspectJ提供了五种定义通知的标注:

@Before:前置通知,在调用目标方法之前执行通知定义的任务
@After:后置通知,在目标方法执行结束后,无论执行结果如何都执行通知定义的任务
@After-returning:后置通知,在目标方法执行结束后,如果执行成功,则执行通知定义的任务
@After-throwing:异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
@Around:环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务
通过标注定义通知只需要两个步骤:

将以上五种标注之一添加到切面的方法中
在标注中设置切点的定义

这里主要是around通知 比较特殊,具体案例如下

@Around("execution(* cn.tedu.note.service.*Service.*(..))")
    public Object around(
            ProceedingJoinPoint joinPoint)  
            throws Throwable{
        System.out.println("开始");
        //调用业务方法
        Signature m=joinPoint.getSignature();
        System.out.println(m);//输出方法签名

        //在执行方法之前

        Object value=joinPoint.proceed();//执行方法


        //执行方法之后
        System.out.println("结束");
        return value;//业务方法的返回值
    }

下面讲解 基于注解的AOP配置
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:context="http://www.springframework.org/schema/context" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
    xmlns:jee="http://www.springframework.org/schema/jee" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">

    <!-- 扫描到控制器组件 -->
    <context:component-scan 
        base-package="cn.note.aop"/>
    <!-- 用于支持注解版的AOP @Aspect -->
    <aop:aspectj-autoproxy/>
</beans>



就只有两句话,第一句是把cn.note.aop包下带注解的类交给spring管理
第二句用于支持aop的注解

具体类的实现

package cn.note.aop;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component//组件扫描
@Aspect//aop标识
public class ExceptionLogger {
    @After("within(cn.note.web.*)")
    public void log(){
        System.out.println("进入controller之后");
    }

    //错误日志报告
    @AfterThrowing(throwing="ex",pointcut="execution(* cn.note.web.*.*(..))")
    public void logEx(Exception ex){
        try {
            FileWriter fw=new FileWriter("D:\\error.text");
            PrintWriter ps=new PrintWriter(fw);
            ex.printStackTrace(ps);
            ps.flush();
            ps.close();
            fw.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


}

关于多个切面的顺序,参考大神的博客

<aop:config>
            <aop:aspect id="time" ref="timeHandler" order="1">
                <aop:pointcut id="addTime" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
                <aop:before method="printTime" pointcut-ref="addTime" />
                <aop:after method="printTime" pointcut-ref="addTime" />
            </aop:aspect>
            <aop:aspect id="log" ref="logHandler" order="2">
                <aop:pointcut id="printLog" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
                <aop:before method="LogBefore" pointcut-ref="printLog" />
                <aop:after method="LogAfter" pointcut-ref="printLog" />
            </aop:aspect>
        </aop:config>

要想让logHandler在timeHandler前使用有两个办法:

(1)aspect里面有一个order属性,order属性的数字就是横切关注点的顺序

(2)把logHandler定义在timeHandler前面,Spring默认以aspect的定义顺序作为织入顺序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值