代理这节又是一个难点,原因在于大多数学者心不静,看着复杂,就静不下心来学了。通过一段时间学习后,其实它也不过如此。接下来,我把我的随堂所得和心得写下来,以便自己以后好复习,也和大家分享下
代理从名字上可以知道,它是为别人做事。在我们平时写程序中,会经常遇到一些交叉业务,系统中又存在交叉业务,所谓交叉业务,我把它理解为在执行某个动作实现种功能之前或者之后都有做同一件事情。比如说,我在调用每一个方法之前都有将这个动作写入日志,在调用每一个方法之后,我也要将它执行的结果写入日志。写入日志这个动作可以把它理解为交叉业务。也可以把它成为系统中的一个切面。交叉业务的编程问题即为面向方面的编程(Aspect oriented program,简称A0P),AOP技术的目标就是要是这些交叉业务模块化。我们的代理可以很方便的解决这些问题。
切面:
下面是代理原理的结构图
从上图可以清晰的知道要想写一个代理必须具有一些条件:
1.代理类具有和目标类相同的方法声明;
2.编写一个与目标类具有相同接口的代理类,代理类的每个方法必须调用目标类的相同方法,并在调用方法时加上系统功能的代码;
3、要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,则可以使用代理类;
在实际应用中,我们不可能为每一个目标类都生成一个代理类,这样工作就太复杂了,所以我们一般都用动态代理来实现相应的代理类
1、JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
2、JVM生成的动态类必须实现一个或者多个接口,所以,JVM生成的动态类只能用在具有相同接口的目标类
3、如果生成的代理类没有实现相应的接口,则可以是有CGLIB库
4、代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回结果外,还可以在代理方法中
的如下四个位置加上系统功能代码:
<1>、在调用目标方法之前
<2>、在调用目标方法之后
<3>、在调用目标方法前后
<4>、在处理目标方法异常的catch块中
下面是一段简单获取动态代理类的代码已经其相应的方法
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
Class clazzProxy=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//获取动态代理类的字节码
System.out.println(clazzProxy.getName());
System.out.println("-------begin constructor------");
Constructor[]constructors=clazzProxy.getConstructors();
for(Constructor constructor:constructors){
StringBuilder str=new StringBuilder();
str.append(constructor.getName());
str.append("(");
Class[]clazzParamters=constructor.getParameterTypes();
for(Class clazzParamter:clazzParamters){
str.append(clazzParamter.getName()+",");
}
if(str.length()!=0&&clazzParamters!=null)
str.deleteCharAt(str.length()-1);
str.append(")");
System.out.println(str);
}
System.out.println("-------begin Method------");
Method[]methods=clazzProxy.getMethods();
for(Method constructor:methods){
StringBuilder str=new StringBuilder();
str.append(constructor.getName());
str.append("(");
Class[]clazzParamters=constructor.getParameterTypes();
for(Class clazzParamter:clazzParamters){
str.append(clazzParamter.getName()+",");
}
if(str.length()!=0&&clazzParamters!=null)
str.deleteCharAt(str.length()-1);
str.append(")");
System.out.println(str);
}
输出的结果:
$Proxy0
-------begin constructor------
$Proxy0(java.lang.reflect.InvocationHandler)
-------begin Method------
add(java.lang.Object)
hashCode)
clear)
equals(java.lang.Object)
toString)
contains(java.lang.Object)
isEmpty)
addAll(java.util.Collection)
iterator)
size)
toArray([Ljava.lang.Object;)
toArray)
remove(java.lang.Object)
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait)
wait(long,int)
wait(long)
getClass)
notify)
notifyAll)
Proxy类的方法如下
创建代理的实例对象
方式一:
//创建动态代理对象方式一
Constructor constructor1=(Constructor)clazzProxy.getConstructor(InvocationHandler.class);//得到相应的构造函数
//通过构造函数创建动态代理对象
Collection proxy1=(Collection)constructor1.newInstance(new InvocationHandler(){
//由于其构造函数的参数为必须实现了InvocationHandler()接口,所以通过构造匿名类作为参数传递
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
System.out.println(proxy1.toString());
方式二:
//创建动态代理对象二,这是由于Proxy类提供了相应的静态方法
Collection proxy2=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(), //指定相应的类加载器
new Class[]{Collection.class}, //指定代理类所要实现的接口
new InvocationHandler(){//指定所以实现的InvocationHanlder接口
ArrayList lists=new ArrayList();//指定目标类
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// TODO Auto-generated method stub
Long startTime=System.currentTimeMillis();//系统添加的代码,也可以称它为一个切面
Object reval=method.invoke(lists, args);//执行目标类的方法
Long endTime=System.currentTimeMillis();
System.out.println("methodName="+method.getName()+"Time="+(endTime-startTime));
return reval;
}
});
调用相应的方法:
proxy2.add("lisi");
proxy2.add("wangwu");
proxy2.add("chengda");
System.out.println(proxy2.size());
最终结果:
-------begin Create Instance------
null
methodName=addTime=0
methodName=addTime=0
methodName=addTime=0
methodName=sizeTime=0
3
调用方法的结构原理图如下:
上面的图很清楚的说明代理的工作原理,个人觉得它很重要,以前也没有认真的去学习它,所以这次,看着老师的视频,详细的介绍了它的工作原理,以便以后复习,希望对大家有用。