1 概述
面向切面编程 AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。 所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。 Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。
2 技术名词
连接点(Joinpoint):
连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。 核心业务类中的方法 切入点(Pointcut):
被Spring切入连接点。 核心业务类中被添加辅助功能的方法 通知、增强(Advice):
可以为切入点添加额外功能,分为:
前置通知、 后置通知、 异常通知、 最终通知、 环绕通知等。 目标对象(Target):
引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。 织入(Weaving):
代理(Proxy):
切面(Aspect):
由切点和通知组成,将横切逻辑织入切面所指定的连接点中。
3 案例
AccountServiceImpl
package com. sw. service. impl ;
import com. sw. entity. Account ;
import com. sw. service. AccountService ;
public class AccountServiceImpl implements AccountService {
@Override
public Integer addAccount ( Account account) {
System . out. println ( "AccountServiceImpl ===> addAccount" ) ;
System . out. println ( 10 / 0 ) ;
return null ;
}
@Override
public Integer removeAccount ( Integer id) {
return null ;
}
@Override
public Integer modifyAccount ( Account account) {
return null ;
}
@Override
public Account queryAccount ( Account account) {
return null ;
}
}
增强类
package com. sw. advice ;
public class TranscationAdvice {
public void begin ( ) {
System . out. println ( "事务开启..." ) ;
}
public void commit ( ) {
System . out. println ( "事务提交..." ) ;
}
public void rollback ( ) {
System . out. println ( "事务回滚..." ) ;
}
public void release ( ) {
System . out. println ( "释放资源..." ) ;
}
public void around ( ) {
System . out. println ( "环绕通知..." ) ;
}
}
配置
<?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: context= " http://www.springframework.org/schema/context"
xmlns: aop= " http://www.springframework.org/schema/aop"
xsi: schemaLocation= " http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd" >
< context: component-scan base-package = " com.sw" > </ context: component-scan>
< bean id = " accountService" class = " com.sw.service.impl.AccountServiceImpl" > </ bean>
< bean id = " transcationAdvice" class = " com.sw.advice.TranscationAdvice" > </ bean>
< aop: config>
< aop: pointcut id = " pt" expression = " execution(public Integer com.sw.service.impl.AccountServiceImpl.addAccount(com.sw.entity.Account))" />
< aop: aspect ref = " transcationAdvice" >
< aop: before method = " begin" pointcut-ref = " pt" > </ aop: before>
< aop: after-returning method = " commit" pointcut-ref = " pt" > </ aop: after-returning>
< aop: after-throwing method = " rollback" pointcut-ref = " pt" > </ aop: after-throwing>
< aop: after method = " release" pointcut-ref = " pt" > </ aop: after>
</ aop: aspect>
</ aop: config>
</ beans>
测试
package com. sw. account ;
import com. sw. service. AccountService ;
import org. junit. Test ;
import org. springframework. context. support. ClassPathXmlApplicationContext ;
public class TestAccount {
@Test
public void getAccountService01 ( ) {
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
AccountService accountService = ioc. getBean ( AccountService . class ) ;
Integer ret = accountService. addAccount ( null ) ;
}
}
4 切点通配符
< aop: pointcut id = " myPointCut" expression = " execution(* *(com.sw.aaron.aop.basic.User))" />
< aop: pointcut id = " myPointCut" expression = " execution(* save())" />
< aop: pointcut id = " myPointCut" expression = " execution(* save(..))" />
< aop: pointcut id = " myPointCut" expression = " execution(com.sw.aaron.aop.basic.User *(..))" />
< aop: pointcut id = " myPointCut" expression = " execution(* com.sw.aaron.aop.basic.UserServiceImpl.*(..))" />
< aop: pointcut id = " myPointCut" expression = " execution(* com.sw.aaron.aop.basic.*.*(..))" />
< aop: pointcut id = " myPointCut" expression = " execution(* com.sw.aaron.aop..*.*(..))" />
5 添加多个通知
创建Logger增强类
package com. sw. advice ;
import org. aspectj. lang. JoinPoint ;
import org. slf4j. Logger ;
import org. slf4j. LoggerFactory ;
import java. util. Arrays ;
import java. util. Date ;
public class LoggerAdvice {
Logger logger = LoggerFactory . getLogger ( Logger . class ) ;
public void begin ( JoinPoint joinPoint) {
String name = joinPoint. getSignature ( ) . getName ( ) ;
Object [ ] args = joinPoint. getArgs ( ) ;
Object target = joinPoint. getTarget ( ) ;
logger. debug ( target + "==>" + name + "==>方法即将执行,开启日志记录..." + new Date ( ) + ( args== null ? null : Arrays . toString ( args) ) ) ;
}
}
增加切面配置
< aop: aspect ref = " loggerAdvice" >
< aop: before method = " begin" pointcut-ref = " pt" > </ aop: before>
</ aop: aspect>
6 获取切点数据
package com. sw. advice ;
import org. aspectj. lang. JoinPoint ;
import org. slf4j. Logger ;
import org. slf4j. LoggerFactory ;
import java. util. Arrays ;
import java. util. Date ;
public class LoggerAdvice {
Logger logger = LoggerFactory . getLogger ( Logger . class ) ;
public void begin ( JoinPoint joinPoint) {
String name = joinPoint. getSignature ( ) . getName ( ) ;
Object [ ] args = joinPoint. getArgs ( ) ;
Object target = joinPoint. getTarget ( ) ;
logger. debug ( target + "==>" + name + "==>方法即将执行,开启日志记录..." + new Date ( ) + ( args== null ? null : Arrays . toString ( args) ) ) ;
}
}
7 环绕通知
环绕通知
public void around ( ProceedingJoinPoint joinPoint) {
Object [ ] args = joinPoint. getArgs ( ) ;
try {
System . out. println ( "事务开启...携带参数:" + ( args== null ? null : Arrays . toString ( args) ) ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
System . out. println ( new Random ( ) . nextInt ( 100 ) ) ;
if ( i == 6 ) {
System . out. println ( 10 / 0 ) ;
}
}
System . out. println ( "事务提交..." ) ;
} catch ( Exception e) {
System . err. println ( "事务回滚..." ) ;
} finally {
System . out. println ( joinPoint. getSignature ( ) . getName ( ) + "==>方法运行结束,释放资源" ) ;
}
}
8 注解实现AOP
配置类
package com. sw. comfig ;
import org. springframework. context. annotation. ComponentScan ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. context. annotation. EnableAspectJAutoProxy ;
@Configuration
@ComponentScan ( basePackages = "com.sw" )
@EnableAspectJAutoProxy
public class SpringConfig {
}
通知类
package com. sw. advice ;
import org. aspectj. lang. ProceedingJoinPoint ;
import org. aspectj. lang. annotation. * ;
import org. springframework. stereotype. Component ;
import java. util. Arrays ;
import java. util. Random ;
@Component
@Aspect
public class TransactionAdvise {
@Pointcut ( "execution(* com.sw.service.impl..*(..))" )
public void pt ( ) { }
public void begin ( ) {
System . out. println ( "事务开启..." ) ;
}
public void commit ( ) {
System . out. println ( "事务提交..." ) ;
}
public void rollback ( ) {
System . out. println ( "事务回滚..." ) ;
}
public void release ( ) {
System . out. println ( "释放资源..." ) ;
}
@Around ( value = "pt()" )
public void around ( ProceedingJoinPoint joinPoint) {
Object [ ] args = joinPoint. getArgs ( ) ;
try {
System . out. println ( "事务开启...携带参数:" + ( args== null ? null : Arrays . toString ( args) ) ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
System . out. println ( new Random ( ) . nextInt ( 100 ) ) ;
if ( i == 6 ) {
System . out. println ( 10 / 0 ) ;
}
}
System . out. println ( "事务提交..." ) ;
} catch ( Exception e) {
System . err. println ( "事务回滚..." ) ;
} finally {
System . out. println ( joinPoint. getSignature ( ) . getName ( ) + "==>方法运行结束,释放资源" ) ;
}
}
}
测试
@Test
public void getAccountService04 ( ) {
AnnotationConfigApplicationContext ioc =
new AnnotationConfigApplicationContext ( SpringConfig . class ) ;
AccountService accountService = ioc. getBean ( AccountService . class ) ;
Account account = accountService. queryAccount ( 10010 ) ;
}