------- android培训、java培训、期待与您交流! ----------
动态代理类
1.什么是代理:
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2.Java中代理
按照代理的创建时期,代理类可以分为两种:
Java静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行 前,代理类的.class文件就已经存在了。
Java动态代理:与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行 时由Java反射机制动态生成,无需程序员手工编写它的源代码。动 态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因 为Java 反射机制可以生成任意类型的动态代理类。 java.lang.reflect 包中的Proxy类和InvocationHandler 接口提 供了生成动态代理类的能力。
3.Java中的动态代理类结构
通过图形我们可以知道:
1.代理类和目标类必须公用一个接口。
2.代理类为目标类添加系统功能代码。
3.代理类可以阻止客户端直接访问目标类(又叫委托类)。
4.与代理类相关的类和接口
java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
Proxy 的静态方法
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
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 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
InvocationHandler 的核心方法
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数是代理类实例,第二个参数是被调用的方法对象// 第三个参数是调用参数。
Object invoke(Object proxy, Method method, Object[] args)
对InvocationHandler 的方法进行分析:
例:
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest {
public static void main(String[] args) throws Exception{
Class clazzProxy =
Proxy.getProxyClass(Collection.class.getClassLoader(),
Collection.class );
System.out.println(clazzProxy.getName());
//创建动态类
Collectionproxy3= (Collection)Proxy.newProxyInstance(Constructor.class.getClassLoader(),
new Class[] {Collection.class} ,
new InvocationHandler(){
//指定目标1中的ArrayList
//ArrayList target = new ArrayList();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// TODO Auto-generated method stub
//指定目标2中ArrayList
//ArrayList target = new ArrayList();
long beginTime = System.currentTimeMillis();
Object retVal = method.invoke(target , args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" renning time of " + (endTime - beginTime));
System.out.println(target .size());
return retVal;
}
});
//每调用一次add方法,都是重新调用InvocationHandler中的invoke方法
proxy3.add("asd");
proxy3.add("ass");
proxy3.add("acs");
System.out.println(proxy3.size());
System.out.println(proxy3.getClass().getName());
//返回结果是$Proxy0为什么不是ArrayList呢?原因看Proxy说明文档
//只有hashcode,equals和toString给handler去实现,其他的方法都有object自己实现
}
}
通过上述的例子:
得知代理类运行都干了些什么?
1.通过System.out.println(method.getName()+" renning time of " + (endTime - beginTime));
得知代理类proxy3每运行一次add的方法时,都调用了一次invocationHandler对象。
2.通过创建ArrayList集合看出代理类proxy3是InvocationHandler还是调用invoke方法。
指定目标1在invoke方法的外面:通过proxy3.size()的方法得出结果为1,2,3,则说明代理类添加字符串都添加在同一个ArrayList里面。
指定目标2在invoke方法的里面:通过proxy3.size()的方法得出结果为1,1,0,则说明代理类每运行一次add方法就调用一次invoke方法,并且还创建一次ArrayList集合。
总结:
代理类每运行一次方法,就去调用invocationHandler对象中的invoke方法。
5.创建Java 动态代理实例,有四步骤:
1.所有方式的前提都要用反射获得构造方法。
Class clazzProxy =
Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class );
System.out.println(clazzProxy.getName());
Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
2.单独编写一个简单的InvocationHandler类。
class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
3.并且调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去。
Collection proxy1 =
(Collection) constructor.newInstance(new MyInvocationHandler());
4.打印创建的对象和调用对象的没有返回值的方法和getclass方法。
打印创建的对象
例:System.out.println(proxy1);
调用对象的没有返回值的方法
例:proxy1.clear();
getclass方法
例:System.out.println(proxy1.getClass());
创建Java 动态代理实例简写方式一:用内部类方式
Collection proxy2 = (Collection) constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
创建Java 动态代理实例简写方式二:用proxy的api(最常用方式)
Collection proxy3 = (Collection)Proxy.newProxyInstance(Constructor.class.getClassLoader(),
new Class[] {Collection.class} ,
new InvocationHandler(){
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
return retVal;
}
});
6.把动态代理扩展成框架
1)先了解什么是面向切面编程?
面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码
总结:AOP就是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
2)AOP的应用交叉业务的代码中的体现。
就是把硬编码中的日志功能,系统功能,等等功能变成了一个对象。也就是说切面的代码用对象把它给封装。然后以对象的形式传递。
那么给Invocationhandller要至少传递两个对象一个是目标类,另外一个是系统功能类。目标类以参数方式传递,系统功能以对象传递。
例:
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest2 {
public static void main(String[] args) throws Exception {
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class );
System.out.println(clazzProxy.getName());
System.out.println("-------------begin create instance object --------------");
Constructor constructors = clazzProxy.getConstructor(InvocationHandler.class);
1.把目标抽取成一个参数
//内部类要使用外部变量则要加final;
final Object target = new ArrayList();
}
// 通过下面那个方法我们得知,只要给一个目标参数,给一个系统对象就能自动生成一个代理。
private static Object getProxy(final Object target, final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces() ,
new InvocationHandler(){
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//把系统功能抽取称为一个对象
advice.beforeMethod(method);
Object retVal = method.invoke(target , args);
advice.afterMethod(method);
return retVal;
}
});
return proxy3;
}
}
2.在这里要把系统功能抽取成对象。
首先创建系统功能接口:
import java.lang.reflect.Method;
public interface Advice {
//一般这个接口有四个方法
//在日常里面执行的方法。
//在方法之前执行的方法。
void beforeMethod(Method method);//一般会接受三个参数(targer, args, method)
void afterMethod(Method method);
//在方法之后执行的方法。
//还有在方法前后执行的方法。
}
然后是实现接口。
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long beginTime = 0;
public void beforeMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("开始");
beginTime = System.currentTimeMillis();
}
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("结束");
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" renning time of " + (endTime - beginTime));
}
}