文档章节

Spring笔记 - 面向切面编程AOP

Peter_Peng
 Peter_Peng
发布于 2015/07/15 13:44
字数 1789
阅读 63
收藏 0

1. 面向切面编程AOP

1.1 概述

- AOP是解耦的重要手段,让业务实体专注于业务逻辑,其他诸如安全验证、日志记录等辅助功能通过切面的方式,切入到业务实体的执行过程。

- 主流的AOP框架有:AspectJ、JBoss AOP、Spring AOPg

- Spring有自己实现的AOP,可以覆盖很多场景;也可以和AspectJ集成,获得更强的功能。Spring号称永远不会提供一个强大的解决方案和AspectJ竞争。

[参考]


1.2 AOP术语

- Aspect: 切面,是切入目标对象后执行的方法以及所属的对象,通常是非业务的辅助功能,可以说是切点、通知、织入对象的组合

- Joint Point: 连接点,是被织入目标可以被织入的地方,通常是业务对象的方法、字段、异常等

- Advice: 通知,是切面应用在连接点的时机,包括around、before、after

- Pointcut: 切点,符合AspectJ切点表达式的连接点集合,通常一个切面可以作用于多个连接点

- Weaving: 织入,把切面应用在连接点的过程,可以是:

  • 编译期compile time,例如AspectJ的织入编译器

  • 加载器load time,例如AspectJ5的LTW

  • 运行时run time,例如Spring AOP等纯Java AOP框架

- Introduction: 引入,为被织入对象定义新的方法或字段


1.3 Spring AOP

- Spring提供了4种方式的AOP支持:

  • 基于代理的经典AOP,由于繁琐很少使用

  • 纯POJO作为切面,通过xml的spring aop schema定义

  • @AspectJ注解驱动,功能基本等同于aop schema;Spring支持部分AspectJ注解;注意:@Aspect注解加上component-scan机制,才会被Spring容器发现并注册

  • 注入式AspectJ切面,将AspectJ本地方法加载到Spring容器

- 如果织入的目标对象是接口,Spring AOP基于JDK动态代理进行运行时织入;否则使用CGLIB。

- Spring AOP的连接点只能是public方法;而通过Spring集成AspectJ可以是private/protected方法或构造函数等。

1.3.1 切点

1.3.1.1 切点类型

- Spring AOP支持部分AspectJ的切点类型,且有自己扩展的类型

- Spring自动优化切点顺序,但定义时尽量缩小Spring的搜索范围

- 切点类型[参考1][参考2][参考3]

  • execution:用于匹配符合表达式的连接点;execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

  • within:用于匹配指定类型(指定类型或类型模式)内的方法执行;

  • target:用于匹配目标对象(指定类型)的方法;注意是目标对象的类型匹配,这样就不包括类型匹配的引入接口;

  • this:用于匹配AOP代理对象的方法;注意是AOP代理对象的类型匹配,这样就可以包括类型匹配的引入接口;

  • args:用于匹配当前执行的方法传入的参数为指定类型的方法;

  • @within:用于匹配所以持有指定注解类型内的方法,静态匹配

  • @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解,运行时匹配,在Spring容器中,和@within一样,在AspectJ中,区别很大;

  • @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

  • @annotation:用于匹配当前执行方法持有指定注解的方法;

  • bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

  • reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

- 例子

  • execution(public * *(..)) 所有公共方法

  • execution(* set*(..)) 所有名字以set开头的方法

  • execution(* com.xyz.service.AccountService.*(..)) 所有AccountService接口方法的实现

  • execution(* com.xyz.service.*.*(..)) 所有service包里的方法

  • execution(* com.xyz.service..*.*(..)) 所有service包及子包里的方法

  • execution(* *(java.io.Serializable)) 参数类型可序列号,与args不同

  • within(com.xyz.service.*) 所有service包的连接点

  • this(com.xyz.service.AccountService) 连接点的代理实现了AccountService接口

  • target(com.xyz.service.AccountService) 目标对象实现了AccountService接口

  • args(java.io.Serializable) 运行时传递的参数可序列化

  • @target(org.springframework.transaction.annotation.Transactional) 目标对象加了@Transactional注解

  • @within(org.springframework.transaction.annotation.Transactional) 目标对象的声明类型加了@Transactional注解

  • @annotation(org.springframework.transaction.annotation.Transactional) 方法加了@Transactional注解

  • @args(com.xyz.security.Classified) 运行时传入的参数加了@Classified注解

  • bean(*Service)  Bean Id需以Service结尾

1.3.1.2 切点组合

@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..*)"/>

1.3.1.3 切点重用

// 定义切点
@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>

1.3.2 通知

1.3.2.1 通知类型

@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"/>

13.2.2 传递参数到通知

// 前面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"/>

13.2.3 传递参数到连接点

@Around("x.y.z.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    Object retVal = pjp.proceed(new Object[] {"some arg"});
    return retVal;
}

1.3.3 引入Introduction

引入是利用代理为被代理的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>

1.3.4 打开AspectJ auto-proxying自动代理

让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>

1.3.5 Advisors

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>


© 著作权归作者所有

Peter_Peng
粉丝 4
博文 8
码字总数 10793
作品 0
普陀
私信 提问
Spring面试,IoC和AOP的理解

spring 的优点? 1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了AOP技术,利用它很容易...

Sandy_wu
2013/06/08
0
0
spring面试题 对DI , AOP概念的理解

spring 的优点? 1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了AOP技术,利用它很容易...

罗荣熙
2012/11/12
0
0
大数据学习之大数据技术笔记—spring入门

篇一 spring介绍 spring.io 官网 快速开始 Aop 面向切面编程,可以任何位置,并且可以细致到方法上 连接框架与框架 Spring 就是 IOC AOP 思想 有效的组织中间层对象一般都是切入 service 层 ...

董黎明
2018/12/16
0
0
Spring AOP解释及在项目中使用举例

一.AOP是什么 AOP - Aspect Oriented Programing,面向切面编程。将封装好的对象切开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”,切...

Jacktanger
2018/06/08
0
0
《Spring5学习》04 - 面向切面编程

一、Spring面向切面编程的基本概念 面向切面编程(即AOP):把项目中需要再多处使用的功能比如日志、安全和事务等集中到一个类中处理,而不用在每个需要用到该功能的地方显式调用。 横切关注...

老韭菜
2018/08/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

react-nativeAndroid打包报错:Daemon: AAPT2 aapt2-3.2.1-4818971-osx Daemon #0

笔者在工作开发任务中,最近在进行Android打release包测试时,遇到了如下报错,鼓捣了好久(甚是郁闷),终于解决了。 ReactNative版本环境如下 问题描述 直接使用react-native run-android运行...

凌宇之蓝
25分钟前
1
0
每天挂机10小时最低赚17美元|能挂机赚钱

2017 年 01 月 05 日 每天挂机10小时最低赚17美元|能挂机赚钱 每 天 挂 机 10 小 时 最 低 赚 17 美 元 ( 骗 你 我 不 是 人 ) , 就 像 挂 Q Q一样,一天挂十小时 , 最低每天赚 17 美金! 教 ...

linjin200
40分钟前
1
0
Class 文件结构

https://www.cnblogs.com/honghuzidelaoren/p/3622021.html

Java搬砖工程师
56分钟前
3
0
未分类资料

$ Inside the Linux Kernel 与知乎出产 一张漫画看懂Linux系统结构

十万猛虎下画山
57分钟前
1
0
Android人脸识别Demo竖屏YUV方向调整和图片保存

本博客包含三个常用方法,用于盛开Android版人脸识别Demo中竖屏使用时送入yuv数据,但一直无法识别的情况。 1.首先可以尝试顺时针旋转90°或270°,然后送入识别SDK。 2.旋转方向后依然无法识...

是哇兴哥棒棒哒
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部