阅读 144

java基础 之 动态代理实现

由上文我们开始进行自行搞出个动态代理。

首先创建一个KInVocationHandler接口

/\*\*
 \* 创建一个KInVocationHandler的接口
 \*/
public interface KInVocationHandler {
    public Object invoke(Object proxy, Method method,Object\[\] args) throws Throwable;
}
复制代码

然后我们再创建一个KClassLoader,我们自己的ClassLoder

package com.kaysanshi.design\_pattern.proxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
// 定义一个类加载器

/\*\*
 \* ClassLoader类使用委托模型搜索类和资源。每个ClassLoader实例都有一个关联的父类加载器。当请求查找类或资源时,
 \* ClassLoader实例会将对类或资源的搜索委托给其父类加载器,然后再尝试查找类或资源本身。虚拟机的内置类加载器(称为“引导类加载器”)本身没有父级,
 \* 但可以用作ClassLoader实例的父级
 \*/
public class KClassLoader extends ClassLoader{

    private File classPathFile;

    public KClassLoader(){
        // 获取KClassLoader类的路径
        String classPath = KClassLoader.class.getResource("").getPath();
        this.classPathFile=new File(classPath);
    }

    /\*\*
     \* 查找具有指定的<a href="name">二进制名称<a>的类。该方法应由遵循用于加载类的委托模型的类加载器实现重写,
     \* 并且将在检查父类加载器中的所请求类之后由{@link loadClass <tt> loadClass <tt>}方法调用
     \* @param name
     \* @return
     \* @throws ClassNotFoundException
     \*/
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 获取 class的name xxx.package.name
        String className= KClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile !=null){
            // 创建文件
            File classFile = new File(classPathFile,name.replaceAll("\\\\.","/")+".class");
            if (classFile.exists()){
                FileInputStream inputStream =null;
                ByteArrayOutputStream outputStream=null;
                try {
                    inputStream = new FileInputStream(classFile);
                    outputStream = new ByteArrayOutputStream();
                    byte \[\] buffer = new byte\[1024\];
                    int len;
                    // 读取并写入流中
                    while((len = inputStream.read(buffer))!=-1){
                        outputStream.write(buffer,0,len);
                    }
                    // 将字节数组转换为Class类的实例。在使用该类之前,必须先对其进行解析。
                    //此方法将默认的ProtectionDomain分配给新定义的类。
                    // Policy.getPolicy().getPermissions(new CodeSource(null, null))时,
                    // 将有效地授予ProtectionDomain相同的权限集。默认域在第一次调用defineClass创建,并在后续调用中重用
                    return defineClass(className,outputStream.toByteArray(),0,outputStream.size());

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

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

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

        return null;
    }
}

复制代码

创建代理的生成代理代码的类:

package com.kaysanshi.design\_pattern.proxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/\*\*
 \* 生成源代码的工具类
 \*/
public class KProxy {
    public static final String ln = "\\r\\n";

    public static Object newProxyInstance(KClassLoader classLoader, Class<?>\[\] interfaces, KInVocationHandler inVocationHandler) {
        try {
            // 动态生成源代码 .java文件
            String src = generateSrc(interfaces);

            // java文件输出磁盘输出成$Proxy0.java的文件
            String filePath = KProxy.class.getResource("").getPath();
            File file = new File(filePath + "$Proxy0.java");
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(src);
            fileWriter.flush();
            fileWriter.close();

            // 把生成的.java文件编译成.class文件
            // 获取与此平台一起提供的Java™编程语言编译器。
            JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
            // 获取此工具的标准文件管理器实现的新实例。文件管理器将使用给定的诊断侦听器来生成任何非致命的诊断。致命错误将以适当的例外方式发出信号
            StandardJavaFileManager standardFileManager = systemJavaCompiler.getStandardFileManager(null, null, null);
            // 获取表示给定文件的文件对象。
            Iterable<? extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(file);
            // 使用给定的组件和参数为编译任务创建未来。如CompilationTask界面中所述,编译可能尚未完成
            JavaCompiler.CompilationTask task = systemJavaCompiler.getTask(null, standardFileManager, null, null, null, iterable);
            // 执行此编译任务。编译只能执行一次。随后对该方法的调用抛出IllegalStateException
            task.call();
            standardFileManager.close();
            // 把编译后生成.class的文件加载到jvm中
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor constructor = proxyClass.getConstructor(KInVocationHandler.class);
            // 删除生的文件
            file.delete();
            // 返回字节码重组以后的新的代理对象
            return constructor.newInstance(inVocationHandler);
        } catch (Exception e) {

        }
        return null;
    }

    private static String generateSrc(Class<?>\[\] interfaces) {
        // 线程安全
        StringBuffer buffer = new StringBuffer();
        // 加入头文件
        buffer.append("package com.kaysanshi.design\_pattern.proxy;" + ln)
                .append("import com.kaysanshi.design\_pattern.proxy.Person;" + ln)
                .append("import java.lang.reflect.\*;" + ln)
                .append("public class $Proxy0 implements " + interfaces\[0\].getName() + "{" + ln)
                .append("KInVocationHandler inVocationHandler;" + ln)
                .append("public $Proxy0(KInVocationHandler inVocationHandler) {" + ln)
                .append("this.inVocationHandler = inVocationHandler;" + ln)
                .append("}" + ln);
        for (Method method : interfaces\[0\].getMethods()) {
            // 反射的中的获取参数类型
            Class<?>\[\] parameterTypes = method.getParameterTypes();
            StringBuffer paramNames = new StringBuffer();
            StringBuffer paramValues = new StringBuffer();
            StringBuffer paramClasses = new StringBuffer();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class clazz = parameterTypes\[i\];
                String type = clazz.getName();
                // 返回源代码中给定的基础类的简单名称。如果基础类是匿名的,则返回一个空字符串
                String paramName = toLowerFirstCase(clazz.getSimpleName());
                paramNames.append(type + " " + paramName);
                paramValues.append(paramName);
                paramClasses.append(clazz.getName() + ".class");
                if (i > 0 && i < parameterTypes.length - 1) {
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValues.append(",");
                }
            }
            // 创建方法
            buffer.append(" public  " + method.getReturnType().getName() + " " + method.getName() + "("
                    + paramNames.toString() + ") {" + ln)
                    .append("try{" + ln)
                    .append("Method m=" + interfaces\[0\].getName() + ".class.getMethod(\\"" + method.getName() + "\\",new Class\[\]{" +
                            paramClasses.toString() + "});" + ln)
                    .append((hasReturnValue(method.getReturnType()) ? "return " : "") + getCaseCode("this.inVocationHandler.invoke(this,m,new Object\[\]{" + paramValues + "})", method.getReturnType()) + ";" + ln)
                    .append("}catch(Error \_ex){}")
                    .append("catch(Throwable e){"+ln)
                    .append("throw new UndeclaredThrowableException(e);"+ln)
                    .append("}"+ln)
                    .append(getReturnEmptyCode(method.getReturnType()))
                    .append("}");
        }
        buffer.append("}" + ln);
        return buffer.toString();
    }




    private static Map<Class,Class> mappings = new HashMap<>();
    static {
        mappings.put(int.class,Integer.class);
    }
    /\*\*
     \* 首字母小写
     \*
     \* @param simpleName
     \* @return
     \*/
    private static String toLowerFirstCase(String simpleName) {
        char\[\] chars = simpleName.toCharArray();
        chars\[0\] += 32;
        return String.valueOf(chars);
    }

    /\*\*
     \* 判断是否有返回
     \* @param returnType
     \* @return
     \*/
    private static boolean hasReturnValue(Class<?> returnType) {
        return returnType!=void.class;
    }

    /\*\*
     \*
     \* @param
     \* @param returnType
     \* @return
     \*/
    private static String getCaseCode(String code, Class<?> returnType) {
        if(mappings.containsKey(returnType)){
            return "(("+mappings.get(returnType).getName()+")"+code+")."+returnType.getSimpleName()+"Value()";
        }
        return code;
    }
    private static String getReturnEmptyCode(Class<?> returnType) {
        if(mappings.containsKey(returnType)){
            return  "return 0;";
        }else if(returnType == void.class){
            return "";
        }else{
            return "return null;";
        }
    }

}

复制代码

然后分别进行对进行代理的类进行创建

public interface Person {
    // 每一个人都办理一些事情
    void handlerBusiness();
}



/\*\*
 \* user:kay三石
 \* time: 21:25
 \* desc:
 \*\*/
public class Customer implements Person{
    public void handlerBusiness(){
        System.out.println("花费充值办理业务");
    }

}

/\*\*
 \* KBoss是移动代理点的,
 \*/
public class KBoss implements KInVocationHandler{
    // 被代理的对象
    private Object target;
    @Override
    public Object invoke(Object proxy, Method method, Object\[\] args) throws Throwable {
        before();
        method.invoke(this.target,args);
        after();
        return null;

    }

    private void after() {
        System.out.println("缴纳完毕,等待移动返回数据");
    }

    private void before() {
        System.out.println("我是中国移动代理商现在为你缴纳花费");
        System.out.println("开始缴纳");
    }

    public Object getInstance(Object target) throws Exception{
        this.target = target;
        Class<?> clazz = target.getClass();
        return  KProxy.newProxyInstance(new KClassLoader(),clazz.getInterfaces(),this);
    }
}

/\*\*
 \* user:kay三石
 \* time: 21:24
 \* desc:
 \*\*/
public class TestProxy {
    public static void main(String\[\] args) throws Exception {
        Person obj = (Person) new KBoss().getInstance(new Customer());
        System.out.println(obj.getClass());
        obj.handlerBusiness();
    }
    /\*\*
     \* ~output
     \* 我是中国移动代理商现在为你缴纳花费
     \* 开始缴纳
     \* 花费充值办理业务
     \* 缴纳完毕,等待移动返回数据
     \*/
}
复制代码

经过以上的步骤就可以可能到我们自己实现的是可以使用的。核心逻辑都有注释请耐心看,可以直接跟着写一下看看效果的。ok好了

文章分类
后端
文章标签