代理模式:
- 作用:
为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
其特征是代理与委托类有同样的接口。
代理模式是常用的设计模式。
- 功能:
代理类不仅仅是一个隔离客户端和委托类的中介。
我们还可以借助代理类再次增加一些功能,而不需要修改原有代码。符合开闭原则。
代理类主要为委托类处理消息、过滤消息、把消息转发给委托类、以及事后处理工作。
代理类与委托类之间会存在关联关系,一个代理类的对象与一个委托类的对相关联。
代理类的对象本身并不实现服务,而是通过委托类的对象的方法来提供特定的服务。
其实:就是真正的业务功能还是由委托类来实现,但是在实现业务之前的一些公共服务,例如在项目开发中忘记了加入缓冲、日志等的功能。后期想加入,就可以使用代理来实现而没有必要打开已经封装好的委托类。
- 分类:
代理可以分为两种:静态代理、动态代理。
静态代理:由程序员创建或特定工具自动生成的源代码,在对其编译。
需要注意的是在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,利用反射机制动态创建。
接下来一 一代码实现
静态代理:
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 定义业务接口
*
* @Title Account.java
* @Package proxy.staticProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:28:47
* @version V1.0
*/
public interface Account {
//查询功能
public void queryAccount();
//修改功能
public void updateAccount();
}
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 接口实现类(包含业务逻辑)
* 委托类
* @Title AccountImpl.java
* @Package proxy.staticProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:30:32
* @version V1.0
*/
public class AccountImpl implements Account{
@Override
public void queryAccount() {
System.out.println("委托类的查询方法...");
}
@Override
public void updateAccount() {
System.out.println("委托类的修改方法.....");
}
}
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 代理类(增强AccountImpl的功能)
*
* @Title AccountProxy.java
* @Package proxy.staticProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:32:29
* @version V1.0
*/
public class AccountProxy implements Account {
private AccountImpl accountImpl;
/**
*
* @Title: AccountProxy
* @Description: 重写默认的构造函数。真正执行的业务对象是accountImpl
* @param: @param accountImpl
* @throws
*/
public AccountProxy(AccountImpl accountImpl){
this.accountImpl=accountImpl;
}
@Override
public void queryAccount() {
System.out.println("查询业务处理之前...");
System.out.println("查询业务处理之后....");
}
@Override
public void updateAccount() {
System.out.println("修改业务处理之前...");
System.out.println("修改业务处理之后....");
}
}
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 静态代理的测试类
*
* @Title AccountTest.java
* @Package proxy.staticProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:37:57
* @version V1.0
*/
public class AccountTest {
public static void main(String[] args) {
AccountImpl accountImpl=new AccountImpl();
AccountProxy accountProxy=new AccountProxy(accountImpl);
accountProxy.queryAccount();
accountProxy.updateAccount();
}
}
代码执行结果:
可以看出,就是在委托类执行前后加上了代理的工作。这样不用动委托类也可以实现类似于面向切面的编程效果。
但是,我们发现了问题:
当把静态代码实现后,发现每一个代理类只能为一个接口服务,
一个AccountProxy类实现了Account接口,一个委托类也实现了Account接口。
但是如果接口多了呢?就必须利用多个代理类来继承接口。重复代码太多,这就引入了
动态代理。
代码实现:
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 定义一个业务接口
*
* @Title Account.java
* @Package proxy.dynamicProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:54:25
* @version V1.0
*/
public interface Account {
//查询
public void queryAccount();
//更新
public void updateAccount();
}
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 接口实现类(包含业务逻辑)
* 委托类
* @Title AccountImpl.java
* @Package proxy.staticProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:30:32
* @version V1.0
*/
public class AccountImpl implements Account{
@Override
public void queryAccount() {
System.out.println("委托类的查询方法...");
}
@Override
public void updateAccount() {
System.out.println("委托类的修改方法.....");
}
}
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 关键的动态代理是这个类。 该代理类不去实现任何一个任务接口。 而是实现了JDK的InvocationHandler接口。
* 在Proxy中我们不需要具体的知道具体的委托类。 在运行之前将委托类和代理类进行解耦,在运行期间再发生联系。 最重要的是通过 private object
* target实现的。 然后再调用getInstance,再确定谁是委托对象。
*
* @Title Proxy.java
* @Package proxy.dynamicProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:55:46
* @version V1.0
*/
public class Proxy implements InvocationHandler {
private Object target;
public Proxy(AccountImpl impl) {
this.target=impl;
}
/**
*
* Title: invoke
* Description:
*
* InvocationHandler接口有一个方法:
* public Object invoke(Object proxy,
* Method method,
* Object[] args) throws Throwable;
* Object proxy:指被代理的对象;
* Method method:要调用的方法。
* Object[]args:方法调用时所需要的参数。
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result=null;
System.out.println("before");
result=method.invoke(target, args);
System.out.println("after");
return result;
}
}
import java.lang.reflect.InvocationHandler;
public class DynamicProxy {
public static void main(String[] args) {
AccountImpl impl=new AccountImpl();
Proxy proxy = new Proxy(impl);
InvocationHandler h=new Proxy(impl);
Account account = (Account) java.lang.reflect.Proxy.newProxyInstance(
impl.getClass().getClassLoader(), impl.getClass()
.getInterfaces(), proxy);
account.queryAccount();
account.updateAccount();
}
}
代理类和测试类还可以这样写:
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* 关键的动态代理是这个类。 该代理类不去实现任何一个任务接口。 而是实现了JDK的InvocationHandler接口。
* 在Proxy中我们不需要具体的知道具体的委托类。 在运行之前将委托类和代理类进行解耦,在运行期间再发生联系。 最重要的是通过 private object
* target实现的。 然后再调用getInstance,再确定谁是委托对象。
*
* @Title Proxy.java
* @Package proxy.dynamicProxy
* @Description: TODO
* @author Young
* @date 2018年1月5日 下午7:55:46
* @version V1.0
*/
public class CopyOfProxy implements InvocationHandler {
private Object target;
/**
*
* 生如逆旅单行道, 哪有岁月可回头。
*
* Proxy类:proxy类是专门完成代理的操作类。
* 位于java.lang.reflect包中。
* 可以通过此类为一个或者多个接口动态的生成实现类。
public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
ClassLoader loader:类加载器
Class<?>[] interfaces:得到的全部接口
InvocationHandler h:得到的InvocationHandler接口的子类实例
此方法根据得到的InvocationHandler的子类实例动态加载全部接口
*
*
* @Description: TODO
* @return Object
* @throws
* @author Young
* @date 2018年1月5日
*/
public Object getInstance(Object target) {
this.target = target;
return java.lang.reflect.Proxy.newProxyInstance(target.getClass()
.getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
*
* Title: invoke
* Description:
*
* InvocationHandler接口有一个方法:
* public Object invoke(Object proxy,
* Method method,
* Object[] args) throws Throwable;
* Object proxy:指被代理的对象;
* Method method:要调用的方法。
* Object[]args:方法调用时所需要的参数。
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result=null;
System.out.println("before");
result=method.invoke(target, args);
System.out.println("after");
return result;
}
}
public class CopyOfDynamicProxy {
public static void main(String[] args) {
CopyOfProxy proxy = new CopyOfProxy();
Account account = (Account) proxy.getInstance(new AccountImpl());
account.queryAccount();
account.updateAccount();
}
}
其实原理都是相同的。都是根据接口的类加载器来使用代理类代理委托类。
输出结果肯定都一样啦。
动态代理;
类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性 。
因为Java的反射机制可以生成任意类型的动态代理类。
java.lang.reflact包中的Proxy类和InvocationHandler接口 提供了生成动态代理的能力
但是使用JDK动态代理
生成的代理类是继承了Proxy类的,这就是说明了为什么使用JDK动态代理不能实现继承式动态代理,
原因是Java不允许多继承,而生成的代理类本身就已经继承了Proxy类。
这又引入了Cglib。
Cglib是一个强大的代码生成器。
大名鼎鼎的spring中就有使用到。
使用Cglib需要引入cglib-nodep-2.1_3.jar包
CGLIB是一个强大的高性能的代码生成包。
它被许多AOP的框架(例如SpringAOP)使用,为他们提供方法的interception(拦截)。
Hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联。EasyMock通过使用模仿(moke)对象来测试java代码的包。
它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
下面代码实现:
/**
*具体主题
*/
public class Train {
public void move(){
System.out.println("货车行驶....");
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{
private Enhancer enhaner=new Enhancer();
public Object getProxy(Class<?> cla){
enhaner.setSuperclass(cla);
enhaner.setCallback(this);
return enhaner.create();
}
/**
*
* Title: intercept
* Description: 拦截所有目标方法的调用
* @param arg0: 目标实例对象
* @param arg1: 目标方法的反射对象
* @param arg2: 方法的参数
* @param arg3: 代理类的实例
* @return
* @throws Throwable
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
*/
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
System.out.println("日志开始.....");
arg3.invokeSuper(arg0, arg2);
System.out.println("日志结束....");
return null;
}
}
public class Test {
public static void main(String[] args) {
CglibProxy proxy=new CglibProxy();
Train t=(Train) proxy.getProxy(Train.class);
t.move();
}
}
可以看出可以不用使用接口,直接来类就能实现前后功能的叠加。
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。
在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。
Proxy 很美很强大,但是仅支持 interface 代理。
Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。
好在有cglib为Proxy提供了弥补。
class与interface的区别本来就模糊,在java8中更是增加了一些新特性,使得interface越来越接近class,当有一日,java突破了单继承的限制,动态代理将会更加强大。