Sping AOP之代理模式

Spring框架使用了很多的设计模式,所以在开始学习Spring源码之前,我们先对Spring中使用比较多的几种设计模式先做个简单的学习。这会帮助我们更好的理解Spring源码。
首先是代理模式,Spring的AOP功能就是基于动态代理来实现的。

代理模式

代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式又分为静态代理和动态代理:

  • 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类 的.class文件就已经存在了。
  • 动态:在程序运行时运用反射机制动态创建而成。

静态代理

静态代理模式很简单,代理对象实现了被代理对象相同的接口并持有被代理对象的引用。如下面的例子:

// 用户接口
public interface Person {
    void doSomething();
}

// 用户接口的实现类
public class PersonImpl implements Person {
    @Override
    public void doSomething() {
        System.out.println("do something...");
    }
}

// 用户接口实现类的代理类
public class PersonImplProxy implements Person {
    private PersonImpl person;
    public PersonImplProxy(PersonImpl person) {
        this.person = person;
    }
    @Override
    public void doSomething() {
        person.doSomething();
    }
}

// 测试主类
public class Test {
    public static void main(String[] args) {
        Person person = new PersonImplProxy(new PersonImpl());
        person.doSomething();
    }
}
// 输出结果:
// do something...

有一个Person接口,被代理对象PersonImpl和代理对象PersonImplProxy都实现该接口,PersonImplProxy对象持有PersonImpl的引用,当调doSomething()方法时,代理对象实际上会调用被代理对象的doSomething()方法。
静态代理的写法很简单,但是不够灵活,一个代理类只能代理一种类型,而且是在编译时就已经确定被代理的对象。

动态代理

代理类在程序运行时创建的方式被成为动态代理。也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理的实现方式主要分为JDK动态代理和CGLIB动态代理。

JDK动态代理

JDK动态代理是代理模式的一种实现,其只能代理接口。
使用步骤:
1.新建一个接口
2.为接口创建一个实现类
3.创建代理类实现java.lang.reflect.InvocationHandler接口
4.测试

// 用户接口类
public interface Person {
    void doSomething();
}

// 用户实现类
public class PersonImpl implements Person {
    @Override
    public void doSomething() {
        System.out.println("so something...");
    }
}

// 代理类
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("--before--");
        Object result = method.invoke(target, args);
        System.out.println("--after--");
        return result;
    }
}

// 测试主类
public class Test {
    public static void main(String[] args) {
    	System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Person person = new PersonImpl();
        Person personProxy = (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new MyInvocationHandler(person));
        personProxy.doSomething();
    }
}
// 输出结果:
// ---before---
// do something...
// ---ater---

原理分析:
JDK动态代理其实是JDK在运行期间帮我们动态的生成了字节码文件,然后再加载类,实例化成我们的代理对象。因为我们设置了属性sun.misc.ProxyGenerator.saveGeneratedFiles,可以在项目根目录下找到生成的字节码文件。

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
	// 父类Proxy中维持了 InvocationHandler h 的引用
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
	
	// 我们真正调用的是该方法,该方法又将调用交给 InvocationHandler
    public final void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            // 该方法为需要被代理的方法
            m3 = Class.forName("com.example.demo.proxy.jdk.Person").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到生成的代理类中属性m3就是需要被代理的方法doSomething(),当调用代理类的doSomethin()方法时代理类会交给InvocationHandler.invoke()处理,这个方法里面通过反射调用真正的doSomething()方法。而且可以在调用前后插入我们自己的代码,这也就是AOP的原理。

CGLIB动态代理

CGLIB动态代理原理类似与JDK动态代理,只是不需要实现接口,CGLIB是生成被代理类的子类作为代理类,简单实现方式如下:

// 用户接口
public class Person {
    public void doSomething() {
        System.out.println("do something...");
    }
}

// 代理切面类
public class ProxyPerson implements MethodInterceptor {
    public Object getInstance(Class clazz) {
        if (clazz != null) {
            Enhancer enhancer = new Enhancer();
            // 设置代理类的父类
            enhancer.setSuperclass(clazz);
            // 设置回调对象
            enhancer.setCallback(this);
            // 生成代理对象
            return enhancer.create();
        }
        return null;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before...");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("after...");
        return result;
    }
}

// 测试主类
public class Test {
    public static void main(String[] args) {
        Person me = (Person) new ProxyPerson().getInstance(Person.class);
        me.doSomething();
    }
}
// 输出结果:
// before...
// do something...
// after...
发布了8 篇原创文章 · 获赞 7 · 访问量 707

猜你喜欢

转载自blog.csdn.net/hao_yan_bing/article/details/101423703
今日推荐