动态代理的意义
动态代理:在减少耦合的前提下,扩展功能,利用反射动态生成代理类(与静态代理的区别),减少编码。
代理的结果:生成了一个继承于Proxy类,实现了传入接口的类。
为什么我看不懂动态代理?
因为本人非科班,自学的过程中产生了一些疏漏,动态代理看了几遍都记不住。 从我自己出发,如果看不懂动态代理的过程,这可能是由于缺乏:
- 对JVM不了解,类加载、Class类的知识。
- 反射的知识。
- 静态代理的知识。
加载过程
Class对象
Class Class<T>
T - the type of the class modeled by this Class object. For example, the type of String.class is Class<String>. Use Class<?> if the class being modeled is unknown.
Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.
Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.
在对象实例化时,对象实例会被存放在堆空间中,方法区的方法则由所有对象共用。
获得Class对象的方法:
1. Class.forName("全限定类名");
2. 类名.getClass();
3. 对象实例.getClass();
由上可知,想要获取Class对象,必须有Class对象对应的对象的Class文件,或者实例,也就是说至少得先有代理类才能通过反射创建代理类。 下面给出一个例子,例子中使用了Proxy的newInstance()静态方法。
public interface UserDao {
int add(int a, int b);
String update(String s);
}
public class JdkProxy {
public static void main(String[] args) {
UserDao userDao = (UserDao) Proxy.newProxyInstance(ProxyHandler.class.getClassLoader(), new Class<?>[]{UserDao.class}, new ProxyHandler(new UserDaoImpl()));
userDao.update("123");
}
}
class ProxyHandler implements InvocationHandler {
private Object object;
public ProxyHandler(Object obj){
this.object = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(object, args);
return res;
}
}
class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String update(String s) {
System.out.println(s);
return s;
}
}
将上面newInstance()方法拆开,其内部步骤如下,运行结果等同:
创建动态代理对象的步骤包括:
1. 创建一个invocationHandler的实现类,该类持有一个UserDaoImpl实例。
传入UserDaoImpl实例是自然的,代理归代理,原来的方法该谁做还是谁做。
InvocationHandler ProxyHandler = new ProxyHandler(new UserDaoImpl());
2. 使用Proxy类中的getProxyClass()方法生成一个动态代理的Class对象,所以需要传入classloader
Class<Proxy> proxyClass = Proxy.getProxyClass(ProxyHandler.class.getClassLoader(), new Class<?>[] {UserDao.class});
3. 获得代理类的带有InvocationHandler参数的构造器constructor
因为重载机制的存在,想要获得指定的构造函数,必须传入参数类型,获取方法的写法也类似
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
4. 通过构造器constructor来创建一个代理类对象实例,传入创建的Handler
因为代理类实现了接口,所以可以直接向上转型并调用方法。
UserDao userDao = (UserDao) constructor.newInstance(proxyHandler);
效果等同于:
UserDao userDao = (UserDao) Proxy.newProxyInstance(ProxyHandler.class.getClassLoader(), new Class<?>[]{UserDao.class}, new ProxyHandler(new UserDaoImpl()));
还有很关键的一点是,从反编译的最终的Proxy类,可以看到,所有方法的执行,都通过接口的invoke方法间接调用(super.h.invoke())。而实现handler的类的invoke方法中,调用了被代理类实例的方法,并增加了额外的代码,这就是增强的过程。
图片来源:
反编译看JDK的动态代理原理