1. BeforeAdvice和AfterAdvice
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
@Aspect
public class LogAdvice {
//基于Annotation的支持,将不用实现特定的接口来实现Advice(spring2.0以上)
@Before("execution(* *..service.*Manager.*(..))")
public void before(JoinPoint joinPoint) {
//System.out.println(" method before: " + joinPoint.getTarget().getClass().getName() +"."+ joinPoint.getSignature().getName() + " START");
for(Object o :joinPoint.getArgs()){
if (o == null){
//System.out.println("param : " + o);
} else {
//System.out.println("param : " + o.toString());
}
}
}
@After("execution(* *..service.*Manager.*(..))")
public void after(JoinPoint joinPoint) {
//System.out.println(" method after : " + joinPoint.getTarget().getClass().getName() +"."+ joinPoint.getSignature().getName() + " END");
}
//Spring AOP支持的@AspectJ切入点表达式
//execution:匹配方法执行连接点。我们可以指定包、类或者方法名,以及方法的可见性、返回值和参数类型。这是应用的最为广泛的切入点表达式。例如,execution(* com.apress..TestBean.*(..)表示执行com.apress包中TestBean中的所有方法,无论是何种参数或返回类型
//within:匹配那些在已声明的类型中执行的连接点。例如,within(com.apress..TestBean)匹配从TestBean的方法中产生的调用
//this:通过用bean引用(即AOP代理)的类型跟指定的类型作比较来匹配连接点。例如,this(SimpleBean)将只会匹配在SimpleBean类型的bean上的调用
//target:通过用被调用的bean的类型和指定的类型作比较来匹配连接点。比如target(SimpleBean)将只会匹配在SimpleBean类型bean上方法的调用
//args:通过比较方法的参数类型跟指定的参数类型来匹配连接点。例如,args(String, String)将只会匹配那些有两个String类型参数的方法
//@target:通过检查调用的目标对象是否具有特定注解来匹配连接点。例如,@target(Magic)将会匹配带有@Magic注解的类中的方法调用
//@args:跟args相似,不过@args检查的是方法参数的注解而不是它们的类型。例如,@args(NotNull)会匹配所有那些包含一个被标注了@NotNull注解的参数的方法
//@within:跟within相似,这个表达式匹配那些带有特定注解的类中执行的连接点。例如,表达式@within(Magic)将会匹配对带有@Magic注解的类型的bean上方法的调用
//@annotation:通过检查将被调用的方法上的注解是否为指定的注解来匹配连接点。例如,@annotation(Magic)将会匹配所有标有@Magic注解的方法调用
//bean: 通过比较bean的ID(或名称)来匹配连接点。我们也可以在bean名模式中使用通配符。例如,bean("simple")将会匹配ID或名称为simple的bean中的连接点
//execution(* *..service.*Manager.*(..)) && within(com.apress.prospring2..*)
/*
1. Execution表达式
用过的* com.apress.prospring2.ch06.simple.TestBean2.*(..)表达式:星号*表示任何返回类型(ret-type-pattern,返回类型模式),后面跟着一个全限定类名(declaring-type-pattern,声明类型模式)。
我们在这个类名后又跟另一个星号*(..),这表示一个任意名称、任意数量(包括零)和任意参数类型。因为我们没有指定修饰符模式(modifiers-pattern)或异常抛出模式(throws-pattern),Spring AOP将匹配任意修饰符和抛出任意异常的方法。
2. within表达式
可以使用..和*这样普通的通配符。例如,要声明一个可以匹配在com包及其子包的任意类中对任意方法调用的执行过程的切入点,应该写成within(com..*)。
3. this表达式
this切入点表达式的语法跟within表达式的语法相似,唯一的区别是前者不能使用..或*这样的通配符。this切入点的语义会匹配某一对象上所有的方法执行过程,该对象的类型匹配指定的表达式,
但我们怎样匹配com包及其子包下所有的类呢?因此,唯一允许的语法是this(class-name)。例如,this(com.apress.prospring2.ch06.simple.TestBean2)。
4. target表达式
target表达式的语法跟this表达式的完全一样。因为target表达式定义了一个可能匹配某一特定表达式的类的对象上的所有方法执行过程的切入点,所以它不能使用通配符。因此,唯一可用的语法就是target(class-name),
例如,target(com.apress.prospring2.ch06.simple.SimpleBean)。
5. args表达式
args表达式的语法是args(type-pattern? (, type-pattern)*)。换言之,我们可以指定零个、一个或多个type-pattern表达式。需要注意的是,当你在execution表达式中使用argument-pattern时,
这个execution匹配会为形参的类型求值。args表达式匹配为传给方法的参数的实际类型求值。
切入点execution(* SimpleBean.*(CharSequence, String))可以匹配对方法x("A", "B")的调用,因为这个方法名和形参类型都匹配。然而,切入点execution(* SimpleBean.*(String, String))
却不能匹配哪怕是使用两个String参数的方法调用(一个StringBuilder和一个String也不行)。如果我们想创建一个切入点来匹配当且仅当实参为String,String时对x(CharSequence, String)方法的调用,
那么我们应该将表达式写成args(String, String)。
我们也可以在type-pattern中使用..通配符。如果要匹配某个方法的调用,而该方法由一个Integer类型的参数开始,以一个String类型的参数结尾,中间包含任意类型的任意数量个参数,那么我们就应该将表达式写成args(Integer, .., String)。
6. @target表达式
@target表达式是另一个需要一个完全类型名的简单表达式的例子。而且,这个类型名应该表示为一个@interface
7. @within表达式
@within表达式需要一个完全@interface类型名,它将会匹配在具有特定注解的对象(或者方法)中对任意方法的调用。例如,我们可以这样写,@within(StartsTransaction),其中StartsTransaction是一个@interface。
8. @annotation表达式
跟@target表达式类似,@annotation表达式将匹配任何具有特定注解的方法的执行。@annotation(Transactional)便是一个很好的例子,它会匹配具有Transactional注解的任意方法的执行。
9. @args表达式
@args表达式与args表达式相似,它与后者唯一的是它比较参数的注解而不是参数的类型。我们可以使用这个表达式来匹配所有对带有指定注解参数的方法的调用。我们也能使用跟在args表达式中相同的通配符。
10. bean表达式
它将会匹配某一个bean中对所有方法的调用,而该bean的id或bean名能与特定名称相匹配。我们可以在bean名中使用星号通配符*。为了匹配simple bean中所有方法的调用,我们可以写成bean(simple);
为了匹配id或其中一个名称以Service结尾的bean中所有方法的调用,则可以写成bean(*Service)。*/
}
2. AroundAdvice
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
@Aspect
public class LogAroundAdvice {
//基于Annotation的支持,将不用实现特定的接口来实现Advice(spring2.0以上)
@Around("execution(* *..service.*Manager.*(..))")
public Object invoke(ProceedingJoinPoint joinPoint) {
//System.out.println(" LogAroundAdvice before " + joinPoint.getSignature().getName());
Object retVal = null;
try {
retVal = joinPoint.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//System.out.println(" LogAroundAdvice after " + joinPoint.getSignature().getName());
return retVal;
}
}
3. ThrowingAdvice
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class LogThrowingAdvice {
//基于Annotation的支持,将不用实现特定的接口来实现Advice(spring2.0以上)
@AfterThrowing(pointcut="execution(* *..service.*Manager.*(..))",throwing="throwable")
public void afterThrowing (JoinPoint joinPoint, Throwable throwable) {
System.out.println(" LogThrowingAdvice : " + joinPoint.getSignature().getName() + "..." + throwable.getClass() );
}
}
4.
<!-- Enable @Transactional support 注解驱动的事务支持-->
<!-- <tx:annotation-driven/>标签使用@Transactional注解创建恰当的事务管理切面 -->
<!-- <tx:annotation-driven/>标签是注解驱动的事务管理支持的核心,有以下属性
transactionManager : 指定到现有的PlatformTransaction Manager bean的引用,通知会使用该引用
mode : 指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理
order : 指定创建的切面的顺序。只要目标对象有多个通知就可以使用该属性
proxy-target-class : 该属性如果为true就表示你想要代理目标类而不是bean所实现的所有接口
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Enable @AspectJ support AOP自动代理 -->
<!-- <aop:aspectj-autoproxy/>通知匹配的bean -->
<aop:aspectj-autoproxy/>
<bean class="cn.xue.app.advice.LogAdvice"/>
<bean class="cn.xue.app.advice.LogAroundAdvice"/>
<bean class="cn.xue.app.advice.LogThrowingAdvice"/>
5. 业务方法中
//事务隔离级别
//TransactionDefinition.ISOLATION_DEFAULT : PlatformTransactionManager的默认隔离级别(对大多数数据库来说就是ISOLATION_ READ_COMMITTED)
//TransactionDefinition.ISOLATION_READ_UNCOMMITTED : 最低的隔离级别。事实上我们不应该称其为隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。 而在其他事务提交前,该事务也可以看到其他事务所做的修改
//TransactionDefinition.ISOLATION_READ_COMMITTED : 大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入或更新的数据。这意味 着在事务的不同点上,如果其他事务修改了数据,你就会看到不同的数据
//TransactionDefinition.ISOLATION_REPEATABLE_READ : 比ISOLATION_READ_COMMITTED更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据
//TransactionDefinition.ISOLATION_SERIALIZABLE : 代价最大、可靠性最高的隔离级别,所有的事务都是按顺序一个接一个地执行
//传播行为值
//TransactionDefinition.PROPAGATION_REQUIRED : 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务
//TransactionDefinition.PROPAGATION_SUPPORTS : 当前如果有事务,Spring就会使用该事务;否则不会开始一个新事务
//TransactionDefinition.PROPAGATION_MANDATORY : 当前如果有事务,Spring就会使用该事务;否则会抛出异常
//TransactionDefinition.PROPAGATION_REQUIRES_NEW : Spring总是开始一个新事务。如果当前有事务,则该事务挂起
//TransactionDefinition.PROPAGATION_NOT_SUPPORTED : Spring不会执行事务中的代码。代码总是在非事务环境下执行,如果当前有事务,则该事务挂起
//TransactionDefinition.PROPAGATION_NEVER : 即使当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常
//TransactionDefinition.PROPAGATION_NESTED : 如果当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与Transaction- Definition.PROPAGATION_REQUIRED一样
//凭借@Transactional注解可以控制通知将要创建的事务定义的方方面面。像transactionAttributes属性表达式一样,可以指定传播、隔离级别、超时以及允许和不允许的异常 ,有以下属性
//propagation : 指定事务定义中使用的传播
//isolation : 设定事务的隔离级别
//timeout : 指定事务的超时(单位为秒)
//readOnly : 如果为true,事务就被标识为只读
//noRollbackFor : 目标方法可抛出的异常所构成的数组,但通知仍会提交事务
//rollbackFor : 异常所构成的数组,如果目标方法抛出了这些异常,通知就会回滚事务
/*//用例示例如下:
@Transactional(rollbackFor=Exception.class)
// 如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.REQUIRED)
// 容器不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
// 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
// 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)
// 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
// 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.SUPPORTS)
@Transactional(propagation=Propagation.NESTED)
// readOnly=true只读,不能更新,删除
@Transactional (propagation = Propagation.REQUIRED,readOnly=true)
// 设置超时时间
@Transactional (propagation = Propagation.REQUIRED,timeout=30)
// 设置数据库隔离级别
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)*/
//默认@Transactional设置如下:(事务传播设置是 PROPAGATION_REQUIRED),(事务隔离级别是 ISOLATION_DEFAULT),(事务是 读/写),(事务超时默认是依赖于事务系统的,或者事务超时没有被支持),(任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚)
@Transactional
public Map getDataItemAndLoopbkmTree(String type){
//Map map = new HashMap();
Map map = null; //测试LogThrowingAdvice
map.put("dataItems", dataItemDao.getByDocType(type) );
map.put("loopbkmkTrees", loopbkmkTreeDao.getAll() );
return map;
}
本文介绍Spring AOP中的BeforeAdvice、AfterAdvice、AroundAdvice及ThrowingAdvice等通知类型的应用,并展示了如何利用@AspectJ注解实现切面逻辑。同时,文中还详细解释了切入点表达式以及@Transactional注解的使用。
687

被折叠的 条评论
为什么被折叠?



