SpringAOP神秘面纱之动态代理

1、写代码的原则
开闭原则
开闭原则就是说对扩展开放,对修改关闭。
2、静态代理
以找对象为例子讲解静态代理
张三写代码、玩游戏没空找对象
张三他爹着急,年龄大了,想抱孙子了。张三他爹拿着张三的照片(持有张三的引用)给张三满世界找对象
张三他爹就是张三的一个代理
1、关键是持有被代理类的引用
2、一开始就被加载到内存了,不是运行时动态生成的

代码

package design.proxy.staticproxy;

public interface People {
    void zhaoduixiang();
}
package design.proxy.staticproxy;

public class ZhangSan implements People {

    @Override
    public void zhaoduixiang() {
        System.out.println("我只要漂亮的");
    }

}
package design.proxy.staticproxy;

public class HisDaddy implements People {

    // 父亲持有儿子的引用
    private ZhangSan zhangSan;

    public HisDaddy(ZhangSan zhangSan) {
        this.zhangSan = zhangSan;
    }

    @Override
    public void zhaoduixiang() {
        before();

        zhangSan.zhaoduixiang();

        after();
    }

    public void before() {
        System.out.println("老子是张三的老子, 先要过老子这关, 要有房子, 车子, 票子...");
    }

    public void after() {
        System.out.println("这个姑娘是一个好姑娘!");
    }

}
package design.proxy.staticproxy;

public class MainClass {

    public static void main(String[] args) {
        People daddy = new HisDaddy(new ZhangSan());
        daddy.zhaoduixiang();
        People zhangSan = new ZhangSan();
        zhangSan.zhaoduixiang();
    }

}

3、动态代理

写一个jdk的动态代理

 1. 动态代理的动态体现在代理类是动态生成的
 2. 动态代理类的声明才是我们写业务代码的地方, 实现了InvocationHandler接口的类叫做声明 
 3. **jdk内存中的动态代理类是没有对被代理类进行任何操作的**

问题
- 为什么会调到invovationHandler的invoke方法,以及为什么会传那几个参数
- 去工程里面找代理类找不到, 其实代理类在内存中
- 用jdk的api去生成一个代理类,用反编译工具看代理类

public final class $Proxy0 extends java.lang.reflect.Proxy implements design.proxy.dynamicproxy.People {...}

代理类继承了Proxy 并实现了People 接口

public final void eat() throws Throwable{
    //h 为父类Proxy中的 protected InvocationHandler h
    //m4 为 Method对象 在代理类的静态代码块中 通过Class.forName("接口名").getMethod('eat'/*方法名*/,new Class[0])取得 null 该方法为参数
    //可以看出 是由代理类调用了我们声明中的invoke方法
    this.h.ivoke(this,m4,null);
}

代码

package design.proxy.dynamicproxy;

public interface People {
    void eat();

    void sleep();

    void sport();
}

package design.proxy.dynamicproxy;

public class ZhangSan implements People {

    @Override
    public void eat() {
        System.out.println("张三吃饭的时候喜欢看美女");
    }

    @Override
    public void sleep() {
        System.out.println("张三睡觉了");
    }

    @Override
    public void sport() {
        System.out.println("张三运动");
    }

}

package design.proxy.dynamicproxy;

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

public class ProxyHandler implements InvocationHandler {

    private People people;

    public ProxyHandler(People people) {
        this.people = people;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();

        method.invoke(people, args);

        after();
        return null;
    }

    private void before() {
        System.out.println("吃饭之前要洗手");
    }

    private void after() {
        System.out.println("吃饭之后要洗碗");
    }
}

package design.proxy.dynamicproxy;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

@SuppressWarnings("restriction")
public class MainClass {
    public static void main(String[] args) throws Exception {

        People proxy = (People) Proxy.newProxyInstance(People.class.getClassLoader(), //
                ZhangSan.class.getInterfaces(), //
                new ProxyHandler(new ZhangSan()));
        proxy.eat();// 方法得到了增强 调用了InvocationHandler的invoke方法
        System.out.println(proxy.getClass().getName());// com.sun.proxy.$Proxy0

        createProxyClassFile();
        // proxy.sleep();

    }

    // 还原我们的代理类
    public static void createProxyClassFile() throws Exception {
        byte[] generateProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", new Class[] { People.class });
        // 将字节数组输出到$Proxy0.class文件中
        OutputStream outputStream = new FileOutputStream("$Proxy0.class");
        outputStream.write(generateProxyClass);
        outputStream.close();
    }
}

  1. 不用jdk的动态代理,自己写一个动态代理
    1、Proxy.newProxyInstance里面到底干了些什么呢?
    2、自己定义代理类,MyProxy,自己在运行时用流的方式生成一个代理类的java文件,用jdk的编译器编译java文件生成class文件,把class文件加载到内存
    3、new 一个代理类的实例并返回

    代码

package design.proxy.dynamicproxy.my;

import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

//自定义Proxy
public class MyProxy {

    // 定义回车键
    private static String rt = "\r\n";

    public static Object createProxyInstance(ClassLoader classLoader, //
            Class clazz, //
            MyInvocationHandler handler) {
        // 首先拿到 被代理类实现的接口中的 方法数组(动态代理类要实现这些方法)
        Method[] methods = clazz.getMethods();

        // 用流的方式创建一个Java文件
        String proxyClass = "package design.proxy.dynamicproxy.my;" + rt//
                + "import java.lang.reflect.Method;" + rt//
                + "public class $Proxy0 implements " + clazz.getName() + "{" + rt//
                + "MyInvocationHandler h;" + rt//
                + "public $Proxy0(MyInvocationHandler h){ this.h=h; }" + rt//
                + getMethodString(methods, clazz) + "}";
        // 把类生成文件
        String fileName = "D:/Users/workspace-sts-3.7.1.RELEASE/demo/src/main/java/design/proxy/dynamicproxy/my/$Proxy0.java";
        try {
            FileWriter fileWriter = new FileWriter(fileName);
            fileWriter.write(proxyClass);
            fileWriter.flush();
            fileWriter.close();

            // 编译java文件 存在于磁盘中
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();// 获取系统编译器
            StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);// 标准Java文件管理器
            Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(fileName);
            CompilationTask task = compiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
            task.call();
            standardFileManager.close();

            // 加载到内存中
            MyClassLoader myClassLoader = new MyClassLoader("D:/Users/workspace-sts-3.7.1.RELEASE/demo/src/main/java/design/proxy/dynamicproxy/my/");
            Class<?> proxy0Class = myClassLoader.findClass("$Proxy0");
            Constructor<?> constructor = proxy0Class.getConstructor(MyInvocationHandler.class);
            Object newInstance = constructor.newInstance(handler);

            return newInstance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getMethodString(Method[] methods, //
            Class clazz) {
        String proxyMethod = "";
        for (int i = 0; i < methods.length; i++) {
            proxyMethod += "public void " + methods[i].getName() + "() throws Throwable {" + rt//
                    + "Method md = " + clazz.getName() + ".class.getMethod(\"" + methods[i].getName() + "\",new Class[]{});" + rt//
                    + "this.h.invoke(this,md,null);" + rt//
                    + "}" + rt;
        }
        return proxyMethod;

    }
}
package design.proxy.dynamicproxy.my;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

//将流文件加载到内存中
public class MyClassLoader extends ClassLoader {

    private File dir;

    public MyClassLoader(String path) {
        dir = new File(path);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (dir != null) {
            File clazzFile = new File(dir, name + ".class");
            if (clazzFile.exists()) {
                FileInputStream input = null;
                try {
                    input = new FileInputStream(clazzFile);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = input.read(buffer)) != -1) {
                        byteArrayOutputStream.write(buffer, 0, len);
                    }

                    return defineClass("design.proxy.dynamicproxy.my." + name, byteArrayOutputStream.toByteArray(), 0, byteArrayOutputStream.size());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (input != null) {
                            input.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return super.findClass(name);
    }
}
package design.proxy.dynamicproxy.my;

import java.lang.reflect.Method;

public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object args) throws Throwable;
}
package design.proxy.dynamicproxy.my;

import java.lang.reflect.Method;

import design.proxy.dynamicproxy.jdk.People;

//自定义声明类
public class MyProxyHandler implements MyInvocationHandler {

    private People people;

    public MyProxyHandler(People people) {
        this.people = people;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object args) throws Throwable {
        before();

        method.invoke(people, args);

        after();
        return null;
    }

    private void before() {
        System.out.println("吃饭之前要洗手");
    }

    private void after() {
        System.out.println("吃饭之后要洗碗");
    }
}
package design.proxy.dynamicproxy.jdk;

import design.proxy.dynamicproxy.my.MyProxy;
import design.proxy.dynamicproxy.my.MyProxyHandler;

public class MainClass {
    public static void main(String[] args) throws Throwable {

        People proxy = (People) MyProxy.createProxyInstance(People.class.getClassLoader(), //
                People.class, //
                new MyProxyHandler(new ZhangSan()));
        proxy.eat();
    }

}
  1. 思维发散
    1、Mybatis中只有接口定义没有实现类
    2、Spring的aop的事务管理
    3、持久带和heap space溢出,持久带溢出原因,动态加载了大量的java类而导致溢出
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值