AspectJ语法

 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);
    }
}
  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值