Spring学习之AOP
什么是AOP
- AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
- 通俗来讲,在我们的应用中,诸如常见的日志、安全、异常处理和事物等逻辑都很重要,在不适用AOP的过去,我们需要将共同部分的这段代码写在一个独立的类独立的方法里,然后再去调用。但是有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
举一个比较形象的例子。在不使用AOP的应用中,一般日志处理都是这个样子的:
因为日志处理代码是相同的,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法。这样子做虽然是符合面向对象思想,但是方法之间是强耦合的,一旦处理日志的方法需要更改,就必须在每个调用日志处理的方法中做修改,这样子显然是不好的。
使用AOP之后可以是这样子的:
AOP的思想是:我们可以通过动态代理,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。比如我们可以将test1()作为一个切入点,也就是说在test1()执行前或者执行后,执行recordLog()方法。
AOP的基本概念
描述切面的常用术语,也是我们在开发中经常使用的有通知(advice)、切点(pointcut)、连接点(joinpoint)、切面(Aspect)。
- 通知(Advice)
通知定义了切面是什么以及什么时候使用。Spring切面可以应用五种类型的通知:
前置通知(Before): 在目标方法被调用之前通知功能。
后置通知(After):在目标方法被调用之后通知功能。
返回通知(After-returning):在目标方法执行成功之后调用通知。
异常通知(After-throwing):在目标方法抛出异常之后调用通知。
环绕通知(Around):通知包含了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。 - 连接点(Join Point):在应用中执行过程中能够插入切面的一个点。
- 切点(Pointcut):切点定义了匹配通知在何处织入。
- 切面(Aspect):切面是通知和切点的集合。通知和切点共同定义了切面的全部内容–它是什么、在何时在何处完成功能
Spring对AOP的支持
Spring提供了四种类型的AOP支持
- 基于纯代理的经典Spring AOP
- 纯POJO切面
- @AspectJ注解驱动的切面
- 注入式AspectJ切面(适用于Spring各个版本)
AOP切点表达式
AspectJ指示器 | 描述 |
---|---|
arg() | 限制连接点匹配参数由指定类型的执行方法 |
@arg() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 |
target | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型 |
@annotation | 限制连接点匹配有指定注解的连接点 |
AOP使用场景
AOP用来封装横切关注点,具体可以在下面的场景中使用:
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务
AOP功能简单实现
创建接口
package com.example.aop;
public interface Person {
public void say();
}
创建实现类
package com.example.aop;
public class Student implements Person{
@Override
public void say() {
System.out.println("I'm a student");
}
}
创建切面类
package com.example.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;@Aspect
public class AspectPerson {@Pointcut("execution(* *.say(..))")
public void say() {}
@Before("say()")
public void before() {
System.out.println("before");
}
@After("say()")
public void after() {
System.out.println("after");
}
@Around("say()")
public Object around(ProceedingJoinPoint pj) {
System.out.println("around before");
Object o = null;
try {
o = pj.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around after");
return o;
}
}
创建xml(当然你也可以使用javaConfig的方法,主要使用EnableAspectJAutoProxy注解)
<?xml version=”1.0” encoding=”UTF-8”?>
创建测试类
package com.example.aop;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class aopTest {
@Test
public void test() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("aopBean.xml");
Person bean2 = (Person)ac.getBean("student");
bean2.say();
}
}
还没有评论,来说两句吧...