设计模式之代理模式(Java示例)及其在Spring-AOP特性之中的应用

代理模式:

代理模式就是作为需要被代理的类与使用被代理的桥梁,首先代理类去代理需要被代理的对象,在别的对象需要使用到该对象的时候通过代理类而非直接去找被代理的对象。这样就可以在代理类中对被代理的对象进行增强。代理模式分为静态代理与动态代理。

静态代理:

静态就意味着代理对象代理谁在编译时期就确定了,其实现就是通过去实现被代理类的接口然后再通过组合来实现。

例子:

被代理类接口:

package com.ustc;

public interface Sub {
    public void doSomething();
}

被代理类:

package com.ustc;

import java.sql.SQLOutput;

public class SubImp implements Sub {

    @Override
    public void doSomething() {
        System.out.println("i have done");
    }
}

代理类:

package com.ustc;

public class SubProxy implements Sub {
   //通过组合来实现对需要被代理的类的增强
    private Sub object=null;

    public SubProxy (){
    }
    public SubProxy(Sub object){
        this.object=object;
    }
    @Override
    public void doSomething() {
        //在这儿对被代理的类进行增强
        System.out.println("enhance the object");
        //在这调用被代理类的对象的对应方法
        SubProxy subProxy =new SubProxy(new SubImp());
        subProxy.object.doSomething();
        //对被代理的对象进行后处理
        System.out.println("handle somethings");
    }
}

可以看到,代理对象代理谁,在编译时期就已经确定了,这样就会存在一个缺点:每个需要被代理的都要有一个代理类去代理它,且代理类要实现被代理的类的接口的所有方法,代码冗余量大。====》动态代理可以弥补这一缺点。

动态代理

代理对象在编译时期并不确定,在运行时期通过反射来动态的创建代理对象,从而在运行时期才确定被代理的对象是谁。这样就可以提高代码复用性。(不用为每个被代理的对象去创建一个代理类,且被代理类的方法服用率大大提升)。

首先介绍:动态代理类的实现所依靠的一个接口和一个方法。

接口InvocationHandler

官方介绍:

InvocationHandler 是代理实例的调用处理程序 实现的接口。

每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 

其实它对应的就是静态代理类中覆写被代理了的接口那段程序。(不过调用哪个方法则是通过反射机制)

他只有一个需要实现的接口:

 

 

 

 

 

三个参数的意义:

 invoke的三个参数:
Object  proxy:代理对象
Method method:代理对象当前执行方法的描述对象(通过反射)
Object[] args:方法的实际参数
如何动态创建代理对象呢?
Proxy.newProxyInstance

 

他的三个参数:

参数:

loader - 定义代理类的类加载器     

interfaces - 代理类要实现的接口列表   

 h - 指派方法调用的调用处理程序 

动态代理实例:

package com.ustc;

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

public class SubDynamicProxy {
    public  SubDynamicProxy createSubDynamicProxy(String[] args) {
        //仍然是通过组合来达到目的
        Sub sub=new SubImp();
        //动态的产生代理对象
        SubDynamicProxy proxy=(SubDynamicProxy) Proxy.newProxyInstance(SubDynamicProxy.class.getClassLoader(), sub.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] args) throws Throwable {
                //目标方法执行前的增强方法
                System.out.println("enhance before target method");
                //调用目标方法,并得到目标方法的返回结果
                Object outcome=method.invoke(sub,args);
                //目标方法执行后的增强方法
                System.out.println("enhance after target method");
                return outcome;
            }
        });
        return proxy;
    }
}

在需要使用SubImp时通过其代理类获得即可,且可通过增强方法对SubImp进行增强。另一个好处是:看到动态代理不同去实现被代理类的接口,所以被代理类的接口在更改时是不是就不用去更改代理类了呢?这样耦合性就降低了许多。

动态代理在Spring-AOP中的应用:

AOP(面向切面编程):AOP采用了横向的抽取机制,取代了传统的纵向继承的体系结构。

如何理解上边这句话呢?

试想一个场景:

有一个基础类,我们需要对其进行增强,传统我们怎么做?===》继承,继承是纵向的,所以叫传统的纵向继承。

而现在我们通过横向抽取,即编写一个增强类(里边放增强方法),这样增强类就和被增强类在同一等级,然后代理类通过组合去排序这两个类中的方法,是不是也达到了增强的目的,同时避免了继承,达到了降低耦合度的作用呢?

Spring-AOP的例子:

需要被代理的类与接口:

public interface UserService {
      public void addUser();
      public void updataUser();
      public void deleUsr();
}
//target   :需要被代理的类
public class UserServiceImp implements UserService {
      @Override
      public void addUser() {
             // TODO Auto-generated method stub
             System.out.println("addUser");
      }
      @Override
      public void updataUser() {
             // TODO Auto-generated method stub
             System.out.println("updateUser");
             
      }
      @Override
      public void deleUsr() {
             // TODO Auto-generated method stub
             System.out.println("deleUser");
             
      }
}

切面类:(防止增强方法)

public class MyAspect implements org.aopalliance.intercept.MethodInterceptor  {
      @Override
      public Object invoke(MethodInvocation mi) throws Throwable {
             System.out.println("前");
             // 调用目标方法
             Object proceed = mi.proceed();
             System.out.println("后");
             return proceed;
      }
}

然后写配置文件,Spring工厂通过配置文件生成代理类(这里用到了AOP的一个框架AspectJ,自行查看)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">
      <!-- 把目标类交给spring -->
    <bean id="userServiceId" class="com.ustc.AOP.auto.UserServiceImp"></bean>
      <!-- 把切面类交给spring -->
      <bean id="myAspectId" class="com.ustc.AOP.auto.MyAspect"></bean>
<!-- 通过aopconfig把他们连接 PointCut:切入点 表达式:用来匹配切入点的。也就是哪个方法需要加强。 --> <!-- advisor:特殊的切面,只有一个通知和一个切入点 pointcut——ref:这种切面的切入点 advice-ref就是通知。把他们连成一个切面 --> <!-- * 要确定目标类,aspectj 切入点表达式,导入jar包 --> <!-- com.springsource.org.aspectj.weaver.jar --> <!-- 切入点表达式:execution(* com.ustc.AOP.auto.UserServiceImp.*(..)--> <aop:config > <aop:pointcut expression="execution(* com.ustc.AOP.auto.UserServiceImp.*(..))" id="myPointCut"/> <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/> </aop:config> </beans>

 

之后再获取UserService是,得到的就是其代理对象。其实Spring生成代理对象使用的就是动态代理的机制。

通过源码验证Spring的AOP底层采用的就是动态代理的机制:

AOP代理的结构:

 

 

 当没有接口,只有实现类时采用cglib字节码增强,当有接口时采用动态代理。

AOP代理接口

public interface AopProxy {

    /**
     * Create a new proxy object.
     * <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
     * usually, the thread context class loader.
     * @return the new proxy object (never {@code null})
    Object getProxy();

    /**
     * Create a new proxy object.
     * <p>Uses the given class loader (if necessary for proxy creation).
     * {@code null} will simply be passed down and thus lead to the low-level
     * proxy facility's default, which is usually different from the default chosen
     * by the AopProxy implementation's {@link #getProxy()} method.
     * @param classLoader the class loader to create the proxy with
     * (or {@code null} for the low-level proxy facility's default)
    Object getProxy(@Nullable ClassLoader classLoader);

}

拿动态代理增强为例来进行查看其获得代理类的方法

SpringAOP生成动态代理的方法:

public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

    /**
     * Finds any {@link #equals} or {@link #hashCode} method that may be defined
     * on the supplied set of interfaces.
     * @param proxiedInterfaces the interfaces to introspect
     */
    private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
        for (Class<?> proxiedInterface : proxiedInterfaces) {
            Method[] methods = proxiedInterface.getDeclaredMethods();
            for (Method method : methods) {
                if (AopUtils.isEqualsMethod(method)) {
                    this.equalsDefined = true;
                }
                if (AopUtils.isHashCodeMethod(method)) {
                    this.hashCodeDefined = true;
                }
                if (this.equalsDefined && this.hashCodeDefined) {
                    return;
                }
            }
        }
    }

看到上边代码,是不是很熟悉?特别是这一句:

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

就是之前介绍的动态代理的代理模式。

所以SpringAOP机制的实现在被代理类有接口时底层采用的就是动态代理的方法。

查看Spring-AOP源码可以到Maven仓库下载并去查看,附上Maven仓库对应地址:

  https://repo1.maven.org/maven2/springframework/spring-aop/

 

 水平有限,有错误请指出!

posted @ 2019-12-08 12:46  跳梁小丑李某某  阅读(542)  评论(0编辑  收藏  举报