设计模式之代理模式

什么是代理模式?

简单来说,代理模式是常用的java设计模式,我们在访问实际对象时,是通过代理对象来访问的,代理对象本身并不真正实现服务,而是通过调用实际对象的相关方法来提供特定的服务,即代理模式在在访问实际对象时引入了一定程度的间接性,因为这种间接性可以附加多种用途。

什么是静态代理?

静态代理由程序员创建源代码,也就是说在编译时已经将接口,代理类,被代理类确定下来。在程序运行时代理类的.class文件就已经生成。

	静态代理简单实现:

	1. 创建接口,确定具体行为
public interface Computer {
    public void add();
}

2.创建被代理类

public class User implements Computer {

    private String password;

    private String username;

    public String  a;

  public User() {
        System.out.println("构造方法执行了");
    }

    public User(String password, String username) {
        this.password = password;
        this.username = username;
        System.out.println("有参构造方法");
    }

    public String getPassword() {
		  return password;
    }

    public void setPassword(String password) {
        this.password = password;
        System.out.println("password的set方法调用");
    }

    public String getUsername() {

        return username;
    }

    public void setUsername(String username) {
        this.username = username;
        System.out.println("username的set方法调用");
    }

    public void add(){

        System.out.println("add.....");
    }

    @Override
    public String toString() {
        return "User{" +
                "password='" + password + '\'' +
                ", username='" + username + '\'' +
                '}';
    }
}

3.创建代理类

public class UserProxy implements Computer {

    private User user;

    public UserProxy(User user) {
        this.user = user;
    }

    @Override
    public void add() {
        System.out.println("add前");
        user.add();
        System.out.println("add后");
    }
}

4.测试

@Test
    public void test22(){
        User user=new User("123","张山");
        UserProxy userProxy=new UserProxy(user);
        userProxy.add();
    }

这里通过UserProxy类的实例代替User类实例来执行add(),这就是代理模式。代理模式最主要的就是有一个公共接口,一个被代理类,一个代理类,代理类持有被代理类的实例对象,代替被代理类执行方法。

什么是动态代理?

动态代理指的是在运行的时候才创建代理类并生成代理对象,而静态代理的代理类是程序员预先定义好了的。他的优点是,方便对代理类的方法进行统一管理,不用修改每个代理类中的方法。(通过InvocationHandler的实现类的invoke()进行统一管理,静态代理中代理类就是对被代理类中的方法进行覆盖重写,要对被代理类的方法进行增强就需要一个个修改代理类的方法)

动态代理的的简单实现:
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
1.创建接口,确定行为
public interface Computer {
    public void add();
}
2.创建被代理类实现接口
public class User implements Computer {

    private String password;

    private String username;

    public String  a;


    public User() {

        System.out.println("构造方法执行了");
    }

    public User(String password, String username) {
        this.password = password;
        this.username = username;
        System.out.println("有参构造方法");
    }

    public String getPassword() {

        return password;
    }

    public void setPassword(String password) {
        this.password = password;
        System.out.println("password的set方法调用");
    }

    public String getUsername() {

        return username;
    }

    public void setUsername(String username) {
        this.username = username;
        System.out.println("username的set方法调用");
    }



    public void add(){

        System.out.println("add.....");
    }



    @Override
    public String toString() {
        return "User{" +
                "password='" + password + '\'' +
                ", username='" + username + '\'' +
                '}';
    }
}

3.创建中介类实现InvocationHandler接口(该类对象将作为代理类的一个变量,并在代理类中调用invoke方法)

public class UserProxy implements InvocationHandler {

    private User user;

    public UserProxy(User user) {
        this.user = user;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("111");
        Object object=method.invoke(user);
        return object;
    }
}

4.测试

 @Test
    public  void test23(){
        User user=new User("123","张山");
        UserProxy userProxy=new UserProxy(user);
        Computer computer=(Computer) Proxy.newProxyInstance(Computer.class.getClassLoader(),new Class<?>[]{Computer.class},userProxy);
        computer.add();
    }

我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象

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

重点是这四处位置:

final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});

最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类。
代理类的源代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
 
public final class $Proxy0 extends Proxy implements Person {
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  //m3是接口的add方法对象,如果接口中有多个方法这里将会出现m4,m6,m7...
  /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  *
  *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  *父类持有:protected InvocationHandler h;
  *Proxy构造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  //这个静态块本来是在最后的,我把它拿到前面来,方便描述
   static
  {
    try
    {
      //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.User").getMethod("add", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
 
  /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);这里简单,明了。
  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
  */
  //重写add方法并在add方法中调用Invocation实现类对象的invoke方法,这就是调用代理类的方法会调用invoke方法的原因
  public final void add()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);//add方法没有参数所以这里为null
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

}

动态代理总结
要完成jdk动态代理需要有三部分:公共接口,被代理类,InvocationHandler实现类(中介类),中介类需要持有被代理类对象(作为method.invoke(target, args)的参数在中介类对象中调用要增强的方法)。通过Proxy的newProxyInstance(User.class.getClassLoader(), new Class[]{User.class}, Userproxy)创建代理类对象,参数1用于加载要代理的类,参数2为代理类提供被代理类的方法和属性的定义,参数3通过invoke方法为代理类提供要增强的功能,并调用被代理类的方法。调用代理类的方法会被替换成invoke方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜里都傻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值