1. AOP定义和术语
AOP(Aspect Oriented Programming):面向切面编程。AOP可以把各类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面,便于减少系统的重复代码,降低模块之间的耦合度。常用的主要功能:日志记录,性能统计,安全控制,事务处理,异常处理。
术语介绍:
- 切面(Aspect):是对横切关注点的抽象。
- 切入点(Pointcut):对连接点进行拦截的定义。
- 连接点(Join point):指被拦截到的方法。
- 增强(Advice):指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
- 引入(Introduction):引用允许你添加新方法或属性到现有的类中。
- 目标对象(Target object):被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。
- 织入(Weaving):将切面应用到目标对象并导致代理对象创建的过程。
2. Spring对AOP的支持
Spring AOP 事务管理的2种代理方式:
① 基于 JDK 接口的动态代理;
② 基于 CGLib 动态生成子类的代理。
2.1 使用注解创建切面
AspectJ的风格类似纯java注解的普通java类。AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用。
用@Aspect注解的类可以有方法和字段,他们也可能包括切入点(pointcut),增强(Advice)和引入(introduction)声明。
@Aspect注解不能通过类路径自动检测发现,需要配合使用@Component注释或者在xml配置bean。
代码:
1.定义切面代码
@Aspect
public class Audience {
@Pointcut("execution(* com.chen.aop.Concert.perform(..))")
public void performance() {
}
@Before("performance()")
public void slienceCellPhones() {
System.out.println("Silencing cell phones.");
}
@After("performance()")
public void takeSeats() {
System.out.println("Leaving away.");
}
@AfterReturning("performance()")
public void applause() {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Demanding a refund.");
}
}
说明:
@Aspect:表明类PreGreetingAspect不仅仅是一个POJO,还是一个切面。
@Pointcut:在切面内定义可重用的切点。
2.配置代码
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
@Bean
public Concert concert() {
return new Concert();
}
@Bean
public Audience audience(){
return new Audience();
}
}
说明:
@EnableAspectJAutoProxy:注解启动自动代理功能。proxyTargetClass默认为false,表示使用JDK动态代理技术织入增强;当配置为true时,表示使用CGLib动态代理技术织入增强。
3.测试代码
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = AopConfig.class)
public class ConcertTest {
@Autowired
private Concert concert;
@Test
public void testPerform() {
assertNotNull(concert);
concert.perform();
}
@Test
public void testPerformByAutoProxy() {
Concert target = new Concert();
AspectJProxyFactory factory = new AspectJProxyFactory();
// 1.设置目标对象
factory.setTarget(target);
// 2.添加切面类
factory.addAspect(Audience.class);
// 3.生成织入切面的代理对象
Concert concert = factory.getProxy();
concert.perform();
}
}
AspectJ提供的5个注解:
- @Before:增强方法会在目标方法调用之前执行;
- @Around:增强方法将目标方法封装起来;
- @After:增强方法会在目标方法返回或抛出异常后调用;
- @AfterReturning:增强方法会在目标方法返回后调用;
- @AfterThrowing:增强方法会在目标方法抛出异常后调用。
2.2 使用XML配置AOP
Spring所有的切面和增强都必须放在一个<aop:config>
内(可以配置包含多个<aop:config>
元素),每一个<aop:config>
可以包含pointcut,advisor和aspect元素,它们必须按照这个顺序进行声明。
<aop:config>
使用Spring的自动代理机制。
代码:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--基于@Aspect切面的驱动器-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--目标Bean-->
<bean id="concert" class="com.chen.aop.Concert"/>
<bean id="audience" class="com.chen.aop.Audience"/>
<aop:config>
<aop:aspect ref="audience">
<aop:pointcut id="performance" expression="execution(* com.chen.aop.Concert.perform(..))"/>
<aop:before pointcut-ref="performance" method="silenceCellPhones"/>
<aop:after pointcut-ref="performance" method="leaveAway"/>
<aop:after-returning pointcut-ref="performance" method="applause"/>
<aop:after-throwing pointcut-ref="performance" method="demandRefund"/>
<aop:around pointcut-ref="performance" method="watchPerformance"/>
</aop:aspect>
</aop:config>
</beans>
源代码:
https://github.com/leifchen/hello-spring
代码包:com.chen.aop