下面以添加日志记录为例,分析静态代理的使用。创建一个用户管理类UserManagerImpl,并创建添加用户方法addUser,为其良好扩展性,创建一个通用接口UserManager,代码分别如下:
接口代码:
- packagecom.snail.pattern;
- publicinterfaceUserManager{
- publicvoidaddUser(StringuserId,StringuserName);
- }
实现类代码:
- packagecom.snail.pattern;
- publicclassUserManagerImplimplementsUserManager{
- publicvoidaddUser(StringuserId,StringuserName){
- try{
- //System.out.println("开始执行");
- System.out.println("HelloWorld!");
- //System.out.println("执行成功!");
- }catch(Exceptione){
- e.printStackTrace();
- //System.out.println("执行失败!");
- thrownewRuntimeException();
- }
- }
- }
从代码可以看出,注释里面的日志内容和业务逻辑毫无关系,无形中使耦合性增加,如果很多类中需要添加这些日志代码,工作量不言而喻,修改起来也非常麻烦。如果采用静态代理把打印日志的代码抽取到代理类中,通过代理类和业务逻辑类继承自同一个父类,客户端直接调用代理类完成需求,这样就解决了客户端与业务逻辑类的耦合。示例代码如下:
- packagecom.snail.pattern;
- publicclassUserManagerImplProxyimplementsUserManager{
- privateUserManageruserManager;
- publicUserManagerImplProxy(UserManageruserManager){
- this.userManager=userManager;
- }
- @Override
- publicvoidaddUser(StringuserId,StringuserName){
- try{
- System.out.println("开始执行");
- userManager.addUser(userId,userName);
- System.out.println("执行成功!");
- }catch(Exceptione){
- e.printStackTrace();
- System.out.println("执行失败!");
- }
- }
- }
客户端调用代码如下:
- packagecom.snail.pattern;
- publicclassClient{
- /**
- *@paramargs
- */
- publicstaticvoidmain(String[]args){
- UserManageruserManager=newUserManagerImplProxy(newUserManagerImpl());
- userManager.addUser("0111","张三");
- }
- }
静态代理虽隔离了与业务逻辑无关的代码,降低了耦合,让业务逻辑类更专注于业务逻辑,但无法减少代码量,系统重复代码过多,加大了程序员工作量。因此,JDK动态代理完美解决了此问题,动态代理支持在系统运行期给类动态添加代理,然后通过操控代理类完成对目标类的调用。
继续演化上面举的例子,将静态代理改为动态代理,抽象类UserManager和目标类UserManagerImpl中的代码不变,将静态代理类UserManagerImplProxy删除,添加LoadHandler类,并让它实现InvocationHandler接口中的invoke方法,代码如下:
- packagecom.snail.pattern;
- importjava.lang.reflect.InvocationHandler;
- importjava.lang.reflect.Method;
- importjava.lang.reflect.Proxy;
- publicclassLogHandlerimplementsInvocationHandler{
- //保留一份targetObject目标类对象
- privateObjecttargetObject;
- //Proxy类动态创建一份目标代理类
- publicObjectnewProxyInstance(ObjecttargetObject){
- this.targetObject=targetObject;
- returnProxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
- }
- @Override
- publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
- throwsThrowable{
- System.out.println("开始执行!");
- for(inti=0;i<args.length;i++){
- System.out.println(args[i]);
- }
- Objectret=null;
- try{
- //调用目标方法
- ret=method.invoke(targetObject,args);
- System.out.println("执行成功!");
- }catch(Exceptione){
- e.printStackTrace();
- System.out.println("执行失败!");
- throwe;
- }
- returnret;
- }
- }
Proxy类所创建的目标类必须实现至少一个接口,在调用newProxyInstance方法时必须与目标类的类加载器和接口一致;invoke方法非常类似Filter中的doFilter方法,它将调用目标类的所有方法在未到达UserManagerImpl之前截获,根据我们自己的需求进行预处理后,继续调用UserManagerImpl。
为了保持invoke方法的通用性,目标方法中的参数以数组args形式传递,如果方法中有返回值,则返回,没有返回值,则返回null。如此一来,程序员不必为每个目标类设计一个代理类,所有需要打印日志的类都可以共用这个LogHandler,如果不想使用日志功能就可以直接删除LogHandler类,对原功能没有丝毫影响,如同揭去显示器上的保护膜,不会影响显示器的使用一般。
客户端调用代码如下:
- packagecom.snail.pattern;
- publicclassClient{
- /**
- *@paramargs
- */
- publicstaticvoidmain(String[]args){
- LogHandlerlogHandler=newLogHandler();
- UserManageruserManager=(UserManager)logHandler.newProxyInstance(newUserManagerImpl());
- userManager.addUser("id","name");
- }
- }