1. 代理模式
1.1定义
代理模式(Proxy)为另一个对象提供一个替身或占位符以控制对这个对象的访问,简而言之就是用一个对象来代表另一个对象。他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
1.2 结构图
1.3 示例
1.3.1 接口类Acount,.java
public interface Account {
// 查看账户方法
public void queryAccount();
// 修改账户方法
public void updateAccount();
}
1.3.2 实现类AccountImpl.java
public class AccountImpl implements Account {
@Override
public void queryAccount() {
System.out.println("查看账户方法……");
}
@Override
public void updateAccount() {
System.out.println("修改账户方法……");
}
}
1.3.3 代理类AccoutProxy.java
/**
* 这是一个代理类(增强CountImpl实现类)
*
* @author Administrator
*
*/
public class AccountProxy implements Account {
private Account count;
/**
* 覆盖默认构造器
*
* @param count
*/
public AccountProxy(Account count) {
this.count = count;
}
@Override
public void queryAccount() {
System.out.println("静态代理:事务处理之前");
// 调用委托类的方法;
count.queryAccount();
System.out.println("静态代理:事务处理之后");
}
@Override
public void updateAccount() {
System.out.println("静态代理:事务处理之前");
// 调用委托类的方法;
count.updateAccount();
System.out.println("静态代理:事务处理之后");
}
}
1.3.4 Test
AccountImpl countImpl = new AccountImpl();
Account count = new AccountProxy(countImpl);
count.updateAccount();
count.queryAccount();
1.3.5 结果
静态代理:事务处理之前
修改账户方法……
静态代理:事务处理之后
静态代理:事务处理之前
查看账户方法……
静态代理:事务处理之后
2 Java动态代理
2.1 定义
静态代理如1.3节所示,一个代理类只能代理一个接口的类,我们有多少个接口需要代理则要创建多少个代理类,而动态代理的意思是一个代理类可以动态的代理多个接口的类,这样,我们只需要有一个动态代理类就可以代理无限个接口或类。
Java有两种方法实现动态代理:通过JDK本身的方法以及cglib包实现的方法。通过JDK实现的动态代理需要被代理的类必须实现接口,而cglib包方法则没有要求。这两种方法都是通过Java的反射机制实现的。
2.2 JDK实现
2.2.1 示例
public class DynamicProxy implements InvocationHandler{
private Object target;
DynamicProxy(Object target){
this.target = target;
}
DynamicProxy(){
}
/**
* 绑定需要代理的对象
* @param target
* @return
*/
public void bind(Object target) {
this.target = target;
}
/**
* 获取代理对象
* @return
*/
public Object getProxy(){
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
}
@Override
/**
* 调用方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result=null;
//..... advice()-->前置通知
System.out.println("动态代理:事务开始");
try {
result = method.invoke(target, args);
// afteradvice() -->后置通知
System.out.println("动态代理:事务结束");
} catch (RuntimeException e) {
System.out.println("动态代理:意外情况");
//exceptionadvice()--> 例外通知
}finally{
System.out.println("动态代理:最终通知");
//finallyadvice(); -->最终通知
}
return result;
}
}
2.2.2 说明
DynamicProxy类有两个功能:1、实现InvocationHandler接口;2、创建动态代理。其中创建代理最重要的是getProxy()方法的代码:
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
target为被代理的对象,该方法有三个参数:1、被代理类加载器;2、被代理对象所实现的接口;3、实现了InvocationHandler接口的对象。
通过分析JDK源码可知该句代码所创建代理类(部分参考自http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/),假设为AccountProxy的情况如下图所示:
所创建的AccountProxy类继承java.lang.reflect.Proxy类,并且实现了代理对象AccountProxy所实现的接口Account,注意,这个接口可以不止一个。对被代理对象AccountImpl方法的实现调用了InvocationHandler子类的invoke方法。由此可知,由此可知,当我们需要动态代理时,需要有两个条件:
1、被代理对象必须实现某个接口。Proxy.newProxyInstance创建的代理类继承了Proxy类,Java的单一继承机制决定了不能再继承代理对象,只能转为实现被代理对象的接口,并且引用被代理对象。因此DynamicProxy 类所绑定的代理对象target必须有实现的接口。
2、实现InvocationHandler接口的对象。对代理类函数的调用都转为对InvocationHandler实现子类的invoke方法的调用。因此DynamicProxy 类实现了InvocationHandler接口。
该类的测试代码如下:
DynamicProxy dynamicProxy = new DynamicProxy(new AccountImpl());
Account account = (Account)dynamicProxy.getProxy();
account.queryAccount();
account.updateAccount();
需要说明的是,dynamicProxy.getProxy()获取了通过JDK创建的代理对象,它实现了Account接口,所以我们可以向上转型为Account类型。
2.3 CGLIB实现
2.3.1 示例
/**
* 依赖的jar包有:cglib-nodep.jar和asm.jar
*/
public class CglibProxy implements MethodInterceptor {
private Object target;
CglibProxy(Object target){
this.target = target;
}
CglibProxy(){
}
/**
* 绑定需要代理的对象
* @param target
* @return
*/
public void bind(Object target) {
this.target = target;
}
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
// 回调方法
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("事物开始");
proxy.invokeSuper(obj, args);
System.out.println("事物结束");
return null;
}
}
2.3.2 说明
CglibProxy类有两个功能:1、实现MethodInterceptor 接口;2、创建动态代理。其中创建代理最重要的是getProxy()方法,它通过Enhancer 类创建动态代理,它需要两个参数:1、被代理类;2、实现MethodInterceptor 接口的类。依据代码推断所创建的动态代理,假设为AccountProxy,其类图如下图所示。由于AccountProxy是通过继承方式创建的,因此对被代理对象没有任何要求。
测试代码如下:
CglibProxy cglib = new CglibProxy(new AccountImpl());
AccountImpl accountCglib = (AccountImpl)cglib.getProxy();
accountCglib.queryAccount();