自定义实现Java动态代理

Posted hangzhi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义实现Java动态代理相关的知识,希望对你有一定的参考价值。

转自:https://www.cnblogs.com/rjzheng/p/8750265.html

一 借助JDK的API实现:

1.先创建一个接口,并实现它

public interface Person {
    void eat();
}
public class PersonImpl implements Person {
@Override
public void eat() {
System.out.println("eat............");
}
}

 

2.实现InvocationHandler:每一个动态代理类都要实现这个接口

 1 public class PersonInvocationHandler implements InvocationHandler {
 2 
 3     //我们要代理的真实对象
 4     private Object obj;
 5 
 6     public PersonInvocationHandler(Object obj) {
 7         this.obj = obj;
 8     }
 9 
10     /**
11      * Object proxy:
12      * 1. 可以使用反射获取代理对象的信息(也就是proxy.getClass().getName())。
13      * 2. 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象。
14      */
15     @Override
16     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
17         System.out.println("before eat.......");
18         //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
19         method.invoke(obj, args);
20         System.out.println("after eat.......");
21         return null;
22     }
23 }

3.测试

 1 public class JdkTest {
 2     public static void main(String[] args) {
 3         PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(new PersonImpl());
 4 
 5         Person personProxy = (Person) Proxy.newProxyInstance(PersonImpl.class.getClassLoader(),
 6                 PersonImpl.class.getInterfaces(), personInvocationHandler);
 7 
 8         personProxy.eat();
 9     }
10 }

返回结果:

 

 二 自定义动态代理

1.自定义InvocationHandler

1 public interface MyInvocationHandler {
2     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
3 }

 

2.实现自定义的MyInvocationHandler

 1 public class PersonInvocationHandler implements MyInvocationHandler {
 2 
 3     //我们要代理的真实对象
 4     private Object obj;
 5 
 6     public PersonInvocationHandler(Object obj) {
 7         this.obj = obj;
 8     }
 9 
10     /**
11      * Object proxy:
12      * 1. 可以使用反射获取代理对象的信息(也就是proxy.getClass().getName())。
13      * 2. 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象。
14      */
15     @Override
16     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
17         System.out.println("before eat.......");
18         //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
19         method.invoke(obj, args);
20         System.out.println("after eat.......");
21         return null;
22     }
23 }

 

3.自定义ClassLoader

 1 public class MyClassLoader extends ClassLoader{
 2 
 3     private File classPathFile;
 4 
 5     public MyClassLoader() {
 6         String classPath = MyClassLoader.class.getResource("").getPath();
 7         this.classPathFile = new File(classPath);
 8     }
 9 
10     public Class<?> findClass(String name) {
11         String className = MyClassLoader.class.getPackage().getName() + "." + name;
12         if (classPathFile != null){
13             File file = new File(classPathFile, name + ".class");
14             FileInputStream inputStream = null;
15             ByteArrayOutputStream outputStream = null;
16             try {
17                 inputStream = new FileInputStream(file);
18                 outputStream = new ByteArrayOutputStream();
19                 byte[] buf = new byte[1024];
20                 int len ;
21                 while ((len = inputStream.read(buf)) != -1){
22                     outputStream.write(buf, 0, len);
23                 }
24                 return defineClass(className, outputStream.toByteArray(), 0, outputStream.size());
25             }catch (Exception e){
26                 e.printStackTrace();
27             }finally {
28                 if(null!=inputStream){
29                     try {
30                         inputStream.close();
31                     } catch (IOException e) {
32                         // TODO Auto-generated catch block
33                         e.printStackTrace();
34                     }
35                 }
36                 if(null!=outputStream){
37                     try {
38                         outputStream.close();
39                     } catch (IOException e) {
40                         // TODO Auto-generated catch block
41                         e.printStackTrace();
42                     }
43                 }
44             }
45         }
46         return null;
47     }
48 }

4.自定义Proxy代理类

  主要流程:1.将源码输入到Java文件

       2.将Java文件编译成class文件

       3.将class加载进jvm

       4.返回代理类对象

public class MyProxy {
    public static final String ln = "\\r\\n";

    public static Object newProxyInstance(MyClassLoader classLoader,
                                          Class<?>[] interfaces, MyInvocationHandler handler) {
        try {
            //1.java源码
            String src = generateSrc(interfaces);
            //2.将源码输出到Java文件中
            String filePath = MyProxy.class.getResource("").getPath();
            System.out.println(filePath);
            File file = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(file);
            fw.write(src);
            fw.flush();
            fw.close();
            //3.将Java文件编译成class文件
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = javaCompiler.getTask(null, manager, null, null,null, iterable);
            task.call();
            manager.close();
            //4.将class加载进jvm
            Class proxyClass = classLoader.findClass("$Proxy0");
            file.delete();
            //5.返回代理类对象
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            return constructor.newInstance(handler);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    //Java源码
    private static String generateSrc(Class<?>[] interfaces) {
        // TODO Auto-generated method stub
        StringBuffer sb = new StringBuffer();
        sb.append("package xin.hangzhi.jdk.proxy.demo.custom;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("private MyInvocationHandler h;"+ln);
        sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
        sb.append("this.h = h;"+ln);
        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("}"+ln);
            sb.append("}"+ln);
        }
        sb.append("}" + ln);
        return sb.toString();
    }
}

5.测试结果

 

以上是关于自定义实现Java动态代理的主要内容,如果未能解决你的问题,请参考以下文章

自定义SPI使用JDK动态代理遇到UndeclaredThrowableException异常排查

JAVA之AOP

AOP和动态代理-自定义注解切入使用-01

AOP和动态代理-自定义注解切入使用-01

AOP和动态代理-自定义注解切入使用-01

十分钟理解Java中的动态代理