1.启用@AspectJ,需要下载aspectjweaver.jar   

<!-- 默认启用动态代理 -->
<aop:aspectj-autoproxy/> <!-- 注解启用CGliB -->
<aop:aspectj-autoproxy proxy-target-class="true"/> <!--XML方式启用CGLIB -->
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>

2.声明一个切面(Aspect)

/** 注解的方式声明 **/
package org.xyz;
import org.aspectj.lang.annotation.Aspect; @Aspect
@Component
public class MyAspect{ } 添加@Component注解是为了让Spring自动搜索到并进行管理,当然还需要告诉Spring搜索路径:
<context:component-scan base-package="org.xyz"/>
<!-- XML方式声明,不需要@Aspect和@Component注解 -->
<bean id="masp" class="org.xyz.MyAspect">
<!-- 配置切面属性 -->
</bean>
<aop:config>
<aop:aspect id="myAspect" ref="masp">
... ...
</aop:aspect>
</aop:config>

3.声明一个切点(Pointcut)

  Spring AOP只支持在方法上定义连接点,所以只需考虑如何让切点匹配到目标方法,声明一个切点需要2步:一个包含名称的签名及参数(方法返回值必须为void);一个切点表达式。切点表达式使用@Pointcut注解表示。

@Pointcut("execution(* com.xyz.myapp.service..(..))")
public void anyOldTransfer(){ } /**
* anyOldTransfer即为切点签名
* execution为切点表达式,这里表示任意返回值,service包下(包括子包)任意形参的接口实现类方法
*/
<!-- XML方式配置 -->
<aop:config>
<aop:aspect id="myAspect" ref="masp">
<aop:pointcut id="anyOldTransfer" expression="execution(* com.xyz.myapp.service..(..))"/> </aop:aspect>
</aop:config>

4.声明一个通知(Advice)

@Aspect
public class AspectExample(){ @Before("execution(* com.xyz.myapp.dao..(..)")
public void beforeTest(){ } @After("execution(* com.xyz.myapp.dao..(..)")
public void afterTest(){ } @AfterReturning("execution(* com.xyz.myapp.dao..(..)")
public void afterReturnTest(){ } /** 将返回值传递给切点 */
@AfterReturning("execution(* com.xyz.myapp.dao..(..)",returning="retVal")
public void afterReturningTest(Object retVal){ } @AfterThrowing("execution(* com.xyz.myapp.dao..(..)")
public void afterThrowingTest(){ } /** 捕捉指定异常 */
@AfterThrowing("execution(* com.xyz.myapp.dao..(..)",throwing="ex")
public void afterThrowingTest(DataAccessException ex){ } @Around("execution(* com.xyz.myapp.dao..(..)")
public Object aroundTest(ProceedingJoinPoint pjp) throws Throwable{
//前处理
Object retVal = pjp.proceed();
//后处理
return retVal;
}
}

 

<!-- 使用注解的方式声明 -->
<aop:aspect id="beforeExample" ref="aBean">
<aop:pointcut id="dataAccessOperation" expression="execution(* com.xyz.myapp.dao..(..))" /> <!-- Before -->
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/> <!-- After returning -->
<aop:after-returning
pointcut-ref="dataAccessOperation"
returning="retVal"
method="doAccessCheck"/> <!-- After throwing-->
<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing="dataAccessEx"
method="doRecoveryActions"/> <!-- After -->
<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/> <!-- Around -->
<aop:around
pointcut-ref="dataAccessOperation"
method="doBasicProfiling"/> </aop:aspect>

 访问当前JoinPoint

  任何Advice类型方法都可以声明第一个形参为org.aspectj.lang.JoinPoint(Around的为ProceedingJoinPoint,JoinPoint的子类)

  JoinPoint接口提供了:getArgs()获取方法形参,getThis()获取代理对象,getTarget()获取目标对象

  将调用方法参数传递到advice

  后置的上面已经给出实例,下面看看前置的

    @Before("execution(* com.xyz.myapp.dao..(..) && args(account,..)")
public void beforeTest(Account account){ }

  自定义注解使用

    //定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
AuditCode value();
} //获取注解
@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
AuditCode code = auditable.value();
// ...
}

  Advice参数和泛型

  Spring AOP还可以处理带泛型的类和方法参数

    public interface Sample<T> {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> param);
} @Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
// Advice implementation
} /** 对于集合的泛型形参要用?代替,真正类型由调用者自行转换 */
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<?> param) {
// Advice implementation
}

  参数名称确定

 这里的参数名称主要指目标方法的形参名称和Advice方法的形参名称如何确定,Spring AOP通过以下方式来确定参数名称:

  •   如果明确指定了参数名称,就使用指定的参数名称;如何指定呢?advice和pointcut注解有一个可选的"argNames"属性可以用于指定参数名称,如
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", 
argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code and bean
}
  • 如果第一个参数是JoinPointProceedingJoinPoint, or JoinPoint.StaticPart 类型,"argNames"属性中不需要包含他们的命名,如

    @Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
    argNames="bean,auditable")
    public void audit(JoinPoint jp, Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code, bean, and jp
    }
  • 如果你不需要再advice方法中获取目标方法的参数,可以省略"argNames"属性

    @Before("com.xyz.lib.Pointcuts.anyPublicMethod()")
    public void audit(JoinPoint jp) {
    // ... use jp
    }
//或者直接使用aspectJ表达式中的arg
@Before("execution(* x.y.service.FooService.getFoo(String,int) && arg(name,age))")
public void audit(JoinPoint jp,String name,int age) {
// ... use jp
}

  参数处理

  如果你想在调用目标方法之前处理下传入的参数,可以这样做:

@Around("execution(List<Account> find*(..)) && com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
String accountHolderNamePattern) throws Throwable {
String newPattern = preProcess(accountHolderNamePattern);
return pjp.proceed(new Object[] {newPattern});
} /**
* 这里要注意形参的顺序,第一个传入的也要作为第一个传进proceed方法中
*/

  Advice 顺序

  当多个连接点重合时,如何进行有序的执行呢?Spring AOP遵循AspectJ确定的相同的优先级规则作为advice的执行顺序。

    在进入时,优先级越高的越先执行,如两个before advice,优先级高的先执行

    在退出时,优先级越高的越后执行,如两个after advice,优先级高的后执行

  当两个advice定义在不同的切面(Aspect)上且都需要运行在相同的连接点,这种情况下除非你指定顺序,否则执行顺序是不确定的。那如何指定执行顺序呢?

    Aspect 类实现 org.springframework.core.Ordered 接口或添加Order注解,从Ordered的getValue() 返回的值越小优先级越高

  当两个advice定义在相同的切面(Aspect)上且都需要运行在相同的连接点上,这种情况因为Ordered接口也没办法定义顺序了,那建议对advice进行合并或对aspect进行重构。

  

@Aspect
public class ConcurrentOperationExecutor implements Ordered { private static final int DEFAULT_MAX_RETRIES = 2; private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1; public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
} public int getOrder() {
return this.order;
} public void setOrder(int order) {
this.order = order;
} @Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
} }
<aop:aspectj-autoproxy/>

<bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>

Spring AOP与AspectJ 如何选择?

  如果你只是在Spring管理的bean上(如controller,service,dao)执行advice,并且没有很复杂的参数传递(如将目标方法的参数传递到Aspect类中),那Spring AOP是最佳的选择

  如果需要在非Spring管理的对象(如domain对象,程序中显示创建的对象)上执行advice,或有复杂的参数传递,建议使用AspectJ

是否该启用CGLIB?

  如果你需代理目标都有实现的接口,那就无需启用CGLIB了,通常我们service,dao层都有接口,如果只是代理这些实现类,使用Java 动态代理即可

  如果你代理的目标没有实现的接口,那需要启用CGLIB,但这里要注意3点:

    1.final方法是不能被代理的,因为CGLIB无法重写final方法

    2.从Spring 3.2开始CGLIB就集成到Spring core包中,因此无需导入CGLIB包了

    3.使用CGLIB作为代理时,代理对象的构造器会执行2次,但一般代理构造器中并无逻辑处理,所以调用2次也不会有什么影响

Spring-AOP-学习笔记(2)-AspectJ的更多相关文章

  1. Spring AOP学习笔记01:AOP概述

    1. AOP概述 软件开发一直在寻求更加高效.更易维护甚至更易扩展的方式.为了提高开发效率,我们对开发使用的语言进行抽象,走过了从汇编时代到现在各种高级语言繁盛之时期:为了便于维护和扩展,我们对某些相 ...

  2. Spring AOP学习笔记02:如何开启AOP

    上文简要总结了一些AOP的基本概念,并在此基础上叙述了Spring AOP的基本原理,并且辅以一个简单例子帮助理解.从本文开始,我们要开始深入到源码层面来一探Spring AOP魔法的原理了. 要使用 ...

  3. Spring AOP学习笔记03:AOP的核心实现之获取增强器

    上文讲了spring是如何开启AOP的,简单点说就是将AnnotationAwareAspectJAutoProxyCreator这个类注册到容器中,因为这个类最终实现了BeanPostProcess ...

  4. Spring AOP学习笔记05:AOP失效的罪因

    前面的文章中我们介绍了Spring AOP的简单使用,并从源码的角度学习了其底层的实现原理,有了这些基础之后,本文来讨论一下Spring AOP失效的问题,这个问题可能我们在平时工作中或多或少也会碰到 ...

  5. Spring AOP学习笔记

      Spring提供了一站式解决方案:          1) Spring Core  spring的核心功能: IOC容器, 解决对象创建及依赖关系          2) Spring Web ...

  6. Spring AOP学习笔记(1)-概念

    1.Aspect 横切在多个类的一个关注点,在Spring AOP中,aspect实现是一个规则的类或@Aspect标注的规则类.例如:事务管理 2.Join point 程序执行过程中的一个点,例如 ...

  7. Spring AOP学习笔记04:AOP核心实现之创建代理

    上文中,我们分析了对所有增强器的获取以及获取匹配的增强器,在本文中我们就来分析一下Spring AOP中另一部分核心逻辑--代理的创建.这部分逻辑的入口是在wrapIfNecessary()方法中紧接 ...

  8. Spring入门IOC和AOP学习笔记

    Spring入门IOC和AOP学习笔记 概述 Spring框架的核心有两个: Spring容器作为超级大工厂,负责管理.创建所有的Java对象,这些Java对象被称为Bean. Spring容器管理容 ...

  9. 【转】Spring.NET学习笔记——目录

    目录 前言 Spring.NET学习笔记——前言 第一阶段:控制反转与依赖注入IoC&DI Spring.NET学习笔记1——控制反转(基础篇) Level 200 Spring.NET学习笔 ...

  10. Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建

    Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...

随机推荐

  1. JoinableQueue队列,线程,线程于进程的关系,使用线程,线程的特点,守护线程,线程的互斥锁,死锁问题,递归锁,信号量

    1.JoinableQueue队列 JoinableQueue([maxsize]):这就像是一个Queue对象,但是队列允许项目的使用者通知生成者项目已经被成功处理.通知进程是使用共享的信号和条件变 ...

  2. co源码

    co模块整体包括三部分 对于几种参数类型的判断,主要判断是否object,array,promise,generator,generatorFunction这几种; 将几种不同的参数类型转换为prom ...

  3. Guava源码阅读-base-Strings

    package com.google.common.base; 今天阅读的是Srings类,这在程序中经常使用. 比如判断字符串是否为空,我们在之前用jdk方法判断是会用下面这个判断语句. if( i ...

  4. 【转】redis数据库入门教程(全面详细)+面试问题

    [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用redis4.学会安装redis5.学会启动redis6.使用redis客户端7.redis数据结构 – 简介8.redis ...

  5. [转帖]postgres 创建新用户并授权-- 非常好的

    postgres 创建新用户并授权 https://blog.csdn.net/XuHang666/article/details/81506297 原作者总结的挺好的 可以用来学习一下. grant ...

  6. 怎样解决SQL Server内存不断增加问题

    原文:怎样解决SQL Server内存不断增加问题 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn. ...

  7. springboot整合httpClient

    创建httpClientConfig配置类 @Configuration @PropertySource(value="classpath:/properties/httpClient.pr ...

  8. Lucky Sorting(CodeForces-109D)【思维】

    题意:给出一组数,要求从小到大排序,并且排序的过程中,发生交换的两个数至少一个为幸运数(十进制位均为4或7),问能否在(2×n)次交换内完成排序,如果能,输出交换的方案(不要求步骤数最少). 思路:首 ...

  9. POJ - 3687 Labeling Balls (拓扑)

    (点击此处查看原题) 题意 此处有n盏灯,编号为1~n,每盏灯的亮度都是唯一的,且在1~n范围之间,现已知m对灯之间的关系:a b ,说明灯a的亮度比灯b小,求出每盏灯的亮度,要求字典序最小(编号小的 ...

  10. 并不对劲的P5589

    题目大意 有\(n\)(\(n\leq 10^9\))个数:\(1,2,...,n\),每次操作是随机取一个没被删除的数\(x\),并删去\(x,x^2,x^3,...\). 求期望几次删完所有数. ...