AspectJ语法

AspectJ语法本文详细介绍了 AspectJ 如何通过连接点 切点 建议和切面扩展 Java 编程 展示了其语法 类型间声明以及在开发和生产阶段的应用实例 包括插拔式调试和观察者模式的实现

大家好,欢迎来到IT知识分享网。

 AspectJ 通过连接点向Java添加一些新的程序元素来扩展Java,是Java面向切点一种实现。其主要包括连接点、切点、建议、切面及类型间声明。

连接点:程序在执行过程中明确的点。包括方法和构造函数调用及字段访问等。

切点:用来挑选连接点。

建议:在程序运行到由切点挑选的连接点时执行的程序片段。

切面:类似于类,成员包含了连接点、切点及切面等。

类型间声明:静态改变类的结构及层级关系。 可以为已有的类定义新的成员、方法。也可以使其继承新的类及实现接口。

1 AspectJ 语法

aspectJ 需要使用ajc编译器进行编译。

1.1 切点

切点类似于类中的方法,有访问权限修饰符、可以被定义为final,但也有些不同:1)不能重载。2)作用域包括切面声明及主体(方法的作用域是在类主体)。

if

条件判断

within

witin(全限定名),在某个类的全部连接点。

cflow

cflow(pointcut), 连接点pointcut 及之后的且在其封闭区间内的所有连接点。包括在在该方法中所调用的其他方法的连接点。

get

当某个字段被访问时会被捕获。(不能捕获参数)

set

当某个字段被赋值时会被捕获。(可以捕获参数)

handler

try..catch中的catch捕获的错误类型是Throwable类型及其子类型时不被捕获。(可以捕获参数)

adviceexecution

adviceexecution() 所有建议在执行时会被捕获。

表 切点的部分语法

public class PointcutEntity { private String name; public void fun(String val) { this.name = val; fun(); System.out.println("-----"); } private void fun() { System.out.println(this.name); } public void fun2() { System.out.println("fun2"); try { throw new Throwable(); } catch (Throwable e) { System.out.println("处理"); } } } public aspect PointcutEntityAspect perthis(withPointcut()){ // 相当于全局声明了,在这个切面中,所有的连接点都在PointcutEntity里运行 final pointcut withPointcut(): within(service.PointcutEntity); // PointcutEntity内的所有连接点 pointcut funPointcut(): execution(* service.PointcutEntity.fun()); pointcut cFlowPointcut(): cflow(funPointcut()); // funPointcut 连接点及之后切在封闭区间内的所以连接点 pointcut getPointcut(): get(String service.PointcutEntity.name); // 捕获PointcutEntity的name字段被访问 pointcut setPointcut(String name): set(String service.PointcutEntity.name) && args(name); // 捕获PointcutEntity的name字段被赋值 pointcut handlerPointcut(Throwable e): handler(Throwable) && args(e); // try..catch中的catch捕获的错误类型是Throwable类型及其子类型时不被捕获 // before(): cFlowPointcut() { // System.out.println("cFlowPointcut---"); // System.out.println(thisJoinPoint.getSourceLocation()); // System.out.println(thisJoinPoint.getSignature()); // System.out.println(thisJoinPoint.getKind()); // System.out.println(); // } // before(): getPointcut() { // System.out.println("getPointcut---"); // System.out.println(thisJoinPoint.getSourceLocation()); // System.out.println(thisJoinPoint.getSignature()); // System.out.println(thisJoinPoint.getKind()); // System.out.println(); // } // before(String name): setPointcut(name) { // System.out.println("getPointcut---"); // System.out.println("赋值value:" + name); // System.out.println(thisJoinPoint.getSourceLocation()); // System.out.println(thisJoinPoint.getSignature()); // System.out.println(thisJoinPoint.getKind()); // System.out.println(); // } before(Throwable e): handlerPointcut(e) { System.out.println("handlerPointcut---"); System.out.println("抛错:" + e); System.out.println(thisJoinPoint.getSourceLocation()); System.out.println(thisJoinPoint.getSignature()); System.out.println(thisJoinPoint.getKind()); System.out.println(); } public static void main(String[] args) { PointcutEntity pointcutEntity = new PointcutEntity(); pointcutEntity.fun2(); } }

1.2 建议

有三种类型,before、after(包括 after() returning、after() throwing及after)及around。around 可以替换方法的返回值。

public class AdviceEntity { public String fun() { System.out.println("fun"); return "建议"; } public void fun1() throws ClassNotFoundException { System.out.println("fun1"); throw new ClassNotFoundException(); } public String fun2() { System.out.println("fun2"); return "fun2"; } } public aspect AdviceEntityAspect { pointcut funPointcut(): execution(* service.AdviceEntity.fun*(..)); pointcut fun2Pointcut(): execution(* service.AdviceEntity.fun2(..)); after() returning (String val): funPointcut() { System.out.println("after() returning"); System.out.println("返回值:" + val); System.out.println(thisJoinPoint.getSourceLocation()); System.out.println(thisJoinPoint.getSignature()); System.out.println(); } after() throwing (ClassNotFoundException e): funPointcut() { System.out.println("after() throwing"); System.out.println("捕获错误:" + e); System.out.println(thisJoinPoint.getSourceLocation()); System.out.println(thisJoinPoint.getSignature()); System.out.println(); } String around(): fun2Pointcut() { System.out.println("String around()"); System.out.println(thisJoinPoint.getSourceLocation()); System.out.println(thisJoinPoint.getSignature()); System.out.println(); return "around() String"; } public static void main(String[] args) throws ClassNotFoundException { AdviceEntity entity = new AdviceEntity(); entity.fun(); System.out.println("fun2的返回值:" + entity.fun2()); entity.fun1(); } }

1.3 类型间声明

AspectJ 可以为类定义方法及字段,可以使其继承新的类及实现接口。

public class InterEntity { private final String name = "inter_entity"; public void fun() { System.out.println("InterEntity定义的name:" + this.name); this.speak(); // 在类中可以直接使用在切面中定义的方法 } } public interface SpeakInterface { void speak(); } public aspect InterEntityAspect { private String InterEntity.name = "InterEntityAspect"; // 给AdviceEntity定义name字段,虽然这个字段在原结构已存在, // 但是因为这里和AdviceEntity中其访问类型是private,所以不会冲突 declare parents: InterEntity implements SpeakInterface; // 定义实现接口,还需要定义实现该接口的方法 public void InterEntity.speak() { // 上面接口方法的实现 System.out.println("InterEntityAspect "); } private void InterEntity.show() { // 这里的this 是指InterEntity的实例 System.out.println(this.name); // InterEntityAspect this.fun(); // InterEntity定义的name:inter_entity this.speak(); } public static void main(String[] args) { InterEntity interEntity = new InterEntity(); interEntity.show(); } }

declare soft: Type: Pointcut; 捕获类型为Type的异常并且重抛成SoftException。(Type 不能是RuntimeException)。

declare precedence: TypePatternList; 为切面定义优先级,TypePatternList为切面列表,“,”隔开,越前面优先级越高。

public class DeclareEntity { public void fun() { System.out.println("fun"); } public void fun(int num) throws CustomException { if (num == 0) throw new CustomException(); System.out.println(33 / num); } } public abstract aspect DeclareEntityParentAspect pertarget(DeclareEntityClassPointcut()){ pointcut DeclareEntityClassPointcut(): within(service.DeclareEntity); pointcut funPointCut(): execution(* fun(..)); declare precedence: DeclareEntityChildrenAspectB,DeclareEntityChildrenAspectA; // 定义切面的优先级 declare soft:CustomException: funPointCut(); public static void main(String[] args) throws CustomException { DeclareEntity entity = new DeclareEntity(); entity.fun(); entity.fun(0); } } public aspect DeclareEntityChildrenAspectA extends DeclareEntityParentAspect{ before(): funPointCut() { System.out.println("DeclareEntityChildrenAspectA"); System.out.println(thisJoinPoint.getSourceLocation()); System.out.println(); } } public aspect DeclareEntityChildrenAspectB extends DeclareEntityParentAspect{ before(): funPointCut() { System.out.println("DeclareEntityChildrenAspectB"); System.out.println(thisJoinPoint.getSourceLocation()); System.out.println(); } } 

1.4 切面

切面像类一样也可以被继承,被继承的切面须为抽象切面。 也可以继承类及实现接口。

[issingleton()]

默认类型,将不限定切点。

perthis(Pointcut)

全局限定连接点在this(Pointcut)中。

pertarget(Pointcut)

全局限定连接点在target(Pointcut)中。

percflow(Pointcut)

全局限定连接点在cflow(Pointcut)z中。

percflowbelow(Pointcut)

全局限定连接点在cflowbelow(Pointcut)中。

图 五种在切面中全局限定连接点的方式

2 AspectJ 的应用场景

2.1 开发阶段

我们在开发过程中,需要进行调试,有时会往业务代码中插入跟踪代码。在上线的时候我们要把这些代码删除,否则可能会降低系统性能。有时我们插入的代码会比较多和广,可能会漏删这些代码。因为有些人常常会通过写脚本的方式来进行调试。

而AspectJ 在这方面具有独特的优势,可以实现对调试代码的“插拔”。

需求:打印develop.service 包下所有类以get或set开头方法访问日志。

public class Student { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } public class Teacher { private String name; private String course; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCourse() { return course; } public void setCourse(String course) { this.course = course; } } public class GetAndSetTrace { public static void info(String className,String methName,String location,Object result,Object... args) { String sb = "访问时间:" + new Date() + "\n" + "类名:" + className + "\n" + "方法:" + methName + "\n"; if (args != null) sb += "参数名:" + Arrays.asList(args) + "\n"; if (result != null) sb +="返回值:" + result + "\n"; sb += "连接点:" + location; System.out.println(sb); System.out.println("--------------------"); } } public aspect GetAndSetTraceAspect pertarget(targetClassPointcut()){ pointcut targetClassPointcut(): within(develop.service.*); pointcut getPointcut(): execution(Object+ get*()); pointcut setPointcut(Object obj): execution(void set*(*)) && args(obj); after() returning(Object obj): getPointcut() { trace(thisJoinPoint,obj); } before(Object obj): setPointcut(obj) { trace(thisJoinPoint,null); } private void trace(JoinPoint joinPoint,Object result) { String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().toShortString(); String location = joinPoint.getSourceLocation().getLine() + ""; GetAndSetTrace.info(className,methodName,location,result,joinPoint.getArgs()); } public static void main(String[] args) { Student student = new Student(); Teacher teacher = new Teacher(); student.setName("小名同学"); student.setAge(18); teacher.setName("刘老师"); teacher.setCourse("英语"); System.out.println("学生:" + student.getName()); System.out.println("年龄:" + student.getAge()); System.out.println("教师:" + teacher.getName()); System.out.println("课程:" + teacher.getCourse()); } } 

AspectJ 可重用,比如上面需求有改动,对日志打印格式及要求传递的参数做了修改。如果不使用Aspect而使用传统方式-往业务代码插入跟踪代码。那么每个被插入的跟踪代码都需要修改。而对于AspectJ 就仅仅修改相关切面及跟踪类即可。

2.2 生产阶段

观察者模式有两种角色:Subject目标类,被观察的对象;Observer 观察者,将观察的目标的改变而做出改变。

PropertyChangeSupport和PropertyChangeListener类是Java中用来监听对象属性变化的。

public class User { private String name; private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } public void setName(String name) { propertyChangeSupport.firePropertyChange("name",this.name,name); this.name = name; } public String getName() { return name; } public static void main(String[] args) { User user = new User(); PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { System.out.println(evt.getPropertyName() + "发生了改变,旧值:" + evt.getOldValue() + ",新值:" + evt.getNewValue()); } }; // 监听器 user.addPropertyChangeListener(listener); user.setName("黄先生"); user.setName("刘女士"); } }

我们可以使用AspectJ 实现上面的功能。

public aspect PropertyChangeAspect{ private Set<PropertyChangeObserver> User.observerSet = new HashSet<>(); public void User.addListener(PropertyChangeObserver observer) { observerSet.add(observer); } public Set<PropertyChangeObserver> User.getListenerSet() { return this.observerSet; } pointcut setMethod(Object arg): execution(* set*(*)) && args(arg); before(Object org): setMethod(org) { Object target = thisJoinPoint.getTarget(); if (target instanceof User) { for(PropertyChangeObserver listener : ((User) target).getListenerSet()) { listener.update(thisJoinPoint.getSignature().getName(),org); } } System.out.println(); } public static void main(String[] args) { User user = new User(); PropertyChangeObserver observer1 = (methodName, arg) -> { System.out.println("观察者1,方法:" + methodName + ",参数:" + arg); }; PropertyChangeObserver observer2 = new PropertyChangeObserver() { @Override public void update(String methodName, Object arg) { System.out.println("观察者2,参数:" + arg); } }; user.addListener(observer1); user.addListener(observer2); user.setName("黄"); user.setName("刘"); } // 自定义建设者 interface PropertyChangeObserver { void update(String methodName,Object arg); } }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/127785.html

(0)
上一篇 2025-09-05 13:15
下一篇 2025-09-05 13:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信