AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
1.基础
- 在不修改源码的情况下对程序行为进行扩展。
- Spring AOP - Aspect Oriented Programming面向切面编程。
- AOP的做法是将通用、与业务无关的功能抽象封装为切面类。
- 切面可配置在目标方法的执行前、后运行,真正做到即插即用
- SpringAop与AspectJ的关系:
- Eclipse AspectJ,一种基于Java平台的面向切面编程的语言。
- Spring AOP使用AspectJWeaver实现类与方法匹配。
- Spring AOP利用代理模式实现对象运行时功能扩展。
- SpringAop的导包
-
使用aop的xml配置=》spring.io=>springframework=》the apo schema
- 一个简单的demo(关键字:切面类(JoinPoint:连接点、通过连接点可以获取目标点/方法的信息)):
-
<!-- AOP配置 --> <bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"></bean> <aop:config> <!-- PointCut 切点,使用execution表达式描述切面的作用范围 --> <!-- execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上 --> <aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut> <!--只对所有Service类生效--> <!-- <aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>--> <!--只对所有返回值为String类型方法生效--> <!--<aop:pointcut id="pointcut" expression="execution(String com.imooc..*Service.*(..))"></aop:pointcut>--> <!--对方法名进行约束 --> <!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.create*(..))"></aop:pointcut>--> <!-- 对参数进行约束 --> <!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(String,*))"></aop:pointcut>--> <!-- 定义切面类 --> <aop:aspect ref="methodAspect"> <!-- before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime() --> <aop:before method="printExecutionTime" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
2.有关参数
2.1基础
- Object getTarget():获取IoC容器内目标对象。
- Signature getSignature()获取目标方法。
- Object[] getArgs() 获取目标方法参数。
- demo(从基础的demo中拓展过来)
2.2PointCut切点表达式
* 通配符:一切可出现情况
..:对包的省略
(..):对参数的省略,对填写的参数数量、类型都不做限制
<aop:config>
<!-- PointCut 切点,使用execution表达式描述切面的作用范围 -->
<!-- execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上 -->
<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>
<!--只对所有Service类生效 只要类名是 Service结尾的就行 比如 appService-->
<!-- <aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>-->
<!--只对所有返回值为String类型方法生效-->
<!--<aop:pointcut id="pointcut" expression="execution(String com.imooc..*Service.*(..))"></aop:pointcut>-->
<!--对方法名进行约束 对create开头的方法进行捕获 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.create*(..))"></aop:pointcut>-->
<!-- 对参数进行约束 比如目前只要两个参数,且第一个为string类型-->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(String,*))"></aop:pointcut>-->
<!-- 定义切面类 -->
<aop:aspect ref="methodAspect">
<!-- before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime() -->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
3.五种环绕通知
- Before Advice:执行前通知,目标方法运行前执行。
- After Returning Advice :返回后通知,目标方法返回数据后执行。【执行成功之后返回】
- After Throwing Advice: 异常通知,目标方法抛出异常后执行。【执行异常之后返回】
- After Advice :最后(最终)通知,目标方法运行后执行。【无法获取返回值或者内部抛出异常】
- Around Advice (环绕通知):最强大通知,自定义通知执行时机,可决定目标方法是否运行。
- After Returning Advice和After Throwing Advice二者只能额选其一。
- After Returning Advice和After Advice和After Throwing Advice的执行顺序由配置的顺序决定。
- 特殊的通知:引介增强
- 引介增强(IntroductionInterceptor)是对类的增强,而非方法。
- 引介增强允许在运行时为目标类增加新属性或方法。
- 引介增强允许在运行时改变类的行为,让类随运行环境动态变更
- 除了环绕通知的demo:
- 环绕通知demo,关键字 Object(返回值必须是这个) ProceedingJoinPoint:
4.aop注解模式
- 五种通知方法:
- 前置通知(@Before)
- 后置通知(@After)
- 返回通知 (@AfterReturning)
- 异常通知 (@AfterThrowing)
- 环绕通知 (@Around)
- @PointCut:公共切入点表达式
- JoinPoint: 作为函数的参数传入切面方法,可以得到目标方法的相关信息
- @Aspect : 指定切面类
- @EnableAspectJAutoProxy : 开启基于注解的AOP模式
- demo(依旧是xml中配置)
- demo2.程序中配置
5.springAop底层实现原理(代理)
5.1基础
- 代理=》加强原始方法或者类。可以随时添加删除且不影响原始代码(类似浏览器插件,可要可不要。对浏览器本身没有影响)。
- Spring基于代理模式实现功能动态扩展,包含两种形式:
- 目标类拥有接口,通过JDK动态代理实现功能扩展。
- 目标类没有接口,通过CGLib组件实现功能扩展。
5.2静态代理
- 代理类和委托类实现相同的接口。 代理类对功能有额外的拓展。
- 静态代理使用:
- 代理类嵌套使用
5.3jdk动态代理
- jdk代理基于反射完成。
- demo:
5.3CGLib实现代理类
- CGLib是运行时字节码增强技术。
- Spring AOP扩展无接口类使用CGLib。
- AOP会运行时生成目标继承类字节码的方式进行行为扩展。
- 如果目标类实现了接口:就是用jdk动态代理
- 如果目标类没有实现接口则使用CGLib代理