一、代理模式简介
代理模式是一种常用的设计模式。代理模式为其对象提供了一种代理以控制对这个对象的访问。代理模式可以将主要业务与次要业务进行松耦合的组装。根据代理类的创建时机和创建方式的不同,可以将其分为静态代理和动态代理两种形式:
在程序运行前就已经存在的编译好的代理类是为静态代理,
在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能是为动态代理。
二、静态代理
静态代理是代理模式的实现方式之一,是相对于动态代理而言的。所谓静态代理是指,在程序运行前,由程序员创建或特定工具类自动生成源代码并对其编译生成.class文件。代理模式的介绍与静态代理的实现见博文:设计模式之代理模式
三、动态代理
动态代理简介
对代理模式而言,一般来说,具体主题类与其代理类是一一对应的,这也是静态代理的特点。但是,也存在这样的情况:有N个主题类,但是代理类中的“预处理、后处理”都是相同的,仅仅是调用主题不同。那么,若采用静态代理,必然需要手动创建N个代理类,这显然让人相当不爽。动态代理则可以简单地为各个主题类分别生成代理类,共享“预处理,后处理”功能,这样可以大大减小程序规模,这也是动态代理的一大亮点。
在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等。
JDK动态代理机制的相关类与接口
java.lang.reflect.Proxy
:该类用于动态生成代理类,只需传入被监控对象隶属的类文件在内存中真实地址、被监控对象隶属的类文件实现接口以及InvocationHandler通知对象便可为目标接口生成代理类及代理对象。
// 方法 1: 该方法用于获取指定代理对象所关联的InvocationHandler static InvocationHandler
getInvocationHandler(Object proxy)// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象 static Class
getProxyClass(ClassLoader loader, Class[] interfaces)// 方法 3:该方法用于判断指定类是否是一个动态代理类 static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 static Object
newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)
java.lang.reflect.InvocationHandler
:该接口包含一个invoke方法,通过该方法实现对委托类的代理的访问,是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑。
Object invoke(Object proxy, Method method, Object[] args)
该方法是代理类完整逻辑的集中体现。在被监控行为将要执行时,会被JVM拦截。被监控行为和行为实现方法会被作为参数输送invoke,通常通过反射完成对具体角色业务逻辑的调用,并对其进行增强。
第一个参数既是代理类实例。
第二个参数是被调用的方法对象。
第三个方法是调用参数。
java.lang.ClassLoader
:类加载器类,负责将类的字节码装载到Java虚拟机中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成动态代理类同样需要通过类加载器来进行加载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class 文件中。
JDK动态代理使用步骤
JDK动态代理的一般步骤如下:
1、创建被代理的接口和类;
2、实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
3、调用Proxy的静态方法,创建代理类并生成相应的代理对象;
4、使用代理。
生活案例
饭前便后要洗手
一、分析出主要业务和次要业务
【主要业务】:吃饭,上厕所
【业务业务】:洗手
二、JDK代理模式实现
1、接口角色: 定义所有需要被监听行为
2、接口实现类:中国人,印度人
3、通知类:
1)次要业务进行具体实现
2)通知JVM,当前被拦截的主要业务方法与次要业务方法应该如何绑定执行
4.监控对象(代理对象)
1) 被监控实例对象
2) 需要被监控的行为
3)具体通知类实例对象
代码实现
通过接口,定义被监控的行为。
/**
* 通过接口定义行为
* 只有需要被监控的行为才有资格
* 在这里声明
*/
public interface BaseService {
void eat();
void wc();
}
编写接口的实现类,即具有某些行为的实体。
public class Person implements BaseService {
@Override
public void eat() {//主要业务,代理模式要求开发人员只关心主要业务
System.out.println("使用筷子吃饭....");
}
@Override
public void wc() {
System.out.println("测试地球重力是否存在");
}
}
创建通知类。
public class Invocation implements InvocationHandler {
private BaseService obj;//具体被监控对象
public Invocation(BaseService param){
this.obj =param;
}
/*
*
* invoke方法:在被监控行为将要执行时,会被JVM拦截
* 被监控行为和行为实现方会被作为参数输送invoke
* ****
* 通知JVM,这个被拦截方法是如何与当前次要业务方法绑定实现
* invoke方法三个参数
*
* int v= 小明.eat();//JVM拦截
* eat方法封装为Mehtod类型对象
* eat方法运行时接受所有的实参封装到Object[]
* 将负责监控小明的代理对象作为invoke方法第一个参数
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
//0.局部变量,接受主要业务方法执行完毕后返回值
Object value;
//1.确认当前被拦截行为
String methodName= method.getName();
//2.根据被拦截行为不同,决定主要业务和次要业务如何绑定执行
if("eat".equals(methodName)){//饭前要洗手
wash(); //洗手
value=method.invoke(this.obj, params); //吃饭
}else{//便后要洗手
value=method.invoke(this.obj, params);
wash();
}
return value; //返回被拦截方法,需要调用地方
}
//次要业务
public void wash(){
System.out.println("-----洗手----");
}
}
创建监控对象(代理对象)
通过Proxy类的静态方法newProxyInstance创建代理对象。
public class ProxyFactory {
/*
*
* JDK动态代理模式下,代理对象的数据类型
* 应该由监控行为来描述
* 参数: Class文件,监控类
*/
public static BaseService Builder(Class classFile) throws Exception {
//1.创建被监控实例对象
BaseService obj = (BaseService) classFile.newInstance();
//2.创建一个通知对象 用接口来描述
InvocationHandler adviser = new Invocation(obj);
//3.向JVM申请负责监控obj对象指定行为的监控对象(代理对象)
/*
* loader:被监控对象隶属的类文件在内存中真实地址
* interfaces:被监控对象隶属的类文件实现接口
* h:监控对象发现小明要执行被监控行为,应该有哪一个通知对象进行辅助
*/
BaseService $proxy = (BaseService) Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
adviser);
return $proxy;
}
}
测试
public class TestMain {
public static void main(String[] args) throws Exception {
BaseService mike= ProxyFactory.Builder(Person.class);
mike.eat();
}
}