设计模式——Proxy

什么是代理

代理就是增强一个对象的功能

java实现的代理的两种办法

代理的名词

代理对象 增强后的对象

目标对象 被增强的对象

他们不是绝对的,会根据情况发生变化

静态代理

继承

代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,非常复杂

  • 基本类
public class UserDaoImp {
    public void query(){
        System.out.println("业务逻辑");
    }
}
  • 对类进行增强,想要输出日志
public class UserDaoLogImp extends UserDaoImp {
    @Override
    public void query() {
        System.out.println("-----Log-------");
        super.query();
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        UserDaoImp dao = new UserDaoLogImp();
        dao.query();
    }
}
  • 对类进行增强,想要输出时间
public class UserDaoTimeImp extends UserDaoImp {
    @Override
    public void query() {
        System.out.println("-----------Time-------------");
        super.query();
    }
}
  • 若想对基本类进行增强,输入时间和日志
    解决方式是:单链继承
    若想先输出日志,那就让time继承基础类,再让log继承time.(我是先执行增强的事务,在执行业务)。至于怎么输出看逻辑

若是想输出时间,地点,日志,且输出的顺序不一样,那就需要创建大量的类来实现增强。(排列组合)

聚合(接口)

目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:也会产生类爆炸,只不过比继承少一点点。(有多少增强方法就需要实现多少次接口类)

  • 基础接口
public interface UserDao {
    void query();
}
  • 实现接口
public class UserDaoImp implements UserDao{
    public void query(){
        System.out.println("业务逻辑");
    }
}
  • 日志代理对象
public class UserDaoLog implements UserDao {
    UserDao dao;
    public UserDaoLog(UserDao dao){
        this.dao = dao;
    }

    public void query() {
        System.out.println("-----Log-------");
        dao.query();
    }
}

  • 时间代理对象
public class UserDaoTime implements UserDao {
    UserDao dao;

    public UserDaoTime(UserDao dao){
        this.dao = dao;
    }

    public void query() {
        System.out.println("-----------Time-----------");
        dao.query();
    }
}
public class Test {
    public static void main(String[] args) {
        /**
         * 就相当于
         * UserDao target = new UserDaoImp();
         * 
         * UserDao proxy = new UserDaoLog(target);
         */
        
        UserDao dao = new UserDaoLog(new UserDaoImp());
        dao.query();
        System.out.println("#####################");
        /**
         * UserDao target = new UserDaoImp();
         *
         * UserDao proxy = new UserDaoTime(target);
         */

        UserDao dao1 = new UserDaoTime(new UserDaoImp());
        dao1.query();
        System.out.println("#########################");
        /**
         * UserDao target = new UserDaoLog(new UserDaoImp());
         *
         * UserDao proxy = new UserDaoTime(target);
         */
        UserDao dao2 = new UserDaoTime(new UserDaoLog(new UserDaoImp()));
        dao2.query();
    }
}

在这里插入图片描述

总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。

动态代理(不能new)

手写模拟动态代理

不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。

  • 任意接口,任意参数,任意返回值的动态代理
public class ProxyUtil {

    public static Object newInstance(Object target){  //传递一个类对象其中有接口
        Object proxy = null;
        Class targetInf = target.getClass().getInterfaces()[0];  //获取类接口(它是一个类)
        //============================以下是动态代理的字符串编写=================================
        String line= "\n";
        String tab = "\t";
        String infName = targetInf.getSimpleName(); //只获取类名
        Method[] methods = targetInf.getDeclaredMethods();
        String content = "";
        String packageContent = "package com.google;" + line;
        String importContent = "import " + targetInf.getName() + ";" +  line; //获取全类名
        String classFirstLineContent = "public class $Proxy implements " + infName + "{" + line;
        String filedContent = tab + "private " + infName + " target;" + line;
        String constructContent = tab + "public $Proxy(" + infName +" target) { " + line
                                + tab + tab + "this.target = target;" + line
                                + tab + "}" + line;
        String methodContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName = method.getName();
            Class args[] = method.getParameterTypes();  //不是对象,就是类
            String argsContent = "";
            String parameContent = "";
            int flag = 0;
            for (Class arg : args) {
                String temp = arg.getSimpleName();
                argsContent += temp + " i" + flag + ",";
                parameContent += "i" + flag + ",";
                flag ++;
            }
            if(argsContent.length() > 0){
                argsContent = argsContent.substring(0, argsContent.lastIndexOf(',')-1);
                parameContent = parameContent.substring(0,parameContent.lastIndexOf(',')-1 );
            }

            methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent +") {" + line
                            + tab + tab  + "System.out.println(\"log\");" + line;
            if(returnTypeName.equals("void")) {
                methodContent += tab + tab + "target." + methodName + "(" + parameContent + ");" + line;
            } else {
                methodContent += tab + tab + "return ("  + returnTypeName + ") target." + methodName + "(" + parameContent + ");" + line;

            }
            methodContent += tab + "}" + line;
        }

        content = packageContent + importContent + classFirstLineContent + filedContent + constructContent + methodContent + "}";
        //============================编写完成=================================
//        System.out.println(content);

        //===============================写入文件==========================
        File file =new File("c:\\com\\google\\$Proxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }

            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();

            // 编写.class文件
            JavaCompiler compiler = getSystemJavaCompiler();

            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);

            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();

            //加载.class进内存
            URL[] urls = new URL[]{new URL("file:c:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.google.$Proxy");

            //构造器创建类
            Constructor constructor = clazz.getConstructor(targetInf);  //传递接口
            proxy = constructor.newInstance(target);
        } catch (Exception e){
                e.printStackTrace();
        }


        return proxy;
    }
}

代码解读:
在这里插入图片描述
由对象可以获取类,类在获取接口列表: 对象。getClass().getInterfaces()[0],获取第一个接口(Class,注:接口也是Class)
只实现了第一个接口的代理。
在这里插入图片描述
获取接口(Class)中的所有方法。
在这里插入图片描述
获取接口(Class)的名字(全类名,包含包名)
在这里插入图片描述

  • Method反射里面的内容
    在这里插入图片描述
    类对象.getDeclaredMethods()
/**
 * A {@code Method} provides information about, and access to, a single method
 * on a class or interface.  The reflected method may be a class method
 * or an instance method (including an abstract method).
 *
 * <p>A {@code Method} permits widening conversions to occur when matching the
 * actual parameters to invoke with the underlying method's formal
 * parameters, but it throws an {@code IllegalArgumentException} if a
 * narrowing conversion would occur.
 *
 * @see Member
 * @see java.lang.Class
 * @see java.lang.Class#getMethods()
 * @see java.lang.Class#getMethod(String, Class[])
 * @see java.lang.Class#getDeclaredMethods()
 * @see java.lang.Class#getDeclaredMethod(String, Class[])
 *
 * @author Kenneth Russell
 * @author Nakul Saraiya
 */

类对象= 对象.getClass() ==> 类对象.getClassName()

  • 缺点
  1. 首先要生成文件
  2. 动态编译文件 class
  3. 需要一个URLclassloader

软件性能的最终体现在IO操作(自己写的动态代理中间IO操作)

  • 需要代理的字符串内容
package com.google;
import com.luban.dao.UserDao;
public class $Proxy implements UserDao{
	private UserDao target;
	public $Proxy(UserDao target) { 
		this.target = target;
	}
	public String print(String i0,int i) {
		System.out.println("log");
		return (String) target.print(i0,i);
	}
	public void query(String i) {
		System.out.println("log");
		target.query(i);
	}
}

上面的代理虽然不用重写类,但是不能实现动态代理

动态逻辑的动态代理如下

package com.luban.dao;

import java.lang.reflect.Method;

public interface CustomeInvocationHandler {
    public Object invoke(Method method);
}

package com.google;

import com.luban.dao.CustomeInvocationHandler;
import com.luban.dao.UserDao;

import java.lang.reflect.Method;

public class $Proxy implements UserDao {
    private CustomeInvocationHandler h;
    public $Proxy(CustomeInvocationHandler h){
        this.h = h;
    }
    public String proxy() throws Exception{
        Method method = Class.forName("com.luban.dao.UserDao").getDeclaredMethod("proxy");
        return (String)h.invoke(method);
    }
}

JDK动态代理

通过接口反射得到字节码,然后把字节码转成class native openJDK c++

Cglib

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值