Java动态代理

一、代理模式的作用

  • 功能增强: 基于某个功能,再增加一些功能。
    (比如目标类只负责核心功能,其他附属功能通过代理类完成。代理类的方法名与目标类的方法相同,内容不同,在核心功能外加了一些额外逻辑)
  • 控制访问: 防止直接访问目标。

在这里插入图片描述

代理类代理目标类(代理)
目标类是实现目标功能的类(杨超越)

二、实现代理的方式

2.1 动态代理:

特点:

在程序执行过程中,自动使用jdk反射机制创建代理对象(而不需要写.java源文件),并且动态指定要代理的目标类。

优点:

缺点

2.2 静态代理:

特点:

代理类是自己手动实现的。自己创建一个类作为代理类要代理的目标类是确定的。

优点:

实现简单,容易理解;
在不改变原目标对象代码的情况下,对目标的功能进行拓展

缺点:

当目标类增加了,代理类可能要成倍的增加,代理类过多;
当接口的功能增加或修改,会影响所有实现类,修改工作量大,不好管理维护 。

三、动态代理的实现

jdk动态代理

基于接口实现代理

使用java反射包中的接口和类实现动态代理,要求代理类和工具类实现同一个接口。
其中反射包是java.lang,reflect,里面有三个类:InvocationHandler、Method、Proxy

3.1 创建目标接口

public interface Star {
    String sing(String name);
    void dance();
}

3.2 创建目标类实现目标接口

public class BigStar implements Star {
    private String name;

    public BigStar(String name) {
        this.name = name;
    }

    public String sing(String name) {
        System.out.println(this.name + "正在唱歌儿:" + name);
        return "谢谢儿!谢谢儿!";
    }

    public void dance() {
        System.out.println(this.name + "正在跳舞儿");
    }
}

3.3 创建代理工具

public class ProxyUtil {
    public static Star createProxy(BigStar bigStar) {
        /*newProxyInstance(ClassLoader loader,
                Class<?>[] interfaces,
                InvocationHandler h)
                参数1:用于指定一个类加载器
                参数2:指定生成的代理长什么样子,也就是有哪些方法
                参数3:用来指定生成的代理对象要干什么事情
                */
        // Star starProxy = ProxyUtil.createProxy(s);
        // starProxy.sing("好日子") starProxy.dance()
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override //回调方法:当主程序执行sing方法时会调用invoke,并将参数传过来
                    // proxy就是代理对象starProxy,method指sing,args指"好日子"
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //代理对象要做的事情,会在这里写代码
                        if (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱20万");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱1000万");
                        }
                        return method.invoke(bigStar, args);
                    }
                });
        return starProxy;
    }
}

3.4 测试调用

public class Test {
    public static void main(String[] args) {
        BigStar s = new BigStar("杨超越");
        Star starProxy = ProxyUtil.createProxy(s);
        String rs = starProxy.sing("好日子");
        System.out.println(rs);
        starProxy.dance();
    }
}

3.5 运行结果:

在这里插入图片描述

3.6 执行逻辑:

在这里插入图片描述
在这里插入图片描述

当主程序调用sing方法时,会调用invoke方法,因为代理干什么事情由代理决定。此时invoke方法里的proxy代表starProxy,method代表sing方法,args代表sing方法中的参数。因为是sing方法符合if里面的条件输出内容,然后执行return method.invoke(bigStar,args)。因为传进来是BigStar对象所以执行BigStar对象中的sing方法,输出内容返回返回值,主程序输出返回的结果。
执行dance方法同理。

四、案例-使用代理优化用户管理类

场景

  • 某系统有一一个用户管理类,包含用户登录,删除用户,查询用户等功能,系统要求统
    计每个功能的执行耗时情况,以便后期观察程序性能。

需求

  • 现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的
    问题,并对其进行改造。
/**
 * 用户业务接口
 */
public interface UserService {
    //登录功能
    void login(String loginName, String passWord) throws InterruptedException;

    //删除用户
    void deleteUsers() throws InterruptedException;

    //查询用户,返回数组的形式
    String[] selectUsers() throws InterruptedException;
}


/**
 * 用户业务实现类(面向接口编程)
 */
public class UserServiceImpl implements UserService {
    @Override
    public void login(String loginName, String passWord) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        if ("admin".equals(loginName) && "123456".equals(passWord)) {
            System.out.println("您登录成功,欢迎光临本系统~");
        } else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
        long endTime = System.currentTimeMillis();
        System.out.println("login方法执行耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    @Override
    public void deleteUsers() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        System.out.println("成功删除了1万个用户");
        Thread.sleep(1500);
        long endTime = System.currentTimeMillis();
        System.out.println("deleteUsers方法执行耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    @Override
    public String[] selectUsers() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        System.out.println("查询出了3个用户");
        String[] names = {"金庸", "莫言", "南派三叔"};
        Thread.sleep(500);
        long endTime = System.currentTimeMillis();
        System.out.println("selectUsers方法执行耗时:" + (endTime - startTime) / 1000.0 + "s");
        return names;
    }
}

/**
 * 目标: 使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception {
        //1、创建用户业务对象
        UserService userService = new UserServiceImpl();
        //2、调用用户业务的功能
        userService.login("admin", "123456");
        System.out.println("--------------------------------------");
        userService.deleteUsers();
        System.out.println("--------------------------------------");
        String[] names = userService.selectUsers();
        System.out.println("查询到的用户是:" + names);
        System.out.println("--------------------------------------");

    }
}

测试结果:
在这里插入图片描述

public class ProxyUtil {
    public static UserService createProxy(UserService userService) {
        /*newProxyInstance(ClassLoader loader,
                Class<?>[] interfaces,
                InvocationHandler h)
                参数1:用于指定一个类加载器
                参数2:指定生成的代理长什么样子,也就是有哪些方法
                参数3:用来指定生成的代理对象要干什么事情
                */
        // Star starProxy = ProxyUtil.createProxy(s);
        // starProxy.sing("好日子") starProxy.dance()
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{UserService.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //代理对象要做的事情,会在这里写代码
                        if (method.getName().equals("login") || method.getName().equals("deleteUsers") ||
                                method.getName().equals("selectUsers")) {
                            long startTime = System.currentTimeMillis();
                            Object rs = method.invoke(userService, args);
                            long endTime = System.currentTimeMillis();
                            System.out.println(method.getName() + "方法执行耗时:" + (endTime - startTime) / 1000.0 + "s");
                            return rs;
                        } else {
                            Object rs = method.invoke(userService, args);
                            return rs;
                        }
                    }
                });
        return userServiceProxy;
    }
}

修改UserServiceImpl

/**
 * 用户业务实现类(面向接口编程)
 */
public class UserServiceImpl implements UserService {
    @Override
    public void login(String loginName, String passWord) throws InterruptedException {
        if ("admin".equals(loginName) && "123456".equals(passWord)) {
            System.out.println("您登录成功,欢迎光临本系统~");
        } else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
    }

    @Override
    public void deleteUsers() throws InterruptedException {
        System.out.println("成功删除了1万个用户");
    }

    @Override
    public String[] selectUsers() throws InterruptedException {
        System.out.println("查询出了3个用户");
        String[] names = {"金庸", "莫言", "南派三叔"};
        Thread.sleep(500);
        return names;
    }
}

测试结果:
在这里插入图片描述
修改Test

/**
 * 目标: 使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception {
        //1、创建用户业务对象
        UserService userService = (UserService)ProxyUtil.createProxy(new UserServiceImpl());
        //2、调用用户业务的功能
        userService.login("admin", "123456");
        System.out.println("--------------------------------------");
        userService.deleteUsers();
        System.out.println("--------------------------------------");
        String[] names = userService.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));
        System.out.println("--------------------------------------");

    }
}

测试结果:
在这里插入图片描述
执行逻辑:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值