一、代理模式的作用
- 功能增强: 基于某个功能,再增加一些功能。
(比如目标类只负责核心功能,其他附属功能通过代理类完成。代理类的方法名与目标类的方法相同,内容不同,在核心功能外加了一些额外逻辑) - 控制访问: 防止直接访问目标。
代理类代理目标类(代理)
目标类是实现目标功能的类(杨超越)
二、实现代理的方式
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("--------------------------------------");
}
}
测试结果:
执行逻辑: