关于AOP
AOP,面向切面编程是OOP之后出现的概念(大概)。
面向对象基本上就是针对类来设计代码,类中定义方法,逻辑中调用不同的类中不同的方法构成业务。
切面
而面向切面中的切面到底是什么。在业务逻辑中,我们会分很多不同的模块,也有不同的类,而这些类的一些方法中,有一些共性功能。比如认证、日志、限流等功能,在各个模块都需要,那每一个功能可以被认为是一个切面。
类似一个三明治,面包、火腿、菜叶、番茄等等每一层都是一个功能模块,一刀切下去这个切面贯穿整个三明治各层,这一刀就形成一个切面,而这个切面就可以是比如日志功能。这是我的理解。
术语
三个术语,不初步看一下后面看的时候就会一头雾水。
- pointcut - 这个可以理解为一个定义,他定义了一个切面(Aspect) 切在哪。实际上是一个表达式,程序中和表达式匹配的地方,切面里的动作会被执行。
- Advice - 建议?实际就是指切面定义的某个动作
- joinpoint - 这代表了一个实际的结合点,即一个被pointcut匹配到的模块中的方法。
我们定义一个或多个切面(Aspect),切面中有各种需要执行的方法(Advice,如记录日志,认证请求),每个Advice又有一个pointcut定义这个Advice应该再什么地方执行。而当功能模块的某个方法,匹配到一个pointcut定义时,该pointcut对应的Advice就会介入执行,这就是一个joinpoint
。
Spring AOP
对应AOP概念,Spring AOP主要做这几件事
- 定义一个Aspect
- 定义Aspect中的pointcut和Advice
- 定义被执行的joinpoint
Aspect
和Spring Core中对bean的定义类似,也分为注解方式和配置文件方式(Spring里目前看到基本上所有相关功能都包含注解和配置文件两种配置方式)。
注解方式需要:
- 在Aspect类上标注
@Aspect
- 在配置文件中使能
<aop:aspectj-autoproxy />
,这样Aspect才能找到对应的joinpoint - 配置该Aspect为一个bean(似乎@Aspect注解并不会被识别为bean?)
配置方式好像不太是Spring推荐的方式了这里贴一个完整配置,其中定义了Aspect、pointcut表达式、Advice。
<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-3.0.xsd
http://www.springframework.org/schema/aop/
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- We don‘t need to this; This is required only in annotation based AOP support -->
<!-- <aop:aspectj-autoproxy /> -->
<aop:config>
<!-- Spring AOP Pointcut definitions -->
<aop:pointcut id="loggingOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.*(..))" />
<aop:pointcut id="transactionOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.getEmployeeById(..))" />
<!-- Spring AOP aspect 1 -->
<aop:aspect id="loggingAspect" ref="loggingAspectBean">
<!-- Spring AOP advises -->
<aop:before pointcut-ref="loggingOperation" method="logBefore" />
<aop:after pointcut-ref="loggingOperation" method="logAfter" />
</aop:aspect>
<!-- Spring AOP aspect 2 -->
<aop:aspect id="transactionAspect" ref="transactionAspectBean">
<aop:before pointcut-ref="transactionOperation" method="getEmployeeById" />
</aop:aspect>
</aop:config>
<!-- Spring AOP aspect instances -->
<bean id="loggingAspectBean" class="com.howtodoinjava.demo.aop.EmployeeCRUDLoggingAspect" />
<bean id="transactionAspectBean" class="com.howtodoinjava.demo.aop.EmployeeCRUDTransactionAspect" />
<!-- Target Object -->
<bean id="employeeManager" class="com.howtodoinjava.demo.aop.EmployeeManagerImpl" />
</beans>
pointcut和Advice
一个简单的例子:
@Before("execution(* EmployeeManager.*(..))")
public void logBefore(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBeforeV2() : " + joinPoint.getSignature().getName());
}
logBefore方法就是一个Advice(要执行的动作),他是一个Before类型的Advice,会在pointcut指定位置之前执行。
这个Advice对应的pointcut就是("execution(* EmployeeManager.*(..))")
这句,标识匹配的方法时EmployeeManagerpacakge下的所有方法,方法参数和返回值都不限,匹配的是这个方法的执行。
Advice除了Before,还有After
- 执行后、Around
- 方法执行前后、 AfterReturn
- 方法正常结束、 AfterThrowing
- 方法抛出异常时执行时。
pointcut除了execution
也有多种表达式方式,比如匹配一个包中的所有方法within(packagename.xxx)
,匹配一个beanbean(beanNamexxx)
等。
被执行的joinpoint
只要是普通的bean即可,譬如@Component, @Controller等。在bean上不需要任何额外的定义,只要符合pointcut,相应的advice就会执行。
这也体现了Spring的涉及思路,非侵入式,减少依赖。
如果需要做日志,那么定义好Aspect就好,不需要动相应的组件代码
原文地址:https://www.cnblogs.com/mosakashaka/p/12609089.html