----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1.代理的概念与作用
程序中的代理:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类得相同方法,并在调用方法时加上系统功能的代码。如果采用工厂模式和配置文件的方法进行管理,则不需要修改客户端程序,在配置文件中配置时使用目标类还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样增加系统功能很容易。以后运行一段时间后,又想去掉系统功能也能很容易。
2.动态代理技术
①要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方法,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
②JVM可以在运行期动态生成出类得字节码,这种动态生成的类往往被用作代理类,,即动态代理类。
③JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
④CGLIB库可以动态生成一个类得子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CBLIB库。
⑤代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
2.1 在调用目标方法之前
2.2 在调用目标方法之后
2.3 在调用目标方法前后
2.4 在处理目标方法异常的catch块中
3.JVM生成动态类
创建实现了Collection接口的动态类和查看起名称,分析Proxy.getProxyClass方法的各个参数。
编码列出动态类中的所有构造方法和参数签名。
编码列出动态类中的所有方法和参数签名。
创建动态类的实例对象
用反射获得构造方法
编写一个最贱的InvocationHandler类
调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类得实例对象传进去
打印创建的对象和调用对象没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
将创建动态类的实例对象的代理改成匿名内部类的形式编写
代码如下:
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());
System.out.println("-----------begin constructor list----------");
Constructor[] constructors = clazzProxy1.getConstructors();
for(Constructor constructor : constructors) {
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name); //单线程中用StringBuilder效率更快,多线程用StringBuffer
sBuilder.append('(');
Class[] clazzParames = constructor.getParameterTypes();
for(Class clazzParame : clazzParames ) {
sBuilder.append(clazzParame.getName()).append(',');
}
if(clazzParames != null && clazzParames.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
System.out.println("-----------begin method list----------");
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods) {
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('('); //动态添加字符
Class[] clazzParames = method.getParameterTypes();
for(Class clazzParame : clazzParames ) {
sBuilder.append(clazzParame.getName()).append(',');
}
if(clazzParames != null && clazzParames.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
System.out.println("-----------begin create instance object----------");
//clazzProxy1.newInstance();
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1);//打印出null
proxy1.clear();
// proxy1.size(); //编译出错(返回集合的元素数int类型)(当调用size()方法时,内部会调用InvocationHandler的invoke()方法,而invoke方法返回空)
由于上面的MyInvocationHandler只是用过一次,因此可以在上面代码的基础上做些修改,让代码看起来更加紧凑:
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
采用工厂模式和配置文件的形式来改写代码,通过newProxyInstance()方法,让动态生成的类称为目标的代理:
private static Object getProxy(final Object target, final Advice advice) { //抽取方法(advice相当于系统功能,抽象为一个对象)
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
// new Class[]{Collection.class},
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
// ArrayList target = new ArrayList(); //这句话写在这里,返回值为0(每调用一次add方法,就要找InvocationHandler())
//的invoke()方法,每次都处理一个独立的目标(target),故元素数为0
/* long beginTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " +(endTime - beginTime));
return retVal;*/
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
上面的集中方法,都实现了同一种功能,只不过,代码一步步深入,做了一些修改,代码看起来更加专业,更符合面向对象的思想。张孝祥老师带领我们一步步的熟悉面向对象的过程,在这个过程中,我学会了很多,但是有些地方不是很懂,在以后的学习中,慢慢体会。
----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------
详情请查看: