---------- android培训、java培训、期待与您交流! ----------
代理
概述
生活中的代理:就是常说的代理商,从厂商将商品卖给消费者,消费者不用很麻烦的到厂商在购买了。
程序中的代理:要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理、日志、计算方法的运行时间、事物管理等等。
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码
代理类的优点
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类还是代理类。
这样以后很容易切换,如果想要日志功能时,就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想换掉系统功能也很容易。
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest {
public static void main(String[] args) throws Exception{
//获取代理类Proxy的Class对象,传入的是类加载器和相应的字节码对象
Class clazzProxy1 = Proxy.getProxyClass(
Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());//$Proxy0
System.out.println("---begin constructor list------");
//获取代理类的构造方法,可能含有多个,得到数组
Constructor[] constructors = clazzProxy1.getConstructors();
//遍历数组,获取每个构造方法
for(Constructor constructor : constructors){
//先得到构造方法的名字,并装入字符串容器中
String name = constructor.getName();
StringBuilder sBul = new StringBuilder(name);
sBul.append('(');
//获取构造方法中的参数类型,并遍历
Class[] clazzParams = constructor.getParameterTypes();
for(Class clazzParam : clazzParams){
sBul.append(clazzParam.getName()).append(',');
}
//将最后一个逗号去除
if(clazzParams != null && clazzParams.length!=0)
sBul.deleteCharAt(sBul.length()-1);
sBul.append(')');
System.out.println(sBul.toString());
}
System.out.println("---begin method list------");
//获取代理类的方法,存入数组
Method[] methods = clazzProxy1.getMethods();
//遍历数组,获取每个方法
for(Method method : methods){
//先得到方法的名字,并装入字符串容器中
String name = method.getName();
StringBuilder sBul = new StringBuilder(name);
sBul.append('(');
//获取方法中的参数类型,并遍历
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sBul.append(clazzParam.getName()).append(',');
}
//将最后一个逗号去除
if(clazzParams!=null && clazzParams.length!=0)
sBul.deleteCharAt(sBul.length()-1);
sBul.append(')');
System.out.println(sBul.toString());
}
}
Aop面向方面编程
系统中存在着交叉业务(安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务。)一个交叉业务就是要切入到系统中的一个方面。
交叉业务的编程问题即面向方面的编程(AOP,Aspect Orentied Program),AOP的目标就是使交叉业务模块化,
可以采用将切面代理移动到原始方法的周围,这与直接在方法中编写切面代理的过程效果是一样的系统功能的代码,
可以写在调用目标方法的前后,或catch块内部。
动态代理
概述:
1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,这时就不能采用静态代理方式,需用动态代理技术。
2、动态代理类:JVM可在运行时,动态生成类的字节码,这种动态(不是代理,只是拿出来作为代理类)生成的类往往被用作代理类,即动态代理类。
注:JVM生成的动态类必须实现一或多个接口,所以JVM生成的动态代理类只能用作具有相同接口的目标类代理。
3、CGLIB库可以动态生成一个类的子类,一个类的子类也可以作为该类的代理,所以,如果要为一个没有实现接口的类生成动态代理,那么可以使用 CGLIB库。
4、代理类各个方法通常除了调用目标相应方法和对外返回目标返回的结果外,还可以再代理方法中的如下位置上加上系统功能代码:
1)在调用目标方法之前
2)在调用目标方法之后
3)在调用目标方法前后
4)在处理目标方法异常的catch块中。
分析JVM动态生成的类
1、创建动态类的实例对象:
1)用反射获得构造方法
2)编写一个最简单的InvocationHandler的类
3)调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去。
示例:
第一、打印创建的对象和调用对象的无返回值的方法和getClass方法,演示调用其他没有返回值的方法报告的异常
第二、将创建的动态类的实例对象的代理改写成为匿名内部类的形式编写。
package cn.itcast.test3;
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest {
public static void main(String[] args) throws Exception{
//创建动态代理类的三种方式
//方式一:通过接口的子类创建对象
Collection proxy1 = (Collection)
constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1);//null
System.out.println(proxy1.toString());//null
proxy1.clear();//无异常
//proxy1.size();//异常
//方式二:匿名内部类
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;
}
});
//方式三:
//通过代理类的newProxyInstance方法直接创建对象
Collection proxy3 = (Collection)Proxy.newProxyInstance(
//定义代理类的类加载器
Collection.class.getClassLoader(),
//代理类要实现的接口列表
new Class[]{Collection.class},
//指派方法调用的调用处理程序
new InvocationHandler() {
//创建集合,制定一个目标
ArrayList target = new ArrayList();
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//测试程序运行时间
long beginTime = System.currentTimeMillis();
//调用目标方法,将其从return抽出来,加入代理所需的代码
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
//测试
System.out.println(method.getName() +
" run time of " +
(endTime - beginTime));
return retVal;
}
}
);
//通过代理类调用目标方法,每调用一个目标的方法就会执行代理类的方法
//当调用一次add方法时,就会找一次InvocationHandler这个参数的invoke方法
proxy3.add("sdfd");
proxy3.add("shrt");
proxy3.add("rtbv");
System.out.println(proxy3.size());
System.out.println(proxy3.getClass().getName());
}
}
创建Advice进行时间计算
public class MyAdvice implements Advice {
Long startTime; //因为有两个方法用到了startTime(下面相减的输出方法调用中)
@Override
public void afterMethod(Method method) {
System.out.println("睡觉");
Long endTime = System.currentTimeMillis();
System.out.println(method.getName()+"rum Time of "+(endTime - startTime));
}
@Override
public void beforMethod(Method method) {
System.out.println("起床");
startTime = System.currentTimeMillis();
}
}
创建接口Advice
import java.lang.reflect.Method;
public interface AdviceTwo {
void befor(Method method);
void after(Method method);
}