代理是基本的设计模式之一,使用代理对象代替真实对象的方法调用,可以扩展真实对象某些方法的功能。
在 java 中又分为静态代理和动态代理, 先看静态代理:
静态代理
public class DynamicDemo {
public static void main(String[] args) {
/**
* 静态代理示例
*/
System.out.println("=======静态代理========");
// user 真实对象
Save user = new UserDAO();
// userTransaction 是代理对象
Save userTransaction = new UserTransaction(user);
// 使用代理对象执行方法
userTransaction.save("dc1");
}
}
interface Save {
/**
* 保存
*/
void save(String name);
/**
* 删除
* @param name
*/
void delete(String name);
}
class UserDAO implements Save {
public void save(String name) {
System.out.println("真实对象执行 save 方法");
}
public void delete(String name) {
System.out.println("真实对象执行 delete 方法");
}
}
/**
* 手动编写代理类
*/
class UserTransaction implements Save {
/**
* 真实对象
*/
private Save realObject;
UserTransaction(Save save) {
this.realObject = save;
}
/**
* 在真实对象前后做一些操作
*/
public void save(String name) {
System.out.println("静态代理: 在真实对象做事之前,代理要做的事情");
// 真实对象开始执行save方法
realObject.save(name);
System.out.println("静态代理: 在真实对象做事之后,代理要做的事情");
}
public void delete(String name) {
/**
* 什么都不做,直接执行真实对象的 delete 方法。
* 这里就体现了静态代理方式的弊端,哪怕我只想代理一个方法,也需要实现所有方法。
* 只用真实对象代理。有很多无意义的代码
*/
realObject.save(name);
}
}
通过以上代码可以知道java中的静态代理有几个关键的地方:
- 代理对象和真实对象实现了相同的接口,或者继承同一个类。换句话说就是可以向上转型为相同的对象
- 真实对象要作为代理对象中一个属性。方便真实对象调用方法
有时我们只想扩展原对象的某个方法,给这个方法添加一些功能。但是我们仍要编写一个代理类,实现真实类中的所有方法。导致很多冗余代码, java 中的动态代理可以解决这个问题
动态代理
先看代码:
public class DynamicDemo {
public static void main(String[] args) {
/**
* 动态代理示例
*/
System.out.println("======动态代理======");
// 动态生成一个代理对象
Save saveProxy = (Save) Proxy.newProxyInstance(
// 真实对象的类加载器
user.getClass().getClassLoader(),
// 要代理的接口,数组
new Class[]{ Save.class},
// 将 user 真实对象传入代理处理类
new DynamicHandler(user)
);
// 代理对象执行方法,内部会调用真实对象执行对应方法
saveProxy.save("dc1");
}
}
interface Save {
/**
* 保存
*/
void save(String name);
/**
* 删除
* @param name
*/
void delete(String name);
}
class UserDAO implements Save {
public void save(String name) {
System.out.println("真实对象执行 save 方法");
}
public void delete(String name) {
System.out.println("真实对象执行 delete 方法");
}
}
/**
* 代理对象要执行的方法
*/
class DynamicHandler implements InvocationHandler {
private Object realObject;
public DynamicHandler(Object real) {
this.realObject = real;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理对象类:" + proxy.getClass());
System.out.println("要代理的方法是: " + method.getName() );
System.out.println("参数是:");
for (Object arg : args) {
System.out.printf(arg + " ");
}
System.out.println("");
if(method.getName().equals("delete")){
System.out.println("执行了 delete 方法");
}
/**
* 这里的操作体现了动态代理的优点: 在需要代理的方法前后编写功能
*/
if(method.getName().equals("save")){
System.out.println("执行了 save 方法");
System.out.println("真实对象执行save方法前");
}
// 真实对象执行方法
method.invoke(this.realObject,args);
if(method.getName().equals("save")){
System.out.println("真实对象执行save方法后");
}
return null;
}
}
从上面代码中可以发现,实现动态代理的关键点:
- InvocationHandler 的实现类,invoke 方法中编写你要扩展的功能
- InvocationHandler 的实现类中要有一个属性保存真实对象。也就是例中的 realObject, 以便真实对象调用方法,如果不需要真实对象调用方法,那么也就不需要这个真实对象了。
- 使用 Proxy.newProxyInstance 方法创建代理对象。
动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,它并没有什么黑魔法。它创建的字节码就是我们在静态代理中编写的代理类。只不过不用我们手动编写了而已。