Spring源码阅读:Spring AOP设计与实现(一):动态代理

在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);

     }

}

Spring源码阅读:Spring AOP设计与实现(一):动态代理

时间: 2024-08-05 20:42:15

Spring源码阅读:Spring AOP设计与实现(一):动态代理的相关文章

Spring源码阅读:使用标准AOP的API模拟Spring AOP + AspectJ的设计与实现

在上一篇博客中,提到了标准AOP与Spring AOP.这一篇就来把他们模拟出来. 在模拟之前,还需要提及的是,在Spring框架中,对于AOP的支持: Spring 支持的AOP AspectJ是另外一个有名的AOP框架,Spring也集成AspectJ,同时Spring AOP与AspectJ有一定程度的集成,这样一来Spring中就支持两种AOP:1)Spring AOP.2)AspectJ.而使用AOP的方式却又三种: 1)完全使用Spring AOP 2)完全使用AspectJ(分为注

Spring源码阅读:Spring声明式事务处理和编程式事务处理的设计与实现

之前的学习,了解了Spring事务管理的基础框架(查看).Spring在此基础上又提到了声明式事务管理和编程式事务管理.这里就来看看Spring是如何实现的. Spring声明式事务与EJB事务管理对比 Spring的声明式管理,类似于EJB的CMT,但又有不同.他们的不同之处有: 1)EJB的CMT是与JTA结合使用,而Spring框架的声明式事务管理可以在任何环境下工作.既可以使用全局事务管理,如JTA,也可以使用局部事务管理如JDBCJPA.Hibernate.JDO等. 2)可以在任何类

Spring源码阅读:IOC容器的设计与实现(二)——ApplicationContext

上一主题中,了解了IOC容器的基本概念,以及BeanFactory的设计与实现方式,这里就来了解一下ApplicationContext方式的实现. ApplicationContext 在Spring的参考文档中,为啥要推荐使用ApplicationContext?它能给我们的应用带来什么好处呢?作为BeanFactory的实现之一,它又是如何设计的?在SpringMVC中使用的WebApplictionContext\XmlApplicationContext与之有何关联? Applicat

Spring源码阅读:Spring JDBC 组件的设计与实现

昨天回忆了我在学习JDBC时自己设计的JDBCTemplate(写在上一篇博客中),在使用Spring过程中,有时会用到Spring给我们提供的JdbcTemplate,这里看看Spring是如何实现这个组件的. 在使用Spring JDBC是你要做的工作很少: 从上面的图上可以看出来,使用Spring JDBC,你只需要做四个工作: 1)定义连接参数:也就是定义url,driver,user,password这个几个参数,一般我们会用一个jdbc.properties文件来配置. 2)指定要执

Spring源码阅读:Spring事务管理的基础

上一节了解了全局事务与局部事务以及Spring提供的两种事务模式:编程式事务与声明式事务. 不论是编程式的事务处理,还是声明式的事务处理.他们都要对局部事务和全局事务以支持,也就是说要对JDBC进行支持.ORM框架,同时也要对JTA进行支持.他们的公共部分是commit,rollback.通过这一节的了解,我相信以后配置Spring事务时,就不需要在去网上查资料了或者去查Spring的参考文档了. 因此,Spring设计了如下的事务管理框架: 从上面的类图中和容易可以看出分为三部分:Platfo

Spring源码阅读系列总结

最近一段时间,粗略的查看了一下Spring源码,对Spring的两大核心和Spring的组件有了更深入的了解.同时在学习Spring源码时,得了解一些设计模式,不然阅读源码还是有一定难度的,所以一些重要的设计模式简单的做了阐述.同时还会简单的加入一些GOF中提到的设计原则.Spring的源码阅读系列,也暂告一段落.下面是就带你走进Spring世界: Spring系列的引子 1)Spring WebApplicationContext初始化与消亡 这一节帮我们了解Spring是如何初始化WebAp

Spring源码阅读:Spring MVC 如何处理HTTP请求

Spring MVC 对HTTP请求的处理流程 通过之前的源码阅读,知道了ApplicationContext初始的过程,也知道了Spring MVC环境的初始化过程,今天就来了解一下SpringMVC是如何处理HTTP请求的. HTTP请求根据请求方式可以分为GET.POST.PUT.DELETE.OPTIONS.TRACE,最常用的还是GET和POST. Spring对于这几种HTTP请求的处理都是使用了processRequest(req,rep); @Override protected

Spring源码阅读:Spring MVC 初始化

通过之前的源码学习,了解了Spring的两个核心IOC和AOP.也了解到系统初始化时,就已经将所有applicationContext.xml中的bean Definintion加载并初始化了. 如果使用了SpringMVC框架,MVC框架指定的namespace-servlet.xml也已经被初始化了. 使用过SpringMVC,都知道需要在web.xml配置配置DispatcherServlet,它是处理请求的入口.Servlet是单例的,系统指挥在第一次处理用户请求时初始化Servlet对

Spring源码阅读:Spring如何支持各种ORM框架

为了让开发程序更容易,到现在为止,已经有很多ORM框架了,例如:JPA,JDO,Hibernate,Mybatis(之前版本是IBatis)等等.也正因为已经有这么多优秀的ORM框架,Spring团队并没有自己开发一套ORM框架,而是对这些框架都进行了支持,让这些框架在Spring环境下可以得到完全的应用. 通常,在Spring环境下使用这些ORM框架时,都会通过一个Template来使用.Spring对这些框架的集成是这样的: 例如Hibernate,在使用Hibernate时(没有在Spri