代理模式
代理模式即ProxyPattern,什么是代理模式呢?即对其他对象提供一种代理以控制对这个对象的访问。
例如,我们有一个类不想被其他类直接调用,我们怎么办呢?我们只能通过代理的形式,让其他的类来调用代理类,代理类必须和目标类是用的同一个方法,且代理类的方法可以在目标类的基础上进行功能扩展,形式如下:
目标类:
class Person{
voidsay{
syso(‘hello’);
}
}
代理类:
class PersonProxy{
voidsay{
start time
Person.say();
end time
}
}
代理的架构图如下:
面向方面编程AOP
什么是AOP(Aspect oriented program)呢?系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面。交叉业务即安全,事务,日志等功能贯穿到好多个模块中。即它的重要原则是,不要把供货商暴露给你的客户。AOP的目标就是要使交叉业务模块化。
AOP即代理类在目标类的方法的周围放上一些安全,事物,日志,我们能实现和目标类一样的功能。
动态代理技术
什么是动态代理呢?即JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
注意:JVM生成的动态类必须实现一个或多个接口,所以Jvm生成的动态类只能用作具有相同接口的目标类的代理
代理类的各个方法通常除了要调用目标的相应的方法对外返回目标的结果外,还可以在代理方法中的四个位置加上系统的功能代码:
a) 在调用目标方法之前、
b) 在调用目标方法之后
c) 在调用目标方法的前后都有
d) 在处理目标方法异常的catch块中
总结:即:当我们代理类想去代理一个目标类的时候,我们通过什么方式来知道目标类的方法呢?我们只能让要被代理的目标类继承一个或多个接口,这些接口的方法,目标类并继承了这些方法。代理类也必须继承目标类所继承的接口,即也必须实现了接口的的方法,而且我们在实现接口方法的同时可以在周围加上安全,事物,日志等扩展功能。当我目标类没有继承接口,我们只能通过CGLIB库动态的生成一个类的子类,一个类的子类也可以作为一个代理类。
我们通常在写代理常用的方法.代理类为Proxy。
a) static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):返回代理类的 java.lang.Class对象,并向其提供类加载器和接口数组。
b) static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h):返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
我们根据方法,总共有两种方式:
第一种:
publicclass ProxyTest2 {
publicstaticvoid main(String[] args)throws Exception{
Class clazzProxy=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
Constructor consclazz=clazzProxy.getConstructor(InvocationHandler.class);
class MyInvocationHandlerimplements InvocationHandler{
ArrayList target=newArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable {
Object retVal=method.invoke(target, args);
return retVal;
}
}
Collection coll=(Collection)consclazz.newInstance(newMyInvocationHandler());
coll.add("haha");
coll.add("heihei");
System.out.println(coll.size());
}
}
第二种:
publicclass ProxyTest3 {
publicstaticvoid main(String[] args) {
Collection coll=(Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler() {
ArrayList target=newArrayList();
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable {
long begin=System.currentTimeMillis();
Object retVal=method.invoke(target, args);
Thread.sleep(100);
long end=System.currentTimeMillis();
System.out.println(method+"runningof time:"+(end-begin));
return retVal;
}
});
coll.add("haha");
coll.add("heihei");
System.out.println(coll.size());
}
}
我们看到以上两个代码都是已经指定好了目标类ArrayList,我们在实际开发中就不能这样了,因为那样没有灵活性。,我们可以写个方法是getProxy()来绑定接受目标同时返回代理对象,让调用者更方便,甚至不用接触任何代理的API。
那么我们怎么做呢?我们把系统功能代码模块化,即将切面代码改为通过参数形式提供
我们将上面第二种方式改为具有灵活性的代码
首先我们先定义一个接口,并让一个类去实现这个接口。
//需要实现的接口
publicinterface Advice {
publicvoid startMethod(Method method);
publicvoid endMethod(Method method);
}
//我们让一个类去实现这接口
publicclass MyAdviceimplements Advice {
privatelongbegin=0;
privatelongend=0;
@Override
publicvoid startMethod(Method method) {
begin=System.currentTimeMillis();
}
@Override
publicvoid endMethod(Method method) {
end=System.currentTimeMillis();
System.out.println(method+"runningof time:"+(end-begin));
}
}
publicclass ProxyTest3 {
publicstaticvoid main(String[] args) {
finalArrayList target=newArrayList();
Collection coll = (Collection)getProxy(target,new MyAdvice());
coll.add("haha");
coll.add("heihei");
System.out.println(coll.size());
}
//传入两个参数,一个是目标类,一个是日志,即advice
privatestaticObject getProxy(final Object target,final Advice advice) {
Object coll=Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Objectinvoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.startMethod(method);
Object retVal=method.invoke(target, args);
Thread.sleep(100);
advice.endMethod(method);
return retVal;
}
});
return coll;
}
}