什么是AOP?
AOP是一种思想,它与具体的实现技术无关,任何一种符合AOP的思想的技术实现,都可以看做是AOP的实现。通过java的动态代理机制,就可以很容易实现AOP的思想,实际上Spring的AOP也是建立在Java的代理机制上。——我们发现AOP实际上是由目标类的代理类实现的。AOP代理其实是由AOP框架动态生成的一个对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但是AOP代理中的方法与目标对象的方法存在差异,AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。
AOP的目标
是让我们可以“专心做事”。
AOP的原理:
- 将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决。
- 采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能。
AOP理解
- 业务处理的主要流程就是核心关注点,与之关系不大的部分就是横切关注点。横切关注点的一个特点就是:他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
- 所谓面向切面编程,是一种通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态添加功能的技术。
AOP相关术语
增强(Advice):
定义了切面是什么以及何时使用,描述了切面要完成的工作和何时需要执行这个工作。是织入到目标类连接点上的一段程序代码。增强又分前置增强、后置增强、环绕增强、异常抛出增强、最终增强等类型。
切入点(Pointcut):
Advice定义了切面要发生“故事”和时间,那么切入点就定义了“故事”发生的地点。例如某个类或者方法名,Spring中允许我们使用正则来指定。
连接点(Joinpoint):
切入点匹配的执行点称作连接点。如果说切入点是查询条件,那连接点就是被选中的具体的查询结果。程序执行的某个特定位置,程序能够应用增强代码的一个“时机”,比如方法调用或者特定异常抛出。
切面(Aspect):
切点和增强组成切面。它包括了横切逻辑的定义,也包括了连接点的定义。Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
代理(Proxy):
AOP框架创建的对象。一个类被AOP织入增强之后,就产生了一个结果类,它是融合了原类和增强逻辑的代理类。
目标对象(Target):
增强逻辑的织入的目标类。
织入(Weaving):
将增强添加到目标类具体连接点上的过程。AOP有三种织入的方式:编译期织入、类装载期织入、动态代理织入(spring采用动态代理织入)。
编码示例
-
新建java项目
-
(1)目标方法
public class UserServiceImpl implements UserService { //…省略代码 public void addNewUser(User user) { dao.save(user); } }
(2)增强处理
public class UserServiceLogger { private static Logger log=Logger.getLogger(UserServiceLogger.class); public void before(JoinPoint jp) { log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature(). getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs())); } public void afterReturning(JoinPoint jp, Object result) { log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature(). getName() + " 方法。方法返回值:" + result); } } //JoinPoint:连接点对象。此处指UserServiceImpl对象。 //jp.getArgs():连接点方法的参数数组。 //jp.getTarget():目标类。 //jp.getSignature():连接点方法信息。 //result:连接点方法返回值。
(3)定义切入点
<aop:config> <aop:pointcut id="pointcut" expression="execution(public void addNewUser(entity.User))"/> </aop:config> //public * addNewUser(entity.User): “*”表示匹配所有类型的返回值。 //public void *(entity.User): “*”表示匹配所有方法名。 //public void addNewUser(..): “..”表示匹配所有参数个数和类型。 //* com.service.*.*(..):匹配com.service包下所有类的所有方法。 //* com.service..*.*(..):匹配com.service包及其子包下所有类的所有方法。
(4)织入增强处理
<aop:config> <aop:pointcut id="pointcut" expression="execution(public void addNewUser(entity.User))" /> <aop:aspect ref="userServiceLogger"> <aop:before method="before" pointcut-ref="pointcut"></aop:before> <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/> </aop:aspect> </aop:config> //在<aop:config>中使用<aop:aspect>引用包含增强方法的Bean。然后分别通过<aop:before>和<aop:after-returning> 将方法声明为前置增强和后置增强,在<aop:after-returning>中可以通过returning属性指定需要注入返回值的属性名。 方法的JoinPoint类型参数无须特殊处理,Spring会自动为其注入连接点实例。 很明显UserService的addNewUser()方法可以和切入点pointcut相匹配, Spring会生成代理对象在它执行前后分别调用before()和afterReturning()方法,这样就完成了日志输出。
常用增强处理类型
增强处理类型 | 特 点 |
---|---|
Before | 前置增强处理,在目标方法前织入增强处理 |
AfterReturning | 后置增强处理,在目标方法正常执行(不出现异常)后织入增强处理 |
AfterThrowing | 异常增强处理,在目标方法抛出异常后织入增强处理 |
After | 最终增强处理,不论方法是否抛出异常,都会在目标方法最后织入增强处理 |
Around | 环绕增强处理,在目标方法的前后都可以织入增强处理 |
AOP常用配置元素
AOP配置元素 | 描 述 |
---|---|
< aop:config> | AOP配置的顶层元素,大多数的aop:*元素必须包含在aop:config元素内 |
< aop:pointcut> | 定义切点 |
< aop:aspect> | 定义切面 |
< aop:after> | 定义最终增强(不管被通知的方法是否执行成功) |
< aop:after-returning> | 定义after-returning增强 |
< aop:after-throwing> | 定义after-throwing增强 |
< aop:around> | 定义环绕增强 |
< aop:before> | 定义前置增强 |
< aop:aspectj-autoproxy> | 启动@AspectJ注解驱动的切面 |