我们知道如何使用依赖注入(DI)管理和配置我们的应用对象,DI有助于应用对象之间的解耦,而AOP可以实现横切关注点与他们所影响的对象之间的解耦。切面能帮助我们模块化横切关注点,横切关注点可以被描述为影响应用多处的功能。
定义AOP术语,描述切面的术语有通知,切点和连接点。
通知定义了切面是什么以及切面何时开始使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用在某个方法被调用之前?之后?之前和之后都调用?还是只在方法抛出异常时调用?
Spring切面可以应用5种类型的通知:
前置通知:在目标方法被调用之前调用通知功能;
后置通知:在目标方法完成之后调用通知功能;
返回通知:在目标方法成功执行之后调用通知功能;
异常通知:在目标方法抛出异常后调用的通知功能;
环绕通知:通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
我们的应用可能有数千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至修改一个字段时,切面代码可以利用这些点插入到应用正常的流程之中,并添加新的行为。
如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式来匹配通知所要织入的一个或多个连接点。
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容--它是什么,在何时和何处完成其功能。
织入是把切面应用到目标对象并创建新的代理对象的过程。
我们先定义一个Performance接口
package concert;
public interface Performance {
public void perform();
}
再定义一个它的实现类
package concert;
public class ConcertPerformance implements Performance {
@Override
public void perform() {
System.out.println("Performing...");
}
}
重点是这里的Audience类
package concert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class Audience {
@Pointcut(value ="execution(* concert.Performance.perform(..))")
public void performance() {}
@Before("performance()")
public void silenceCellPhone() {
System.out.println("silencing cellPhone");
}
@After("performance()")
public void afterPerformance() {
System.out.println("after performance");
}
@AfterThrowing("performance()")
public void demanRefund() {
System.out.println("demanding a refund");
}
// 环绕通知
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint joinPoint) {
try {
System.out.println("around before performance...");
joinPoint.proceed();
System.out.println("around after performance...");
}catch(Throwable t) {
t.printStackTrace();
}
}
}
@Aspect声明这是一个Aspect注解驱动的切面。
@Poincut定义了一个切点,execution(* concert.Performance.perform(..))代表perform这个方法执行时将会声明为一个切点
“ * ” 代表任意返回值类型,“ .. ”代表任意参数类型。也可以写成 * *.perform(..)代表任意类的perform方法。
@Before,@After,@AfterThrowing,分别是前置,后置,异常通知。
@Around声明一个环绕通知
这还不够,我们需要一个配置类,来启用自动代理功能。
package concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"concert"})
public class ConcertConfig {
@Bean
public Performanceperformance(){
return new ConcertPerformance();
}
@Bean
public Audienceaudience(){
return new Audience();
}
}
@EnableAspectJAutoProxy注解启动了自动代理功能,这里我们声明了两个Bean。
接下来测试一下,AOPTest类
package concert;
import concert.ConcertConfig;
import concert.Performance;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= {ConcertConfig.class})
public class AOPTest {
@Autowired
private Performance performance;
@Test
public void testAop() {
performance.perform();
}
}
performance对象会自动注入到AOPTest类中。
切面运行结果
这里详细写一下IDEA 配置AspectJ的步骤:
1、启用IDEA AspectJ plugin File---Settings--Plugins,搜索栏内搜索AspectJ,启用支持,注意这里只有Ultimate版才会有。
2、添加依赖
build.gradle添加
compile"org.springframework:spring-context:4.0.7.RELEASE"
compile"org.springframework:spring-test:4.0.7.RELEASE"
compile group:'org.aspectj',name:'aspectjtools',version:'1.8.9'
testCompile"org.springframework:spring-test:4.0.7.RELEASE"
implementation'junit:junit:4.12'
其他博客说要换Ajc Java 编译器,我没换也能运行,不知道为啥。。