从零开始JAVA AOP

1、初识AOP

更多文章请查看跟人博客。

很久以前就听说了AOP编程,也知道字母上的解释是“面向切面的编程”,更久之前我也听过OOP“面向对象编程”。然而AOP不是OOP的代替,而是基于OOP 的完善,AOP只是一种编程风格。

下面是一个OOP用烂了的例子,账户类封装了存钱和取钱的接口供外部类调用。

但是现实中的账户存取操作远比这个复杂,需要有事务、日志、同步等操作。如果使用Java面向对象的方法来扩展该类,常用的方式是继承,但是JAVA的单继承属性导致扩充起来很麻烦,而且每当要增加一个新的功能就需要修改原有的代码,其可扩展性比较差。如果使用AOP编程,其效果是这样的:

 

通过AOP的方式,使得类拓展功能起来非常方便,而且功能与类之间的耦合度更低。下面从零开始编写一个AOP小程序,我们要仿照主流的框架Spring来编写,顺便理解Spring AOP的原理。

2、简单AOP框架实现

根据上图所示的模型,在不改变类的基础上能够给类的实现进行扩充,除了继承还有一种方式,那就是代理(Proxy)。代理是什么,举个简单的例子,明星唱歌,明星只需要会唱歌就好了,那么签约什么技能该怎么办?这时候需要一个经纪人(代理),在明星唱歌的时候,代理要做一些额外的工作。

下面依然用为账户存取添加事务功能为例,给账户添加一个代理。Java中,代理的生成过程比较简单,首先我们需要一个基础账户的接口,以及一个账户类。

1、账户接口

package proxy;

import annotation.MyAnnotation;
import annotation.Transaction;

/**
 * Created by maitian13 on 2016/11/3.
 */
public interface CountInterface {
    public void add(int c);
    public void sub(int c);
}

2、账户类

/**
 * Created by maitian13 on 2016/11/3.
 */
public class Count implements CountInterface{
    private int money;
    public Count(int m){this.money=m;}
    public void add(int c){this.money+=c;}
    public void sub(int c){this.money-=c;}
    public void display(){
        System.out.println(this.money);
    }
}

3、代理调用

public class Main {
    public static void main(String[] args) {
        Count c=new Count(10);
        Object pro=Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(), new MyProxyHandler(c));
        ((CountInterface)pro).add(1);
        c.display();
    }
}

代理生成代码非常简单,需要一个ClassLoader、代理类的接口、以及InvocationHandler实例,其中InvocationHandler定义了代理在调用的时候的操作,需要自己定义,如下所示为自定义的InvocationHandler:

4、自定义InvocationHandler

/**
 * Created by maitian13 on 2016/11/3.
 */
public class MyProxyHandler implements InvocationHandler{
    Object target;
    public MyProxyHandler(Object o){this.target=o;}
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Annotation[] a=method.getAnnotations();
        System.out.println("start session");
        Object res=method.invoke(target,args);
        System.out.println("end session");
        return res;
    }
}


要看懂上述代码,需要知道一些反射的知识。如果通过代理类来调用方法,例如上述Main中((CountInterface)pro).add(1)就会调用invoke方法,在本例中代理先输出“start session”,然后调用acount类的add方法,在调用成功之后打印“end session”,该过程模拟了事务的操作过程。

对比Spring事务的流程,我们可能会质疑,我们从来就没有定义过代理类啊?我们在增加事务支持的时候只需要添加一个@Transaction注解就行了,如下所示

@Transaction

public void add(int money){...}

 其中Transaction称为注解,以前一直不明白,为何在Spring里面写上一句注解就可以让一个方法具有事务的功能。经过仔细地调研,我才知道一句注解后面也包含了反射、代理等JAVA中高级又核心的技术。下面我们也使用注解的方式来支持事务。

5、事务注解

Java有系统自带注解以及自定义注解之分。系统自带注解用途也不尽相同,如@override注解用于编译时检测,@Document、@Target等属于注解的注解。下面自定义一个注解:

@Documented//生成JAVADoc的时候会自动生成
@Target({ElementType.TYPE})//只用于类型定义前注解,如Class定义
public @interface MyAnnotation {
    String author();
    String date();
    int version() default 1;
}

 上述注解可以用在类定义前,表明作者信息。下面是一个Transaction注解,该注解只能在方法前注解,能够在运行时被发现。

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@Inherited
public @interface Transaction {
    /*
    use to defined Transaction method
     */
}

此时,为了支持注解,需要稍微修改一下MyProxyHandler:

public class MyProxyHandler implements InvocationHandler{
    Object target;
    public MyProxyHandler(Object o){this.target=o;}
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Annotation[] a=method.getAnnotations();
        if(method.getAnnotation(Transaction.class)!=null){//判断该函数是否支持事务
            System.out.println("start session");
        }
        Object res=method.invoke(target,args);
        if(method.getAnnotation(Transaction.class)!=null){//判断该函数是否支持事务
            System.out.println("end session");
        }
        return res;
    }
}

6、测试

下面使用3中的测试代码,得到如下结果:

start session
end session
11

可以看到调用的同事还模拟了事务的流程,同事账户的数值也得到了增加。

3、小结

此博客中对AOP的介绍还处于很肤浅的阶段,大家仅当科普,有错误还望指正。Spring是一个使用了AOP编程模式的框架,很多地方值得学习,本文中的例子只是从一个侧面模仿Spring,还有很多不足之处,例如通常在使用事务的时候我们都没有发现代理的存在,而实际上代理由Spring容器维护着,不需要我们去生成,这就是Spring的厉害之处,即使不懂AOP也能编写功能完善的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值