代理模式
代理模式是常用的java设计模式,特征是代理类与委托类实现相同的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,进行总控。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务,真正干活的还是委托类。
分类
按照代理的创建时期,代理类可以分为两种:静态代理、动态代理。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
示例
静态代理:
1、Person.java
public interface Person {
//查询人信息
public void queryPerson();
//更新人的信息
public void updatePerson();
}
2、PersonImpl.java
public class PersonImplimplements Person {
publicvoid queryPerson() {
System.out.println("查询人类信息方法");
}
publicvoid updatePerson() {
System.out.println("更新人类信息方法");
}
}
3、PersonProxy.java
public class PersonProxyimplements Person {
privatePersonImpl personImpl;
publicPersonProxy(PersonImpl personImpl){
this.personImpl=personImpl;
}
publicvoid queryPerson() {
//调用更新之前先验证
testLoad();
//调用委托类的方法
personImpl.queryPerson();
}
publicvoid updatePerson() {
//调用更新之前先验证
testLoad();
//调用委托类的方法
personImpl.updatePerson();
}
publicvoid testLoad(){
System.out.println("验证用户是否已经登录,登录之后才能进行查询更新操作");
}
}
4、客户端
public class TestPerson {
publicstatic void main(String[] args) {
PersonImplpersonImpl = new PersonImpl();
PersonProxypersonProxy = new PersonProxy(personImpl);
personProxy.queryPerson();
personProxy.updatePerson();
}
}
观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码,如果业务变动,代码改动起来是个大问题。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,动态代理就出现了。
动态代理
JDK动态代理需要实现InvocationHandler接口,并且使用Proxy类;
InvocationHandler接口:
publicinterface InvocationHandler {
public Objectinvoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法 。
Object[] args:方法调用时所需要的参数。
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public staticObject newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandlerh) throws IllegalArgumentException
参数说明:
ClassLoaderloader:类加载器 。
Class<?>[]interfaces:得到全部的接口 。
InvocationHandlerh:得到InvocationHandler接口的子类实例 。
动态代理示例:
1.DynaProxy.java
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
public class DynaProxyimplements InvocationHandler {
privateObject targetObject;
publicObject createProxyInstance(Object targetobject){
this.targetObject=targetobject;
returnProxy.newProxyInstance(targetobject.getClass().getClassLoader(),targetobject.getClass().getInterfaces(),this);
}
privatevoid testLoad(){
System.out.println("验证用户是否登录,登录后才能进行操作");
}
publicObject invoke(Object proxy, Method method, Object[] args)
throwsThrowable {
//传入的目标类
testLoad();
//调用目标方法
Objectret = method.invoke(targetObject, args);
return ret;
}
}
2.客户端:
import Proxy.DynaProxy;
import Proxy.Person;
import Proxy.PersonImpl;
public class Client {
publicstatic void main(String args[]){
DynaProxyhandler=new DynaProxy();
//客户端不知道调用的是代理类
Personperson=(Person)handler.createProxyInstance(new PersonImpl());
person.queryPerson();
person.updatePerson();
}
}
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
但是通过看代码,大家可能注意到要想使用动态代理类,我们需要传入委托类的所有接口,但是,如果有些类并没有实现接口怎么办?则不能使用JDK代理,这就要使用cglib动态代理了。
Cglib动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
Cglib代理示例
1.PersonImpl类:
publicclass PersonImpl {
@Override
publicvoid queryPerson() {
System.out.println("查询人类信息方法");
}
@Override
publicvoid updatePerson() {
System.out.println("更新人类信息方法");
}
}
不用必须实现Person的接口。
2.CgLibProxy类:
importjava.lang.reflect.Method;
importnet.sf.cglib.proxy.Enhancer;
importnet.sf.cglib.proxy.MethodInterceptor;
importnet.sf.cglib.proxy.MethodProxy;
publicclass CgLibProxy implements MethodInterceptor {
privateObject target;
//创建代理对象
public Object getInstance(Object target){
this.target =target;
Enhancer enhancer=newEnhancer();
enhancer.setSuperclass(this.target.getClass());
//回调方法
enhancer.setCallback(this);
//创建代理对象
returnenhancer.create();
}
//回调方法
publicObject intercept(Object obj, Method method, Object[] args,
MethodProxyproxy) throws Throwable {
testLoad();
proxy.invokeSuper(obj,args);
return null;
}
publicvoid testLoad(){
System.out.println("验证用户是否登录,登录后才能进行操作");
}
}
3.客户端:
publicclass TestCgLlib {
publicstatic void main(String[] args){
CgLibProxycgp =new CgLibProxy();
PersonImplperson=(PersonImpl)cgp.getInstance(new PersonImpl());
person.queryPerson();
person.updatePerson();
}
}
因为是动态代理,所以使用cglib动态代理的客户端与使用动态代理的客户端代码一致,用户都不知道真正调用的是代理类。
结语
Java里代理的思想是通过加入中间的一个类,起到总控的目的。尤其是有了动态代理,我们增加一些集体的基础功能就方便了很多。