IOC和AOP是Spring的两大基石,AOP(面向切面编程),是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP)。
在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组合,OOP 最大问题就是无法解耦组件进行开发。AOP 为开发者提供一种进行横切关注点(比如日志关注点)分离并织入的机制,把横切关注点分离,然后通过某种技术织入到系统中,从而无耦合的完成了我们的功能。
AOP世界中的居民
- Joinpoint(连接点):指那些被拦截到的点。在Spring中只支持方法级别的连接点。
- Pointcut(切入点):指需要配置的Joinpoint。
- Advice(通知):指拦截到Joinpoint后要做的操作,通知分为前置通知/后置通知/异常通知/后置通知/环绕通知。
- Aspect(切面):切入点和通知的结合。
- Target(目标对象):需要被代理的对象。
- Proxy(代理对象):目标对象被AOP 织入通知产生的对象。
- Weaving(织入):指把通知应用到目标对象来创建代理对象的过程(Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入)。
AOP中的通知类型
- 前置通知:在切入点选择的连接点处的方法之前执行的通知,该通知不影响正常程序执行流程,除非发生了异常。
- 后置通知:在切入点选择的连接点处的方法正常执行完毕时执行的通知。
- 返回通知:在切入点选择的连接点处的方法返回时执行的通知,不管抛没抛出异常都执行,类似于 Java 中的 finally 块。
- 异常通知:在切入点选择的连接点处的方法抛出异常返回时执行的通知。
-
环绕通知:环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知可以在方法调用之前和之后自定义任何行为,并且可以决定是否执行连接点处的方法、替换返回值、抛出异常等等。
AOP程序示例
@Component
public interface HelloAop {
void hello();
}
@Component
public class HelloAopImpl implements HelloAop{
@Override
public void hello() {
System.out.println("---hello HelloAopImpl---");
}
}
@Aspect
@Component
public class HelloAopAspect {
@Before("execution(public void com.luo.aop.HelloAopImpl.hello())")
public void beforeAdvice(JoinPoint point) {
String methodName = point.getSignature().getName();
System.out.println("beforeAdvice: " + methodName);
}
@After("execution(public void com.luo.aop.HelloAopImpl.hello())")
public void afterAdvice(JoinPoint point) {
String methodName = point.getSignature().getName();
System.out.println("afterAdvice: " + methodName);
}
@AfterThrowing("execution(public void com.luo.aop.HelloAopImpl.hello())")
public void afterThrowingAdvice(JoinPoint point) {
String methodName = point.getSignature().getName();
System.out.println("afterThrowingAdvice: " + methodName);
}
@AfterReturning("execution(public void com.luo.aop.HelloAopImpl.hello())")
public void afterReturningAdvice(JoinPoint point) {
String methodName = point.getSignature().getName();
System.out.println("afterReturningAdvice: " + methodName);
}
@Around("execution(public void com.luo.aop.HelloAopImpl.hello())")
public Object aroundAdvice(ProceedingJoinPoint point) {
Object result = null;
// 环绕通知(前通知)
System.out.println("aroundAdvice start...");
try {
// 前置通知
result = point.proceed(); // 目标方法执行
} catch (Throwable throwable) {
// 异常通知
throwable.printStackTrace();
}
// 环绕通知(后通知)
System.out.println("aroundAdvice end...");
// 后置通知
// 返回通知
return result;
}
}
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("springConfig.xml");
HelloAop helloAop = ctx.getBean(HelloAop.class);
helloAop.hello();
}
spring配置文件:
<!-- 自动扫描 -->
<context:component-scan base-package="com.luo.aop"/>
<!-- 使用AspjectJ自动生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
AOP通知输出结果
指定切面优先级
在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的。切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定。实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.若使用 @Order 注解, 序号出现在注解中。
@Aspect
@Order(0)
@Component
public class HelloAspect2 { }
@Aspect
@Order(1)
@Component
public class HelloAspect { }
参考资料:
- 《Spring揭秘》AOP章节
- Spring学习之AOP总结帖
网友评论