Spring AOP详解

第一节 AOP 简介

1. 概念

AOP全称为Aspect Oriented Programming,表示面向切面编程。何为切面呢?

由此可以得出,切面是一种将那些与业务无关,但业务模块都需要使用的功能封装起来的技术。这样便于减少系统的重复代码,降低模块之间的耦合度。

2. AOP 基本术语

  • 连接点( Joinpoint ):

    连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。连接点由两个信息确定:

    • 方法( 表示程序执行点,即在哪个目标方法)

    • 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)

  • 切入点(Pointcut):

    切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。 一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则来匹配连接点,给满足规则的连接点添加通知。

  • 通知、增强(Advice):

    可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知、最终通知等。

  • 目标对象(Target):

    目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

  • 织入(Weaving):

    织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理

  • 代理(Proxy):

    被AOP织入通知后,产生的结果类。

  • 切面(Aspect):

    切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

第二节 AOP 应用

AOP应用场景有许多,最典型的应用场景就是日志和事务。这里以事务实现为例进行讲解。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#117700"><</span><span style="color:#117700">dependency</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">groupId</span><span style="color:#117700">></span>org.springframework<span style="color:#117700"></</span><span style="color:#117700">groupId</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">artifactId</span><span style="color:#117700">></span>spring-context<span style="color:#117700"></</span><span style="color:#117700">artifactId</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">version</span><span style="color:#117700">></span>5.3.11<span style="color:#117700"></</span><span style="color:#117700">version</span><span style="color:#117700">></span>
<span style="color:#117700"></</span><span style="color:#117700">dependency</span><span style="color:#117700">></span>
<span style="color:#aa5500"><!-- 切面相关的包 --></span>
<span style="color:#117700"><</span><span style="color:#117700">dependency</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">groupId</span><span style="color:#117700">></span>org.aspectj<span style="color:#117700"></</span><span style="color:#117700">groupId</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">artifactId</span><span style="color:#117700">></span>aspectjweaver<span style="color:#117700"></</span><span style="color:#117700">artifactId</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">version</span><span style="color:#117700">></span>1.8.9<span style="color:#117700"></</span><span style="color:#117700">version</span><span style="color:#117700">></span>
<span style="color:#117700"></</span><span style="color:#117700">dependency</span><span style="color:#117700">></span></span></span>

1. 编写业务层

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">UserService</span> {
​
    <span style="color:#008855">int</span> <span style="color:#000000">saveUser</span>(<span style="color:#000000">Map</span><span style="color:#981a1a"><</span><span style="color:#008855">String</span>,<span style="color:#008855">Object</span><span style="color:#981a1a">></span> <span style="color:#000000">params</span>);
}
​
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">UserServiceImpl</span> <span style="color:#770088">implements</span> <span style="color:#000000">UserService</span> {
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">saveUser</span>(<span style="color:#000000">Map</span><span style="color:#981a1a"><</span><span style="color:#008855">String</span>, <span style="color:#008855">Object</span><span style="color:#981a1a">></span> <span style="color:#000000">params</span>) {
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"保存用户信息"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">params</span>);
        <span style="color:#770088">return</span> <span style="color:#116644">0</span>;
    }
}</span></span>

2. 配置业务层

AOP 功能的实现是基于 IOC 的,因此,业务层对象应该纳入 IOC 容器来管理。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#555555"><?xml</span> <span style="color:#555555">version="1.0" encoding="UTF-8"?></span>
<span style="color:#aa5500"><!-- xmlns = xml namespace--></span>
<span style="color:#117700"><</span><span style="color:#117700">beans</span> <span style="color:#0000cc">xmlns</span>=<span style="color:#aa1111">"http://www.springframework.org/schema/beans"</span>
       <span style="color:#0000cc">xmlns:xsi</span>=<span style="color:#aa1111">"http://www.w3.org/2001/XMLSchema-instance"</span>
       <span style="color:#0000cc">xsi:schemaLocation</span>=<span style="color:#aa1111">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"</span><span style="color:#117700">></span>
    <span style="color:#aa5500"><!-- 业务层对象--></span>
    <span style="color:#117700"><</span><span style="color:#117700">bean</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"userService"</span> <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.service.impl.UserServiceImpl"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">beans</span><span style="color:#117700">></span></span></span>

3. 编写通知类

通知分为前置通知、后置通知、异常抛出通知、环绕通知、最终通知五种。首先实现前置通知。

前置通知接口

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">MethodBeforeAdvice</span> <span style="color:#770088">extends</span> <span style="color:#000000">BeforeAdvice</span> {
​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* Callback before a given method is invoked.</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#008855">void</span> <span style="color:#000000">before</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>, <span style="color:#555555">@Nullable</span> <span style="color:#008855">Object</span> <span style="color:#000000">target</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span>;
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">BeforeAdvice</span> <span style="color:#770088">implements</span> <span style="color:#000000">MethodBeforeAdvice</span> {
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">before</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>, <span style="color:#008855">Object</span> <span style="color:#000000">target</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
        <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
        <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备执行方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>));
    }
}</span></span>

4. 配置通知

通知的实现也是基于 IOC 容器的,因此需要将通知对象纳入 IOC 容器管理。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#555555"><?xml</span> <span style="color:#555555">version="1.0" encoding="UTF-8"?></span>
<span style="color:#aa5500"><!-- xmlns = xml namespace--></span>
<span style="color:#117700"><</span><span style="color:#117700">beans</span> <span style="color:#0000cc">xmlns</span>=<span style="color:#aa1111">"http://www.springframework.org/schema/beans"</span>
       <span style="color:#0000cc">xmlns:xsi</span>=<span style="color:#aa1111">"http://www.w3.org/2001/XMLSchema-instance"</span>
       <span style="color:#0000cc">xsi:schemaLocation</span>=<span style="color:#aa1111">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"</span><span style="color:#117700">></span>
    <span style="color:#aa5500"><!-- 业务层对象--></span>
    <span style="color:#117700"><</span><span style="color:#117700">bean</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"userService"</span> <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.service.impl.UserServiceImpl"</span> <span style="color:#117700">/></span>
    <span style="color:#aa5500"><!--配置通知对象--></span>
    <span style="color:#117700"><</span><span style="color:#117700">bean</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"before"</span> <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.advice.BeforeAdvice"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">beans</span><span style="color:#117700">></span></span></span>

5. AOP 配置

当通知对象和业务层对象都纳入 IOC 容器管理之后,需要将通知对象作用在业务层对象上。Spring 提供了 aop 标签来完成这一功能。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#117700"><</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span>
    <span style="color:#aa5500"><!--</span>
            <span style="color:#aa5500">pointcut表示切点,也就是通知会在哪些位置触发</span>
            <span style="color:#aa5500">expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上</span>
            <span style="color:#aa5500">比如 * com.qf.spring.aop.service..*(..)</span>
            <span style="color:#aa5500">第一个 * 表示任意访问修饰符</span>
            <span style="color:#aa5500">com.qf.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类</span>
            <span style="color:#aa5500">*(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数</span>
        <span style="color:#aa5500">--></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:pointcut</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"切点ID"</span> <span style="color:#0000cc">expression</span>=<span style="color:#aa1111">"切点表达式"</span><span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"通知ID"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"切点ID"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span></span></span>

要使用 aop 标签,必须要在 beans 标签上添加 aop 命名空间

<span style="background-color:#f8f8f8"><span style="color:#333333">xmlns:aop="http://www.springframework.org/schema/aop"</span></span>

然后在 xsi:schemaLocation 属性值中添加 aop 命名空间和约束文档

<span style="background-color:#f8f8f8"><span style="color:#333333">http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#117700"><</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:pointcut</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#0000cc">expression</span>=<span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span><span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"before"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span></span></span>

6. 测试

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">AopTest</span> {
​
    <span style="color:#555555">@Test</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">saveUserTest</span>(){
        <span style="color:#000000">ApplicationContext</span> <span style="color:#000000">context</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">ClassPathXmlApplicationContext</span>(<span style="color:#aa1111">"applicationContext.xml"</span>);
        <span style="color:#000000">UserService</span> <span style="color:#000000">userService</span> <span style="color:#981a1a">=</span> <span style="color:#000000">context</span>.<span style="color:#000000">getBean</span>(<span style="color:#aa1111">"userService"</span>, <span style="color:#000000">UserService</span>.<span style="color:#770088">class</span>);
        <span style="color:#000000">Map</span><span style="color:#981a1a"><</span><span style="color:#008855">String</span>,<span style="color:#008855">Object</span><span style="color:#981a1a">></span> <span style="color:#000000">params</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">HashMap</span><span style="color:#981a1a"><></span>();
        <span style="color:#000000">params</span>.<span style="color:#000000">put</span>(<span style="color:#aa1111">"name"</span>, <span style="color:#aa1111">"刘德华"</span>);
        <span style="color:#000000">params</span>.<span style="color:#000000">put</span>(<span style="color:#aa1111">"sex"</span>, <span style="color:#aa1111">"男"</span>);
        <span style="color:#000000">userService</span>.<span style="color:#000000">saveUser</span>(<span style="color:#000000">params</span>);
    }
}</span></span>

7. 后置通知

后置通知接口

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">AfterReturningAdvice</span> <span style="color:#770088">extends</span> <span style="color:#000000">AfterAdvice</span> {
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* Callback after a given method successfully returned.</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#008855">void</span> <span style="color:#000000">afterReturning</span>(<span style="color:#555555">@Nullable</span> <span style="color:#008855">Object</span> <span style="color:#000000">returnValue</span>, <span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>, <span style="color:#555555">@Nullable</span> <span style="color:#008855">Object</span> <span style="color:#000000">target</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span>;
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">AfterAdvice</span> <span style="color:#770088">implements</span> <span style="color:#000000">AfterReturningAdvice</span> {
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">afterReturning</span>(<span style="color:#008855">Object</span> <span style="color:#000000">returnValue</span>, <span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>, <span style="color:#008855">Object</span> <span style="color:#000000">target</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
        <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
        <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行完方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",得到返回值:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">returnValue</span>);
    }
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#117700"><</span><span style="color:#117700">bean</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"after"</span> <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.advice.AfterAdvice"</span> <span style="color:#117700">/></span>
<span style="color:#117700"><</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span>
    <span style="color:#aa5500"><!--</span>
            <span style="color:#aa5500">pointcut表示切点,也就是通知会在哪些位置触发</span>
            <span style="color:#aa5500">expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上</span>
            <span style="color:#aa5500">比如 * com.qf.spring.aop.service..*(..)</span>
            <span style="color:#aa5500">第一个 * 表示任意访问修饰符</span>
            <span style="color:#aa5500">com.qf.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类</span>
            <span style="color:#aa5500">*(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数</span>
        <span style="color:#aa5500">--></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:pointcut</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#0000cc">expression</span>=<span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span><span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"before"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"after"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span></span></span>

8. 异常抛出通知

异常抛出通知接口

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* There are not any methods on this interface, as methods are invoked by</span>
 <span style="color:#aa5500">* reflection. Implementing classes must implement methods of the form:</span>
 <span style="color:#aa5500">* void afterThrowing([Method, args, target], ThrowableSubclass);</span>
 <span style="color:#aa5500">* public void afterThrowing(Exception ex)</pre></span>
 <span style="color:#aa5500">* public void afterThrowing(RemoteException)</pre></span>
 <span style="color:#aa5500">* public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</span>
 <span style="color:#aa5500">* public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">ThrowsAdvice</span> <span style="color:#770088">extends</span> <span style="color:#000000">AfterAdvice</span> {
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">ExceptionAdvice</span> <span style="color:#770088">implements</span> <span style="color:#000000">ThrowsAdvice</span> {
​
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">afterThrowing</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>, <span style="color:#008855">Object</span> <span style="color:#000000">target</span>, <span style="color:#000000">Exception</span> <span style="color:#000000">ex</span>){
        <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
        <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行方法时:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",发生了异常:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">ex</span>.<span style="color:#000000">getMessage</span>());
    }
}
​
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">UserServiceImpl</span> <span style="color:#770088">implements</span> <span style="color:#000000">UserService</span> {
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">saveUser</span>(<span style="color:#000000">Map</span><span style="color:#981a1a"><</span><span style="color:#008855">String</span>, <span style="color:#008855">Object</span><span style="color:#981a1a">></span> <span style="color:#000000">params</span>) {
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"保存用户信息"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">params</span>);
        <span style="color:#770088">throw</span> <span style="color:#770088">new</span> <span style="color:#000000">RuntimeException</span>(<span style="color:#aa1111">"异常抛出演示"</span>);
    }
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#117700"><</span><span style="color:#117700">bean</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"exception"</span> <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.advice.ExceptionAdvice"</span> <span style="color:#117700">/></span>
<span style="color:#117700"><</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span>
    <span style="color:#aa5500"><!--</span>
            <span style="color:#aa5500">pointcut表示切点,也就是通知会在哪些位置触发</span>
            <span style="color:#aa5500">expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上</span>
            <span style="color:#aa5500">比如 * com.qf.spring.aop.service..*(..)</span>
            <span style="color:#aa5500">第一个 * 表示任意访问修饰符</span>
            <span style="color:#aa5500">com.qf.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类</span>
            <span style="color:#aa5500">*(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数</span>
        <span style="color:#aa5500">--></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:pointcut</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#0000cc">expression</span>=<span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span><span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"before"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"after"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"exception"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span></span></span>

9. 环绕通知

环绕通知接口

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#555555">@FunctionalInterface</span>
<span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">MethodInterceptor</span> <span style="color:#770088">extends</span> <span style="color:#000000">Interceptor</span> {
​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* Implement this method to perform extra treatments before and</span>
     <span style="color:#aa5500">* after the invocation.</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#555555">@Nullable</span>
    <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#555555">@Nonnull</span> <span style="color:#000000">MethodInvocation</span> <span style="color:#000000">invocation</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span>;
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">AroundAdvice</span> <span style="color:#770088">implements</span> <span style="color:#000000">MethodInterceptor</span> {
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#000000">MethodInvocation</span> <span style="color:#000000">invocation</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
        <span style="color:#000000">Method</span> <span style="color:#000000">method</span> <span style="color:#981a1a">=</span> <span style="color:#000000">invocation</span>.<span style="color:#000000">getMethod</span>(); <span style="color:#aa5500">//获取被拦截的方法对象</span>
        <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span> <span style="color:#981a1a">=</span> <span style="color:#000000">invocation</span>.<span style="color:#000000">getArguments</span>();<span style="color:#aa5500">//获取方法的参数</span>
        <span style="color:#008855">Object</span> <span style="color:#000000">target</span> <span style="color:#981a1a">=</span> <span style="color:#000000">invocation</span>.<span style="color:#000000">getThis</span>(); <span style="color:#aa5500">//获取代理对象</span>
        <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
        <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
        <span style="color:#770088">try</span> {
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备执行方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>));
            <span style="color:#008855">Object</span> <span style="color:#000000">returnValue</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">target</span>, <span style="color:#000000">args</span>);
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行完方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",得到返回值:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">returnValue</span>);
            <span style="color:#770088">return</span> <span style="color:#000000">returnValue</span>;
        } <span style="color:#770088">catch</span> (<span style="color:#000000">Throwable</span> <span style="color:#000000">t</span>){
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行方法时:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",发生了异常:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">t</span>.<span style="color:#000000">getMessage</span>());
            <span style="color:#770088">throw</span> <span style="color:#000000">t</span>;
        }
    }
}</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#aa5500"><!--配置通知对象--></span>
<span style="color:#aa5500"><!--  <bean id="before" class="com.qf.spring.aop.advice.BeforeAdvice" /></span>
    <span style="color:#aa5500"><bean id="after" class="com.qf.spring.aop.advice.AfterAdvice" /></span>
    <span style="color:#aa5500"><bean id="exception" class="com.qf.spring.aop.advice.ExceptionAdvice" />--></span>
<span style="color:#117700"><</span><span style="color:#117700">bean</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"around"</span> <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.advice.AroundAdvice"</span> <span style="color:#117700">/></span>
<span style="color:#117700"><</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span>
    <span style="color:#aa5500"><!--</span>
            <span style="color:#aa5500">pointcut表示切点,也就是通知会在哪些位置触发</span>
            <span style="color:#aa5500">expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上</span>
            <span style="color:#aa5500">比如 * com.qf.spring.aop.service..*(..)</span>
            <span style="color:#aa5500">第一个 * 表示任意访问修饰符</span>
            <span style="color:#aa5500">com.qf.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类</span>
            <span style="color:#aa5500">*(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数</span>
        <span style="color:#aa5500">--></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:pointcut</span> <span style="color:#0000cc">id</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#0000cc">expression</span>=<span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span><span style="color:#117700">/></span>
    <span style="color:#aa5500"><!--   <aop:advisor advice-ref="before" pointcut-ref="pc" /></span>
        <span style="color:#aa5500"><aop:advisor advice-ref="after" pointcut-ref="pc" /></span>
        <span style="color:#aa5500"><aop:advisor advice-ref="exception" pointcut-ref="pc" />--></span>
    <span style="color:#117700"><</span><span style="color:#117700">aop:advisor</span> <span style="color:#0000cc">advice-ref</span>=<span style="color:#aa1111">"around"</span> <span style="color:#0000cc">pointcut-ref</span>=<span style="color:#aa1111">"pc"</span> <span style="color:#117700">/></span>
<span style="color:#117700"></</span><span style="color:#117700">aop:config</span><span style="color:#117700">></span></span></span>

第三节 AspectJ

1. AspectJ 简介

AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP 语法,能够在编译期提供代码的织入。Spring通过集成AspectJ实现了以注解的方式定义增强类,大大减少了配置文件中的工作量

2. AspectJ 注解

  • @Aspect 切面标识

  • @Pointcut 切入点

  • @Before 前置通知

  • @AfterReturning 后置通知

  • @Around 环绕通知

  • @AfterThrowing 异常抛出通知

3. AspectJ 应用

3.1 通知类编写

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#555555">@Aspect</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">AspectJAdvice</span> {
​
    <span style="color:#555555">@Before</span>(<span style="color:#000000">value</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span>)
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">before</span>(<span style="color:#000000">JoinPoint</span> <span style="color:#000000">jp</span>){
        <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span> <span style="color:#981a1a">=</span> <span style="color:#000000">jp</span>.<span style="color:#000000">getArgs</span>(); <span style="color:#aa5500">//获取方法参数</span>
        <span style="color:#000000">Signature</span> <span style="color:#000000">signature</span> <span style="color:#981a1a">=</span> <span style="color:#000000">jp</span>.<span style="color:#000000">getSignature</span>(); <span style="color:#aa5500">//获取签名</span>
        <span style="color:#770088">if</span>(<span style="color:#000000">signature</span> <span style="color:#770088">instanceof</span> <span style="color:#000000">MethodSignature</span>){ <span style="color:#aa5500">//如果签名是方法签名</span>
            <span style="color:#000000">Method</span> <span style="color:#000000">method</span> <span style="color:#981a1a">=</span> ((<span style="color:#000000">MethodSignature</span>) <span style="color:#000000">signature</span>).<span style="color:#000000">getMethod</span>(); <span style="color:#aa5500">//获取方法</span>
            <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
            <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备执行方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>));
        }
    }
​
    <span style="color:#555555">@AfterReturning</span>(<span style="color:#000000">value</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span>, <span style="color:#000000">returning</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">"returnValue"</span>)
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">after</span>(<span style="color:#000000">JoinPoint</span> <span style="color:#000000">jp</span>, <span style="color:#008855">Object</span> <span style="color:#000000">returnValue</span>){
        <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span> <span style="color:#981a1a">=</span> <span style="color:#000000">jp</span>.<span style="color:#000000">getArgs</span>(); <span style="color:#aa5500">//获取方法参数</span>
        <span style="color:#000000">Signature</span> <span style="color:#000000">signature</span> <span style="color:#981a1a">=</span> <span style="color:#000000">jp</span>.<span style="color:#000000">getSignature</span>(); <span style="color:#aa5500">//获取签名</span>
        <span style="color:#770088">if</span>(<span style="color:#000000">signature</span> <span style="color:#770088">instanceof</span> <span style="color:#000000">MethodSignature</span>){ <span style="color:#aa5500">//如果签名是方法签名</span>
            <span style="color:#000000">Method</span> <span style="color:#000000">method</span> <span style="color:#981a1a">=</span> ((<span style="color:#000000">MethodSignature</span>) <span style="color:#000000">signature</span>).<span style="color:#000000">getMethod</span>(); <span style="color:#aa5500">//获取方法</span>
            <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
            <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行完方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",得到返回值:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">returnValue</span>);
        }
    }
​
    <span style="color:#555555">@AfterThrowing</span>(<span style="color:#000000">value</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span>, <span style="color:#000000">throwing</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">"t"</span>)
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">exception</span>(<span style="color:#000000">JoinPoint</span> <span style="color:#000000">jp</span>, <span style="color:#000000">Throwable</span> <span style="color:#000000">t</span>){
        <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span> <span style="color:#981a1a">=</span> <span style="color:#000000">jp</span>.<span style="color:#000000">getArgs</span>(); <span style="color:#aa5500">//获取方法参数</span>
        <span style="color:#000000">Signature</span> <span style="color:#000000">signature</span> <span style="color:#981a1a">=</span> <span style="color:#000000">jp</span>.<span style="color:#000000">getSignature</span>(); <span style="color:#aa5500">//获取签名</span>
        <span style="color:#770088">if</span>(<span style="color:#000000">signature</span> <span style="color:#770088">instanceof</span> <span style="color:#000000">MethodSignature</span>){ <span style="color:#aa5500">//如果签名是方法签名</span>
            <span style="color:#000000">Method</span> <span style="color:#000000">method</span> <span style="color:#981a1a">=</span> ((<span style="color:#000000">MethodSignature</span>) <span style="color:#000000">signature</span>).<span style="color:#000000">getMethod</span>(); <span style="color:#aa5500">//获取方法</span>
            <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
            <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行方法时:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",发生了异常:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">t</span>.<span style="color:#000000">getMessage</span>());
        }
    }
​
    <span style="color:#555555">@Around</span>(<span style="color:#aa1111">"execution(* com.qf.spring.aop.service..*(..))"</span>)
    <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">around</span>(<span style="color:#000000">ProceedingJoinPoint</span> <span style="color:#000000">pjp</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
        <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span> <span style="color:#981a1a">=</span> <span style="color:#000000">pjp</span>.<span style="color:#000000">getArgs</span>();<span style="color:#aa5500">//获取方法的参数</span>
        <span style="color:#008855">Object</span> <span style="color:#000000">target</span> <span style="color:#981a1a">=</span> <span style="color:#000000">pjp</span>.<span style="color:#000000">getTarget</span>(); <span style="color:#aa5500">//获取代理对象</span>
        <span style="color:#000000">Signature</span> <span style="color:#000000">signature</span> <span style="color:#981a1a">=</span> <span style="color:#000000">pjp</span>.<span style="color:#000000">getSignature</span>(); <span style="color:#aa5500">//获取签名</span>
        <span style="color:#770088">if</span>(<span style="color:#000000">signature</span> <span style="color:#770088">instanceof</span> <span style="color:#000000">MethodSignature</span>) { <span style="color:#aa5500">//如果签名是方法签名</span>
            <span style="color:#000000">Method</span> <span style="color:#000000">method</span> <span style="color:#981a1a">=</span> ((<span style="color:#000000">MethodSignature</span>) <span style="color:#000000">signature</span>).<span style="color:#000000">getMethod</span>(); <span style="color:#aa5500">//获取被拦截的方法对象</span>
            <span style="color:#008855">String</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getName</span>();
            <span style="color:#008855">String</span> <span style="color:#000000">className</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">getDeclaringClass</span>().<span style="color:#000000">getName</span>();
            <span style="color:#770088">try</span> {
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备执行方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>));
                <span style="color:#008855">Object</span> <span style="color:#000000">returnValue</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">target</span>, <span style="color:#000000">args</span>);
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行完方法:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",得到返回值:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">returnValue</span>);
                <span style="color:#770088">return</span> <span style="color:#000000">returnValue</span>;
            } <span style="color:#770088">catch</span> (<span style="color:#000000">Throwable</span> <span style="color:#000000">t</span>){
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"执行方法时:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">className</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">"."</span> <span style="color:#981a1a">+</span> <span style="color:#000000">methodName</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">",参数:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">Arrays</span>.<span style="color:#000000">toString</span>(<span style="color:#000000">args</span>) <span style="color:#981a1a">+</span> <span style="color:#aa1111">",发生了异常:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">t</span>.<span style="color:#000000">getMessage</span>());
                <span style="color:#770088">throw</span> <span style="color:#000000">t</span>;
            }
        }
        <span style="color:#770088">return</span> <span style="color:#221199">null</span>;
    }
}</span></span>

3.2 启用注解支持

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#117700"><</span><span style="color:#117700">bean</span>  <span style="color:#0000cc">class</span>=<span style="color:#aa1111">"com.qf.spring.aop.advice.AspectJAdvice"</span> <span style="color:#117700">/></span>
<span style="color:#aa5500"><!--配置通知对象--></span>
<span style="color:#aa5500"><!--  <bean id="before" class="com.qf.spring.aop.advice.BeforeAdvice" /></span>
    <span style="color:#aa5500"><bean id="after" class="com.qf.spring.aop.advice.AfterAdvice" /></span>
    <span style="color:#aa5500"><bean id="exception" class="com.qf.spring.aop.advice.ExceptionAdvice" />--></span>
<span style="color:#aa5500"><!--    <bean id="around" class="com.qf.spring.aop.advice.AroundAdvice" />--></span>
<span style="color:#aa5500"><!--<aop:config></span>
        <span style="color:#aa5500">&lt;!&ndash;</span>
            <span style="color:#aa5500">pointcut表示切点,也就是通知会在哪些位置触发</span>
            <span style="color:#aa5500">expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上</span>
            <span style="color:#aa5500">比如 * com.qf.spring.aop.service..*(..)</span>
            <span style="color:#aa5500">第一个 * 表示任意访问修饰符</span>
            <span style="color:#aa5500">com.qf.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类</span>
            <span style="color:#aa5500">*(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数</span>
        <span style="color:#aa5500">&ndash;&gt;</span>
        <span style="color:#aa5500"><aop:pointcut id="pc" expression="execution(* com.qf.spring.aop.service..*(..))"/></span>
     <span style="color:#aa5500">&lt;!&ndash;   <aop:advisor advice-ref="before" pointcut-ref="pc" /></span>
        <span style="color:#aa5500"><aop:advisor advice-ref="after" pointcut-ref="pc" /></span>
        <span style="color:#aa5500"><aop:advisor advice-ref="exception" pointcut-ref="pc" />&ndash;&gt;</span>
        <span style="color:#aa5500"><aop:advisor advice-ref="around" pointcut-ref="pc" /></span>
    <span style="color:#aa5500"></aop:config>--></span>
​
<span style="color:#aa5500"><!-- 启动AspectJ 注解  自动为类生成代理--></span>
<span style="color:#117700"><</span><span style="color:#117700">aop:aspectj-autoproxy</span> <span style="color:#0000cc">proxy-target-class</span>=<span style="color:#aa1111">"true"</span> <span style="color:#117700">/></span></span></span>

第四节 代理模式

代理模式一共分为两种: 静态代理和动态代理

1. 静态代理

静态代理模式由三个部分构成:

  • 一个公共的接口

  • 一个代理角色

  • 一个被代理角色

其中代理角色和被代理角色均需要实现公共接口。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 人类接口,描述人类购物</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">Person</span> {
    <span style="color:#aa5500">//购物</span>
    <span style="color:#008855">void</span> <span style="color:#000000">shopping</span>();
}
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 被代理的角色</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">ZhangSan</span> <span style="color:#770088">implements</span> <span style="color:#000000">Person</span>{
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">shopping</span>() {
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"购物"</span>);
    }
}
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 代理角色</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">PersonProxy</span> <span style="color:#770088">implements</span> <span style="color:#000000">Person</span>{
​
    <span style="color:#770088">private</span> <span style="color:#000000">ZhangSan</span> <span style="color:#000000">zhangSan</span>; <span style="color:#aa5500">//维护一个被代理的角色</span>
​
    <span style="color:#770088">public</span> <span style="color:#000000">PersonProxy</span>(<span style="color:#000000">ZhangSan</span> <span style="color:#000000">zhangSan</span>) {
        <span style="color:#770088">this</span>.<span style="color:#000000">zhangSan</span> <span style="color:#981a1a">=</span> <span style="color:#000000">zhangSan</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">shopping</span>() {
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理人准备代理购物"</span>);
        <span style="color:#000000">zhangSan</span>.<span style="color:#000000">shopping</span>();
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理人代理购物完毕"</span>);
    }
}
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 静态代理测试</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">StaticProxyTest</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
        <span style="color:#000000">Person</span> <span style="color:#000000">p</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">PersonProxy</span>(<span style="color:#770088">new</span> <span style="color:#000000">ZhangSan</span>());
        <span style="color:#000000">p</span>.<span style="color:#000000">shopping</span>();
    }
}</span></span>

思考:如果有多人想要代理购物,那么像PersonProxy这样的类就需要写多次;从编程的角度出发,这显然不合理。应该如何解决?

使用泛型进行解决。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">StaticProxy</span><span style="color:#981a1a"><</span><span style="color:#000000">T</span><span style="color:#981a1a">></span> <span style="color:#770088">implements</span> <span style="color:#000000">Person</span> {
​
    <span style="color:#770088">private</span> <span style="color:#000000">T</span> <span style="color:#000000">t</span>;
​
    <span style="color:#770088">public</span> <span style="color:#000000">StaticProxy</span>(<span style="color:#000000">T</span> <span style="color:#000000">t</span>) {
        <span style="color:#770088">this</span>.<span style="color:#000000">t</span> <span style="color:#981a1a">=</span> <span style="color:#000000">t</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">shopping</span>() {
        <span style="color:#aa5500">//判断当前对象是否继承或者实现了Person接口</span>
        <span style="color:#770088">if</span>(<span style="color:#981a1a">!</span><span style="color:#000000">Person</span>.<span style="color:#770088">class</span>.<span style="color:#000000">isAssignableFrom</span>(<span style="color:#000000">t</span>.<span style="color:#000000">getClass</span>()))
            <span style="color:#770088">throw</span> <span style="color:#770088">new</span> <span style="color:#000000">RuntimeException</span>(<span style="color:#000000">t</span>.<span style="color:#000000">getClass</span>().<span style="color:#000000">getName</span>() <span style="color:#981a1a">+</span> <span style="color:#aa1111">"没有实现Person接口"</span>);
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理人准备代理购物"</span>);
        ((<span style="color:#000000">Person</span>)<span style="color:#000000">t</span>).<span style="color:#000000">shopping</span>();
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理人代理购物完毕"</span>);
    }
}
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 静态代理测试</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">StaticProxyTest</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
<span style="color:#aa5500">//        Person p = new PersonProxy(new ZhangSan());</span>
<span style="color:#aa5500">//        p.shopping();</span>
        <span style="color:#000000">Person</span> <span style="color:#000000">p</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">StaticProxy</span><span style="color:#981a1a"><></span>(<span style="color:#770088">new</span> <span style="color:#000000">ZhangSan</span>());
        <span style="color:#000000">p</span>.<span style="color:#000000">shopping</span>();
    }
}</span></span>

思考:如果有多个接口想要代理,应该如何解决?

使用动态代理。

2. 动态代理

动态代理也分为两种:JDK 动态代理 和 CGLIB 动态代理

2.1 JDK 动态代理

JDK 动态代理是基于接口实现的,因此只能为实现了接口的类做代理。其实现原理是:在内存中生成一个代理类继承与 Proxy, 同时实现代理接口,然后在内存中进行编译,编译后使用类加载器将该代理类加载进来,从而创建一个代理对象。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">DynamicProxyTest</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
        <span style="color:#000000">Person</span> <span style="color:#000000">p</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">ZhangSan</span>();
        <span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">clazz</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Person</span>.<span style="color:#770088">class</span>;
        <span style="color:#000000">Class</span>[] <span style="color:#000000">interfaces</span> <span style="color:#981a1a">=</span> { <span style="color:#000000">clazz</span> };
        <span style="color:#000000">ClassLoader</span> <span style="color:#000000">loader</span> <span style="color:#981a1a">=</span> <span style="color:#000000">clazz</span>.<span style="color:#000000">getClassLoader</span>();
        <span style="color:#000000">InvocationHandler</span> <span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">InvocationHandler</span>() {
            <span style="color:#555555">@Override</span>
            <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#008855">Object</span> <span style="color:#000000">proxy</span>, <span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备进行代理购物"</span>);
                <span style="color:#770088">return</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">p</span>, <span style="color:#000000">args</span>);
            }
        };
        <span style="color:#000000">Person</span> <span style="color:#000000">proxy</span> <span style="color:#981a1a">=</span> (<span style="color:#000000">Person</span>) <span style="color:#000000">Proxy</span>.<span style="color:#000000">newProxyInstance</span>(<span style="color:#000000">loader</span>, <span style="color:#000000">interfaces</span>, <span style="color:#000000">handler</span>);
        <span style="color:#000000">proxy</span>.<span style="color:#000000">shopping</span>();
    }
}</span></span>

2.2 CGLIB 动态代理

CGLIB 动态代理是基于继承实现的,因此可以为任何类做代理。如果一个类实现了接口,通常会使用 JDK 动态代理,但也可以强制使用 CGLIB 动态代理。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">CgLibProxy</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
        <span style="color:#770088">final</span> <span style="color:#000000">Person</span> <span style="color:#000000">p</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">ZhangSan</span>();
        <span style="color:#aa5500">//创建字节码增强对象</span>
        <span style="color:#000000">Enhancer</span> <span style="color:#000000">enhancer</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">Enhancer</span>();
        <span style="color:#aa5500">//设置父类,需要继承</span>
        <span style="color:#000000">enhancer</span>.<span style="color:#000000">setSuperclass</span>(<span style="color:#000000">p</span>.<span style="color:#000000">getClass</span>());
        <span style="color:#aa5500">//需要注意:这里的InvocationHandler是CGLIB提供的net.sf.cglib.proxy.InvocationHandler</span>
        <span style="color:#000000">enhancer</span>.<span style="color:#000000">setCallback</span>(<span style="color:#770088">new</span> <span style="color:#000000">InvocationHandler</span>() {
            <span style="color:#555555">@Override</span>
            <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#008855">Object</span> <span style="color:#000000">proxy</span>, <span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">params</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备进行代理购物"</span>);
                <span style="color:#770088">return</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">p</span>, <span style="color:#000000">params</span>);
            }
        });
        <span style="color:#aa5500">//创建动态代理实例</span>
        <span style="color:#000000">Person</span> <span style="color:#000000">proxy</span> <span style="color:#981a1a">=</span> (<span style="color:#000000">Person</span>) <span style="color:#000000">enhancer</span>.<span style="color:#000000">create</span>();
        <span style="color:#000000">proxy</span>.<span style="color:#000000">shopping</span>();
    }
}</span></span>

Spring CGLIB

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SpringCgLibProxy</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
        <span style="color:#770088">final</span> <span style="color:#000000">Person</span> <span style="color:#000000">p</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">ZhangSan</span>();
        <span style="color:#aa5500">//创建字节码曾强对象</span>
        <span style="color:#000000">Enhancer</span> <span style="color:#000000">enhancer</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">Enhancer</span>();
        <span style="color:#aa5500">//设置父类,需要继承</span>
        <span style="color:#000000">enhancer</span>.<span style="color:#000000">setSuperclass</span>(<span style="color:#000000">p</span>.<span style="color:#000000">getClass</span>());
        <span style="color:#000000">enhancer</span>.<span style="color:#000000">setCallback</span>(<span style="color:#770088">new</span> <span style="color:#000000">MethodInterceptor</span>() {
            <span style="color:#555555">@Override</span>
            <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">intercept</span>(<span style="color:#008855">Object</span> <span style="color:#000000">o</span>, <span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">params</span>, <span style="color:#000000">MethodProxy</span> <span style="color:#000000">methodProxy</span>) <span style="color:#770088">throws</span> <span style="color:#000000">Throwable</span> {
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"准备进行代理购物"</span>);
                <span style="color:#770088">return</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">p</span>, <span style="color:#000000">params</span>);
            }
        });
        <span style="color:#aa5500">//创建动态代理实例</span>
        <span style="color:#000000">Person</span> <span style="color:#000000">proxy</span> <span style="color:#981a1a">=</span> (<span style="color:#000000">Person</span>) <span style="color:#000000">enhancer</span>.<span style="color:#000000">create</span>();
        <span style="color:#000000">proxy</span>.<span style="color:#000000">shopping</span>();
    }
}</span></span>

2.3 区别

JDK 动态代理只能为实现了接口的类做代理, CGLIB 动态代理能够为所有的类做代理。JDK 动态代理的创建代理从效率上来说要比 CGLIB 动态代理快。Cglib不能对声明final的方法进行代理,因为final关键字修饰的方法不可被重写。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值