客户端不用调用目标对象了,直接调用代理类。最终目标方法还是去实行了。

代理类的每个方法调用目标类的相同方法,并且在调用方法时候加上系统功能的代码

代理和目标实现了相同的接口,有相同的方法。通过接口进行引用

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方法,非常麻烦。

JVM可以在运行期间动态生成类的字节码,这样动态生成的类往往被用作代理类,即动态代理类。

JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理

CGLIB库,可以动态生成一个类的子类(是一个个开源的库,不是sun公司的),一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库

代理类的各个方法通常除了要调用目标的响应方法和对外放回目标返回的结果外

通过Java 的API

getProxyClass 方法  返回字节码   需要告诉这个字节码实现了哪些接口  另外需要ClassLoader (每个Class都是由类加载器加载来的,就好像每个孩子都有妈妈一样) ,但是由内存出来的是没有经过类加载器哦!所以需要指定一个~!

找个接口测试一下:

public class ProxyTest {
    public static void main(String[] args) {
     Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        String name = clazzProxy.getName();//既然是个字节码就可以igetName()
        System.out.println(name);
    }
}

反正获取到了字节码,就玩字节码的那一套API就OK了,随便用

获取到构造方法:

public class ProxyTest {
    public static void main(String[] args) {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        String name = clazzProxy.getName();//既然是个字节码就可以igetName()
        System.out.println(name);

        System.out.println("开始打印构造函数相关");
        Constructor[] constructors = clazzProxy.getConstructors();
        for (Constructor c : constructors){
            String constructorName = c.getName();
            StringBuilder stringBuffer = new StringBuilder(name); //初始值
            stringBuffer.append('(');
            Class[] parameterTypes = c.getParameterTypes();
            for (Class clazzParam : parameterTypes){
                stringBuffer.append(clazzParam.getName()).append(',');
            }
            if (parameterTypes != null && parameterTypes.length == 0){
                stringBuffer.deleteCharAt(stringBuffer.length()-1); //删除掉最后那个字符
            }
            stringBuffer.append(')');
            System.out.println(stringBuffer);
        }

    }
}

打印结果:

代理类:只有一构造方法 参数类型也一目了然,只有一个有参的构造方法。 参数是个新的类型哈,后面会说到他

代理类方法的获取:

public class ProxyTest {
    public static void main(String[] args) {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        System.out.println("打印所有方法:");;
        Method[] methods = clazzProxy.getMethods();
        for (Method m : methods){
            String methodName = m.getName();
            System.out.println("方法名字"+methodName);
            Class<?>[] parameterTypes = m.getParameterTypes();
            for (Class clazzParam : parameterTypes){
                System.out.println("方法类型"+clazzParam.getName());
            }
        }
    }
}

截了一部分图:

下面我们创建动态代理类的实例对象,并且使用它的方法:

ublic interface InvocationHandler {

    /**
     * Processes a method invocation on a proxy instance and returns

是个接口,没法直接new对象,只能匿名对象。自己实现之

public class ProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的!
        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数
        //内部类
        class MyInvocationHandler implements InvocationHandler{
            @Override    //实现它的方法  里面只有一个方法哦
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        }
        //实现的是Collection的接口哦 是Collection类的代理对象
        Collection proxy = (Collection)constructor.newInstance(new MyInvocationHandler());//此时传入真实的值  此时我们要看下InvocationHandler 到底是咋回事
        System.out.println(proxy);
        //proxy代理类是拥有Collection类中的所有方法哦

    }
}

创建了动态类,动态类去实例化对象,动态类的构造方法需要传入一个 InvocationHandler类的参数!

可以通过匿名内部类的方式:

public class ProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的!
        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数

        //实现的是Collection的接口哦 是Collection类的代理对象
        Collection proxy = (Collection)constructor.newInstance(new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });//此时传入真实的值  此时我们要看下InvocationHandler 到底是咋回事
        System.out.println(proxy);
        //proxy代理类是拥有Collection类中的所有方法哦

    }
}

总结:

创建实现了Collection接口的动态类并查看其名称,分析Proxy.getProxyClass方法的各个参数

编写列出动态类汇总的构所有构造方法和参数签名

编写列出动态类中的所有方法和参数签名

创建动态类的实例对象

用反射获得构造方法

编写一个最简单的InvocationHandler类

调用构造方法创建动态类的实例对象,并将别写的InvocationHandler类的实例对象传进去

答应创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常

将创建动态类的实例对象的代理改成匿名内部类的像是编写。

让jvm创建动态类及其实例对象,需要提供信息:

三个方面:

生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知

产生的类字节码必须有一个关联的类加载器对象

生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象转给它,它调用我的方法。相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时候传递进去的。在上面的InvocationHandler对象的invke方法中加一点代码,又可以看到这些代码被调用执行了

可以把创建动态代理类和实例对象,一步到位: 用Proxy里面的一个方法就OK

用Proxy.newInstance 方法直接一步就创建出代理对象  传入的参数:        类加载器, 接口数组 , handler

public class ProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的!
        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数
      //Collection接口的类加载器
       Collection proxy = (Collection) Proxy.newProxyInstance(
               Collection.class.getClassLoader(), //这个接口的类加载器
               new Class[]{Collection.class},  //数组类型的字节码
               new InvocationHandler() {    //InvocationHandler 匿名内部类
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       ArrayList target = new ArrayList();
                       System.out.println("返回之前加点代码");
                       Object returnValue  = method.invoke(target, args);
                       System.out.println("返回之后加点代码");
                       return returnValue;
                   }
               }
       );
       // 每次调用proxy 的方法, Handler的invoke方法就会被执行一次  都是处理全新的目标 target!
        proxy.add("toov5");
        proxy.add("代理类的方法");
        System.out.println(proxy.size());
    }
}

将target挪出来!~

public class ProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的!
        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数
      //Collection接口的类加载器
       Collection proxy = (Collection) Proxy.newProxyInstance(
               Collection.class.getClassLoader(), //这个接口的类加载器
               new Class[]{Collection.class},  //数组类型的字节码
               new InvocationHandler() {    //InvocationHandler 匿名内部类
                   ArrayList target = new ArrayList();
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       System.out.println("返回之前加点代码");
                       Object returnValue  = method.invoke(target, args);
                       System.out.println("返回之后加点代码");
                       return returnValue;
                   }
               }
       );
       // 每次调用proxy 的方法, Handler的invoke方法就会被执行一次  都是处理全新的目标 target!
        proxy.add("toov5");
        proxy.add("代理类的方法");
        System.out.println(proxy.size());
    }
}

分析:

InvocationHandler对象的运行原理:


动态申城的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口汇总的所有方法和一个如下接受InvocationHandler参数的构造方法

构造方法接受一个Invocationhandler对象,接受对象了要干什么用呢? 该方法内部的代码会怎样呢?

实现Collection接口中的各个方法的代码又是怎样的呢? InvocationHandler接口中定义的invoke方法接接受的三个参数又是什么意思?

Client程序嗲用objProxy.add("toov5") 方法时,涉及到三个要素: objProxy对象,add方法, “toov5”参数

Class Proxy${

add(Object objcet){

return hanler.invoke(Object proxy, Method, Objcet[] args);

}

}

图示:

分析先打印动态代理的实例对象,结果为什么会是null呢? 调用有基本类型返回值的方法时为什么会空指针异常?

分析为什么动态代理的实例对象的getClass() 方法返回了正确的结果呢?

分析动态代理的工作原理:

怎样将目标类传进去?

直接在InvocationHandler实现类中创建目标类的实例对象。可以看运行效果和加入实质代码,但是没有实际意义

为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了

让匿名的InvocationHandler实现类访外面方法中的目标实现类实例对象final类型的引用变量

功能接口:

public interface Advice {
   void beforeMethod(Method method);
   void afterMethod(Method method);
}

功能接口实现类:

public class MyAdvice implements Advice{
    long beginTime = 0;
    @Override
    public void beforeMethod(Method method) {
        long beginTime = System.currentTimeMillis();
        System.out.println(method.getName()+"running start time"+beginTime);

    }

    @Override
    public void afterMethod(Method method) {
      long  endTime = System.currentTimeMillis();
        System.out.println(method.getName()+"running end time"+endTime);
     }

代理及其实现测试:

public class ProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器
        //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的!
        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数
      //Collection接口的类加载器

       final ArrayList target = new ArrayList();
        Collection proxy = (Collection)getProxy(target, new MyAdvice());
        // 每次调用proxy 的方法, Handler的invoke方法就会被执行一次  都是处理全新的目标 target!
        proxy.add("toov5"); //把代理穿进去 方法传进去 参数传递进去
    }

    private static Object getProxy( final Object target, final Advice advice){  //目标对象 功能对象
        Object proxy  =  Proxy.newProxyInstance(
                   target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                   new InvocationHandler() {    //InvocationHandler 匿名内部类
                       @Override
                       public Object invoke(Object proxy1, Method method, Object[] args) throws Throwable {
                          long beginTime = System.currentTimeMillis();
                           advice.beforeMethod(method);   //系统功能抽取成为了一个对象  通过接口形式
                           Object returnValue  = method.invoke(target, args);
                           advice.afterMethod(method);   //系统功能抽取成为了一个对象   通过接口形式
                           return returnValue;  //这个返回给代理
                       }
                   }
           );
        return proxy;
     }
}

代理类和AOP的更多相关文章

  1. Spring AOP(基于代理类的AOP实现)

    #基于代理类的AOP实现:step1: 1 package com.sjl.factorybean; /**切面类*/ import org.aopalliance.intercept.MethodI ...

  2. Spring框架学习07——基于传统代理类的AOP实现

    在Spring中默认使用JDK动态代理实现AOP编程,使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring AOP 实现的最基 ...

  3. java代理类及AOP

    1.代理架构图 2.AOP 3.动态代理概念 4.动态代理工作原理图

  4. Spring框架学习08——自动代理方式实现AOP

    在传统的基于代理类的AOP实现中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大.解决方案:自动创 ...

  5. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

  6. Spring《八》AOP/代理类定义

    Spring通知 Interception Around通知 MethodInterceptor类(方法执行前后通知) Before通知 MethodBeforeAdvice类(方法执行前通知) Af ...

  7. Spring AOP 代理类,BeanNameAutoProxyCreator cglib

    BeanNameAutoProxyCreator支持拦截接口和类,但不支持已经被jdk代理过的类$Proxy8.使用cglib才能代理,如下 <!-- 通过bean的名字来匹配选择要代理的bea ...

  8. AOP 代理类的创建

    AOP 代理类的创建 入口:AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 和 AnnotationAwar ...

  9. Spring Aop中,获取被代理类的工具

    在实际应用中,顺着过去就是一个类被代理.反过来,可能需要逆向进行,拿到被代理的类,实际工作中碰到了,就拿出来分享下. /** * 获取被代理类的Object * @author Monkey */ p ...

随机推荐

  1. Title:eNSP 映射到外网

    Title:eNSP 映射到外网 一.系统版本 OS 版本: 10.0.17763 暂缺 Build 17763 eNSP V1.3.00.100 Oracle VM VirtualBox 5.2.2 ...

  2. Laravel —— 自定义登录

    Laravel 中自带了 Auth 模块 默认用 email 登录,并有固定的表字段 有时需要根据项目需求,修改 Auth 功能 1.生成 Auth 执行 php artisan make:auth ...

  3. goto语句——慎用,但是可以用

    最近使用了goto语句,是因为if嵌套太深了,因此把错误处理同意了,直接使用goto语句. 举例: #include <stdio.h> int main () { /* local va ...

  4. CSS3 新增选择器:伪类选择器和属性选择器

    一.结构(位置)伪类选择器( : ) 1.:first-child 2.:last-child 3.:nth-child(n)或者:nth-child(2n)或者:nth-child(2n+1) &l ...

  5. mysql 函数表

    Name Description ABS() Return the absolute value ACOS() Return the arc cosine ADDDATE() Add time val ...

  6. Problem 7 树状数组+转化

    $des$有一棵 $n$ 个点的以 $1$ 为根的树, 以及 $n$ 个整数变量 $x_i$ .树上 $i$ 的父亲是 $f_i$ ,每条边 $(i,f_i)$ 有一 个权值 $w_i$ ,表示一个方 ...

  7. NetworkX系列教程(11)-graph和其他数据格式转换

    小书匠 Graph 图论  学过线性代数的都了解矩阵,在矩阵上的文章可做的很多,什么特征矩阵,单位矩阵等.grpah存储可以使用矩阵,比如graph的邻接矩阵,权重矩阵等,这节主要是在等到graph后 ...

  8. LOJ6071. 「2017 山东一轮集训 Day5」字符串 [SAM]

    LOJ 思路 这种计数题显然是要先把每一个合法的串用唯一的方法表示出来.(我连这都没想到真是无可救药了) 如何唯一?容易想到把前缀尽可能多地在第一个串填掉,然后填第二个,第三个-- 如何做到这样?可以 ...

  9. 《挑战30天C++入门极限》C/C++中结构体(struct)知识点强化

        C/C++中结构体(struct)知识点强化 在上一个教程中我们已经简单的阐述了什么是结构体了,为了进一部的学习结构体这一重要的知识点,我们今天来学习一下链表结构. 结构体可以看做是一种自定义 ...

  10. 《挑战30天C++入门极限》新手入门:C/C++中枚举类型(enum)

        新手入门:C/C++中枚举类型(enum) 如果一个变量你需要几种可能存在的值,那么就可以被定义成为枚举类型.之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来. ...