<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; AOP</title>
	<atom:link href="https://blog.gmem.cc/tag/aop/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Sat, 18 Apr 2026 12:23:57 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>AspectJ编程学习笔记</title>
		<link>https://blog.gmem.cc/aspectj-study-note</link>
		<comments>https://blog.gmem.cc/aspectj-study-note#comments</comments>
		<pubDate>Sun, 10 Apr 2011 02:20:04 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[AOP]]></category>
		<category><![CDATA[AspectJ]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[学习笔记]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=1697</guid>
		<description><![CDATA[<p>AOP基本概念 名词  含义 切面（方面，Aspect） 一个关注点的模块化，这个关注点实现可能横切（crosscutting）多个对象切面的例子包括：事务控制、日志记录、权限控制等在AspectJ中，切面表现为Java类，其源码具有AspectJ的特殊语法增强，使用ajc编译器编译 连接点（Joinpoint） 程序执行过程中明确的点，例如方法的调用开始、结束，或者特定的异常被抛出，横切（crosscutting）在连接点发生 通知（Advice） 在特定的连接点，AOP框架执行的操作 切入点（Pointcut） 切入点是这样的一种程序构造：包含一个连接点的集合、以及收集的连接点的上下文信息（例如方法参数、被执行方法和对象）。通知在这些切入点被触发。AOP框架允许开发者以多种方式定义切入点 引入（Introduction） 添加方法、字段、接口、注解等到被通知的类 目标对象（Target Object） 包含连接点的对象，也被称作被通知或被代理对象 AOP代理（AOP Proxy） AOP框架创建的对象，以目标对象为基础，织入了通知逻辑代理主要包括动态代理、字节码增强两种类型 AspectJ基础知识 AspectJ编译器能识别任何普通的Java代码，可以使用ajc编译.java文件 织入方式： <a class="read-more" href="https://blog.gmem.cc/aspectj-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/aspectj-study-note">AspectJ编程学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">AOP基本概念</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">名词</td>
<td style="text-align: center;"> 含义</td>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 200px;">切面（方面，Aspect）</td>
<td>一个关注点的模块化，这个关注点实现可能横切（crosscutting）多个对象切面的例子包括：事务控制、日志记录、权限控制等在AspectJ中，切面表现为Java类，其源码具有AspectJ的特殊语法增强，使用ajc编译器编译</td>
</tr>
<tr>
<td>连接点（Joinpoint）</td>
<td>程序执行过程中明确的点，例如方法的调用开始、结束，或者特定的异常被抛出，横切（crosscutting）在连接点发生</td>
</tr>
<tr>
<td>通知（Advice）</td>
<td>在特定的连接点，AOP框架执行的操作</td>
</tr>
<tr>
<td>切入点（Pointcut）</td>
<td>切入点是这样的一种程序构造：包含<strong>一个连接点的集合、以及收集的连接点的上下文信息</strong>（例如方法参数、被执行方法和对象）。通知在这些切入点被触发。AOP框架允许开发者以多种方式定义切入点</td>
</tr>
<tr>
<td>引入（Introduction）</td>
<td>添加方法、字段、接口、注解等到被通知的类</td>
</tr>
<tr>
<td>目标对象（Target Object）</td>
<td>包含连接点的对象，也被称作被通知或被代理对象</td>
</tr>
<tr>
<td>AOP代理（AOP Proxy）</td>
<td>AOP框架创建的对象，以目标对象为基础，织入了通知逻辑代理主要包括动态代理、字节码增强两种类型</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">AspectJ基础知识</span></div>
<ol>
<li>AspectJ编译器能识别任何普通的Java代码，可以使用ajc编译.java文件</li>
<li>织入方式：<br />
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td style="width: 100px;">源代码织入</td>
<td>织入器作为编译器的一部分，处理源代码，支持经典语法和注解语法。生成的字节码符合JVM规范，需要使用ajc代替javac</td>
</tr>
<tr>
<td>字节码织入</td>
<td>传递给织入器的是字节码。使用这种方式时，包含编译普通Java类、编译切面，织入3个步骤。</td>
</tr>
<tr>
<td>加载时织入</td>
<td>传递给织入器的是Java类字节码、切面类，以及aop.xml配置文件。</td>
</tr>
</tbody>
</table>
</li>
<li id="joinpoints-exposed-by-aspject">AspectJ暴露的连接点。<strong>只能在这些连接点上应用通知</strong>：<br />
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 80px; text-align: center;"> </td>
<td style="width: 120px; text-align: center;">暴露的连接点</td>
<td style="text-align: center;">代码表现</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2">方法</td>
<td>执行<br />Execution</td>
<td>方法体：该连接点环绕整个方法体的执行过程。这也是Spring AOP唯一支持的接入点类型。通知被织入方法体<br /> <strong>切入点语法：execution(方法签名)</strong></td>
</tr>
<tr>
<td>调用<br />Call</td>
<td>方法调用：该接入点发生在方法被调用时。通知被织入调用者代码处<br /> <strong>切入点语法：call(方法签名)</strong></td>
</tr>
<tr>
<td rowspan="2">构造器</td>
<td>执行<br />Execution</td>
<td>对象构造逻辑：该连接点环绕整个构造器的执行过程<br /> <strong>切入点语法：execution(构造器签名)</strong></td>
</tr>
<tr>
<td>调用<br />Call</td>
<td>对象构造调用<br /> <strong>切入点语法：call(构造器签名)</strong></td>
</tr>
<tr>
<td rowspan="2">字段访问</td>
<td>读访问<br />Read access</td>
<td>读取类或对象的字段<br /> <strong>切入点语法：get(字段签名)</strong></td>
</tr>
<tr>
<td>写访问<br />Read access</td>
<td>写入类或对象的字段<br /> <strong>切入点语法：set(字段签名)</strong></td>
</tr>
<tr>
<td>异常处理</td>
<td>处理器<br />Handler</td>
<td>处理异常的catch块<br /> <strong>切入点语法：handler(类型签名)</strong></td>
</tr>
<tr>
<td rowspan="3">初始化</td>
<td>类初始化<br />Class initialization</td>
<td>类加载，包括静态成员初始化部分<br /> <strong>切入点语法：staticinitialization(类型签名)</strong></td>
</tr>
<tr>
<td>对象初始化<br />Object initialization</td>
<td>在构造器中的对象初始化。包含：从一个父构造器返回，到当前构造器执行完成的部分（不包括调用父构造器的部分）<br /> <strong>切入点语法：initialization(构造器签名)</strong><br /> Spring的@Configurable即为此例 。下面的例子解释了连接点的范围：<br />
<pre class="crayon-plain-tag">public class SavingsAccount extends Account
{
    private boolean isOverdraft;
    private int     minimumBalance;
    //构造器1
    public SavingsAccount( int accountNumber, 
                                 boolean isOverdraft )
    {
        super( accountNumber );
        //对象初始化连接点：构造器1，2
        this.isOverdraft = isOverdraft;
    }
    //构造器2
    public SavingsAccount( int accountNumber )
    {
        //对象初始化连接点：构造器2
        this( accountNumber, false );
        //对象初始化连接点：构造器2
        this.minimumBalance = 25;
    }
}</pre>
</td>
</tr>
<tr>
<td>对象预初始化<br />pre-initialization</td>
<td>在构造器中的对象初始化之前：用的很少，从当前构造器调用，到父构造器调用结束为止<br /> <strong>切入点语法：preinitialization(构造器签名)</strong></td>
</tr>
<tr>
<td>通知</td>
<td>执行<br />Execution</td>
<td>通知的执行：环绕某个通知的整个执行。可以对通知进行通知<br /> <strong>切入点语法：adviceexecution()</strong></td>
</tr>
</tbody>
</table>
</li>
<li>AspectJ切入点声明语法<br />
<pre class="crayon-plain-tag">/**
 * 切入点声明语法，包含5个部分：
 * 访问标识符：例如 public
 * 关键字：pointcut
 * 切入点名称()
 * 切入点类型：例如execution、call，参考：第3点
 * 切入点签名：根据切入点类型不同，可能是类型签名、方法签名、字段签名
 */
public pointcut secureAccess(): execution(* MessageCommunicator.deliver(..));</pre></p>
<p>AspectJ支持仅有切入点名称，而没有类型和签名的切入点，通常使用在抽象切面中。<br /> AspectJ支持匿名切入点，作为通知签名的一部分存在。<br /> AspectJ支持使用 !、 &amp;&amp; 、|| 来作为切入点的运算符</p>
</li>
<li>AspectJ通知定义语法：由<strong>通知声明、切入点定义、通知体三部分</strong>组成<br />
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">组成</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">整体语法形式：<br />
<pre class="crayon-plain-tag">[@AdviceName("通知的名称")]
[返回值] 通知类型([上下文信息]) [returning|throwing] [throws 通知抛出的异常] : 切入点定义([上下文信息]) {
    通知体
}</pre>
</td>
</tr>
<tr>
<td>通知声明</td>
<td>通知类型可以是：before, after 或 around，其中around需要声明返回值通知声明可以指定通知体可用的上下文信息，包括：执行对象通知声明可以指定通知体可能抛出的异常</td>
</tr>
<tr>
<td>切入点定义</td>
<td>位于冒号之后，任何匹配切入点定义的连接点上都会执行本通知</td>
</tr>
<tr>
<td style="width: 100px;">通知体</td>
<td>可以访问一系列特殊的变量或关键字，例如：proceed、thisJoinPoint、thisJoinPointStaticPart、 thisEnclosingJoinPointStaticPart</td>
</tr>
</tbody>
</table>
</li>
<li>AspectJ支持的通知类型<br />
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">通知类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Before advice</td>
<td>在连接点执行之前执行，如果抛出异常，那么连接点不被执行</td>
</tr>
<tr>
<td>After advice</td>
<td>在连接点执行之后执行，根据连接点的执行结果，具有3个子类型：After (finally)：在连接点执行之后执行，不论其结果，通知声明语法：<strong>after()</strong>After returning：在连接点执行成功后执行，通知声明语法：<strong>after() returning(&lt;ReturnType returnObject&gt;)</strong>After throwing：在连接点执行失败后执行，通知声明语法：<strong>after() throwing(&lt;ExceptionType exceptionObject&gt;)</strong></td>
</tr>
<tr>
<td>Around advice</td>
<td>环绕连接点的执行过程，具有修改连接点执行上下文的能力，可以用来：
<ol>
<li>在连接点之前/之后添加额外的逻辑，例如性能分析</li>
<li>跳过原先逻辑还执行备选的逻辑，例如缓存。只要不调用proceed()，即跳过</li>
<li>使用try-catch包裹原先逻辑，提供异常处理策略，例如事务管理</li>
</ol>
</td>
</tr>
</tbody>
</table>
</li>
</ol>
<div class="blog_h2"><span class="graybg">AspectJ切入点签名语法详解</span></div>
<div class="blog_h3"><span class="graybg">通配符</span></div>
<p>通配符用于匹配一系列的连接点。</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td> *</td>
<td>匹配点号.范围内的任意数目的字符在类型签名中：表示部分包名或者部分类名在其他地方：表示部分的方法或者字段名</td>
</tr>
<tr>
<td> ..</td>
<td>匹配任意数目的字符，包括任意数量的点号在类型签名中：表示任意直接、间接的子包在方法签名中：表示任意数量的方法参数</td>
</tr>
<tr>
<td> +</td>
<td>作为类型签名的后缀，表示包含任意的子类型</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">类型签名语法</span></div>
<p>这里的类型泛指：类、接口、注解、切面、基本类型<br /> <strong>基本类型签名示例：</strong></p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Account</td>
<td>仅匹配Account类型</td>
</tr>
<tr>
<td>*Account</td>
<td>匹配任何类名以Account结尾的类型</td>
</tr>
<tr>
<td>java.*.Date</td>
<td>匹配java的<strong>直接子包</strong>中的Date</td>
</tr>
<tr>
<td>java..*</td>
<td>匹配java包及其任意子包中的任意类型</td>
</tr>
<tr>
<td>javax..*Model+</td>
<td>匹配java包及其任意子包中的任意以Model结尾的类型，及其子类型</td>
</tr>
</tbody>
</table>
<p><strong>基于注解的类型签名示例：</strong><br /> 由于Java不支持注解的继承，所有注解名后不会出现加号。注意注解必须运行时可见（<pre class="crayon-plain-tag">RetentionPolicy.RUNTIME</pre> ）</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>@Secured Account</td>
<td>标注了@Secured注解的Account类型</td>
</tr>
<tr>
<td>@Sensitive *</td>
<td>标注了@Sensitive注解的任何类型</td>
</tr>
<tr>
<td>@Business* Customer+</td>
<td>Customer及其子类型，且标注了Business开头的注解</td>
</tr>
</tbody>
</table>
<p><strong>基于泛型的类型签名示例：</strong></p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Map&lt;Long,Account&gt;</td>
<td>仅仅匹配Map&lt;Long,Account&gt;类型</td>
</tr>
<tr>
<td>*&lt;Account&gt;</td>
<td>任何把Account作为其泛型参数的类型</td>
</tr>
<tr>
<td>Collection&lt;@Sensitive *&gt;</td>
<td>Collection类型，其泛型参数类必须被标注了@Sensitive</td>
</tr>
<tr>
<td>Collection&lt;? extends Account&gt;</td>
<td>Collection类型，其泛型参数类必须是Account或者其子类</td>
</tr>
<tr>
<td>Collection&lt;? super Account&gt;</td>
<td>Collection类型，其泛型参数类必须是Account的父类</td>
</tr>
</tbody>
</table>
<p><strong>联合类型签名示例：</strong></p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>!Collection</td>
<td>除了Collection的任何类型</td>
</tr>
<tr>
<td>Collection || Map</td>
<td>Collection或者Map类型</td>
</tr>
<tr>
<td>java.util.RandomAccess+ &amp;&amp; java.util.List+</td>
<td>任何同时实现了这两个接口的子类，例如ArrayList</td>
</tr>
<tr>
<td>!@Secured *</td>
<td>任何没有标注@Secured 的类型</td>
</tr>
<tr>
<td>@Secured @Sensitive *</td>
<td>任何同时标注了@Secured @Sensitive的类型</td>
</tr>
<tr>
<td>@(Secured || Sensitive) *</td>
<td>任何标注了@Secured @Sensitive之一的类型</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">方法和构造器签名语法</span></div>
<p>构造器签名与方法类似，但是：</p>
<ol>
<li>由于构造器没有返回值，因此不得在签名中指定返回值</li>
<li>由于构造器不能为静态，故不得添加static关键字</li>
<li>由于构造器没有名称，必须使用new作为其名称部分</li>
</ol>
<p><a href="/wp-content/uploads/2011/04/method-pattern-format.jpg"><img src="https://blog.gmem.cc/wp-content/uploads/2011/04/method-pattern-format.jpg" alt="" /></a><br /> <strong>基本方法签名示例：</strong></p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>public void Account.set*(*)</td>
<td>Account类中，包含单个参数、返回类型为void、以set开头的公共方法</td>
</tr>
<tr>
<td>public void Account.*()</td>
<td>Account类中，任何返回类型为void、没有参数的公共方法</td>
</tr>
<tr>
<td>public * Account.*()</td>
<td>Account类中，任何没有参数的公共方法</td>
</tr>
<tr>
<td>public * Account.*(..)</td>
<td>Account类中，包含任意参数（包括0参）的公共方法</td>
</tr>
<tr>
<td>* Account.*(..)</td>
<td><span style="color: #000000;">Account类中，包含任意参数（包括0参）的方法</span></td>
</tr>
<tr>
<td>* *.*(..)* *(..)</td>
<td>任意方法</td>
</tr>
<tr>
<td>!public * Account.*(..)</td>
<td>任何Account类的非公共方法</td>
</tr>
<tr>
<td>* *(..) throws SQLException</td>
<td>任何声明抛出SQLException的方法</td>
</tr>
<tr>
<td>* Account+.*(..)</td>
<td>Account及其子类的任何方法</td>
</tr>
<tr>
<td>* java.io.Reader.read(char[],..)</td>
<td>java.io.Reader类的read方法，第一个参数必须为char[]</td>
</tr>
<tr>
<td>* javax..*.add*Listener(EventListener+)</td>
<td>javax的任意子包中的类的方法，必须以add开头、Listener结尾，且包含唯一的参数EventListener类型及其子类型</td>
</tr>
<tr>
<td>* java.io.PrintStream.printf(String,Object...)</td>
<td>java.io.PrintStream的printf方法，第一个参数为字符串，第二个参数为Object...</td>
</tr>
<tr>
<td>Account AccountService.*(..)</td>
<td>AccountService类中任何返回AccountService的方法</td>
</tr>
<tr>
<td>public Account.new()</td>
<td>匹配Account类的0参公共构造器</td>
</tr>
</tbody>
</table>
<p><strong>基于注解的方法签名示例：</strong></p>
<p>注意注解必须运行时可见（<pre class="crayon-plain-tag">RetentionPolicy.RUNTIME</pre> ）</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 250px; text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>@Secured * *(..)</td>
<td>任何标注了@Secured的方法</td>
</tr>
<tr>
<td>@Secured @Transactional * *(..)</td>
<td><span style="color: #000000;">任何同时标注了@</span><span style="color: #000000;">Secured、@Transactional 的方法</span></td>
</tr>
<tr>
<td>@(Secured || Transactional) * *(..)</td>
<td><span style="color: #000000;">任何标注了@</span><span style="color: #000000;">Secured或者@Transactional 的方法</span></td>
</tr>
<tr>
<td>(@Sensitive *) *(..)</td>
<td>任何返回值类型上标注了@Sensitive的方法</td>
</tr>
<tr>
<td>* (@BusinessEntity *).*(..)</td>
<td>任何标注了@BusinessEntity的类的任何方法</td>
</tr>
<tr>
<td>* *(@RequestParam (*))</td>
<td>任何包含了单个参数，且参数上标注了@RequestParam 的方法，例如：void show(@RequestParam Long id)</td>
</tr>
<tr>
<td>* *(@Sensitive *)* *((@Sensitive *))</td>
<td>任何包含了单个参数，且参数的类型上标注了@RequestParam 的方法，例如：void create(ClassAnnotatedWithSensitive obj)</td>
</tr>
<tr>
<td>* *(@RequestParam(@Sensitive *))</td>
<td><span style="color: #000000;">void create(@RequestParam </span><span style="color: #000000;">ClassAnnotatedWithSensitive </span><span style="color: #000000;">obj</span><span style="color: #000000;">)</span></td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">字段签名语法</span></div>
<p>字段签名中，使用get表示读字段，set表示写字段：<br /> <img src="https://blog.gmem.cc/wp-content/uploads/2011/04/field-pattern-format.jpg" alt="" /><br /> <strong>字段签名示例：</strong></p>
<p>注意注解必须运行时可见（<pre class="crayon-plain-tag">RetentionPolicy.RUNTIME</pre> ）</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">签名</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>private double Account.balance</td>
<td>Account类的私有double类型的balance字段</td>
</tr>
<tr>
<td>* Account.*</td>
<td>Account类的任何字段</td>
</tr>
<tr>
<td>* Account+.*</td>
<td>Account类及其子类的任何字段</td>
</tr>
<tr>
<td>@Sensitive * *</td>
<td>字段上标注了@Sensitive的任何字段</td>
</tr>
<tr>
<td>(@Sensitive *) *.*</td>
<td>字段的类型上标注了@Sensitive的任何字段</td>
</tr>
<tr>
<td>* (@Sensitive *).*</td>
<td>标注了@Sensitive的类的任何字段</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">AspectJ切入点的实现</span></div>
<p>AspectJ提供几种切入点指示器（designators），配合上面所述的切入点签名，即组成切入点。<br /> 切入点通过两种方式与连接点进行匹配：</p>
<ol>
<li>类型限定的切入点：直接映射到某个类型的连接点，参考<a href="#joinpoints-exposed-by-aspject">AspectJ暴露的连接点</a>一节中的“切入点语法”部分</li>
<li>无类型的切入点：根据指定的信息，来选择特定的连接点。这些信息可以是：连接点上下文的运行时类型、控制流，或者此法作用域（lexical scope）</li>
</ol>
<div class="blog_h3"><span class="graybg">类型限定的切入点</span></div>
<p>匹配：指定类型和签名的连接点<br /> Spring：仅支持execution类型<br /> 静态确定：是</p>
<div class="blog_h3"><span class="graybg">无类型的切入点</span></div>
<p>不去限定连接点的类型，还是通过若干种规则去匹配多种连接点。</p>
<p>Spring虽然仅仅支持execution一种类型的连接点，但是却支持多种方式来选择它们</p>
<p><strong>基于控制流的切入点</strong><br /> 这一类的切入点匹配 <strong>处于其他切入点的控制流之中</strong> 的连接点。这句话比较难以理解，以贷款业务为例，假设Account.debit()方法调用Account.getBalance()进行余额查询，那么，可以说：getBalance在debit的控制流之中。类似的，字段访问、异常处理，也可以处于某个切入点的控制流之中。<br /> Spring不支持这一类切入点，不能静态确定（需要运行时判断）<br /> 包含两种切入点类型：cflow、cflowbelow，后者不包括启动控制流的连接点本身</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 300px; text-align: center;">切入点</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>cflow(execution(* Account.debit(..)))</td>
<td>匹配所有这样的连接点：位于Account的任何debit方法的控制流之中，包括debit方法本身</td>
</tr>
<tr>
<td>cflowbelow(execution(* Account.debit(..)))</td>
<td>匹配所有这样的连接点：位于Account的任何debit方法的控制流之中，不包括debit方法本身</td>
</tr>
<tr>
<td>cflow(execution(@Transactional * *(..)))</td>
<td>匹配所有位于配置@Transactional方法的控制流之中的任何连接点</td>
</tr>
<tr>
<td>cflow(transacted())</td>
<td>匹配切入点transacted所选择的任何连接点的控制流之中的连接点</td>
</tr>
</tbody>
</table>
<p>下面以事务管理的场景为例，来说明控制流切入点的用法：</p>
<pre class="crayon-plain-tag">package cc.gmem.aj.cflow;
public class Account
{
    //两个方法均具有事务注解，但是，我们需要在“最外层”来启动事务
    //需要忽略内层的事务注解
    @Transactional
    public void debit()
    {
        getBalance();
    }
    @Transactional
    public void getBalance()
    {
    }
}</pre><br />
<pre class="crayon-plain-tag">public aspect TransactionAspect
{
    //匹配所有标注了@Transactional的方法执行
    private pointcut transacted() : execution(@Transactional * *(..));
    //匹配这样的@Transactional方法：不在@Transactional方法的所确定的控制流的内部
    private pointcut topLevelTransacted() : transacted() &amp;&amp; !cflowbelow(transacted());

    Object around() : topLevelTransacted() {
        System.out.println( "启动事务" );
        Object ret = proceed();
        System.out.println( "结束事务" );
        return ret;
    }
}</pre>
<p><strong>基于词法结构的切入点</strong><br /> 匹配：在指定类型、方法、构造器内的连接点<br /> Spring：支持within()，不支持withincode()<br /> 可以静态确定<br /> 包含两种切入点类型：within(类型签名)、withincode(构造器签名|方法签名)</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">切入点</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>within(Account)</td>
<td>Account类，及其嵌套类中的任何连接点</td>
</tr>
<tr>
<td>within(Account+)</td>
<td>Account类（或者子类），及其嵌套类中的任何连接点</td>
</tr>
<tr>
<td>within(@javax.persistence.Entity *)</td>
<td>标注了的@Entity类，及其嵌套类中的任何连接点</td>
</tr>
<tr>
<td>withincode(* Account.debit(..))</td>
<td>Account的debit方法中的任何连接点</td>
</tr>
</tbody>
</table>
<p><strong>执行对象切入点</strong><br /> 根据<strong>运行时对象类型</strong>来选择连接点。并且，可以收集执行对象——<strong>双重功能</strong>。</p>
<p>包含两种类型：this(&lt;Type or ObjectIdentifier&gt;)、target(&lt;Type or ObjectIdentifier&gt;)，前者匹配this引用为指定类型的连接点；后者匹配方法调用target为指定类型的连接点。</p>
<p>该类切入点<strong>不支持类型通配，也不支持泛型</strong>。该类切入点<strong>不会匹配任何静态方法的执行连接点</strong>。</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">切入点</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>this(Account)</td>
<td>任何连接点，只要表达式 this instnaceof Account为真，即匹配</td>
</tr>
<tr>
<td>target(Account)</td>
<td>任何（通常是方法调用类型）连接点，只要被调用的方法所属对象 instanceof Account，即匹配</td>
</tr>
</tbody>
</table>
<p>注意区别：<br /> execution(* Account.*(..)) 匹配Account类的任何静态、成员方法<br /> execution(* *.*(..))  &amp;&amp; this(Account)：匹配Account及其子类的任何成员方法<br /> <strong>参数切入点</strong><br /> 该类切入点根据<strong>连接点的参数对象的运行时类型</strong>来匹配。并且，可以收集匹配的参数对象。<br /> Spring支持此类切入点。无法静态确定。<br /> 所谓参数对象，根据连接点不同，含义也不同：</p>
<ol>
<li>对于方法、构造器连接点，参数对象即方法参数</li>
<li>对于异常处理连接点，参数对象即被处理的异常</li>
<li>对于字段写入连接点，参数对象即被写入的值</li>
</ol>
<p>该类切入点的形式为：args(Type or ObjectIdentifier, ..)</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">切入点</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>args(Account, .., int)</td>
<td>任何方法或者构造器，第一个参数的运行时类型为Account或其子类型，最后一个参数的运行时类型为int</td>
</tr>
<tr>
<td>args(RemoteException)</td>
<td>任何具有单个RemoteException类型参数的连接点。</td>
</tr>
</tbody>
</table>
<p><strong>基于注解的切入点</strong></p>
<p>注意：注解本身被标注的注解影响AOP行为：</p>
<ol>
<li>@Retention(RetentionPolicy.RUNTIME)：只有设置为运行时保留，才能在运行时进行匹配性判断</li>
<li>@<span style="color: #000000;">Inherited：只有标注了此注解，子编程元素（类、方法）才能继承父元素上标注的注解</span></li>
</ol>
<p>AspectJ支持根据类型、方法、字段所标注的注解来匹配连接点</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 350px; text-align: center;">切入点</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>@this(TypePattern or ObjectIdentifier)</td>
<td>任何连接点，只要this的类上具有TypePattern指定的注解注意：如果注解是@Inherited的，父类上有指定注解即可</td>
</tr>
<tr>
<td>@target(TypePattern or ObjectIdentifier)</td>
<td>任何连接点，只要target的类上具有TypePattern指定的注解</td>
</tr>
<tr>
<td>@args(TypePattern or ObjectIdentifier, ..)</td>
<td>任何连接点，只要参数的类上具有TypePattern指定的注解</td>
</tr>
<tr>
<td>@within(TypePattern or ObjectIdentifier)</td>
<td>任何连接点，只要在具有TypePattern指定的注解的类型的词法作用域内</td>
</tr>
<tr>
<td>@withincode(TypePattern or ObjectIdentifier)</td>
<td>任何连接点，只要在具有TypePattern指定的注解的构造器或方法的词法作用域内</td>
</tr>
<tr>
<td>@annotation(TypePattern or ObjectIdentifier)</td>
<td>任何连接点，只要目标上具有指定的注解目标的含义：
<ol>
<li>对于方法、构造器、通知执行连接点：目标为对应的程序元素</li>
<li>对于字段访问、异常处理连接点：目标为被访问的字段或者异常</li>
<li>对于初始化、预初始化连接点：目标为匹配的起始调用(first-called)构造器</li>
<li>对于静态初始化连接点：目标为被初始化的类</li>
</ol>
</td>
</tr>
</tbody>
</table>
<p><strong>条件检查切入点</strong><br /> 此类切入点在连接点上的一些条件检查。通常和其它切入点联合使用</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">切入点</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>if(debug)</td>
<td>任何debug静态字段（位于切面类）设置为true的连接点</td>
</tr>
<tr>
<td>if(System.currentTimeMillis() &gt; triggerTime)</td>
<td>任何发生在triggerTime以后的连接点</td>
</tr>
<tr>
<td>if(circle.getRadius() &lt; 5)</td>
<td>任何连接点，只要circle的radius小于5。circle必须是被收集的上下文对象，或者切面的静态字段。</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">AspectJ切面类基本结构</span></div>
<p>本段代码示意经典的AspectJ语法：</p>
<pre class="crayon-plain-tag">package cc.gmem.aj;

//切面的声明
public aspect SecurityAspect
{

    private Authenticator auth = new Authenticator();

    //切入点的声明
    pointcut secureAccess(): execution(* MessageCommunicator.deliver(..));
    //通知声明：针对secureAccess切入点的before通知
    before() : secureAccess() {
        System.out.println( "Checking and authenticating user" );
        auth.doAuthenticate();
    }
}</pre>
<p>本段代码示意基于注解的AspectJ语法：<br /> 基于注解的语法，可以作为普通Java类编译，并且使用LTW（加载时织入）机制，在类被加载到JVM中时进行织入。</p>
<pre class="crayon-plain-tag">package cc.gmem.aj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SecurityAspect
{

    private Authenticator auth = new Authenticator();

    @Pointcut ( "execution(* cc.gmem.aj.MessageCommunicator.deliver(..))" )
    public void secureAccess()
    {
    }

    @Before ( "secureAccess()" )
    public void secure()
    {
        System.out.println( "Checking and authenticating user" );
        auth .doAuthenticate();
    }
}</pre>
<div class="blog_h2"><span class="graybg">AspectJ切面示例</span></div>
<p>环绕通知的简单例子，注意proceed()的使用：</p>
<pre class="crayon-plain-tag">public aspect SecurityAspect
{
    private Authenticator auth = new Authenticator();

    pointcut secureAccess(): execution(* MessageCommunicator.deliver(..));

    Object around() : secureAccess() {
        long start = System.nanoTime();
        //伪关键字proceed，用于继续执行被通知的目标方法
        Object ret = proceed();
        //thisJoinPointStaticPart是通知可用变量之一，可以获取连接点的静态信息，例如方法名
        System.out.println( thisJoinPointStaticPart.getSignature() + " took " + ( end - start ) + " nanoseconds" );
        return ret;
    }
}</pre>
<p>上下文收集的简单例子：</p>
<pre class="crayon-plain-tag">package cc.gmem.aj;

import java.sql.Connection;
import java.sql.SQLException;

public aspect ConnectionAspect
{
    //匹配Connection的任何抛出SQLException方法的调用
    //被调用（target）的Connection对象收集为connection变量。
    pointcut connectionOperation( Connection connection )//独立定义的切入点，必须使用这种参数声明来传递上下文信息给通知
        : call(* Connection.*(..) throws SQLException) &amp;&amp; target(connection);

    Object around( Connection connection ) : connectionOperation(connection) {//传递了上下文
        long startTime = System.nanoTime();
        Object ret = proceed();
        //thisJoinPoint是通知体可用的变量之一
        System.out.println( "Operation " + thisJoinPoint + " on " + connection + " took " + ( System.nanoTime() - startTime ) );
        return ret;
    }
}</pre>
<p>上下文收集注解的例子：</p>
<pre class="crayon-plain-tag">before(Secured secured ) : 
    //匹配任何方法执行
    execution(* *(..)) 
    //匹配连接点目标————即方法本身————上具有@secured注解的连接点
    &amp;&amp; @annotation(secured)
{
    //注解可以像普通对象一样被使用
    checkPermission(secured.permission());
}</pre>
<p>&nbsp;</p>
<p>上下文收集的另外一个例子，注意传递给目标的参数被收集，可被通知体使用：</p>
<pre class="crayon-plain-tag">package cc.gmem.aj;

public aspect AccountAspect
{

    boolean processOverdraft( Account account, float amount )
    {
        return true;
    }

    void around( Account account, float amount ) throws InsufficientBalanceException
    //匹配任何对Account.debit(float) throws InsufficientBalanceException方法的调用
        : call(void Account.debit(float) throws InsufficientBalanceException)
        &amp;&amp; target(account) &amp;&amp; args(amount)//收集的上下文信息作为局部变量使用
    {
        try
        {
            proceed( account, amount );//传递给目标方法的参数，可以任意修改
        }
        catch ( InsufficientBalanceException ex )
        {
            //进行透支处理
            if ( !processOverdraft( account, amount ) )
            {
                throw ex;
            }
        }
    }
}</pre>
<div class="blog_h2"><span class="graybg">进行加载时织入（LTW）</span></div>
<p>在classpath的META-INF/aop.xml中，声明需要织入的切面：</p>
<pre class="crayon-plain-tag">&lt;aspectj&gt;
    &lt;aspects&gt;
        &lt;aspect name="cc.gmem.aj.SecurityAspect"/&gt;
    &lt;/aspects&gt;
&lt;/aspectj&gt;</pre>
<p>通过指定JVM启动参数-javaagent来启用LTW：</p>
<pre class="crayon-plain-tag">-javaagent:%ASPECTJ_HOME%\lib\aspectjweaver.jar</pre>
<p>如果使用Spring，可以通过配置，避免修改JVM启动参数</p>
<div class="blog_h2"><span class="graybg">AspectJ横切构造（crosscutting construct）</span></div>
<div class="blog_h3"><span class="graybg">通用横切构造</span></div>
<p>AspectJ支持连接点、切入点、切面等构造。</p>
<div class="blog_h3"><span class="graybg">动态横切构造：修改行为</span></div>
<p>AspectJ的动态横切构造依赖于“通知”实现。通知包括Before、After、Around几种。</p>
<p><b>收集连接点上下文</b></p>
<p>连接点上下文包括两种类型：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td> 连接点牵涉的对象</td>
<td>切入点：this(), target(), args()可以用于收集相应的对象可以使用类型（Type）或者对象名称（ObjectIdentifier）两种方式来收集，对于后者，必须在通知声明处声明相应的对象</td>
</tr>
<tr>
<td>连接点关联的注解</td>
<td>切入点：@this(), @target(), @args(), @annotation(),@within(), @withincode()可用于收集相应的注解</td>
</tr>
</tbody>
</table>
<p><strong>通过反射访问连接点上下文</strong></p>
<p>作为备选方式，AspectJ提供了基于反射机制的访问静态（不会随着连接点执行而变化的）、动态连接点上下文信息的方法。这种方式可以完全替换上一条进行动态上下文收集，但是具有以下缺点：</p>
<ol>
<li>性能较为低下</li>
<li>缺少静态语法检查</li>
<li>比较笨重</li>
</ol>
<p>通过AspectJ提供的通知体可用的特殊变量来使用这种反射API：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;">变量</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>thisJoinPoint</td>
<td>变量类型为JoinPoint。可以访问目标对象、执行对象、参数的信息，包括相应的注解信息。可以通过getStaticPart()方法访问静态信息</td>
</tr>
<tr>
<td>thisJoinPointStaticPart</td>
<td>变量类型为JoinPoint.StaticPart。可以访问源码位置、连接点类型、连接点签名。</td>
</tr>
<tr>
<td>thisEnclosingJoinPointStaticPart</td>
<td>变量类型为JoinPoint.StaticPart。用于访问包围的连接点静态上下文，所谓“包围的连接点”依连接点类型而不同，例如：
<ol>
<li>对于方法调用，包围连接点是caller的执行</li>
<li>对于异常处理，包围连接点是try-catch块所在方法</li>
</ol>
</td>
</tr>
</tbody>
</table>
<p><strong>两种访问连接点上下文的方式的对照表</strong></p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">切入点语法</td>
<td style="text-align: center;"> 反射方式</td>
</tr>
</thead>
<tbody>
<tr>
<td>pointcut pc(Account acc) : this(acc)</td>
<td>Account acc = (Account)thisJoinPoint.getThis()</td>
</tr>
<tr>
<td>pointcut pc(Account acc): target(acc)</td>
<td>Account acc = (Account)thisJoinPoint.getTarget()</td>
</tr>
<tr>
<td>pointcut pc(Account acc,Customer cust): args(acc, cust)</td>
<td>Object[] arguments = thisJoinPoint.getArgs();<br /> Account acc = (Account)arguments[0];<br /> Customer cust = (Customer)arguments[1];</td>
</tr>
<tr>
<td>poincut pc(Secure sec) : @this(annot);</td>
<td>Secure sec = thisJoinPoint.getThis().getClass().<br /> getAnnotation(Secure.class)</td>
</tr>
<tr>
<td>poincut pc(Secure sec) : @target(sec);</td>
<td>Secure sec = thisJoinPoint.getTarget().getClass().<br /> getAnnotation(Secure.class);</td>
</tr>
<tr>
<td>pointcut pc(Secure sec, Transactional tx) : @args(sec, tx);</td>
<td>Object[] arguments = thisJoinPoint.getArgs();<br /> Secure sec = arguments[0].getClass().<br /> getAnnotations(Secure.class);<br /> Transactional tx = arguments[1].getClass().<br /> getAnnotations(Transactional.class);</td>
</tr>
<tr>
<td>poincut pc(Sensitive sens) : @annotation(sec);</td>
<td>FieldSignature sig =<br /> (FieldSignature)thisJoinPointStaticPart.getSignature();<br /> Sensitive sens = sig.getField().getAnnotation(Sensitive.class);</td>
</tr>
<tr>
<td>pointcut pc(Secure sec) : @within(sec);</td>
<td>thisJoinPointStaticPart.getSignature().<br /> getDeclaringType().getAnnotation(Secure.class);</td>
</tr>
<tr>
<td>pointcut pc(Secure sec) : @withincode(sec);</td>
<td>MethodSignature sig =<br /> (MethodSignature)thisJoinPointStaticPart.getSignature();<br /> Secure sec = sig.getMethod().getAnnotation(Secure.class);</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">静态横切构造：修改结构</span></div>
<p><strong>静态横切总是在动态横切之前应用</strong>。</p>
<p>静态横切构造由：<strong>跨类型声明（inter-type declaration ，ITD，亦称“引入introduction)”）</strong>和<strong>织入时声明（weave-time declarations）</strong>组成<br /> <strong>跨类型声明（引入）</strong><br /> 可以修改类、接口、或者切面的静态结构，例如增加一个字段，添加一个接口。在一个切面中，声明其他类型的结构，故曰ITD。下面是一个例子：</p>
<pre class="crayon-plain-tag">package cc.gmem.aj;

public aspect TrackingAspect
{

    //声明MessageCommunicator实现接口AccessTracked，效果和Java标准语法一样
    declare      parents: MessageCommunicator implements AccessTracked;

    //声明AccessTracked类型的一个字段，所有它的子类型自动获取此字段
    private long AccessTracked.lastAccessedTime;

    //声明AccessTracked类型的两个方法，所有它的子类型自动继承这些方法
    public void AccessTracked.updateLastAccessedTime()
    {
        lastAccessedTime = System.currentTimeMillis();
    }

    public long AccessTracked.getLastAccessedTime()
    {
        return lastAccessedTime;
    }

    before( AccessTracked accessTracked ) : //声明了一个通知局部变量accessTracked
        execution(* AccessTracked+.*(..)) //AccessTracked子类型的任何方法
        &amp;&amp; !execution(* AccessTracked.*(..))//除了AccessTracked本身的方法
        &amp;&amp; this(accessTracked)//this对象收集为accessTracked变量
    {
        accessTracked.updateLastAccessedTime();
    }

    private static interface AccessTracked
    {
    }
}</pre>
<p><strong>成员引入的规则</strong></p>
<ol>
<li>只能引入private、public成员，前者只能被切面类访问，后者可以被系统中所有类访问</li>
<li>如果是private的，多个切面可以引入同名的成员</li>
<li>切面可以引入字段、函数、构造器到类或者接口。特别的，可以引入方法的实现到接口——缺省实现</li>
<li>如果切面引入一个和类中同名的方法，那么，类中的那个方法被保留</li>
<li>成员引入只能针对一个类型</li>
</ol>
<p><strong>接口缺省实现的例子</strong></p>
<pre class="crayon-plain-tag">package cc.gmem.aj;

public interface Nameable
{
    public void setName( String name );

    public String getName();
    //缺省实现
    static aspect Impl
    {
        private String Nameable.name;

        public void Nameable.setName( String name )
        {
            this.name = name;
        }

        public String Nameable.getName()
        {
            return this.name;
        }
    }
}</pre>
<p><strong>使用ITD来实现Java的Mixin机制</strong></p>
<p>Mixin是一种程序构造，允许在已有类中混入特定的逻辑，Java语言没有语言级别的机制，下面的例子示意了如何使用AspectJ进行混入</p>
<pre class="crayon-plain-tag">//一个标记性接口，用于混入功能
public interface BeanSupport {}
//一个简单的POJO，需要混入：属性变更监听的功能
public class Customer implements BeanSupport
{
    private String address;
    public String getAddress()
    {
        return address;
    }
    public void setAddress( String address )
    {
        this.address = address;
    }
}</pre>
<p>通过下面的代码，即可为Customer或者任何实现了BeanSupport的类型混入属性变更的功能，而没有任何的侵入性。</p>
<pre class="crayon-plain-tag">public aspect BeanMakerAspect
{

    private PropertyChangeSupport BeanSupport.propertyChangeSupport;
    public void BeanSupport.addPropertyChangeListener( PropertyChangeListener listener )
    {
        propertyChangeSupport.addPropertyChangeListener( listener );
    }
    public void BeanSupport.removePropertyChangeListener( PropertyChangeListener listener )
    {
        propertyChangeSupport.removePropertyChangeListener( listener );
    }
    pointcut beanCreation( BeanSupport bean )
    //匹配BeanSupport及其子类型的初始化连接点
    : initialization(BeanSupport+.new(..)) &amp;&amp; this(bean);

    pointcut beanPropertyChange( BeanSupport bean, Object newValue )
    //匹配BeanSupport及其子类型的任何Setter方法的执行
    : execution(void BeanSupport+.set*(*))
    &amp;&amp; args(newValue) &amp;&amp; this(bean);
    //BeanSupport初始化成功后，创建一个propertyChangeSupport字段的实例
    after( BeanSupport bean ) returning : beanCreation(bean) {
        bean.propertyChangeSupport = new PropertyChangeSupport( bean );
    }
    //Setter调用后，发布相应的事件
    void around( BeanSupport bean, Object newValue )  : beanPropertyChange(bean, newValue) 
    {
        String methodName = thisJoinPointStaticPart.getSignature().getName();
        String propertyName = Introspector.decapitalize( methodName.substring( 3 ) );
        Object oldValue = getPropertyValue( bean, propertyName );
        proceed( bean, newValue );
        bean.propertyChangeSupport.firePropertyChange( propertyName, oldValue, newValue );
    }
}</pre>
<p><strong>修改类层次结构</strong></p>
<p>AspectJ提供两种需要类层次的形式：</p>
<pre class="crayon-plain-tag">declare parents : [TypePattern] implements [InterfaceList];
declare parents : [TypePattern] extends [Class or InterfaceList];
//举例：所有实体类实现BeanSupport接口
public aspect EntityBeanParticipationAspect {
    declare parents: @Entity * implements BeanSupport;
}
//hasMethod、hasField，任何包含指定模式方法或者字段的类：
declare parents: hasmethod(@Observed * *(*)) implements BeanSupport;</pre>
<p><strong>引入注解</strong></p>
<p>AspectJ支持为字段、类、方法等添加注解</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 50%; text-align: center;">语法形式</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>declare @method: &lt;Method signature pattern&gt;:&lt;Annotation&gt;;</td>
<td>引入注解给AccountService的任何方法，例如：declare @method: * AccountService.*(..):<br /> @Transactional(Propagation.Required);</td>
</tr>
<tr>
<td>declare @constructor: &lt;Constructor signature pattern&gt;:&lt;Annotation&gt;;</td>
<td>引入注解给构造器，例如：declare @constructor: AccountService+.new():<br /> @ConfigurationOnly;</td>
</tr>
<tr>
<td>declare @field: &lt;Field signature pattern&gt;:&lt;Annotation&gt;;</td>
<td>引入注解给字段，例如：declare @field: * MissileInformation.*:<br /> @Classified;</td>
</tr>
<tr>
<td>declare @type: &lt;Type signature pattern&gt;:&lt;Annotation&gt;;</td>
<td>引入注解给类型，例如：declare @type: banking..* :<br /> @PrivacyControlled;</td>
</tr>
</tbody>
</table>
<p><b>简化异常处理</b></p>
<p>允许不强制捕获受查异常，声明方式如下：</p>
<pre class="crayon-plain-tag">//这里声明不需要强制捕获的异常
declare soft : &lt;ExceptionTypePattern&gt; : &lt;pointcut&gt;;</pre>
<p><strong>织入时声明——警告或者错误信息</strong></p>
<p>可以在织入时，根据一定的模式来生成错误或者警告信息，举例：</p>
<pre class="crayon-plain-tag">//当doAuthenticate方法在SecurityAspect以外调用时，生成警告
declare      warning: 
    call(void Authenticator.doAuthenticate()) //切入点：方法被调用的地方，与execution不同
    &amp;&amp; !within(SecurityAspect)  : //切入点，不在SecurityAspect内
        
        "Authentication should be performed only by SecurityAspect";</pre>
<div class="blog_h2"><span class="graybg">在Spring中使用AspectJ</span></div>
<div id="spring-aop-with-aspectj-config" class="blog_h3"><span class="graybg">使用Spring AOP，结合AspectJ风格的切面声明</span></div>
<p>这种场景下，本质上还是Spring基于JDK动态代理的AOP机制，只是借用AspectJ的语法。</p>
<pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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-2.5.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    "&gt;
    &lt;!-- 提示Spring自动为目标对象创建代理 --&gt;
    &lt;aop:aspectj-autoproxy /&gt;
    &lt;!-- 声明普通Bean --&gt;
    &lt;bean id="messageCommunicator" class="cc.gmem.aj.MessageCommunicator" /&gt;
    &lt;!-- 声明切面Bean，注意，必须使用注解语法定义切面 --&gt;
    &lt;bean id="securityAspect" class="cc.gmem.aj.SecurityAspect" /&gt;
&lt;/beans&gt;</pre>
<div class="blog_h3"><span class="graybg">使用spring-aspects切面库</span></div>
<p>可以基于AspectJ，而不是Spring AOP来实现：事务管理、缓存、任务调度等常用AOP场景，以避免Spring AOP的固有缺陷。<br /> 特别是使用@Configurable注解来支持非Spring Bean的依赖注入，必须基于AspectJ完成。</p>
<pre class="crayon-plain-tag">&lt;!-- 支持非受管对象（Spring Bean）的依赖注入和生命周期注解 --&gt;
&lt;context:spring-configured /&gt;
&lt;!-- 支持基于AspectJ的事务管理 --&gt;
&lt;tx:annotation-driven transaction-manager="txManager" mode="aspectj" /&gt;
&lt;!-- 支持基于AspectJ的缓存机制--&gt;
&lt;cache:annotation-driven cache-manager="cacheManager" mode="aspectj" /&gt;
&lt;!-- 支持基于AspectJ的任务调度和执行 --&gt;
&lt;task:annotation-driven  scheduler="taskScheduler" executor="taskExecutor" mode="aspectj" /&gt;</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/aspectj-study-note">AspectJ编程学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/aspectj-study-note/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
