分析代理类的作用与原理
已经写好了一个类,里面有一个方法
class X{
void sayHello(){
System.out.println();
}
}
要在这个类上面增加一些辅助功能,但是没有类的源代码,怎么办?
现在做一个X的代理
XProxy
{
void sayHello()
{
startTime;
X.sayHello();
send time;
}
}
代理类的方法必须和目标方法一样。
原来客户端需要调用Target,现在不调用Target,调用Proxy, 代理调用Target,Proxy和目标实现相同的接口,在客户端编程时,不调用目标,,也不调用代理,而是直接调用接口。
在代理里面一定要调用目标的对应方法,但是在调用这个方法的时候在前面或者后面需要增加一下动能。
采用工程模式和配置文件进行管理。
交叉业务,不同的模块有相同的事物,这样的业务叫做交叉业务。例如,安全,日志等功能贯穿到很多的模块中。
methodA(){ methodA(){ methodA(){
-------------------------------------------------------切面
... ... .....
-------------------------------------------------------切面
} } }
像上面这样的,插入到了方法中。
像上面的这样的程序就叫做面向方面的编程。
面向方面的编程,AOP,AOP的目标就是要是交叉业务模块化,可以采用讲切面代码移动到原始方法的周围,这与直接在方法中编写切面代码与性的效果是一样的。
-------------------------------------------------------切面
methodA(){ methodA(){ methodA(){
... ... .....
} } }
-------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能模块的核心和关键技术。
面向方面的编程,就要使用代理。
一个系统中的各种接口很多,如果需要给一个系统所有的类增加代理功能,那将会需要很多的代理类,全部采用静态的方式,会是一种很麻烦的事情。
但是JVM提供了一种方便的方式。JVM可以在运行期间动态的生出类的字节码,这种动态生成的类往往被用做代理类,即动态代理类,但是这个类不是真正的代理类。
JVM生成的动态类必须要实现一个或多个接口,为什么呢,因为如果你没有方法,那么这个这个代理类什么都不做就没有什么用了,告诉很多的方法的方法可以直接告诉一个代理类一个借口,因为这样,代理类就必须要实现接口的所有方法,所以,JVM生成的动态类智能用作具有相同接口的目标类的代理。
有的类本身没有实现某个接口,现在通过什么样的方式来告诉JVM生成的代理类和目标类有相同的方法,有一个第三方库,CGLIB,这个CGLIB可以动态的生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常要调用目标的相应的方法,但是光调用目标方法是没有任何作用的,是需要干一些额外的事情,那么这些目标代码放在哪里呢,这里有以下四个地方。
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法之前后
4.在处理目标方法异常的catch块中。
StringBuilder 和StringBuffer的区别
都是动态的往字符串里面添加内容。在单线程的情况下StringBuilder效率高一点,所以在单线程的情况下使用StringBuiilder,现在就算有五个线程,但是也会有五个StringBuilder实例,不用考虑安全的问题,所以开小会小一点,所以效率高,但是StringBuffer任何时候都需要考虑安全问题,所以单线程的情况下效率低。
Collection proxy3 = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},//在这里不能使用可变参数,因为可变参数的最有一个位置
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
ArrayList target = new ArrayList() ;
long startTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis() ;
System.out.println(method.getName()+" running time of "+ (endTime - startTime));
//System.out.println(method.getClass());
return retVal ;
//return null;
}
}
);
Object obj = proxy3.add("zbb") ;
//System.out.println(obj.toString());
proxy3.add("gxd") ;
proxy3.add("zxn") ;
System.out.println(proxy3.size());
这里已经创建了一个InvocationHandler的子类的对象。在这里通过构造方法接受过一个handler,那么这个代理对象就会记住这个handler对象,因为构造方法就是用来给一个类里面的某个成员变量传值的作用,这里通过构造方法传递了一个InovacationHandler类型的对象,所以这里的代理对象就记住了你传递过来的InvocationHandler类型的对象。
当你在调用代理对象的方法是,如下:
proxy3.add("gxd") ;在调用这个add()这个方法时,他会去调用handler对象的invoke方法,只要代理调用一下方法,那么他就会相应的调用这个handler里面的invoke方法。
这里可以简单的写一下这个handler里面的方式是什么样子的。
int size(){
return Handler.invoke(this.this.getClass().getMethod("size"),null);
}
这个就是一个基本的代理对象调用size方法对的流程。add方法的调用也是一样。
刚开始我们第一次传入proxy1代理对象时,里面的invoke方法返回的是null,所以我们在调用有返回值的函数时,就会报错,因为我们这个size()函数要接受一个返回值。
我们看到上面的InvocationHandler接口中定义的invoke方法接受的三个参数,但是不明白里面里面的参数是什么意思,现在来看一下。
Client程序调用objProxy(代理对象).add("abc")方法时,设计三个要素:objProxy对象、add方法、 "abc"参数
ClassProxy${ | | |
add(Object obj){ | | |
return handler.invoke(Object proxy,Method method,Object[] args);
//这里的参数的意思是,调用了那个代理对象,调用了代理对象的哪个方法,传进来的参数是什么。
}
}
现在来看最上面的代码,当proxy3在调用add("abc")是,就调用了invoke(Object proxy, Method method, Object[] args),在这里就传入proxy3代理对象,add方法,和参数“abc”。
现在你需要的参数已经传入进来,就需要在目标的身上执行你需要的操作,例如
method.invoke(target,args);
这里调用代理调用的那个方法,但是不是在代理对象身上执行了,而是在目标对象身上执行。而且,刚才给代理传递什么方法,现在给目标就传递什么值。
在这过程每个方法返回结果的流程是首先目标对象的invoke方法返回结果给代理的invoke,外部代理invoke方法返回的结果返回给add方法,然后add方法再接着返回。
调用动态代理类的getClass方法,返回结果是正确的,什么意思?
System.out.println(proxy3.getClass().getName());//返回结果是$Proxy0
在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。