一提起代理模式相信很多人都会给我这样的答案“代理模式很简单,无非是有两个类(类A和类B),他们实现了同一个接口(或继承同一个抽象类),在类A中拥有类B的引用,当调用类A中方法时实际上调用的是类B中的方法,它屏蔽了真正的实现类,将实现类与客户端解耦”。文字描述起来总是显得那么模糊不清,下面我们通过类图来简单看一下什么是代理模式。
通过上面的类图我们可以很清楚的看到,在客户端所面对的只是一个接口(抽象)这也非常符合我们针对接口(抽象)编程的原则,同时由于代理类的加入,使得客户端和真正实现者之间彻底解耦,设计模式不愧是前人伟大的智慧结晶。但是如果我们根据现实中的真实需求变动来分析一下,代理模式真的是那么完美吗?
新增需求(1):需要改变真正实现结果。比如以前Client调用Proxy中的方法后,Proxy通过调用RealSubject的方法,所实现的效果是给出提示:欢迎您。而现在我需要的效果是给出提示:北京欢迎您。
新增需求(2):记录执行日志。现在不光要调用方法实现效果,而且要记录调用方法的日志。也就是说每当通过Proxy调用了RealSubject的方法后需要记录所调用的方法名称以及调用该方法是否成功。
结合上面的UML图和两个新需求进行分析,如果按照上图中设计模式的方式编程的话,想要满足第一个需求我们有两种解决方案,第一种是更改RealSubject中的实现代码,第二种是新增第二个RealSubject,然后更改代理类中的代码。但是无论是那种方法都不可避免的需要对代码进行改动,严重违反了OCP原则。对于第二个新需求有人可能会说只能在RealSubject实现方法中给出提示了,要是是这样的话,如果RealSubject中一万个方法,那么这样的提示语句我们就要写一万次是吗?好吧,果真是这样的话相信要不了多久你就会被老板请出公司的。说了这么多,难道就没有什么好方法了吗?俗话说”方法总比困难多”,动态代理的横空出世给我们这些成天为代码疯狂的人一个巨大的惊喜。好了,废话不多说下面就来看看java中动态代理的使用是怎样解决这些让人头疼的问题的吧。
首先来介绍一下java中的代理类。在java中凡是实现了InvocationHandler接口的类都有两个方法为newProxyInstance()和invoke(),其中newProxyInstance()方法的作用是返回目标类的代理类,而当调用目标类的方法时首先调用的是代理类的invoke()方法,这个invoke()方法及其类似于前篇文章中介绍过的Filter,在该方法体的前中后分别可以编写目标函数调用前的处理代码,调用目标函数,以及编写调用目标函数后的处理代码。另外,我们完全可以将真正的实现类的配置到配置文件中,一次达到随意更改效果实现的问题。相信看到这你已经被这些枯燥的文字忽悠的迷迷糊糊的了,下面通过代码为你揭开动态代理的神秘面纱,保证你会有一种拨云见日的感觉。代码之前简单画一下UML图帮助你更好的理解代码结构。
有了上面的UML图相信你在理解下面代码的过程中会变得更加得心应手。代码实现过程中提到的dom4j读取xml文件内容的部分请参考《dom4j读取XML文件实现IP数据库连接》一文。废话不多少了,进入期待已久的代码部分:
BaseInvo代码:
package com.bjpowernode.pattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import XMLConfig.XMLReader;
public class BaseInvo implements InvocationHandler{
protected Object targetObject;
protected Object newProxyInstance(Object targetObject){
this.targetObject = targetObject;
XMLReader reader = XMLReader.getInstance();
InvocationHandler object = null;
try {
object = (InvocationHandler)Class.forName(reader.getInstance().getClassPath()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
BaseInvo为继承InvocationHandler的一个类,同时他又是TestInvoAAA和TestInovBBB的父类,经过抽象将原本存在于TestInvoAAA和TestInovBBB中的方法抽取出来供子类继承。
UserManagerImpl类中的代码如下:
package com.bjpowernode.pattern;
public class UserManagerImpl implements UserManager {
public void addUser(String userId, String userName) {
//System.out.println("start-->>addUser() userId-->>" + userId);
try {
System.out.println("UserManagerImpl.addUser() userId-->>" + userId);
//System.out.println("success-->>addUser()");
}catch(Exception e) {
e.printStackTrace();
//System.out.println("error-->>addUser()");
throw new RuntimeException();
}
}
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser() userId-->>" + userId);
}
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
return "张三";
}
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);
}
}
该类即为上文中提到的目标类,在该类中存在真正的Client端想要调用的方法,而代理类也正是为该类进行代理。该类实现UserManager接口,由于在接口中只存在方法的声明,所以在此就不再写出接口中的代码了。 TestInvoAAA类中的代码:
package com.bjpowernode.pattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.sun.xml.internal.org.jvnet.fastinfoset.sax.ExtendedContentHandler;
public class TestInvoAAA extends BaseInvo implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("我是testInvoAAA类的invoke方法");
Object mObject = null ;
try{
//调用目标方法
mObject = method.invoke(targetObject, args);
System.out.println("sucess----->>TestInvoAAA" + method.getName());
}catch(Exception e){
e.printStackTrace();
System.out.println("error----->>" + method.getName());
throw e;
}
return mObject;
}
}
TestInovBBB类中的代码:
package com.bjpowernode.pattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TestInvoBBB extends BaseInvo implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("我是testInvoBBB类的invoke方法");
Object mObject = null ;
try{
//调用目标方法
mObject = method.invoke(targetObject, args);
System.out.println("sucess----->>TestInvoBBB" + method.getName());
}catch(Exception e){
e.printStackTrace();
System.out.println("error----->>" + method.getName());
throw e;
}
return mObject;
}
}
TestInoAAA和TestInvoBBB才是目标类UserManagerImpl真正的代理类,也就是由它对调用UserManagerImpl类中方法前后进行处理,实现上文中提到的日志记录功能。这两个类也是我们配置在配置文件中的类,也正是由于具体实例化哪个类是由配置文件中的配置决定的,所以当我们要更改实现效果时不必改动一个代码,只需要更改配置文件中的实现类,就可以达到目的。下面我们简单来看一下配置文件中的内容:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<dynamic_classpath>com.bjpowernode.pattern.TestInvoAAA</dynamic_classpath>
</config>
该配置文件中的内容即为需要实例化的具体的代理类,通过更改此处,可以在不修改代码的情况下随意更改代理类实现不同处理效果。最后来看客户端的代码:
package com.bjpowernode.pattern;
import java.lang.reflect.InvocationHandler;
import XMLConfig.XMLReader;
import com.sun.org.apache.bcel.internal.generic.NEW;
public class Client {
public static void main(String[] args){
XMLReader reader = XMLReader.getInstance();
BaseInvo realInvocationHandler = null;
try {
realInvocationHandler = (BaseInvo)Class.forName(reader.getClassPath()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
UserManager userManager = (UserManager)realInvocationHandler.newProxyInstance(new UserManagerImpl());
userManager.delUser("0001");
}
}