Spring Aop 基本介绍和底层实现JDk动态代理技术

本文是我根据阅读《Spring4.x》的读书笔记编写的,如果有什么理解错误,还望能告知。

本文会通过为何需要使用Aop,到定义Aop,再到Aop底层实现三部分来详细讲解Aop。

第一部分: 为何需要使用AOP

先看如下的代码:

public class ForumService {
    private PerformaceMonitor monitor = new PerformaceMonitor();
    public void removetopic(){

        monitor.begin("com.smart.service.ForumService.removetopic"); //开启性能监测
        System.out.println("测试性能监测功能");  // 业务逻辑
        monitor.end();      //结束性能监测
    }
}

这是一个简单的业务类,其中包含三句代码,前后都是性能监测的代码,只有中间那句输出语句才是本方法的业务逻辑代码,这个例子只想说明一个问题:如果我们想要在一个类中使用性能监测,就必须固定的“开启监测”然后 “ 结束监测”,如果是在一个类里面使用还好,但是如果在很多类中使用呢。是不是觉得很繁琐。

接口实现和继承父类,都只能提取出重复的方法,也就是说只能在方法级层面,对于包裹在方法内部业务代码中的固定代码(如事务提交,性能监测等)是没有办法简单提取出来的。Aop所做的事情就是把这些无法直接通过继承或者实现就能提取的代码通过横向切割方式抽取出来做成独立模块,

什么是横向切割?  --额,我也不懂,但是如果能把其他代码和业务逻辑分开,然后在需要的时候再插入岂不是很完美。Aop能可以实现。类似下图的一种效果(盗取《Spring4》)

第一部分到这里结束。AOP其实就是解决在不改变原有代码的情况下,添加另外一些需要功能。但视乎他的使用范围有限,目前而言 ,我只看到在事务管理上使用了AOP。

第二部分:AOP一些术语

1.连接点 Joinpoint:   AOP要的就是在某个类中的某个方法横向插入代码,而这些方法就是连接点,一般一个类都有好多个方法,所有一个类可以有好多个连接点。(可以简单理解为Aop的作用点)补充说明:spring目前只支持方法级的连接点,也就是说,仅能在方法调用前,后,异常抛出等时候对该方法进行操作。

2.切点 PointCut:用来查找连接点,spring书中比喻为:连接点是数据库的记录,切点则是查询条件。但问题来了:只是查到了位置,就能执行了吗? 事实上,你还需要提供方位信息 方法(调用前、调用后、异常抛出时,)等几个方位,或者说时间点。 可是切点并没有定义这些方位信息。所以就有了第三个术语:增强

3.增强 advice: 增强定义了两个内容,第一个是描述一段程序代码(就是AOP要具体干嘛的逻辑代码)另外一个就是提供方位,上面大体说了三个方位,增强定义了四个,加了一个“环绕通知” 其实就是方法调用前和后。以上就是advic的两个作用

4.目标对象 Target:这里说的层级是类,也就是连接点所在的类。没什么特殊。

5,引介Introduction: 不想用
6.织入 Weaving:是一个动作,将增强添加到连接点上的过程。
7.代理 Proxy :估计是表示代理类吧,我们都知道Aop的机制是代理机

8.切面Aspect:切点和切面的组合。几乎可以说是Aop了

第三部分:JDK动态代理技术

动态代理的本质是: 。。。其实就是代理,比如我对一个类TestService 的方法test(),插入性能监测代码。我可以创建一个代理类啊,在代理类里面添加代码就好了。这样不就可以不用修改原来的类了。感觉像影分身。分身修改了,但对本尊没影响。

那么怎么创建代理类呢?这里主要涉及两个类:java.lang.reflect包下的Proxy,InvocationHandler, 其中InvocationHandler 是一个接口。(想要了解这两个类的去看jdk文档)

Proxy: 就是这家伙,能创建出代理类的类。

InvocationHandler: 就是这家伙,利用反射机制,获取到需要目标类,然后插入性能监测代码,最后作为参数传给了Proxy。

简单的这样说估计很难理解,还是直接看代码吧:

目标类(某接口的实现类), 依旧是很简单的业务逻辑

/**
 *   使用 Proxy 和 InvocationHandler 类 实现创建本类的代理类
 */
public class TestServiceImpl implements TestService{

    public void test(){
        System.out.println("通过Proxy创建代理对象");
        System.out.println("方法延迟3秒");
        try {
            Thread.currentThread().sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

实现InvocationHandler接口的类

       其中的方法Invoke(),是继承InvocationHandler接口的方法。

**
 *  通过实现InvocationHandler 接口创建一个编织了性能监测的编织类
 */
public class TestServiceHandler implements InvocationHandler {

    private Object target;  //需要性能监测功能的目标类

    public TestServiceHandler(Object target) {
        this.target = target;
    }
    /**
     * @param proxy 最终生成的代理类, 一般不会用到
     * @param method 反射机制的反射类
     * @param args   被代理类的方法的参数
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        PerformaceMonitor.begin(target.getClass().getName()+"."+method.getName());
        Object object = method.invoke(target,args);
        PerformaceMonitor.end();
        return object;
    }
}

最后创建代理类


/**
 * 测试,使用编织类来创建代理类,实现某方法的性能监测
 */
public class TestServiceTest {

    public void proxy() {
        TestService target = new TestServiceImpl(); //目标类,注意,这里是接口接收不是实现类
        TestServiceHandler handler = new TestServiceHandler(target); // 编织类
        TestService proxy = (TestService) Proxy.newProxyInstance(   // 开始创建代理类
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler);
        proxy.test();  // 调用目标类方法
    }

    public static void main(String[] args) {

        new TestServiceTest().proxy();
    }
}

只要逻辑到这里就结束了,总之就是创建一个类,实现了InvocationHandler接口,并在里面添加了性能监测逻辑和通过反射机制得到目标类方法,就这样把两者编织在一起,最后通过Proxy创建出业务类(或者说目标类)的真正代理对象,并强制转换为业务类,最后调用方法。

注意:这个才是重点。 JDk动态代理技术只提供接口代理,只提供接口代理,只提供接口代理。重要的事情说三遍。也就是说如果目标类不是接口,那就会报错 java.lang.ClassCastException。不信的话,可以使用没有实现任何接口的类去试试。反正我试过了。

下面补充一下其他代码,:

实体类:


/**
 *  性能监测类,  实验AOP
 */
public class MethoodPerformace {

    private long start;
    private long end;
    private String serviceMehtod;

    public MethoodPerformace(String serviceMehtod) {
        this.serviceMehtod = serviceMehtod;
        start = System.currentTimeMillis();
    }
    public void MethoodPerformace(){
        long  elaspe = System.currentTimeMillis()-start;
        System.out.println("性能监测:"+elaspe+"毫秒");
    }
}

性能监测方法:


/**
 * 性能监测方法
 * MethoodPerformace: 性能监测实体类
 */
public class PerformaceMonitor {
    private static ThreadLocal<MethoodPerformace> perfomaceRecode= new ThreadLocal<MethoodPerformace>();
    public static void begin(String method){
        System.out.println("monitor begin");
        MethoodPerformace mp = new MethoodPerformace(method);
        perfomaceRecode.set(mp);
    }

    public static void end(){
        System.out.println("monitor end");
        MethoodPerformace mp = perfomaceRecode.get();
       mp.MethoodPerformace();
    }
}

业务类接口


/**
 * 业务类接口
 */
public interface TestService {

    void test();
}

另外,AOP还有一种代理技术CGLib动态代理技术。 。。。预知详情如何,请听。。。还是去百度吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值