13-Spring AOP
13.1 AOP基本概念
13.1.1 AOP概述
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件系统开发中的一个热点,也是spring框架的一个重点。
利用AOP可以实现业务逻辑各个部分的隔离,从而使得业务逻辑各个部分的耦合性降低,提高程序的可重用性,同时提高开发效率
aop是面向切面编程,使用动态代理技术,实现在不修改java源代码的情况下,运行时实现方法功能的增强。
13.1.2 AOP作用
使用动态代理技术,在程序运行期间,不修改java源代码对已有方法功能进行增强
1.减少重复代码,提高开发效率
2.统一管理统一调用,方便维护
13.1.3 AOP常用术语
在spring中,连接点指的都是方法(指的是那些要被增强功能的候选方法)
在运行时已经被spring 的AOP实现了增强的方法
通知指的是拦截到Joinpoint之后要做的事情。即增强的功能
通知类型:前置通知、后置通知、异常通知、最终通知、环绕通知
被代理的对象。比如动态代理案例中的演员
一个类被AOP织入增强后,即产生一个结果代理类。比如动态代理案例中的经纪人
织入指的是把增强用于目标对象。创建代理对象的过程
切面指的是切入点和通知的结合
13.2 完全xml配置AOP
13.2.1 项目目录
13.2.2 配置pom.xml
< properties>
< spring.version> 5.0.2.RELEASE</ spring.version>
< aopalliance.version> 1.0</ aopalliance.version>
</ properties>
< dependencies>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-aspects</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> aopalliance</ groupId>
< artifactId> aopalliance</ artifactId>
< version> ${aopalliance.version}</ version>
</ dependency>
</ dependencies>
13.2.3 编写业务功能
package cn. guardwhy. service;
public interface CustomerService {
void saveCustomer ( ) ;
void findCustomerById ( Integer id) ;
}
package cn. guardwhy. service. impl;
import cn. guardwhy. service. CustomerService;
public class CustomerServiceImpl implements CustomerService {
@Override
public void saveCustomer ( ) {
System. out. println ( "保存客户操作" ) ;
}
@Override
public void findCustomerById ( Integer id) {
System. out. println ( "根据客户id查询客户,客户id: " + id) ;
}
}
13.2.4 编写通知
package cn. guardwhy. advice;
public class LogAdvice {
public void printLog ( ) {
System. out. println ( "记录用户操作日志" ) ;
}
}
13.2.5 编写bean.xml
<?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" >
< bean id = " customerService" class = " cn.guardwhy.service.impl.CustomerServiceImpl" > </ bean>
< bean id = " logAdvice" class = " cn.guardwhy.advice.LogAdvice" > </ bean>
< aop: config>
< aop: aspect id = " logAspect" ref = " logAdvice" >
< aop: before method = " printLog" pointcut-ref = " pt1" > </ aop: before>
< aop: pointcut id = " pt1" expression = " execution(public void
cn.guardwhy.service.impl.CustomerServiceImpl.saveCustomer())" />
</ aop: aspect>
</ aop: config>
</ beans>
13.2.6 执行结果
13.3 切入点表达式
13.3.1 项目目录
13.3.2 bean.xml
<?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" >
< bean id = " customerService" class = " cn.guardwhy.service.impl.CustomerServiceImpl" > </ bean>
< bean id = " logAdvice" class = " cn.guardwhy.advice.LogAdvice" > </ bean>
< aop: config>
< aop: aspect id = " logAspect" ref = " logAdvice" >
< aop: before method = " printLog" pointcut-ref = " pt1" > </ aop: before>
< aop: pointcut id = " pt1" expression = " execution(* cn.guardwhy.service..*.*(..))" >
</ aop: pointcut>
</ aop: aspect>
</ aop: config>
</ beans>
13.3.3 常用标签说明
#< aop: config> :
作用:声明aop配置
#< aop: aspect> :
作用:配置切面
属性:
id:唯一标识切面的名称
ref:引用通知类bean的id
#< aop: pointcut> :
作用:配置切入点表达式
属性:
id:唯一标识切入点表达式名称
expression:定义切入点表达式
#< aop: before> :
作用:配置前置通知
属性:
method:指定通知方法名称
pointcut:定义切入点表达式
pointcut- ref:引用切入点表达式的id
#< aop: after- returning> :
作用:配置后置通知
属性:
method:指定通知方法名称
pointcut:定义切入点表达式
pointcut- ref:引用切入点表达式的id
#< aop: after- throwing> :
作用:配置异常通知
属性:
method:指定通知方法名称
pointcut:定义切入点表达式
pointcut- ref:引用切入点表达式的id
#< aop: after> :
作用:配置最终通知
属性:
method:指定通知方法名称
pointcut:定义切入点表达式
pointcut- ref:引用切入点表达式的id
#< aop: around> :
作用:配置环绕通知
属性:
method:指定通知方法名称
pointcut:定义切入点表达式
pointcut- ref:引用切入点表达式的id
13.4 通知类型案例
13.4.1 通知类型
在目标方法执行前执行
在目标方法正常返回后执行。它和异常通知只能执行一个
在目标方法发生异常后执行。它和后置通知只能执行一个
无论目标方法正常返回,还是发生异常都会执行
综合了前面四类通知,可以手动控制通知的执行时间点和顺序
13.4.2 项目目录
13.4.3 日志通知类
package cn. guardwhy. advice;
import org. aspectj. lang. ProceedingJoinPoint;
public class LogAdvice {
public void beforeLog ( ) {
System. out. println ( "[前置通知]记录用户操作日志" ) ;
}
public void afterReturningLog ( ) {
System. out. println ( "【后置通知】记录用户操作日志" ) ;
}
public void afterThrowingLog ( ) {
System. out. println ( "【异常通知】记录用户操作日志" ) ;
}
public void afterLog ( ) {
System. out. println ( "【最终通知】记录用户操作日志" ) ;
}
public void aroundLog ( ProceedingJoinPoint pjp) {
System. out. println ( "[环绕通知-前置通知]记录用户操作日志" ) ;
try {
Object[ ] args = pjp. getArgs ( ) ;
Object retv = pjp. proceed ( args) ;
System. out. println ( "[环绕通知-后置通知]记录用户操作日志" ) ;
} catch ( Throwable throwable) {
throwable. printStackTrace ( ) ;
System. out. println ( "[环绕通知-异常通知]记录用户操作日志" ) ;
}
System. out. println ( "[环绕通知-最终通知]记录用户操作日志" ) ;
}
}
13.4.4 配置bean.xml
<?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" >
< bean id = " customerService" class = " cn.guardwhy.service.impl.CustomerServiceImpl" />
< bean id = " logAdvice" class = " cn.guardwhy.advice.LogAdvice" />
< aop: config>
< aop: aspect id = " logAspect" ref = " logAdvice" >
< aop: before method = " beforeLog" pointcut-ref = " pt1" />
< aop: after-returning method = " afterReturningLog" pointcut-ref = " pt1" />
< aop: after-throwing method = " afterThrowingLog" pointcut-ref = " pt1" />
< aop: after method = " afterLog" pointcut-ref = " pt1" />
< aop: around method = " aroundLog" pointcut-ref = " pt1" />
< aop: pointcut id = " pt1" expression = " execution(* cn.guardwhy.service..*.*(..))" />
</ aop: aspect>
</ aop: config>
</ beans>
13.4.5 客户表现层(Controller)
package cn. guardwhy. controller;
import cn. guardwhy. service. CustomerService;
import org. springframework. context. ApplicationContext;
import org. springframework. context. support. ClassPathXmlApplicationContext;
public class CustomerController {
public static void main ( String[ ] args) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "classpath:bean.xml" ) ;
CustomerService customerService = ( CustomerService) context. getBean ( "customerService" ) ;
customerService. saveCustomer ( ) ;
}
}
13.4.6 执行结果
13.5 xml和注解配置AOP
13.5.1 项目目录
13.5.2 客户service实现类
package cn. guardwhy. service. impl;
import cn. guardwhy. service. CustomerService;
import org. springframework. stereotype. Service;
@Service ( "customerService" )
public class CustomerServiceImpl implements CustomerService {
@Override
public void saveCustomer ( ) {
System. out. println ( "保存客户操作" ) ;
}
@Override
public void findCustomerById ( Integer id) {
System. out. println ( "根据客户id查询客户,客户id: " + id) ;
}
}
13.5.3 日志通知类
package cn. guardwhy. advice;
import org. aspectj. lang. ProceedingJoinPoint;
import org. aspectj. lang. annotation. *;
import org. springframework. stereotype. Component;
@Component ( "logAdvice" )
@Aspect
public class LogAdvice {
@Pointcut ( "execution(* cn.guardwhy.service..*.*(..))" )
public void pt1 ( ) {
}
@Before ( "pt1()" )
public void beforeLog ( ) {
System. out. println ( "[前置通知]记录用户操作日志" ) ;
}
@AfterReturning ( "pt1()" )
public void afterReturningLog ( ) {
System. out. println ( "【后置通知】记录用户操作日志" ) ;
}
@AfterThrowing ( "pt1()" )
public void afterThrowingLog ( ) {
System. out. println ( "【异常通知】记录用户操作日志" ) ;
}
@After ( "pt1()" )
public void afterLog ( ) {
System. out. println ( "【最终通知】记录用户操作日志" ) ;
}
@Around ( "pt1()" )
public void aroundLog ( ProceedingJoinPoint pjp) {
System. out. println ( "[环绕通知-前置通知]记录用户操作日志" ) ;
try {
Object[ ] args = pjp. getArgs ( ) ;
Object retv = pjp. proceed ( args) ;
System. out. println ( "[环绕通知-后置通知]记录用户操作日志" ) ;
} catch ( Throwable throwable) {
throwable. printStackTrace ( ) ;
System. out. println ( "[环绕通知-异常通知]记录用户操作日志" ) ;
}
System. out. println ( "[环绕通知-最终通知]记录用户操作日志" ) ;
}
}
13.5.4 配置bean.xml
<?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"
xmlns: context= " http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
< context: component-scan base-package = " cn.guardwhy" />
< aop: aspectj-autoproxy/>
</ beans>
13.5.5 执行结果