SSM源码分析之23种设计模式(代理模式)

前言

代理模式引入

作用:

  • aop实现
  • 拦截器
  • 解耦
  • 专人做专事,自己不想做但是又不得不做的事

概念: 代理角色、被代理的角色(目标对象),由被代理角色来做最终的决定
代理角色通常来说会持有被代理角色对象引用(以便于代理角色完成工作之前或者之后能够找到被代理对象,能够通知被代理对象)

代理模式分类

  • 静态代理
  • 动态代理

processon.com

静态代理实现

静态代理的类比较固定,必须业务已知才行,这也是静态代理最大的弊端。
首先做一个接口:

public interface Person {

    public void findLove();

    public void zufangzi();

    public void buy();

    public void findJob();

    //......
}

然后来一个实现类(儿子要结婚):

public class Son implements Person{

    public void findLove(){
        //我没有时间
        //工作忙
        System.out.println("找对象,肤白貌美大长腿");
    }

    public void zufangzi() {}

    public void buy() {}

    public void findJob() {}
}

这个实现类可能需要很多方法被代理,以findLove为例,我们做一个代理类:

public class Father {

    private Person person;

    //没办法扩展
    public Father(Person person){
        this.person = person;
    }

    //目标对象的引用给拿到
    public void findLove(){
        System.out.println("根据你的要求物色");
        this.person.findLove();
        System.out.println("双方父母是不是同意");
    }

}

来测试一下:

    public static void main(String[] args) {

        //只能帮儿子找对象
        //不能帮表妹、不能帮陌生人
        Father father = new Father(new Son());

        father.findLove();

    }

动态代理实现

  • jdk代理实现
    我们说jdk动态代理需要代理对象实现接口才可以,所以,我们实现一下刚才静态代理的person类
public class XieMu implements Person{

   public void findLove(){
       System.out.println("高富帅");
       System.out.println("身高180cm");
       System.out.println("胸大,6块腹肌");

   }

   public void zufangzi() {
       System.out.println("租房子");
   }

   public void buy() {
       System.out.println("买东西");
   }

   public void findJob() {
       System.out.println("月薪20K-50k");
       System.out.println("找工作");
   }
}

比如,谢幕这个人要求代理找对象

public class JDKMeipo implements InvocationHandler{
    //被代理的对象,把引用给保存下来
    private Person target;

    public Object getInstance(Person target) throws Exception{
        this.target = target;

       Class<?> clazz = target.getClass();

        //字节码是如何重组的?
        //用来生成一个新的对象(字节码重组来实现)
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");

        method.invoke(this.target,args);

        System.out.println("如果合适的话,就准备办事");

        return  null;
    }
}

比如,谢幕这个人要求代理找工作

public class JDK58 implements InvocationHandler{
    //被代理的对象,把引用给保存下来
    private Person target;

    public Object getInstance(Person target) throws Exception{
        this.target = target;

       Class<?> clazz = target.getClass();

        //字节码是如何重组的?
        //用来生成一个新的对象(字节码重组来实现)
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("我是58:我要给你找工作,现在已经拿到你的简历");
        System.out.println("开始投递");

        method.invoke(this.target,args);

        System.out.println("安排面试");

        return  null;
    }
}

我们来测试一下:

public class JDKProxyTest {

    public static void main(String[] args) {

        try {
            Person obj = (Person)new JDK58().getInstance(new XieMu());
            System.out.println(obj.getClass());
            obj.findJob();

            //原理:
            //1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
            //2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
            //3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
            //4、编译新生成的Java代码.class
            //5、再重新加载到JVM中运行
            //以上这个过程就叫字节码重组

            //JDK中有个规范,只要要是$开头的一般都是自动生成的

            //通过反编译工具可以查看源代码
            byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
            FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
            os.write(bytes);
            os.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
  • cglib代理实现

首先来一个需要代理的类,不需要实现接口

public class ZhangSan {

   public void findLove(){
       System.out.println("肤白貌美大象腿");
   }

}

然后,媒婆帮忙张三代理:

public class CglibMeipo implements MethodInterceptor{

   public Object getInstance(Class<?> clazz) throws  Exception{

       Enhancer enhancer = new Enhancer();
       //要把哪个设置为即将生成的新类父类
       enhancer.setSuperclass(clazz);

       enhancer.setCallback(this);

       return  enhancer.create();

   }


   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       //业务的增强

       System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
       System.out.println("开始物色");

       methodProxy.invokeSuper(o,objects);

       System.out.println("如果合适的话,就准备办事");
       return null;
   }
}

测试一下:

   public static void main(String[] args) {

        try {
            ZhangSan obj = (ZhangSan)new CglibMeipo().getInstance(ZhangSan.class);
            obj.findLove();
            System.out.println("--------------------------------");
           // System.out.println(obj.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

手写jdk代理模式

在刚才jdk动态代理的实例中,我们发现通过反射获取到目标对象的代理类$Proxy。

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m5;
    private static Method m6;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void findLove() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void zufangzi() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buy() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findJob() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.xxx.vip.pattern.proxy.staticed.Person").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.xxx.vip.pattern.proxy.staticed.Person").getMethod("zufangzi");
            m6 = Class.forName("com.xxx.vip.pattern.proxy.staticed.Person").getMethod("buy");
            m3 = Class.forName("com.xxx.vip.pattern.proxy.staticed.Person").getMethod("findJob");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这回我们自己来实现一下这个过程。
自定义核心类InvocationHandler:

public interface GPInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

我们根据生成的Proxy自定义实现代理类:

public class GPProxy {

    public static final String ln = "\r\n";

    public static Object newProxyInstance(GPClassLoader classLoader,Class<?> [] interfaces,GPInvocationHandler h){

       try {
           //1、动态生成源代码.java文件

           String src = generateSrc(interfaces);

           //2、Java文件输出磁盘
           String filePath = GPProxy.class.getResource("").getPath();
           System.out.println(filePath);
           File f = new File(filePath + "$Proxy0.java");
           FileWriter fw = new FileWriter(f);
           fw.write(src);
           fw.flush();
           fw.close();

           //3、把生成的.java文件编译成.class文件
           JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
           StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
           Iterable iterable = manage.getJavaFileObjects(f);

          JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
          task.call();
          manage.close();

           //4、编译生成的.class文件加载到JVM中来
          Class proxyClass =  classLoader.findClass("$Proxy0");
          Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
          f.delete();

           //5、返回字节码重组以后的新的代理对象
           return c.newInstance(h);
       }catch (Exception e){
           e.printStackTrace();
       }
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces){

            StringBuffer sb = new StringBuffer();
            sb.append("package com.xxx.vip.pattern.proxy.custom;" + ln);
            sb.append("import com.xxx.vip.pattern.proxy.staticed.Person;" + ln);
            sb.append("import java.lang.reflect.Method;" + ln);
            sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);

                sb.append("GPInvocationHandler h;" + ln);

                sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);

                    sb.append("this.h = h;");

                sb.append("}" + ln);


                for (Method m : interfaces[0].getMethods()){
                    sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
                        sb.append("try{" + ln);
                            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
                            sb.append("this.h.invoke(this,m,null);" + ln);
                        sb.append("}catch(Throwable e){" + ln);
                        sb.append("e.printStackTrace();" + ln);
                        sb.append("}");
                    sb.append("}");
                }

            sb.append("}" + ln);

            return sb.toString();
    }

}

自定义加载字节码的classloader

public class GPClassLoader extends ClassLoader{

    private File classPathFile;

    public GPClassLoader(){
        String classPath = GPClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        String className = GPClassLoader.class.getPackage().getName() + "." + name;

        if(classPathFile != null){
            File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
            if(classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out = null;

                try{
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1){
                        out.write(buff,0,len);
                    }
                    return  defineClass(className,out.toByteArray(),0,out.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if(null != in){
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    if(out != null){
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }

        return null;

    }
}

然后我们一个代理对象测试:

public class CustomMeipo implements  GPInvocationHandler {

    //被代理的对象,把引用给保存下来
    private Person target;

    public Object getInstance(Person target) throws Exception{
        this.target = target;

        Class<?> clazz = target.getClass();

        //字节码是如何重组的?
        //用来生成一个新的对象(字节码重组来实现)
        return GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");

        method.invoke(this.target,args);

        System.out.println("如果合适的话,就准备办事");

        return  null;
    }

进行测试:

   public static void main(String[] args) {

        try {
            Person obj = (Person)new CustomMeipo().getInstance(new XieMu());
            System.out.println(obj.getClass());
            obj.findLove();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

总结

静态代理:在代理之前,所有的东西都是已知的(人工)
动态代理:在代理之前,所有的东西都是未知的(智能)

发布了47 篇原创文章 · 获赞 5 · 访问量 1883

猜你喜欢

转载自blog.csdn.net/qq_34361283/article/details/103210702
今日推荐