Java的动态代理
理论知识比较少,都是例子。更容易理解,可以看完之后再看看理论知识。
这个可以算是反射之后的下一步学习的东西吧,涉及到到的基础
- 类加载器(知道反射如何得到就行)
- clazz.getClass().getClassLoader()
- 反射
我们通过一个案例问题来引出动态代理:
我们现在需要满足网站的三个功能:
- 用户登陆
- 用户查找
- 用户删除
并且我们需要统计每个功能的执行程序所用的时间。
不使用代理
首先编写一个接口来模拟三个功能:
package com.qingfeng.servlet;
public interface UserService {
String login(String username, String password);
void searchUser();
boolean deleteUser();
}
然后编写一个实现类:
package com.qingfeng.servlet;
public class UserServiceImpl implements UserService{
@Override
public String login(String username,String password) {
long beforeTime = System.currentTimeMillis();
try {
Thread.sleep(2000);
if ("admin".equals(username) && "123456".equals(password)) {
return "login success";
}
return "用户名或者密码错误";
}
catch (Exception e) {
e.printStackTrace();
} finally {
long afterTime = System.currentTimeMillis();
System.out.println("login time:" + (afterTime - beforeTime) + "ms");
}
return "用户名或者密码错误";
}
@Override
public void searchUser() {
long beforeTime = System.currentTimeMillis();
try {
Thread.sleep(2000);
System.out.println("查询用户--");
}
catch (Exception e) {
e.printStackTrace();
} finally {
long afterTime = System.currentTimeMillis();
System.out.println("search time:" + (afterTime - beforeTime) + "ms");
}
}
@Override
public boolean deleteUser() {
long beforeTime = System.currentTimeMillis();
try {
Thread.sleep(2000);
System.out.println("删除用户");
return true;
}
catch (Exception e) {
e.printStackTrace();
} finally {
long afterTime = System.currentTimeMillis();
System.out.println("delete time:" + (afterTime - beforeTime) + "ms");
}
return false;
}
}
再定义一个测试类:
package com.qingfeng.servlet;
public class Test {
public static void main(String[] args) {
UserServiceImpl usi = new UserServiceImpl();
String res = usi.login("admin", "admin");
System.out.println(res);
usi.searchUser();
usi.deleteUser();
}
}
执行结果:
以上就是典型的静态代理。
但是我们不难发现一些问题:
- 代码冗余,每个方法里面都有统计时间的代码
- 每个方法里面的功能不纯粹,不仅要解决业务功能还要解决统计时间的问题
- 如果新添加一个方法依旧要在代码中写入统计时间的代码
如何解决这些问题呢?
其实发现有冗余代码我们的目的肯定就是整合代码,使用类似函数的东西来实现重复代码的重合,就如下图所示:
用伪代码实现起来就是这样:
public void Test(function()){
long startTime = System.currentTimeMillis();
function();
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
我们希望这里的function可以是我们想要执行的任意方法,其实这个思想就是用动态代理来实现的
动态代理
动态代理实现图:
使用动态代理就可以达到如图的效果,把类里面的方法统计时间的功能交给代理,自己只用实现真实逻辑即可。
代码实现:
定义的接口不变:
package com.qingfeng.servlet;
public interface UserService {
String login(String username, String password);
void searchUser();
boolean deleteUser();
}
实现类可以把统计时间的代码全都删去:
package com.qingfeng.servlet;
public class UserServiceImpl implements UserService{
@Override
public String login(String username,String password) {
try {
if ("admin".equals(username) && "123456".equals(password)) {
return "login success";
}
return "用户名或者密码错误";
}
catch (Exception e) {
e.printStackTrace();
}
return "用户名或者密码错误";
}
@Override
public void searchUser() {
try {
Thread.sleep(2000);
System.out.println("查询用户--");
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean deleteUser() {
try {
Thread.sleep(2000);
System.out.println("删除用户");
return true;
}
catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
测试类的代码我们在第一行做一个微改,把要执行的方法交给代理:
package com.qingfeng.servlet;
public class Test {
public static void main(String[] args) {
// 把业务对象直接做成一个代理对象返回,代理对象的类型也是 UserService
UserService usi = ProxyUtil.gotoProxy(new UserServiceImpl()); // 修改的地方
String res = usi.login("admin", "admin");
System.out.println(res);
usi.searchUser();
usi.deleteUser();
}
}
然后我们编写一个动态代理的Java文件,这个是动态代理的关键
package com.qingfeng.servlet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static UserService gotoProxy(final UserService obj) {
// 返回一个代理对象
return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 参数一:代理对象本身
// 参数二:正在被代理的方法
// 参数三:代理方法传入的参数
long startTime = System.currentTimeMillis();
Object result = method.invoke(obj, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) + "ms");
// 把业务执行的结果返回给调用者
return result;
}
});
}
}
我们用到的核心类:
/*
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* 参数一: 类加载器,负责加载代理类到内存中使用
* 参数二: 获取被代理的对象的全部接口
* 代理是要实现全部接口的全部方法,所以要知道所有的接口
* 参数三: 代理的核心处理逻辑,如何实现的问题
* InvocationHandler 触发方法
*/
我们可以注意到这里的类型是Object,但是Java里面大家都是Object类型的,所以这里可以用类替代
运行结果: