您的位置:首页 > 编程语言 > Java开发

Java学习笔记之动态代理

2016-07-05 14:06 597 查看
鲁春利的工作笔记,谁说程序员不能有文艺范?

代理模式(Proxy Pattern)是GoF 23种Java常用设计模式之一。
代理模式的定义:Provide a surrogate or placeholder for another object to control access to it(为其他对象提供一种代理以控制对这个对象的访问)。
使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。

代理模式一般涉及到的角色有:
1、抽象角色:可以是抽象类,也可以是接口,真实对象和代理对象的共同接口;
2、真实角色:代理角色所代表的真实对象,是我们最终要引用的目标对象。
3、代理角色:也叫委托类、代理类,内部含有对真实对象(目标对象)的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作(如记录日志、进行完全检查等),相当于对真实对象进行封装。

示例:
抽象角色:

package com.invicme.apps.aop.proxy;

/**
*
* @author lucl
*
* 抽象角色
*
*/
public interface IDoSomeThing {
public void doSomeThing ();
}
真实角色:
package com.invicme.apps.aop.proxy;

/**
*
* @author lucl
*
* 真实角色
*
*/
public class DoSomeThing implements IDoSomeThing {

@Override
public void doSomeThing() {
System.out.println("I'm doing some thing...");
}

}
代理角色:
package com.invicme.apps.aop.proxy;

/**
*
* @author lucl
*
* 代理类
*
*/
public class DoSomeThingProxy implements IDoSomeThing {

private IDoSomeThing iDoSomeThing;
private String userName;

public DoSomeThingProxy (IDoSomeThing iDoSomeThing, String userName) {
this.iDoSomeThing = iDoSomeThing;
this.userName = userName;
}

@Override
public void doSomeThing() {
// 调用目标对象之前可以做相关操作(如记录日志、根据用户判断是否能够进行doSomeThing的操作)
System.out.println("Current user " + userName + " want to do some thing.");
this.iDoSomeThing.doSomeThing();
// 调用目标对象之后可以做相关操作
System.out.println("Current user " + userName + " do some thing end.");
}
}
测试驱动类
package com.invicme.apps.aop.proxy;

/**
*
* @author lucl
*
* 驱动类,希望执行某些业务逻辑
*
*/
public class MainDriver {
public static void main(String[] args) {
IDoSomeThing iDoSomeThing = new DoSomeThing();
IDoSomeThing doSomeThingProxy = new DoSomeThingProxy(iDoSomeThing, "张三");
doSomeThingProxy.doSomeThing();

doSomeThingProxy = new DoSomeThingProxy(iDoSomeThing, "李四");
doSomeThingProxy.doSomeThing();
}
}
总结:
代理模式主要使用了java的多态(这里为了简化使用的Sring userName,实际上可以为Person对象),干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚得很,同样一个接口。




《大话设计模式》中描述的为“当追求者想要送礼物时,没有直接将礼物送出去,而是通过代理”。




追求者和代理类均实现该接口,并且唉代理类中实现对追求者的封装,礼物都是通过代理之手出去的。

Proxy提供用于创建动态代理类和代理对象的静态方法,它是所有动态代理类的父类;
Proxy提供了两个方法来创建动态代理类和动态代理实例。

创建某一接口
Foo
的代理:
InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(
Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
或使用以下更简单的方法:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
接口:
package com.invicme.apps.aop.proxy;

/**
*
* @author lucl
*
* 数学计算接口类
*
*/
public interface ArithmeticCalculate {
public int add (int i, int j);
public int div (int i, int j) throws DivisorIsZeroException;
public String validateNum (String level, int i);
}
实现类:
package com.invicme.apps.aop.proxy;

import org.apache.log4j.Logger;

/**
*
* @author lucl
*
* 数学计算实现类
*
*/
public class ArithmeticCalculateImpl implements ArithmeticCalculate {

private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class);

private int i = 0;
private int j = 0;

public ArithmeticCalculateImpl () {
this(0, 0);
}

public ArithmeticCalculateImpl (int i, int j) {
this.i = i;
this.j = j;
}

@Override
public int add(int i, int j) {
logger.info("The method add was invoke with args [" + i + ", " + j + "]");
int sum = i + j;
logger.info("The method add ends with result [" + sum + "]");
return sum;
}

@Override
public int div(int i, int j) throws DivisorIsZeroException {
logger.info("The method div was invoke with args [" + i + ", " + j + "]");
if (j == 0) {
throw new DivisorIsZeroException("除数不可为0");
}
int result = i / j;
logger.info("The method div ends with result [" + result + "]");
return result;
}

@Override
public String validateNum(String level, int i) {
logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]");
String result = this.getMsg(i);
logger.info("The method validateNum ends with result [" + result + "]");
return result;
}

private String getMsg (int i) {
if (i > 0) {
return "正数";
}
return "负数";
}
}
代理类:
package com.invicme.apps.aop.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
*
* @author lucl
*
* 代理处理器,去执行目标对象的方法
*
*/
public class ProxyHandler implements InvocationHandler {
private Object target;

public ProxyHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在转调具体目标对象之前,可以执行一些功能处理
System.out.println(target.getClass().getName() + "@" + method.getName() + " was invoke with args " + Arrays.asList(args) + ".");
// 把方法调用委派给目标对象
Object result = null;
try {
// 前置通知
result = method.invoke(target, args);
// 返回通知,可以访问到方法的返回值
} catch (Exception e) {
e.printStackTrace();
// 异常通知,可以访问到方法返回的异常
}

// 后置通知,因为可能出现异常,不能访问到方法的返回值

System.out.println(target.getClass().getName() + "@" + method.getName() + " was invoke ends with result [" + result + "].");
// 在转调具体目标对象之后,可以执行一些功能处理
return result;
}
}
测试驱动类:
package com.invicme.apps.aop.proxy;

import java.lang.reflect.Proxy;

/**
*
* @author lucl
*
*/
public class DynamicProxyDriver {
public static void main(String args[]) {
ArithmeticCalculate target = new ArithmeticCalculateImpl();
/**
* ClassLoader loader :类加载器,一般使用和被代理对象相同的类加载器
* Class<?>[] interfaces :被代理对象(目标对象)的接口数组
* InvocationHandler h :  设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
*/
ArithmeticCalculate proxy = (ArithmeticCalculate) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[] { ArithmeticCalculate.class } 或者 target.getClass().getInterfaces(),
new ProxyHandler(target));

//
proxy.add(1,  2);
System.out.println("----------------------------------------------------------------");
//
try {
proxy.div(12, 3);
} catch (DivisorIsZeroException e) {
e.printStackTrace();
}
}
}





总结:通过Java动态代理的方式可以实现AOP编程,即在不影响原有业务逻辑的情况下加入新的功能(如日志记录、安全检查等);比如之前的所有日志记录都可以在业务代码之外完成,保证业务代码是干净的。

本文出自 “闷葫芦的世界” 博客,请务必保留此出处http://luchunli.blog.51cto.com/2368057/1795921
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: