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

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

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

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

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. C# 6.0 中的新增功能(.NET Framework 4.6 与 Visual Studio 2015 )

    C#6.0 在 2015 年7月随着.NET Framework 4.6 一同发布,后期发布了.NET Framework 4.6.1,4.6.2. 一.自动属性初始化(Auto-property i ...

  2. Burp Suite的代理Brup Proxy的使用详解

    Burp Proxy 是Burp Suite以用户驱动测试流程功能的核心,通过代理模式,可以让我们拦截.查看.修改所有在客户端和服务端之间传输的数据.  

  3. matlab的poly()函数

    MATLAB中的poly()函数是用于求以向量为解的方程或方阵的特征多项式,可以直接传递多项式方程的系数矩阵. 1.poly([1 2 3])使用的举例. P=poly([1 2 3]) 可以解出P= ...

  4. Linux本地内核提权CVE-2019-13272

    简介:当调用PTRACE_TRACEME时,ptrace_link函数将获得对父进程凭据的RCU引用,然后将该指针指向get_cred函数.但是,对象struct cred的生存周期规则不允许无条件地 ...

  5. 自定义菜单和高级接口-获取Access Token

    自定义菜单和高级接口都需要使用APPID和AppSecret来创建. 对应暂时没有这些权限的微信公众账号,开发者可以申请测试账号来体验和测试体验微信公众平台的所有高级接口的功能.链接 https:// ...

  6. Oracle row_number() over() 分析函数--取出最新数据

    语法格式:row_number() over(partition by 分组列 order by 排序列 desc) 一个很简单的例子 1,先做好准备 create table test1( id v ...

  7. leetcode解题报告(32):Teemo Attacking

    描述 In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poison ...

  8. 《挑战30天C++入门极限》新手入门:C++下的引用类型

        新手入门:C++下的引用类型 引用类型也称别名,它是个很有趣的东西.在c++ 下你可以把它看作是另外的一种指针,通过引用类型我们同样也可以间接的操作对象,引用类型主要是用在函数的形式参数上,通 ...

  9. Java枚举类接口实战

    枚举类可以实现一个或多个接口.与普通类实现接口完全一样,枚举类实现接口时,需要实现该接口所包含的方法. 如果需要每个枚举值在调用同一个方法时呈现不同的行为,则可以让每个枚举值在{...}匿名块中实现自 ...

  10. Sphinx全文索引引擎

    一.什么是sphinx 原理:sphinx将数据库中的表建立索引,php操作sphinx时,将要查询的关键字进行匹配,返回一个id,php通过id到数据库中查询数据. 二.下载 链接:https:// ...