定义AOP术语
通知
通知定义了切面是什么以及何时使用切面。Spring切面可以应用5种类型的通知:
- Before: 在方法被调用之前通知
- After: 在方法完成之后调用通知,无论方法是否执行成功
- After-returning: 在方法成功执行之后调用通知
- After-throwing: 在方法抛出异常之后调用通知
- Around: 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义行为
连接点
连接点是在应用执行过程中 能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
切点
切点即为切面切入的 位置。
引入
引入允许我们向现有的类添加新的方法和属性,目的是在无需修改现有类的情况下,让他们具有新的行为和状态。
织入
织入是将切面应用到目标对象来创建新的代理对象的 过程。
在XML中声明切面
非环绕型通知
<!--两个可爱的JavaBean-->
<bean id="tom" class="entity.Person" />
<bean id="robot" class="entity.Robot" />
<!--切面声明开始-->
<aop:config>
<!--切面部分开始,如你所见一个Robot的类正在被定义成为一个切面-->
<aop:aspect ref="robot">
<!--定义切点-->
<aop:pointcut id="sayHello" expression="execution(* entity.Person.sayHello(..))" />
<!--定义切点:如果通知需要传参-->
<aop:pointcut id="sayHello" expression="execution(* entity.Person.sayWord(String))
and args(word)" />
<!--前置通知-->
<aop:before method="beforeSayHello" pointcut-ref="sayHello" />
<!--前置通知:通知内引用上文的thoughts参数-->
<aop:before method="beforeSayHello" pointcut-ref="sayHello" arg-names="thoughts"/>
<!--后置通知-->
<aop:after method="afterSayHello" pointcut-ref="sayHello" />
<!--方法执行成功后的通知-->
<aop:after-returning method="finishSayHello" pointcut-ref="sayHello" />
<!--执行异常时的通知-->
<aop:after-throwing method="exceptionThrows" pointcut-ref="sayHello" />
</aop:aspect>
</aop:config>
简单测试一下:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Person tom = (Person) ctx.getBean("tom");
tom.sayHello();
环绕型通知
<aop:config>
<aop:aspect ref="robot">
<aop:pointcut id="sayHello" expression="execution(* entity.Person.sayHello(..))" />
<!--环绕通知-->
<aop:around method="watchYouSayHello" pointcut-ref="sayHello" />
</aop:aspect>
</aop:config>
我们来看一下JavaBean中是如何声明这个方法的:
//AOP环绕通知
public void watchYouSayHello(ProceedingJoinPoint joinPoint) {
try {
System.out.println("Before say hello!");
long start = System.currentTimeMillis();
//执行被通知的方法
joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("Say hello finished!");
System.out.println("This project has run " + (end - start) + "ms!");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
在main方法中执行后效果同上。
注解切面
非环绕型通知
注解切面可以使我们免于在XML中配置AOP
@Aspect
public class Robot {
//声明切点
@Pointcut("execution(* entity.Person.sayHello(..))")
//声明切点标识方法
public void sayHello() {
}
//通知前置调用
@Before("sayHello()")
public void beforeSayHello() {
System.out.println("before say hello");
}
//通知后置调用
@After("sayHello()")
public void afterSayHello() {
System.out.println("after say hello");
}
//方法执行成功后调用
@AfterReturning("sayHello()")
public void finishSayHello() {
System.out.println("say hello finished");
}
//方法执行异常时调用
@AfterThrowing("sayHello()")
public void exceptionThrows() {
System.out.println("an exception was throwed");
}
//AOP环绕通知
@Around("sayHello()")
public void watchYouSayHello(ProceedingJoinPoint joinPoint) {
try {
System.out.println("before say hello");
long start = System.currentTimeMillis();
//执行被通知的方法
joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("say hello finished");
System.out.println("this project has run " + (end - start) + "ms");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
但是我们需要在XML中的beans根标签下额外配置一些东西
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
>
<!--如果使用注解形式,请务必加上该配置(常用的做法)-->
<aop:aspectj-autoproxy/>
<bean id="tom" class="entity.Person" />
<bean id="robot" class="entity.Robot" />
</beans>
环绕型通知
@Aspect
public class Robot {
@Pointcut("execution(* entity.Person.sayHello(..))")
public void sayHello() {
}
//AOP环绕通知
@Around("sayHello()")
public void watchYouSayHello(ProceedingJoinPoint joinPoint) {
try {
System.out.println("before say hello");
long start = System.currentTimeMillis();
//执行被通知的方法
joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("say hello finished");
System.out.println("this project has run " + (end - start) + "ms");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}