一、 了解 JDK 动态代理
1.1 什么是代理模式
在了解动态代理之前,我们有必要先来了解一下什么是代理模式。
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。如下图:
1.2 什么是 JDK 动态代理
Java 在 java.lang.reflect 包中有自己的代理支持,利用这个包你可以在运行时动态的创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时创建的,所以称这个 Java 技术为:动态代理。
1.3 JDK 动态代理 UML 图解
二、JDK 动态代理具体应用
2.1 问题描述
增强动物叫的功能:在 Animal 接口中定义两个方法,分别是 cry() 和 eat(),接着定义一些具体的 Animal 类 (比如 Dog、Cat 等),实现其中的方法。要求在不改变原有代码的基础上,对每个具体 Animal 对象的 cry() 方法进行增强。
2.2 代码实现
Animal 接口
package com.jas.jdk.proxy;
/**
* @author Jas
* @create 2018-02-01 9:53
**/
public interface Animal {
/**
* 定义动物叫的方法
*/
void cry();
/**
* 定义动物吃的方法
*/
void eat();
}
Dog 类
package com.jas.jdk.proxy;
/**
* @author Jas
* @create 2018-02-01 9:56
**/
public class Dog implements Animal {
@Override
public void cry() {
System.out.println("Dog wang wang ...");
}
@Override
public void eat() {
System.out.println("Dog eat ...");
}
}
Cat 类
package com.jas.jdk.proxy;
/**
* @author Jas
* @create 2018-02-01 10:39
**/
public class Cat implements Animal {
@Override
public void cry() {
System.out.println("Cat miao miao ...");
}
@Override
public void eat() {
System.out.println("Cat eat ...");
}
}
OwnerInvocationHandler 类
package com.jas.jdk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author Jas
* @create 2018-02-01 9:57
**/
public class OwnerInvocationHandler implements InvocationHandler {
/** 定义被代理的对象*/
private Animal animal;
/**
* 通过构造函数实例化被代理的对象
*
* @param animal
*/
public OwnerInvocationHandler(Animal animal){
this.animal = animal;
}
/**
* 在 cry 方法执行前后分别打印一句话,其他方法直接执行
*
* @param proxy 正在返回的代理对象,一般不怎么使用该对象
* @param method 正在被调用的方法
* @param args 调用方法时,传入的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(("cry").equals(method.getName())){
System.out.println("Before animal crying ...");
// 执行当前的 cry() 方法
Object object = method.invoke(animal, args);
System.out.println("After animal crying ...");
return object;
}
/**
* 如果调用的是其他方法,则直接执行
*/
return method.invoke(animal, args);
}
}
AnimalProxy 类
package com.jas.jdk.proxy;
import java.lang.reflect.Proxy;
/**
* @author Jas
* @create 2018-02-01 10:03
**/
public class AnimalProxy {
/**
* 返回代理对象
*
* @param animal
* @return
*/
public Animal getAnimal(Animal animal){
/**
* newProxyInstance() 方法有三个参数
*
* newProxyInstance(ClassLoader loader, -> 表示加载这个类的类加载器
* Class<?>[] interfaces, -> 代理类实现的接口方法列表
* InvocationHandler h) -> InvocationHandler 控制对真实对象方法的访问
*/
return (Animal) Proxy.newProxyInstance(animal.getClass().getClassLoader(),
animal.getClass().getInterfaces(), new OwnerInvocationHandler(animal));
}
}
测试类
package com.jas.jdk.proxy;
/**
* @author Jas
* @create 2018-02-01 10:05
**/
public class AnimalTestDrive {
public static void main(String[] args) {
Animal dog = new AnimalProxy().getAnimal(new Dog());
Animal cat = new AnimalProxy().getAnimal(new Cat());
dog.cry();
dog.eat();
System.out.println("================");
cat.cry();
cat.eat();
//可以判断当前对象是不是代理类,如果是返回 true ,不是返回 false
System.out.println(Proxy.isProxyClass(dog.getClass()));
}
}
输出
Before animal crying ...
Dog wang wang ...
After animal crying ...
Dog eat ...
================
Before animal crying ...
Cat miao miao ...
After animal crying ...
Cat eat ...
true
三、JDK 动态代理模式总结
3.1 JDK 动态模式知识点总结与注意事项
- 当你需要对一个接口的实现进行功能增强,又不想修改原有的代码时,你可以选择使用 JDK 动态代理模式。
- 可以通过 isProxyClass() 方法判断一个类是不是代理类,除此之外,代理类还会实现特定的接口。
- 动态代理之所以被称为动态,是因为运行时才将它的类创建出来。代码开始执行时还没有 proxy 类,它是根据需要从你传入的接口集创建的。
- InvocationHandler 本身不是 proxy,它只是一个帮助 proxy 的类,proxy 会把对方法的调用转发给它。Proxy 本身是通过 Proxy.newProxyInstance() 方法在运行时动态创建的。
PS:点击了解更多设计模式 http://blog.youkuaiyun.com/codejas/article/details/79236013
参考资料
《Head First 设计模式》