跟王老师学反射(十一):动态代理

跟王老师学反射(十一):动态代理

主讲教师:王少华   QQ群号:483773664

学习内容

学会使用动态代理

一、动态代理

动态代理(Dynamic Proxy):相比上一节所实现的静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现。

动态代理(Dynamic proxies)是 Java 1.3 引入的特性,在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和接口可以生成JDK动态代理对象

二、Proxy和InvocationHandler

(一)Proxy

1 简介

Proxy提供用于创建动态代理和代理对象的静态方法,它也是所有动态代理的父类。如果我们在程序中为一个或多个接口动态生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实现,也可以使用Proxy来创建动态代理实现。

2 Proxy提供以下二个方法用于创建动态代理类和动态代理实例

public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces):创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader指定生成动态代理类的类加载器。

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行invocationHandler对象的invoke方法

(二)InvocationHandler

InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)

InvocationHandler接口中有一个方法


1

Object invoke(Object proxy,Method method, Object[] args)

参数:

proxy - 被代理的对象

method - 要调用的方法

args - 方法调用时所需要的参数,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

返回:

从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。

三、动态代理的实现

(一)动态代理的实现步骤

1. 实现InvocationHandler接口创建自己的调用处理器


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public class DynmicProxy implements InvocationHandler{

    private Object targetObject;

    public DynmicProxy(){}

    public  DynmicProxy(Object targetObject) {

        this.targetObject = targetObject;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args)

            throws Throwable {

        preHandle();

        Object ret = method.invoke(targetObject, args);

        postHandle();

        return ret;

    }

    /**

     * 代理类特有的方法

     */

    private void preHandle(){

        System.out.println("preHandle");

    }

    /**

     * 代理类特有的方法

     */

    private void postHandle(){

        System.out.println("postHandle");

    }

}

2. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类


1

2

3

4

5

6

7

// 目标对象

        UserService userService = new UserServiceImpl();

        // 创建代理类

        UserService proxyUserService = (UserService) Proxy.newProxyInstance(

                userService.getClass().getClassLoader(),

                userService.getClass().getInterfaces(),

                new DynmicProxy(userService));

3、通过代理对象调用目标对象方法


1

2

//通过代理对象调用目标对象的方法

        proxyUserService.save();

(二)代码优化


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

public class LogHandler implements InvocationHandler{

    //目标对象

    private Object targetObject;

    /**

     * 生成代理对象:就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。

     * @param targetObject

     * @return 返回代理对象

     */

    public Object newProxyInstance(Object targetObject) {

        this.targetObject = targetObject;

        /**

         * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例   

            第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 

            第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口 

            第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法

         */

        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),

                    targetObject.getClass().getInterfaces(), this);

    }

    /**

     * proxy:要代理的对象

     * method:要执行的方法

     * args:方法参数

     */

    @Override

    public Object invoke(Object proxy, Method method, Object[] args)

            throws Throwable {

        //原对象方法调用前调用日志处理

        preHandle();

        //调用目标方法:ret为目标方法返回值

        Object ret = method.invoke(targetObject, args);

        //原对象方法调用 后调用 日志处理方法

        postHandle();

        return ret;

    }

    

    /**

     * 代理类特有的方法

     */

    private void preHandle(){

        System.out.println("preHandle");

    }

    /**

     * 代理类特有的方法

     */

    private void postHandle(){

        System.out.println("postHandle");

    }

}


1

2

3

4

5

6

7

public class DynmicTest {

    public static void main(String[] args) {

        LogHandler logHandler = new LogHandler();

        UserService userService = (UserService) logHandler.newProxyInstance(new UserServiceImpl());

        userService.save();

    }

}

来自为知笔记(Wiz)

时间: 2024-10-26 08:15:35

跟王老师学反射(十一):动态代理的相关文章

跟王老师学反射(十):静态代理模式

跟王老师学反射(十):静态代理模式 主讲教师:王少华   QQ群号:483773664 学习内容 理解代理机制 掌握静态代理 一.代理模式的概念 代理模式就是指由一个代理主题来操作真实主题,真实主题执行具体的业务操作,而代理主题负责其他相关业务的处理. 代理模式一般涉及到三个角色 抽象角色:声明真实对象和代理对象的共同接口 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能替代真实对象相同的接口以便在任何时刻都能替代真实对

跟王老师学反射(二):Java类的加载、连接和初始化

跟王老师学反射(二):Java类的加载.连接和初始化 主讲教师:王少华   QQ群号:483773664 学习内容: 了解类的加载.连接和初始化 一.类的生命周期 当我们编写一个java的源文件后,经过编译会生成一个后缀名为class的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在java虚拟机中运行,java类的生命周期就是指一个class文件从加载到卸载的全过程.一个java类的完整的生命周期会经历加载.连接.初始化.使用.和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直

跟王老师学反射(八):使用反射操作数组

跟王老师学反射(八):使用反射操作数组 主讲教师:王少华   QQ群号:483773664 学习内容 使用反射操作数组 在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组.程序可以通过使用Array来动态创建数组. 一.传统的创建数组的方法 1 2 3 4 5 6 7 8 public   static   void  main(String[] args)  {        int vec[] = new int[]{1, 5, 3};  //

跟王老师学反射(一):反射概述

跟王老师学反射(一) java反射概述 主讲教师:王少华   QQ群号:483773664 学习目标: 理解Java反射机制 一.什么是Java反射 现实生活,我们会看到这样的现象,在很多影视城,会看到很多山寨版的古代的建筑,比如故宫,但是由于年代久远,故宫的建筑图纸,丢失了(我们假设故宫的图纸丢失了),这样我们就必须依据现在的故宫实体建筑,画出它的图纸,然后依据现画好来的图纸,来建造山寨版的故宫.这样一个由实物得到图纸的过程,我们也可以称之为"反射过程" Reflection(反射)

跟王老师学反射(三):Class类:获得Class对象

跟王老师学反射(三):Class类:获得Class对象 主讲教师:王少华   QQ群号:483773664 学习内容 掌握获得Class对象的三种方式 理解这三种方式的区别 一.获得Class对象 前面我们已经领略了反射的魅力了,我们知道,利用反射的关键是要获得"图纸"Class对象,那么怎么获得这个Class对象呢!!! 每个类被加载之后,系统会为该类生成一个对应的Class对象,通过该Class对象就可以访问JVM中的这个类.Java程序获得Class对象有如下三种方式: (一)调

跟王老师学反射(四):Class类:从Class类中获取信息

跟王老师学反射(四)Class类:从Class类中获取信息 主讲教师:王少华   QQ群号:483773664 学习内容 获得class类中的信息 根据我们以前学过的一个Java类有以下几部组成,如下代码所示 一.访问Class对应的类所包含的构造方法 (一)public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回此Class对象所表示的类的指定public构造方法. parameterTypes参数是按

跟王老师学反射(五):使用反射生成并操作对象

跟王老师学反射(五):使用反射生成对象 主讲教师:王少华   QQ群号:483773664 学习内容 使用反射生成对象 模板类 一.创建对象 (一).非反射方法 (二).反射方式 通过构造方法生成对象,有二种方式,一种通过无参的构造方法,一种是通过有参的构造方法. 1.无参构造方法 1.1 如果无参数的构造方法是public 也可以使用Class对象的newInstance()方法来创建该Class对象对应类的实例 1.2 如果无参构造方法是私有化的 结论 如果无参构造访问,是私有化的,一定要使

跟王老师学反射(六):使用反射调用方法

跟王老师学反射(六):使用反射调用方法 主讲教师:王少华   QQ群号:483773664 学习内容 使用反射调用方法 当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或getMethod()方法来获取全部方法或指定方法,这二个方法的返回值是Method对象数组,或者Method对象. 一.public Objectinvoke(Object obj,  Object... args) 获得Method对象后,程序就可以通过该Method来调用对应的方

跟王老师学反射(七)使用反射调用属性

跟王老师学反射(七):使用反射调用属性 主讲教师:王少华   QQ群号:483773664 学习内容 使用反射调用属性 通过Class对象的getFields()|getDeclaredFields()或getField()|getDeclaredField()方法可以获取该类所有包括的全部属性或指定的属性 一.Field类用于猎取类中的属性的方法 (一)getXxx(Object obj) 获取obj对象该Field的属性值,此处Xxx对应8个基本类型. (二)setXxx(Object ob