您的位置:首页 > 职场人生

黑马程序员-----动态代理

2014-03-08 18:54 381 查看
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

一、代理简介

生活中的代理:

武汉人从武汉的代理商手中买联想电脑和直接跑到北京联想总部买电脑,它们最终的主体业务目标相同,但是还是又去别的

程序中的代理

1、要为已存在的多个具有相同接口的目标类 的各个方法增加一些系统功能;

2、编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码

3、如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易;

二、AOP:交叉业务的编程问题即为面向方面的编程,简称AOP,AOP目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的;

OOP:面向对象的编程;

注意:代理是实现AOP功能的核心和关键技术;

三、动态代理技术

1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情;

2、JVM可以在运行期动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

3、JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理;

4、CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库;

5、代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统代码:

a、在调用目标方法之前

b、在调用目标方法之后

c、在调用目标方法之前后

d、在处理目标方法异常的catch块中

三、分析JVM动态生成的类

public class ProxyTest {

public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());

System.out.println("------Constructor list-------");
Constructor[] constructors = clazzProxy1.getConstructors();
for(Constructor constructor:constructors){
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = constructor.getParameterTypes();
for(Class clazzParam:clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length!=0){
sBuilder.deleteCharAt(sBuilder.length()-1);
}
sBuilder.append(')');
System.out.println(sBuilder.toString());
}

System.out.println("------methods list-------");
Method[] methods = clazzProxy1.getMethods();
for(Method method:methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam:clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length!=0){
sBuilder.deleteCharAt(sBuilder.length()-1);
}
sBuilder.append(')');
System.out.println(sBuilder.toString());
}

System.out.println("------methods list-------");
//clazzProxy1.newInstance();//Proxy类没有无参的构造方法所以不能调用newInstance方法
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
Collection proxy = (Collection) constructor.newInstance(new InvocationHandler(){

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;


});
System.out.println(proxy);//Collection类对象proxy调用方法toString返回值为null;

Collection proxy1 = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(), 
new Class[]{Collection.class}, 
new InvocationHandler(){
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/*Collection对象每调用一次方法invoke方法就执行一次
Collection接口中的方法的运行原理
int size(){
return handler.invoke(this,this.getMethod("size"),null);
}*/
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;

}
);
proxy1.add("aaa");
proxy1.add("bbb");
proxy1.add("ccc");
System.out.println(proxy1.size());
}

}

让java虚拟机(JVM)创建动态类及其实例对象需要提供的信息:

1、生成的类中有哪些方法,同归哦让其实现哪些接口的方式进行告知;这个类实现了哪些接口

2、产生的类字节码必须有一个关联的类加载器对象;

3、生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了 

invoke方法接收的三个参数的意思:当前正在调用方法的代理对象,代理对象调用的方法,方法接收的参数;

class Proxy{
add(Object obj){
return handler.invoke(Object proxy,Method method,Object[]args);
}

}

四、动态代理工作原理

客户端调用代理,代理的构造方法接受一个handler对象,客户端调用代理的各个方法,代理的各个方法会把调用请求转发给刚才通过构造方法传进去的handler对象,这个handler对象又把各个请求分发给目标的相应方法;handler的invoke方法中可以加入日志功能,还可以调用目标的对应方法;

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流!
----------------------

详细请查看:<a href="http://edu.csdn.net" target="blank">http://edu.csdn.net</a>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: