既然有动态代理,那么确定有静态代理,静态代理请参考设计模式之静态代理java
做用: 与静态代理同样,但动态代理解决了 静态代理中 1个静态代理,只代理1种类型的目标对象 的问题编程
在 JDK 动态代理中涉及以下角色:设计模式
废话少说先上代码bash
要有jdk动态代理,你的类必须实现接口ide
/**
* UserService 是 目标类的接口
* login() 就是要被代理的方法
*/
public interface UserService {
boolean login(String userName, String password);
}
复制代码
注意,要代理的方法,必须是从接口中实现的post
/**
* UserServiceImpl 是目标类,或者叫被代理的类
* login() 就是要被代理的方法
*/
public class UserServiceImpl implements UserService {
@Override
public boolean login(String userName, String password) {
System.out.println("校验帐号");
return false;
}
}
复制代码
这是一个InvocationHandler的实现类测试
做用: 定义一种对目标方法作某种操做的模版(这里运用了面向抽象编程的思想) 以下,定义了一个在目标方法先后输出log的模版。ui
说明: LogHandler 有一个 属性 —— 目标对象,能够看到我特地把它定义成 Object 类型,而不是 UserService 类型,也就是说 LogHandler 的目标对象能够是任意类型,而不局限在 UserService。这样一来 LogHandler 就能够在 任意类型的任意方法 先后输出log,因此称它为处理模版this
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 这是一个自定义的处理模版,它将在 目标方法 的先后输出log
*/
public class LogHandler implements InvocationHandler {
// 目标对象
//private UserService target;
private Object target;
// 初始化时,将目标对象传进来
public LogHandler(Object target) {
this.target = target;
}
/**
* 定义一个在目标方法执行先后输出log的处理模版
*
* @param proxy 动态代理对象
* @param method 目标方法
* @param args 目标的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//调用目标对象的目标方法,target 是初始化时传人的目标对象
Object result = method.invoke(target, args);
after();
return result;
}
private void before() {
System.out.println("调用目标方法以前输出的log");
}
private void after() {
System.out.println("调用目标方法以后输出的log");
}
}
复制代码
这里留一个疑问,谁去调用invoke()方法,方法中的三个参数是什么意思(虽然我已经写上去了)?spa
上面三个类构成了jdk动态代理的最小组成单位,接下来,编写测试类使用jdk动态代理
import java.lang.reflect.Proxy;
public static void main(String[] args) {
//建立一个目标对象
UserService userServiceImpl = new UserServiceImpl();
// 建立 目标对象的 处理模版
LogHandler userServiceLogHandler = new LogHandler(userServiceImpl);
/**
* Proxy.newProxyInstance 会根据 目标对象的 类加载器、接口的Class、目标对象的处理模版,动态生成一个代理类,并返回代理对象
* userServiceProxy 就是 动态建立出的代理对象,它实现了 UserService 接口 的login方法
*/
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userServiceImpl.getClass().getClassLoader(),
new Class[]{UserService.class}, userServiceLogHandler);
userServiceProxy.login("wqlm", "123");
System.out.println("\n代理类的名称为" + userServiceProxy.getClass().getName());
}
复制代码
/Library/Java/jdk1.8.0_201/Contents/Home/bin/java...
调用目标方法以前输出的log
校验帐号
调用目标方法以后输出的log
代理类的名称为com.sun.proxy.$Proxy0
Process finished with exit code 0
复制代码
测试方法中就是使用该类的方法来生成代理对象的!
这是JDK动态代理中最为重要的一个类,经过该类的 newProxyInstance 方法,咱们能够生成一个代理类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
复制代码
如上,newProxyInstance 须要三个参数
生成代理类的过程以下
因为代理类是在运行时动态生成的,没有.java文件,也没有.class文件。可是根据上面的运行结果咱们知道,生成的动态代理类叫 com.sun.proxy.$Proxy0,能够经过
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class<?>[]{userServiceProxy.getClass()});
String pathDir = "/Users/wqlm/Desktop";
String path = "/$Proxy0.class";
File f = new File(pathDir);
path = f.getAbsolutePath() + path;
f = new File(path);
if (f.exists()) {
f.delete();
}
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
复制代码
获取字节码流,在通过反编译,就能够获得大概的.java文件。如下是处理过的 (去掉了不须要关注掉内容)代理类的.java文件
import com.example.demo.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/**
* 代理类 继承了 Proxy,并实现了 目标接口 UserService
*/
public final class $Proxy0 extends Proxy implements UserService {
private static Method m3;
static {
try {
m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("login",
new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.String")});
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
// 初始化时,将处理模版 传递给父类
public $Proxy0(InvocationHandler var1) {
super(var1);
}
/**
* 这就是代理方法,能够看到,它是同过调用父类的 处理模版 的invoke 方法,而后把本身,以及目标方法和参数传递进去
*/
public final boolean login(String var1, String var2) {
try {
return ((Boolean) super.h.invoke(this, m3, new Object[]{var1, var2})).booleanValue();
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
}
复制代码
还记得在 处理模版那节留的疑问吗—— “留一个疑问,谁去调用invoke()方法,方法中的三个参数是什么意思” 如今咱们知道了,是代理类去调用invoke(),它会传3个参数,本身(代理对象本身)、目标方法、参数列表
JDK的动态代理
特色: 不须要显式实现与目标类相同的接口,而是将这种实现推迟到程序调用时由 JVM来实现,
优势:一个代理类就能够代理多个目标类,避免重复、多余代码
缺点
应用场景
与静态代理的区别
设计模式 | 代理类建立时机 | 原理 | 效率 | 能代理的目标类的数量 |
---|---|---|---|---|
动态代理 | 运行时动态建立 | 反射 | 低 | 能代理多个目标类 |
静态代理 | 运行前,先显示建立好 | / | 高 | 只能代理一个目标类 |