代理模式

代理模式:能在不修改现有类的前提下,扩展类的的行为;

 

实现代理模式的核心点:

  1. 代理对象和目标对象实现相同的接口;(这样对调用者来说就是透明的)
  2. 客户端通过代理对象间接调用目标对象;
  3. 代理对象将方法调用委托给目标对象;

实现方式(静态代理+动态代理):

静态代理:编程时确定代理对象和目标对象,每个代理对象只能代理一个目标对象;

动态代理(JDK动态代理+CGLib动态代理):代理对象根据目标对象自动生成,可以代理任意目标对象,不需要为每个目标对象定义一个代理对象;

动态代理主要用来解决代理一系列类的某一些方法---典型栗子:SpringAOP

详情看:http://www.cnblogs.com/zhaojinxin/p/6666309.html

JDK动态代理:使用反射机制分析目标类结构,动态生成代理对象;

具体实现步骤:

(1)声明代理类:实现InvocationHandler接口,重写invoke()方法,实现代理逻辑;

(2)客户端调用:指定代理的目标类,使用Proxy类的newProxyInstance()方法获得代理对象;

注:它会实现被你传入newProxyInstance方法的所有接口,所以我们可以将生成的代理强转为任意一个代理的接口或者Proxy去使用

优点:能将接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke);

无需向静态代理那样进行每个方法的中转,可以灵活处理;而且是我们的类职责更加单一,复用性更强;

缺点:这种方式必须实现接口, 根本原因:内部已经让代理类继承Proxy类。 因此对于没有接口的类,这就需要CGLib来实现动态代理。

CGLib动态代理使用ASM(一个Java字节码操控框架)直接改造目标了目标类的字节码文件,动态生成代理对象;

具体实现步骤:

(1)声明代理类:实现MethodInterceptor接口,重写intercept()方法,实现代理逻辑;

(2)客户端调用:使用代理类的getProxy()方法获得代理对象

注:CGLib是一个高性能代码生成库

JDK动态代理和CGLib动态代理的比较:

  

扩展:

代理模式和装饰者模式区分:

代理模式关注于控制对对象的访问

装饰器模式关注于在一个对象上动态的添加方法

通俗的讲,使用代理模式的时候,代理类可以对它的调用者隐藏目标对象的具体信息。因此,我们通常的做法是在一个代理类中创建一个目标对象的实例。使用装饰者模式的时候,装饰者类所装饰的目标对象对调用者来说是透明的。因此,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

关于静态代理类的代码实现的Demo,我这里就不写了,一问度娘便知;

我下面着重讲一下JDk动态代理的实现:

//定义一个抽象的主题接口
public interface AbstractSubject {
    public void print();
}
//定义一个真实的主题类(被代理的类)
public class RealSubject implements AbstractSubject {
    @Override
    public void print() {
        System.out.println("Hello , 我是真实主题");
    }
}
package com.jdk_dynamicproxy;

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

/**
 * 声明一个代理类(本质上其实算一个调用处理器,指导代理类的生成):要实现MethodInterceptor接口,重写intercept()方法,实现代理逻辑;
 *
 */
public class MyHandler implements InvocationHandler {
    private Object target; //这是一个真实的角色作为代理角色的属性;
    
    //构造器
    public MyHandler(Object target) {
        this.target = target;
    }


    /**
     * 通过反射机制动态执行真实角色的每一个方法;
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
System.out.println("代理类_拦截到方法:" + method.getName()); System.out.println("代理类_预处理."); Object result = method.invoke(target, args);//调用真是对象的method方法 return result; } catch (Exception e) { return null; } finally { System.out.println("代理类_善后处理."); } } }
package com.jdk_dynamicproxy;

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

/**
 *客户端
 */
public class Client {
    
    public static void main(String[] args) {
        //真实对象(被代理对象)
        final AbstractSubject rs1 = new RealSubject();
    
        //为真实对象制定一个调用处理器(自己写的)
        InvocationHandler handler  = new MyHandler(rs1);
        
        //获得真实对象的一个代理类对象 -----重要知识点
        Object proxyObj = Proxy.newProxyInstance(
                    rs1.getClass().getClassLoader(), //真实对象的类加载器
                    rs1.getClass().getInterfaces(), //真实对象实现接口的集合
                    handler                        //真实对象的处理器
        );  
        
        if(proxyObj instanceof AbstractSubject) {
            System.out.println("这是AbstractSubject实体的代理对象");
            //为了能调用真实对象的方法,要强转为真实对象类的代理对象;
            AbstractSubject proxyAS = (AbstractSubject)proxyObj;
            proxyAS.print(); //内部是通过handler的invoke方法执行;
        } else {
            System.out.println("这不是AbstractSubject实体的代理对象");
        }
    }
    
}

运行结果:

下面讨论一种特殊情况:不实现接口就不能使用动态代理吗?? 答案:错,是可以使用

例如:

在使用JDK动态代理时:

Object proxyObj = Proxy.newProxyInstance(
                    B.getClass().getClassLoader(), //真实对象的类加载器
                    rs1.getClass().getInterfaces(),//真实对象实现接口的集合
handler //真实对象的处理器
);

       第二个参数,如果传入的不是真实对象类所实现的接口,会怎么样?

即这样一种情况:

      假设有一个接口A,一个类B, 类B与接口A有一样的方法, 但类B并没有实现接口A; 将接口A去生成B的代理对象吗?即使生成能正常使用吗?

      答案:能生成,但是调用方法时发现只会输出一个字符串。因为在内部invoke方法无法通过method.invoke方法调用B类中的方法    

      首先,因为JDK的动态代理只认你传入的接口,只要传入,代理对象就可以强转成这个接口,所以能生成代理对象;

      然而,因为invoke中传入的Method的class信息是接口A,而类B因为没实现接口A,所以无法在invoke方法使用method.invoke方法调用, 否则会抛出非法参数异常。

如果非要使用类B的方法也不是不行,采用以下方式:要转成source对应的method才可以调用

  Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
  sourceMethod.setAccessible(true);
  Object result = sourceMethod.invoke(source, args);

话说回来:实现个接口能死啊?非要没事找事!

参考博客:http://www.cnblogs.com/zuoxiaolong/p/pattern3.html




原文地址:https://www.cnblogs.com/zhaojinxin/p/6666261.html