AOP 面向切面的编程
一、面向切面的编程需求的产生
- 代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀。每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。
- 代码分散: 以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码。如果日志需求发生变化,必须修改所有模块。
二、实现面向切面的编程
- 将需要实现AOP的类注入到Spring容器中,例如:
package com.neuedu.aop; import org.springframework.stereotype.Component; @Component
public class RawCaculator implements MathCaculator{ @Override
public int add(int i, int j) {
int rs=i+j;
System.out.println(i+"+"+j+"="+rs);
return rs;
} @Override
public int sub(int i, int j) {
int rs=i-j;
System.out.println(i+"-"+j+"="+rs);
return rs;
} @Override
public int mul(int i, int j) {
int rs=i*j;
System.out.println(i+"*"+j+"="+rs);
return rs;
} @Override
public int div(int i, int j) {
int rs=i/j;
System.out.println(i+"/"+j+"="+rs);
return rs;
} }要实现AOP的计算方法类
- 实现切面类
- 使用前置通知、后置通知、返回通知、异常通知实现切面类,注入到Spring容器中
package com.neuedu.aop; import static org.hamcrest.CoreMatchers.nullValue; import java.util.Arrays;
import java.util.List; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; @Component
//@Aspect表明当前类是一个切面类
@Aspect
//@Order表示切面执行顺序,value值越小优先级越高
@Order(value=50)
public class CaculatorAspect {
@Before(value = "execution(public int com.neuedu.aop.RawCaculator.*(int, int))")
public void showBeginLog(JoinPoint point){
//System.out.println("【日志】【前置通知】");
//获得参数列表:
Object[] args = point.getArgs();
List<Object> asList = Arrays.asList(args);
//获得方法名:
Signature signature = point.getSignature();
String name = signature.getName();
System.out.println("【日志】【前置通知】 目标方法名为:"+name+"参数为:"+asList);
}
@AfterThrowing(value = "execution(public int com.neuedu.aop.RawCaculator.*(..))" ,throwing="ex")
public void showThrowing(JoinPoint point,Exception ex){
//System.out.println("【日志】【异常通知】");
System.out.println("【日志】【异常通知】 异常信息:"+ex);
}
@After(value = "execution(public int com.neuedu.aop.RawCaculator.*(..))")
public void showAfter(){
System.out.println("【日志】【后置通知】");
}
@AfterReturning(value = "execution(* * .*(..))",returning="result")
public void showAfterReturning(JoinPoint point,Object result){
//System.out.println("【日志】【返回通知】");
System.out.println("【日志】【返回通知】 目标方法的返回值为"+result);
}
}
- 使用前置通知、后置通知、返回通知、异常通知实现切面类,注入到Spring容器中
- 将需要实现AOP的类注入到Spring容器中,例如:
使用黄色背景标注的代码是声明的通知,其中包括通知类型和切入点表达式,以下就是对通知类型的介绍,切入点表达式在最后
@Before 前置通知:在方法执行之前执行的通知
@After 后置通知:后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候
@AfterReturning 返回通知:
- 无论连接点是正常返回还是抛出异常,后置通知都会执行。如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知。
- 在返回通知中访问连接点的返回值:
- 在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称
- 必须在通知方法的签名中添加一个同名参数。在运行时Spring AOP会通过这个参数传递返回值
- 原始的切点表达式需要出现在pointcut属性中
@AfterThrowing 异常通知:只在连接点抛出异常时才执行异常通知.:
- 将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。
- 如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行
- 将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。
2.使用环绕通知实现切面类,注入到Spring容器中
1 package com.neuedu.aop; import java.util.Arrays;
import java.util.List; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; @Component
//@Aspect表明当前类是一个切面类
@Aspect
@Order(value=40)
public class secondAspect {
@Around(value = "execution(* * .*(..))")
public Object Around(ProceedingJoinPoint point){
Object result=null;
Object[] args = point.getArgs();
List<Object> asList = Arrays.asList(args);
Signature signature = point.getSignature();
String name = signature.getName();
try{
System.out.println("【事物日志】【前置通知】 目标方法名为:"+name+"参数为:"+asList);
try {
result=point.proceed(args);
} finally{
System.out.println("【事物日志】【后置通知】");
}
System.out.println("【事物日志】【返回通知】 目标方法的返回值为"+result);
}catch (Throwable e) {
System.out.println("【事物日志】【异常通知】 异常信息:"+e);
}
return result;
}
38 }
@Around 环绕通知:
- 环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
3.实现测试类
package junit.test; import static org.junit.Assert.*; import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.neuedu.aop.MathCaculator;
import com.neuedu.aop.RawCaculator; public class TestCaculator { @Test
public void test() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//MathCaculator bean = ioc.getBean(RawCaculator.class); 不可用 (代表类 类型不一样)
//rawCaculator即使使用注解时代表类的id;又是xml配置文件里的id
MathCaculator bean = (MathCaculator) ioc.getBean("rawCaculator");
bean.add(10, 5);
System.out.println();
bean.sub(14, 5);
System.out.println();
bean.mul(8, 7);
System.out.println();
bean.div(10, 0);
} }
测试类
三、关于切入点表达式
1.作用:
通过表达式的方式定位一个或多个具体的连接点。
2.语法细节:
1)切入点表达式的语法格式:execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
2)举例说明:
|
表达式 |
execution(* com.atguigu.spring.ArithmeticCalculator.*(..)) |
|
含义 |
ArithmeticCalculator接口中声明的所有方法。 第一个“*”代表任意修饰符及任意返回值。 第二个“*”代表任意方法。 “..”匹配任意数量、任意类型的参数。 若目标类、接口与该切面类在同一个包中可以省略包名。 |
|
表达式 |
execution(public * ArithmeticCalculator.*(..)) |
|
含义 |
ArithmeticCalculator接口的所有公有方法 |
|
表达式 |
execution(public double ArithmeticCalculator.*(..)) |
|
含义 |
ArithmeticCalculator接口中返回double类型数值的方法 |
|
表达式 |
execution(public double ArithmeticCalculator.*(double, ..)) |
|
含义 |
第一个参数为double类型的方法。 “..” 匹配任意数量、任意类型的参数。 |
|
表达式 |
execution(public double ArithmeticCalculator.*(double, double)) |
|
含义 |
参数类型为double,double类型的方法 |
3.重用切入点:
1)
在编写AspectJ切面时,可以直接在通知注解中书写切入点表达式。但同一个切点表达式可能会在多个通知中重复出现
- 在Aspect切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法。切入点的方法体通常是空的,因为将切入点定义与应用程序逻辑混在一起是不合理的。
- 切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。
2)示例代码:
1 @Component
2 //@Aspect表明当前类是一个切面类
@Aspect
4 //@Order表示切面执行顺序,value值越小优先级越高
5 @Order(value=50)
6 public class CaculatorAspect {
//重用切入点
@Pointcut("execution(* * .*(..))")
private void LoggingOperation(){
}
@Before(value = "LoggingOperation()")
public void showBeginLog(JoinPoint point){
//获得参数列表:
Object[] args = point.getArgs();
List<Object> asList = Arrays.asList(args);
//获得方法名:
Signature signature = point.getSignature();
String name = signature.getName();
System.out.println("【日志】【前置通知】 目标方法名为:"+name+"参数为:"+asList);
}
@AfterThrowing(value = "LoggingOperation()" ,throwing="ex")
public void showThrowing(JoinPoint point,Exception ex){
System.out.println("【日志】【异常通知】 异常信息:"+ex);
}
@After(value = "LoggingOperation()")
public void showAfter(){
System.out.println("【日志】【后置通知】");
29 }
@AfterReturning(value = "LoggingOperation()",returning="result")
public void showAfterReturning(JoinPoint point,Object result){
System.out.println("【日志】【返回通知】 目标方法的返回值为"+result);
}
}
AOP 面向切面的编程的更多相关文章
- Guice 学习(八)AOP (面向切面的编程)
Guice的AOP还是非常弱的.眼下只支持方法级别上的,另外灵活性也不是非常高. 看例如以下演示样例: Guice支持AOP的条件是: 类必须是public或者package (default) 类不 ...
- AOP 面向切面编程, Attribute在项目中的应用
一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...
- AOP面向切面编程的四种实现
一.AOP(面向切面编程)的四种实现分别为最原始的经典AOP.代理工厂bean(ProxyFacteryBean)和默认自动代理DefaultAdvisorAutoProxyCreator以及Bea ...
- Javascript aop(面向切面编程)之around(环绕)
Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...
- Method Swizzling和AOP(面向切面编程)实践
Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...
- [转] AOP面向切面编程
AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...
- AOP 面向切面编程、拦截器
AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.它是一种新的方法论, ...
- C# AOP 面向切面编程之 调用拦截
有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...
- 【原创】Android AOP面向切面编程AspectJ
一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...
随机推荐
- 【微信小程序开发教程】如何显示群名称?
今年 5 月份的时候,微信宣布:「为了更好的针对群场景提供个性化服务,当用户在群聊中点击小程序分享卡片时,小程序支持开发者获取群 ID 和群名称」.但随后没多久,发现小程序只返回了群 ID,并没有给我 ...
- Unity strip engine code可能会使程序崩溃
最近正在做新大厅的红包推荐口令快速领金币入口拍卖行之类的功能,同事把我的捕鱼整合到他的项目中时出现了闪退的问题,经排查是因为strip engine code选项. Strip engine code ...
- thinkphp 5 前台格式化输出日期
thinkphp格式化输出 {$time|strtotime|date="Y年m月d日",###} $time 是日期字符串,一般后台的时间是"Y-m-d h:i:s ...
- 2017年7月Web服务器调查报告
在2017年7月的调查中,我们收到了来自1,767,964,429个网站和6,593,508个面向web的计算机的反馈.这是一个小小的进步,网站的数量增加了100万个(+0.06%),面向web的计算 ...
- Spring boot——logback 基础使用篇(一)
1 简单日志配置 spring boot内部使用Commons Logging来记录日志,但也保留外部接口可以让一些日志框架来进行实现,例如Java Util Logging,Log4J2还有Logb ...
- 老生常谈之SQL Server (行转列,列转行)
Open the first article 在本文章中主要介绍以下内容: 1.静态行转列 2.静态列转行 3.动态行转列 4.动态列转行 1.静态行转列 --静态的行转列 --新建一个科目成绩表 - ...
- Ant Design UI组件
Ant Design 是面向中台的 UI 设计语言. http://ant.design/
- ionic时间插件ion-datetime-picker
https://github.com/katemihalikova/ion-datetime-picker
- Java中的24种设计模式与7大原则 (转)
一.创建型模式 1.抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类.2.生成器模式(Builder patter ...
- UITextField成为第一响应者
@interface CSRegisterTwoViewController ()<UITextFieldDelegate> @property (weak, nonatomic) IBO ...