- AOP是解耦的重要手段,让业务实体专注于业务逻辑,其他诸如安全验证、日志记录等辅助功能通过切面的方式,切入到业务实体的执行过程。
- 主流的AOP框架有:AspectJ、JBoss AOP、Spring AOPg
- Spring有自己实现的AOP,可以覆盖很多场景;也可以和AspectJ集成,获得更强的功能。Spring号称永远不会提供一个强大的解决方案和AspectJ竞争。
[参考]
- Aspect: 切面,是切入目标对象后执行的方法以及所属的对象,通常是非业务的辅助功能,可以说是切点、通知、织入对象的组合
- Joint Point: 连接点,是被织入目标可以被织入的地方,通常是业务对象的方法、字段、异常等
- Advice: 通知,是切面应用在连接点的时机,包括around、before、after
- Pointcut: 切点,符合AspectJ切点表达式的连接点集合,通常一个切面可以作用于多个连接点
- Weaving: 织入,把切面应用在连接点的过程,可以是:
|
- Introduction: 引入,为被织入对象定义新的方法或字段
- Spring提供了4种方式的AOP支持:
|
- 如果织入的目标对象是接口,Spring AOP基于JDK动态代理进行运行时织入;否则使用CGLIB。
- Spring AOP的连接点只能是public方法;而通过Spring集成AspectJ可以是private/protected方法或构造函数等。
- Spring AOP支持部分AspectJ的切点类型,且有自己扩展的类型
- Spring自动优化切点顺序,但定义时尽量缩小Spring的搜索范围
- 切点类型[参考1][参考2][参考3]
|
- 例子
|
@Pointcut("execution(public * *(..))") private void anyPublicOperation() {} @Pointcut("within(x.y.z.trading..*)") private void inTrading() {} //and方式组合切点,还可以用||、! @Pointcut("anyPublicOperation() && inTrading()") private void tradingOperation() {}
<!-- 基于schema的定义无法实现切点组合,只能组合表达式 --> <aop:pointcut id="tradingOperation" expression="execution(public * *(..)) **and** within(x.y.z.trading..*)"/>
// 定义切点 @Aspect @Component public class SystemArchitecture { @Pointcut("within(x.y.z.web..*)") public void inWebLayer() {} @Pointcut("within(x.y.z.service..*)") public void inServiceLayer() {} } // 引用切点 @Aspect @Component public class WebProcessor{ @Before(pointcut="x.y.z.SystemArchitecture.inWebLayer()") public void inWebLayer() {} @Pointcut("within(x.y.z.service..*)") public void doSth() { /* ... */ } }
<aop:config> <aop:pointcut id="inWebLayer" expression="within(x.y.z.web..*)"/> <aop:aspect id="webProcessorAspect" ref="webProcessor"> <aop:before method="doSth" pointcut-ref="inWebLayer" /> </aop:aspect> </aop:config>
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") @AfterReturning( pointcut="x.y.z.SystemArchitecture.dataAccessOperation()", returning="retVal") public void doAccessCheck(Object retVal) { /* ... */ } @AfterThrowing( pointcut="x.y.z.SystemArchitecture.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { /* ... */ } //After (finally) @After("x.y.z.SystemArchitecture.dataAccessOperation()") public void doReleaseLock() { /* ... */ } @Around("x.y.z.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // 开始计时 ... Object retVal = pjp.proceed(); // 停止计时 ... return retVal; }
<aop:after-throwing pointcut="..." throwing="ex" method="doRecoveryActions"/>
// 前面AfterReturning可以returning参数、AfterThrowing可以throwing参数,此外,还可以运用args为切点、通知和切面指定参数 @Pointcut("x.y.z.SystemArchitecture.dataAccessOperation() && args(Account,int)") private void accountDataAccessOperation(Account account, int age) {} @Before("accountDataAccessOperation(account)") public void validateAccount(Account account) { /* ... */}
<aop:pointcut id="accountDataAccessOperation" expression=""x.y.z.SystemArchitecture.dataAccessOperation(Account,int) and args(account,age)" /> <aop:before pointcut="accountDataAccessOperation" method="validateAccount" arg-names="account,age"/>
@Around("x.y.z.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { Object retVal = pjp.proceed(new Object[] {"some arg"}); return retVal; }
引入是利用代理为被代理的Bean加入新的方法或字段
// 前提:被代理的Bean public class UserService { public void saveUser(User user) { /* ... */ } // 其他方法和字段略 } // 任务:要在saveUser之前检查user是否满足条件 // a. 定义Checker接口 public interface Checker { public boolean check(User user); } // b. 实现Checker接口 public class UserChecker implements Checker { public boolean check(User user) { /* ... */ } } // c. 定义切面,进行引入 @Aspect @Component public class CheckerAspect { @DeclareParents( value="x.y.z.UserService", defaultImpl=x.y.z.UserChecker.class) public UserChecker userChecker; } // d. 测试 UserService userService = (UserService)ctx.getBean("userService"); UserChecker userChecker = (UserChecker)userService; if(userChecker.check(user) userService.saveUser(user);
<aop:aspect id="checkerAspect" ref="checkerAspect"> <aop:declare-parents type-matching="x.y.z.UserService" implement-interface="x.y.z.Checker" default-impl="x.y.z.UserChecker" /> <aop:before pointcut="x.y.z.UserService.saveUser()" method="check" /> </aop:aspect>
让Spring容器自动为被代理类创建代理并织入切面
/JavaConfig方式 @Configuration @EnableAspectJAutoProxy(proxyTargetClass=false) //默认为false,为true时使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理 @ComponentScan public class ConcertConfig { @Bean public Audience audience() { return new Audience(); } } @Aspect public class Audience { // 定义pointcut和advice }
<!-- xml方式 --> <beans ...> <context:component-scan base-package="x.y.z" /> <aop:aspectj-autoproxy poxy-target-class="false"/> <bean class="Audience /> </bean>
Advisor是advice和pointcut的组合,只包含一个advice,advice实现了各种Advice接口,典型应用例如tx-advice、cache-advice
<aop:config> <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/> <aop:advisor pointcut-ref="businessService" advice-ref="tx-advice"/> </aop:config> <tx:advice id="tx-advice"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>