在Spring的有两个核心:IOC与AOP,AOP又是基于动态代理模式实现的。所以要了解SpringAOP是如何设计的之前,还是先了解一下Java中的动态代理比较好。
认识代理模式
代理模式是这么描述的:
代理模式是为其他对象提供一种代理以控制对这个对象的访问
代理对象的功能:
通过创建一个代理对象,用这个代理对象去代理真实的对象,客户端得到这个代理对象后,对客户端并没有什么影响,就跟真实的对象一样(因为代理对象和真是对象实现了同一接口)。
下面看看代理模式的类图:
解说:
RealSubject是真实对象。
Proxy是代理者,他来代理RealSubject,表现在Proxy对象拥有RealSubject实例,真正执行的还是realSubject.request()。
Client,代理模式的客户,他是直接与Subject接口打交道。
Proxy与RealSubject实现了同一接口Subject,所以Client看到的Proxy就跟看到了RealSubject一样。
对代理模式分析一下:
举一个现实中的例子:现实生活中有很多专卖店,手机提供商等。
做手机的:中华酷联、小米、三星、苹果、魅族等等。
做家电的:海尔、美的等等。
他们其实就是真是的对象,提供手机的。
各大品牌专卖店:海尔专卖店、诺基亚专卖店、三星专卖店、苹果专卖店
他们其实就是代理商,他们不提供手机,他们提供的是额外的服务,例如和电信运营商结合提供买手机送话费服务。
实例说明:
// 接口Foo就充当代理模式中的Subject角色 public interface Foo { public void sayFoo(String name); } // FooImpl充当了RealSubject角色,对Foo提供的方法做了具体实现 public class FooImpl implements Foo{ public void sayFoo(String name) { System.out.println("手机卖给 "+name); } } // FooProxy是代理者 public class FooProxy implements Foo{ Foo foo=null; public FooProxy(Foo foo){ this.foo=foo; } // 处理请求时,还是有真实的对象来处理的 public void sayFoo(String name) { if(foo!=null){ foo.sayFoo(name); } } } // 客户端调用 public class Client { public static void main(String[] args){ Foo proxy=new FooProxy(new FooImpl()); proxy.sayFoo("zhang san"); } } // 执行结果: //手机卖给 zhang san
如果只是像上面那样使用代理模式,还不如不使用代理模式。
代理模式:用于控制对真实对象的访问。这种控制是多方面的,实际应用中,代理模式又被分为下列几种:
就拿最后一种来说吧,智能引用,在访问对象时附加一些操作(这块AOP中要用到)。
接下来,专卖店(代理商)对象要提供一些附加功能:
public class FooProxy implements Foo{ Foo foo=null; public FooProxy(Foo foo){ this.foo=foo; } public void sayFoo(String name) { if(foo!=null){ before(); foo.sayFoo(name); after(); } } public void before(){ System.out.println("before: 介绍手机,介绍服务 ....."); } public void after(){ System.out.println("after: 送话费 ....."); } }
在真实的对象处理请求的前后,附加了两个功能:before(),after()。Client的代码不需要改变,执行的结果是:
before: 介绍手机,介绍服务 .....
手机卖给 zhang san
after: 送话费 .....
这样就达到了附加功能的目的,这样手机就卖的多了。
动态代理模式
AOP使用了代理模式,但并非这个,这个代理模式是设计模式中的代理模式,在Java中有一种特有的代理模式,也称为动态代理模式。
假若上面的例子是卖手机的,现在系统中要有卖家电的,那我可以在系统中添加如下设计:
做法还是这样的:
public interface Hello { public void sayHello(String hello); } public class HelloImpl implements Hello { public void sayHello(String hello) { System.out.println("hello, "+hello); } } public class HelloProxy implements Hello{ Hello hello; public HelloProxy(Hello hello){ this.hello=hello; } public void sayHello(String hello) { this.hello.sayHello(hello); } } public class Client { public static void main(String[] args){ Foo proxy=new FooProxy(new FooImpl()); proxy.sayFoo("zhang san"); Hello hello=new HelloProxy(new HelloImpl() ); hello.sayHello("li si"); } }
如果有更多的呢,例如要加入卖床上用品,卖办公用品,卖相机等数码产品。。。。
提供商直接与客户打交道,这事是很困难的,操作性不好,肯定得有代理商的参与。难道在系统中都为他们设计一个代理?
上面的例子中,一个代理商只代理一种产品,代理商太多的话,提供商与代理商打交道也麻烦,客户与代理商打交道也麻烦,那么多该选哪一个呀?
对于代理商来说,只代理一样产品,对那样产品的依赖性太大了,那样小命就捏在提供商手里了,最重要的是不是很赚钱。因此就会想着要多代理几样产品。
于是商城就出现了,例如国美,京东等,这里不去管国美,京东到底是怎样操作的,只是为了理解动态代理需要。
那么商城反映到程序中就是:
public class FooProxy implements Foo, Hello ,,,,更多接口{ Foo foo=null; Hello hello=null; // 更多真实对象 public FooProxy(Foo foo){ this.foo=foo; } public FooProxy(Hello hello){ this.hello=hello; }
这样一来,一个大的代理商(商城)就出现了,可以代理很多产品了。
这样一来,业务实现了,但是我们程序员悲催了。尼玛呀,每添加一类商品,程序猿就得调整一个或者或者多个xxxProxy类。这样程序员太累,这样的设计也违反了开闭原则。
咋办呢,那些大牛们就根据JVM的特性和ClassLoader设计出来动态代理。使用动态代理,可以动态的生成xxxProxy类呢,这样就避免了编程的方式了。
动态代理涉及到了一个接口和一个类:InvocationHandler、Proxy。
Proxy就是代理了,Proxy的对象就是上面程序中的fooProxy,helloProxy。同时他也提供了生成代理类的静态方法,生成代理对象的静态方法。
InvocationHandler就是利用反射调用真实对象的接口。
现在利用动态代理来改造上面程序,Foo接口,FooImple用做调整。附加一个Handler就行了:
public class MyInvokerHandler implements InvocationHandler { private Foo foo; public MyInvokerHandler(){} public MyInvokerHandler(Foo foo){ this.foo=foo; } public void beforeInvoke(){ System.out.println("doSomething before invoke"); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { beforeInvoke(); method.invoke(foo,args); // 通过反射调用 afterInvoke(); return null; } public void afterInvoke(){ System.out.println("doSomething after invoke"); } } // Client程序调整为: public class Client { public static void main(String[] args){ FooImpl foo=new FooImpl(); MyInvokerHandler handler=new MyInvokerHandler(foo); // proxy是代理对象,它既是Proxy的对象,也是Foo的对象 Object proxy=Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{Foo.class},handler); Foo fooProxy=(Foo)proxy; fooProxy.sayFoo("zhang san"); } } // 程序执行结果: doSomething before invoke 手机卖给 zhang san doSomething after invoke
这样不用写代理类就完成了上面的功能。
要让它也实现Hello接口呢 ?
再对上面的程序调整:
public class MyInvokerHandler implements InvocationHandler { private Foo foo; private Hello hello; public MyInvokerHandler(){} public MyInvokerHandler(Foo foo){ this.foo=foo; } public void setFoo(Foo foo) { this.foo = foo; } public void setHello(Hello hello) { this.hello = hello; } public void beforeInvoke(){ System.out.println("doSomething before invoke"); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { beforeInvoke(); System.out.println(method.getName()); if(method.getName().equals("sayHello") && proxy instanceof Hello) { method.invoke(hello,args); // 通过反射调用 }else if(method.getName().equals("sayFoo") && proxy instanceof Foo){ method.invoke(foo,args); } afterInvoke(); return null; } public void afterInvoke(){ System.out.println("doSomething after invoke"); } } //Client调整为: public class Client { public static void main(String[] args){ FooImpl foo=new FooImpl(); HelloImpl hello=new HelloImpl(); MyInvokerHandler handler=new MyInvokerHandler(); handler.setFoo(foo); handler.setHello(hello); // proxy是代理对象,它既是Proxy的对象,也是Foo的实例 ,还是Hello的实例 Object proxy=Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{Foo.class,Hello.class},handler); Foo fooProxy=(Foo)proxy; fooProxy.sayFoo("zhang san"); Hello helloProxy=(Hello)proxy; helloProxy.sayHello("li si"); } } //执行结果是: doSomething before invoke sayFoo 手机卖给 zhang san doSomething after invoke doSomething before invoke sayHello hello, li si doSomething after invoke
// 如果还要添加更多功能,只需要调整这个Handler即可。
猜想动态生成的代理类
现在可以猜测一下,动态生成的代理类到底是什么样的呢?
public ProxyClass extends Proxy implement Foo, Hello{ InvocationHandler handler; public ProxyClass(InvocationHandler handler){ this.handler=handler; } private execute(String methodName, Object[] args){ Method method=this.getClass().getMethod(methodName); Handler.invoke(this, method, args); } public void sayFoo(String name){ Object[] args=new Object[]{name}; execute(this, "sayFoo", args); } public void sayHello(String hello){ Object[] args=new Object[]{hello}; execute(this, "sayHello", args); } }