讲到代理,好像在之前的springMVC,还是spring中或者是hibernate中学习过,并没有特别在意,这次好好理解一下。(原来是在spring中的AOP,面向切面 Aspect Oriented Program,无语了,这都忘了)

一、代理的概念和作用

1、程序中的代理

要为已存在的多个具有相同接口目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法的运行时间、事务管理等等,

 class x{
void sayHello(){System.out.print("Hello world")}
} // 作为x的代理类除了打印Hello World 还要计算程序执行的时间
class XProxy{
void sayHello(){
startTime;
System.out.print("Hello World")
endTime;
}
}

注意:当调用目标方法的时候,直接调用代理类,既能完成目标方法,还能做一些额外的事情,这就是代理类的作用吧,也就是代理类和目标类具有相同的方法,但是在调用方法时,会加上系统功能的其他代码,下面的图应该更好理解一点:(具有相同的方法,但是调用的时候加上了系统代码)

2、采用工厂模式和配置文件的方式进行管理,则不需要修改客户端的程序,在配置文件中是使用目标类,还是代理类,这样,以后很用以切换

3、代理是实现AOP 技术的核心和关键技术

4、动态代理技术

(1)JVM可以在运行期间动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类

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

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

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

A:调用目标方法之前

B:调用目标方法之后

C:在调用目标方法前后

D:在处理目标方法异常的catch块中

二、分析JVM动态生成的类

1、创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass()方法的各个参数

2、编码列出动态类中的所有构造方法和参数名称

3、编码列出动态类中所有方法和参数名称

         Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName()); System.out.println("------begin constructors 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 > ) {
sBuilder.deleteCharAt(sBuilder.length() - );
}
sBuilder.append(")");
System.out.println(sBuilder.toString());
}
System.out.println("------end constructors list-------------"); System.out.println("------begin 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 > ) {
sBuilder.deleteCharAt(sBuilder.length() - );
}
sBuilder.append(")");
System.out.println(sBuilder.toString());
}
System.out.println("------end methods list-------------");

4、创建动态类的实例对象

A:用反射获得构造方法

B:编写一个简单的InvocationHandler类

C:调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

D:打印创建的对象和调用对象的没有返回值的方法和getClass()方法,演示调用其他有返回值的方法出现了异常

E:将创建动态类的实例对象的代理改成匿名内部类的形式编写

5、让JVM创建动态类及实例对象,需要给它提供哪些信息?

A:生成的类中有哪些方法,通过让其实现哪些接口的方式告知

B:产生的类字节码必须有一个关联的类加载器对象

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

        Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName()); System.out.println("------begin creat instance list-------------");
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class); // 普通的方法进行实例化
class myInvokeHandler1 implements InvocationHandler {
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
return null;
}
} Collection proxy1 = (Collection) constructor.newInstance(new myInvokeHandler1());
System.out.println(proxy1);
proxy1.clear();
//proxy1.size(); // 用内部类的方式进行实例化
Collection proxy2 = (Collection) constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
return null;
}
}); // 直接一步到位 其实本质还是一样的,只不过写法不一样
Collection proxy3 = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler() { List target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long beginTime = System.currentTimeMillis();
// 这里就是Collection类或者是子类中的方法执行 也就是调用了add()方法 同时我还做了一些其他事情。计算程序运行时间
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "running time:" + (endTime - beginTime));
return retVal;
}
}); System.out.println("------end creat instance list-------------"); proxy3.add("aaa");
proxy3.add("bbb");
proxy3.add("ccc");
System.out.println(proxy3.size());

三、动态生成类的运行原理分析

1、先分析一下InvocationHandler对象中的invoke方法的三个参数的意义:

A:当前代理对象

B:代理对象执行哪个方法

C:方法中需要哪些参数

2、动态代理的工作原理图

在InvocationHandler类中执行invoke()方法的时候,如何来编写可配置的代码,让程序在运行的时候,将代码以参数的形式进行传递,这种解决办法,在java中是不常用的,一般的做法是将对象传递给InvocationHandler的invoke()方法中去执行传递的对象中的方法,这样的话,我们就可以,也算是动态的去改变执行的代码了,这就是最关键的部分,最巧妙的部分,值得去学习:这就是面向切面编程,就是把代码封装到一个对象中,将这个对象传递给需要执行的方法,让方法去执行传递中的对象中的方法,传递进来的对象也就是那个切面,去执行切面中的方法,改造之前的代码:

 // 这个就是需要传入的参数对象的抽象接口,同时传入了目标方法
public interface Advice { void beforeMethod(Method method);
void afterMethod(Method method); } // 参数接口的实例化对象
public class MyAdvice implements Advice { long beginTime = 0;
long endTime = 0; @Override
public void beforeMethod(Method method) {
System.out.println("Advice中的方法开始执行了。。。");
beginTime = System.currentTimeMillis();
} @Override
public void afterMethod(Method method) {
endTime = System.currentTimeMillis();
System.out.println(method.getName() + "running time:" + (endTime - beginTime));
System.out.println("Advice中的方法结束执行了。。。");
} }
 // 改造之后的方法
private static Object getProxy(final Object target, final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*long beginTime = System.currentTimeMillis();
// 这里就是Collection类或者是子类中的方法执行 也就是调用了add()方法 同时我还做了一些其他事情。计算程序运行时间
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "running time:" + (endTime - beginTime));
return retVal;*/ // 这里就是Collection类或者是子类中的方法执行 也就是调用了add()方法 同时我还做了一些其他事情。计算程序运行时间
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method); return retVal; }
});
return proxy3;
}
// 实际调用,验证正确性
public static void main(String[] args){
final List target = new ArrayList();
// 直接一步到位 其实本质还是一样的,只不过写法不一样
Collection proxy3 = (Collection) getProxy(target, new MyAdvice()); proxy3.add("aaa");
proxy3.add("bbb");
proxy3.add("ccc");
System.out.println(proxy3.size());
}

总结:这就是传说中的spring框架的雏形,真的是这样子的吗?传说中的spring就是这样的啊!

Java中的代理--proxy的更多相关文章

  1. (转)轻松学,Java 中的代理模式及动态代理

    背景:讲到反射机制,肯定会想到动态代理. 轻松学,Java 中的代理模式及动态代理 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.值得注意的是,代理类和被代理类应该 ...

  2. JAVA设计模式-动态代理(Proxy)示例及说明

    在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...

  3. JAVA设计模式-动态代理(Proxy)源码分析

    在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...

  4. java中动态代理实现机制

    前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...

  5. java中设置代理的两种方式

    1 前言 有时候我们的程序中要提供可以使用代理访问网络,代理的方式包括http.https.ftp.socks代理.比如在IE浏览器设置代理. 那我们在我们的java程序中使用代理呢,有如下两种方式. ...

  6. Java中的代理模式

    代理模式在Java Web的框架中经常使用到.比如说在对数据库的访问中,核心功能是对数据库的增删改查,而连接数据库.处理事务等功能我们在开发中也要考虑到.所以我们将数据库的CRUD抽象到接口中,然后实 ...

  7. 说说Java中的代理模式

    今天看到传智播客李勇老师的JDBC系列的第36节——通过代理模式来保持用户关闭连接的习惯.讲的我彻底蒙蔽了,由于第一次接触代理模式,感到理解很难,在博客园找到一篇文章,先记录如下: 引用自java设计 ...

  8. java中静态代理跟动态代理之间的区别

    文章转载于:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另 ...

  9. java中动态代理

    一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...

随机推荐

  1. rn滑动返回页面监听

    开发rn的同学都已经知道这个问题很坑了,真的很难弄,网上的方法尝试过很多,返回的的时候回调,是用的最多的,最开始我也是用的这种方式,但是滑动返回的时候监听不到.并且用起来也比较麻烦,不但需要在当前页面 ...

  2. PhpStorm之设置字体大小

    1.点击左上角的File,再点击setting:(Ctrl+Alt+S) 2.进入 Editor  /  General,选择 Change font size (Zoom) with Ctrl+Mo ...

  3. hdoj5793 A Boring Question【找规律】

    找出的规律.... 1 2 3 2 2 7 3 2 15 4 2 31 5 2 63 1 3 4 2 3 13 3 3 40 4 3 121 5 3 361 然后我们来推个公式: 比如说a2=3a1+ ...

  4. python __builtins__ filter类 (24)

    24.'filter', 用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表.该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True ...

  5. bzoj 3573: [Hnoi2014]米特运输【树形dp+瞎搞】

    阅读理解题,题意是以1为根的有根树,每个点有点权,求修改最少点权能使每个点的权值等于其所有子节点权值之和并且每个点的所有子节点权值相等的个数 然后就比较简单了,就是有个技巧是数太大,需要对所有操作都取 ...

  6. SQL - 单引号和双引号的区别

    原文转载至:SQL中的单引号和双引号有区别吗? 在标准 SQL 中,字符串使用的是单引号. 如果字符串本身也包括单引号,则使用两个单引号(注意,不是双引号,字符串中的双引号不需要另外转义). 但在其它 ...

  7. 洛谷1083(差分+二分 or 线段树)

    第一种方法:可以二分最大天数订单的答案然后通过差分求一下是否可行. ; int n, m, a[maxn], ans; struct section { int cnt, l, r; }b[maxn] ...

  8. Linux下文件权限的设置

    文件/目录权限设置命令:chmod 这是Linux系统管理员最常用到的命令之一,它用于改变文件或目录的访问权限.该命令有两种用法: 用包含字母和操作符表达式的文字设定法 ) 其语法格式为:chmod ...

  9. Ionic之存储信息、取出存储信息、注销存储信息

    每一个app软件在登录的时候,都会本地存储登录信息,需要用到数据的时候,就直接在本地获取,而不是每一次应用的时候都要请求到服务器来验证登录信息,减少服务器的负担.所以在设计混合HTML5 移动应用程序 ...

  10. [转]深入浅出WPF(7)——数据的绿色通道,Binding

    本文转自:http://liutiemeng.blog.51cto.com/120361/95273 小序: 怎么直接从2蹦到7啦?!啊哦,实在是不好意思,最近实在是太忙了,忙的原因也非常简单——自己 ...