Spring AOP 实现原理

前面在学习代理模式的时候,了解到代理模式分为动态代理和静态代理。现在我们就以代理模式为基础先实现我们自己的AOP框架,再来研究Spring的AOP的实现原理。AOP的原理就是java的动态代理机制,下面先是对java的动态机制进行一个回顾:

一、静态代理

  先以静态代理实现,静态代理关键是在代理对象和目标对象实现共同的接口,并且代理对象持有目标对象的引用。 

public interface IHello {
     /**
      * 业务方法
      * @param str
     */
     void sayHello(String str);
 }

目标类代码: 

public class Hello implements IHello{
     @Override
     public void sayHello(String str) {
         System.out.println("hello "+str);
     }
     
 }

代理类代码,我们给它添加日志记录功能,在方法开始前后执行特定的方法,是不是和AOP特别像呢? 

public class ProxyHello implements IHello{    
    private IHello hello;    
    public ProxyHello(IHello hello) {
        super();
        this.hello = hello;
    }
    @Override
    public void sayHello(String str) {
        Logger.start();//添加特定的方法
        hello.sayHello(str);
        Logger.end();
    }

}

日志类代码: 

public class Logger {
     public static void start(){
         System.out.println(new Date()+ " say hello start...");
     }
     
     public static void end(){
         System.out.println(new Date()+ " say hello end");
     }
 }

测试代码: 

public class Test {
     public static void main(String[] args) {
         IHello hello = new ProxyHello(new Hello());//如果我们需要日志功能,则使用代理类
         //IHello hello = new Hello();//如果我们不需要日志功能则使用目标类
         hello.sayHello("明天");    
     }
 }

这样我们就实现了一个最简单的AOP,但是这样会存在一个问题:如果我们像Hello这样的类很多,那么,我们是不是要去写很多个HelloProxy这样的类呢。其实也是一种很麻烦的事。在jdk1.3以后,jdk跟我们提供了一个API java.lang.reflect.InvocationHandler的类, 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事。下面我们就来实现动态代理的实现。 

二、动态代理

2.1、java动态代理原理

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法: 

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢? 

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

如果不是很明白,等下通过一个实例会对这几个参数进行更深的讲解。

接下来我们来看看Proxy这个类:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法: 

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义: 

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

好了,在介绍完这两个接口(类)以后,看下面的示例。

2.2、动态代理示例说明

  接口实现与静态代理相同,代理类代码:

public class DynaProxyHello implements InvocationHandler {
     
     private Object target;//目标对象
     /**
      * 通过反射来实例化目标对象
      * @param object
      * @return
      */
     public Object bind(Object object){
         this.target = object;
         return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
     }
     
     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
         Object result = null;
         Logger.start();//添加额外的方法
         //通过反射机制来运行目标对象的方法
         result = method.invoke(this.target, args);
         Logger.end();
         return result;
     }
     
 }

测试类代码: 

public class DynaTest {
     public static void main(String[] args) {
         IHello hello = (IHello) new DynaProxyHello().bind(new Hello());//如果我们需要日志功能,则使用代理类
         //IHello hello = new Hello();//如果我们不需要日志功能则使用目标类
         hello.sayHello("明天");
     }
 }

看完上面的代码可能和Spring AOP相比有一个问题,日志类只能在方法前后打印,但是AOP应该是可以在满足条件就可以执行,所有是否可以将DynaPoxyHello对象和日志操作对象(Logger)解耦呢?

示例3

看下面代码实现,将将DynaPoxyHello对象和日志操作对象(Logger)解耦:

  我们要在被代理对象的方法前面或者后面去加上日志操作代码(或者是其它操作的代码),那么,我们可以抽象出一个接口,这个接口里就只有两个方法:一个是在被代理对象要执行方法之前执行的方法,我们取名为start,第二个方法就是在被代理对象执行方法之后执行的方法,我们取名为end。

  Logger的接口:

package com.dxz.dyna2;

import java.lang.reflect.Method;

public interface ILogger {
    void start(Method method);

    void end(Method method);
}

Logger的接口实现: 

package com.dxz.dyna2;

import java.lang.reflect.Method;
import java.util.Date;

public class DLogger implements ILogger {
    @Override
    public void start(Method method) {
        System.out.println(new Date() + method.getName() + " say hello start...");
    }

    @Override
    public void end(Method method) {
        System.out.println(new Date() + method.getName() + " say hello end");
    }
}

动态代理类: 

package com.dxz.dyna2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynaProxyHello1 implements InvocationHandler {
    // 调用对象
    private Object proxy;
    // 目标对象
    private Object target;

    public Object bind(Object target, Object proxy) {
        this.target = target;
        this.proxy = proxy;
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        // 反射得到操作者的实例
        Class clazz = this.proxy.getClass();
        // 反射得到操作者的Start方法
        Method start = clazz.getDeclaredMethod("start", new Class[] { Method.class });
        // 反射执行start方法
        start.invoke(this.proxy, new Object[]{method});
        // 执行要处理对象的原本方法
        method.invoke(this.target, args);
        // 反射得到操作者的end方法
        Method end = clazz.getDeclaredMethod("end", new Class[] { Method.class });
        // 反射执行end方法
        end.invoke(this.proxy, new Object[] { method });
        return result;
    }

}

测试代码: 

package com.dxz.dyna2;

public class DynaTest1 {
    public static void main(String[] args) {
        IHello hello = (IHello) new DynaProxyHello1().bind(new Hello(), new DLogger());// 如果我们需要日志功能,则使用代理类
        // IHello hello = new Hello();//如果我们不需要日志功能则使用目标类
        hello.sayHello("明天");
    }
}

通过上面例子,可以发现通过动态代理和发射技术,已经基本实现了AOP的功能,如果我们只需要在方法执行前打印日志,则可以不实现end()方法,这样就可以控制打印的时机了。如果我们想让指定的方法打印日志,我们只需要在invoke()方法中加一个对method名字的判断,method的名字可以写在xml文件中,这样我们就可以实现以配置文件进行解耦了,这样我们就实现了一个简单的spring aop框架。

三、 Cglib动态代理

   JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

使用cglib为目标类提供动态代理:需要导入cglib.jar和asm.jar。如果出现asm中的类无法找到的异常,在java工程中是真的缺少asm.jar,而在web工程中很可能是asm.jar和spring提供的org.springframework.asm-3.0.4.RELEASE.jar包冲突。

被代理类普通类(无接口)

package com.dxz.cglib;

public class BookFacade {
    public void addBook() {
        System.out.println("增加图书的普通方法...");
    }
}
package com.dxz.cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * 使用cglib动态代理
 */
public class BookFacadeCglib implements MethodInterceptor {
    private Object target;

    /**
     * 创建代理对象
     * 
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    // 回调方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("事物开始");
        proxy.invokeSuper(obj, args);
        System.out.println("事物结束");
        return null;
    }
}
package com.dxz.cglib;

public class TestCglib {

    public static void main(String[] args) {
        BookFacadeCglib cglib = new BookFacadeCglib();
        BookFacade bookCglib = (BookFacade) cglib.getInstance(new BookFacade());
        bookCglib.addBook();
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值