您的位置:首页 > 编程语言 > Java开发

浅析Spring AOP(一)——代理模式

2017-06-22 00:00 323 查看
代理模式
即Proxy Pattern,是常见设计模式之一,它的主要目的是控制用户对某一个对象的直接访问,让程序能根据实际情况,在不影响访问对象的情况下,做一些访问控制的处理。
在JAVA中,对于某个对象,访问无非对属性的访问,对方法的访问。那么前文提及的访问控制会出现在哪些地方呢?无论是对属性还是对方法,我们都可以在三个地方进行访问干涉,即发起访问前,访问结束后以及访问出错时。
假使我们需要访问的对象为Target,但实际访问过程中,我们却访问了一个叫TargetProxy的对象,这个对象控制了对Target的访问过程。

public class Target {
public void foo() throws Exception {
System.out.println("Target.foo()");
}
}

public class TargetProxy{
private Target target = new Target();

public void foo() {
before("foo");
try {
target.foo();
} catch (Exception e) {
error(e);
}
after("foo");
}

private void before(String functionName) {
System.out.println("before access to function Target." + functionName);
}

private void after(String functionName) {
System.out.println("after access to function Target." + functionName);
}

private void error(Exception e){
e.printStackTrace();
}
}

当我们访问TargetProxy时,我们在对Target透明的层面上,在before()、after()和error()中分别处理不同逻辑。但Target和TargetProxy明明是不同的两个类,我们如何知道对于某个JAVA类,该使用哪一个代理类去做这些事情呢?在介绍Spring AOP之前,我们先看一看JDK的动态代理——Proxy。

在使用Proxy之前,我们先做一点自己的实现。为了知道该让哪一个类去做代理这件事情,代理类和被代理了类必须有共同的属性、方法。为了保证拥有共同的属性,我们可以使用继承;为了保证拥有共同的方法,我们可以使用实现接口。即TargetProxy继承自Target或TargetProxy实现Target,我们就知道TargetProxy代理的类到底具有什么样的特征了。
将上述代码稍作修改,我们得到如下结果:

public class Target implements TestInterface {
public void foo() throws Exception {
System.out.println("Target.foo()");
}

[@Override](https://my.oschina.net/u/1162528)
public long testAdd(int a, int b) {
return a + b;
}

[@Override](https://my.oschina.net/u/1162528)
public long testSub(int a, int b) {
return a - b;
}
}

public class TargetProxy implements TestInterface {
private Target target = new Target();

public void foo(){
before("foo");
try {
target.foo();
} catch (Exception e) {
error(e);
}
after("foo");
}

private void before(String functionName) {
System.out.println("before access to function Target." + functionName);
}

private void after(String functionName) {
System.out.println("after access to function Target." + functionName);
}

private void error(Exception e){
e.printStackTrace();
}

[@Override](https://my.oschina.net/u/1162528)
public long testAdd(int a, int b) {
before("testAdd");
long result = target.testAdd(a, b);
after("testAdd");
return result;
}

[@Override](https://my.oschina.net/u/1162528)
public long testSub(int a, int b) {
before("testSub");
long result = target.testSub(a, b);
after("testSub");
return result;
}

}

public interface TestInterface {
// return a + b;
long testAdd(int a, int b);
// return a - b;
long testSub(int a, int b);
}

按照前文所说,TargetProxy应该继承或实现Target,但我们代码里面却不是这样写的。上面的代码里,被代理的对象实际是TestInterface这个接口,接口有一个实现类Target,但我们却不会直接访问Target来获取接口的实现,而是通过TargetProxy来访问接口的实现,做到了对接口的代理。这就是Proxy类做的事。

Proxy类仅支持对Interface的代理,无法直接代理Class,所以也无法控制对对象的某个属性的访问,如果想代理Class,可以使用CGLIB来做代理。当然,我们也可以把所有属性设置为private,让外部对象只能通过getter/setter对属性进行访问,这种情况下也可以勉强实现对属性的访问控制。
先给个代理demo:

public interface TestInterface {
// return a + b;
long testAdd(int a, int b);
// return a - b;
long testSub(int a, int b);
}

public class TestInterfaceImpl implements TestInterface {
[@Override](https://my.oschina.net/u/1162528)
public long testAdd(int a, int b) {
return a + b;
}

@Override
public long testSub(int a, int b) {
return a - b;
}

public static class TestInvocationHandler implements InvocationHandler {
TestInterfaceImpl impl = new TestInterfaceImpl();

// 被代理接口的某个方法被调用时,将执行 invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long result;
if ("testAdd".equals(method.getName())) {
before(method.getName());
result = impl.testAdd((Integer) args[0], (Integer) args[1]);
after(method.getName());
return result;
} else if ("testSub".equals(method.getName())) {
before(method.getName());
result = impl.testSub((Integer) args[0], (Integer) args[1]);
after(method.getName());
return result;
} else {
return null;
}

}

private void before(String functionName) {
System.out.println("before access to " + functionName);
}

private void after(String functionName) {
System.out.println("after access to " + functionName);
}

private void error(Exception e) {
e.printStackTrace();
}

}

public static void main(String[] args) {
TestInterface impl = (TestInterface) Proxy.newProxyInstance(TestInterface.class.getClassLoader(),
new Class[]{TestInterface.class}, new TestInvocationHandler());
System.out.println(impl.testAdd(1,2));
System.out.println(impl.testSub(1,2));
}

}

如上代码所示,我们在main()函数中利用Proxy实现了对TestInterface接口的代理。TestInterface的一个实现类是TestInterfaceImpl,但我们在main()函数中没有使用它,而是使用了一个与它相关的类TestInvocationHandler,这个类实现了InvocationHandler接口的invoke方法。我们在创建动态代理实例时,调用了Proxy.newProxyInstance(ClassLoader cl, Class[] interfaces, InvocationHandler handler)这个方法,它会返回一个实现了interfaces接口的类,这个类所有的方法调用会通过handler.invoke()去完成。所以我们使用TestInvocationHandler完成对实现类TestInterfaceImpl的封装,然后使用Proxy去构造一个TestInterfaceImpl的代理对象。代理对象的处理流程完全由TestInvocationHandler完成,与被代理的类TestInterfaceImpl无关。也就是说,我们实现多少个不同的InvocationHandler,就可以完成多少种对TestInterfaceImpl的代理逻辑。

Proxy的实现原理
Proxy通过Proxy.newProxyInstance(ClassLoader cl, Class[] interfaces, InvocationHandler handler)这个方法构造代理类,那么这个方法做了什么?

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

JDK源码中,newProxyInstance()的实现是这样的,我们可以看到它调用一个实现了所有传入接口的类的构造方法来实现这个代理类。Class cl = getProxyClass0(loader, intfs),这就是实现了所有接口的类的类名。
至于 getProxyClass0(ClassLoader cl, Class[] interfaces) 这个方法,实现太过复杂,不再贴出代码,做一下口头解释。它的实现大概是,遍历interfaces,为所有传入的interfaces生成一个临时的.class文件,文件中的类继承自Proxy并实现传入的接口。这个生成的类将生成一个以 InvocationHandler 为参数的构造函数,以调用我们传入的自定义handler。这个类中,所有它实现的接口定义的函数,都会以Method对象的形式成为它的属性,然后由 handler.invoke() 方法去调用。
参考 http://rejoy.iteye.com/blog/1627405

这一节将JDK Proxy的工作方式,以及它的实现原理和大家分享了一遍。下一次会由此讲解Spring AOP的工作方式和原理。
谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息