Java中的代理--proxy
讲到代理,好像在之前的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的更多相关文章
- (转)轻松学,Java 中的代理模式及动态代理
背景:讲到反射机制,肯定会想到动态代理. 轻松学,Java 中的代理模式及动态代理 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.值得注意的是,代理类和被代理类应该 ...
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- JAVA设计模式-动态代理(Proxy)源码分析
在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...
- java中动态代理实现机制
前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...
- java中设置代理的两种方式
1 前言 有时候我们的程序中要提供可以使用代理访问网络,代理的方式包括http.https.ftp.socks代理.比如在IE浏览器设置代理. 那我们在我们的java程序中使用代理呢,有如下两种方式. ...
- Java中的代理模式
代理模式在Java Web的框架中经常使用到.比如说在对数据库的访问中,核心功能是对数据库的增删改查,而连接数据库.处理事务等功能我们在开发中也要考虑到.所以我们将数据库的CRUD抽象到接口中,然后实 ...
- 说说Java中的代理模式
今天看到传智播客李勇老师的JDBC系列的第36节——通过代理模式来保持用户关闭连接的习惯.讲的我彻底蒙蔽了,由于第一次接触代理模式,感到理解很难,在博客园找到一篇文章,先记录如下: 引用自java设计 ...
- java中静态代理跟动态代理之间的区别
文章转载于:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另 ...
- java中动态代理
一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...
随机推荐
- js动态改变img元素src在IE无效的问题
做了个验证码功能,需要做个点击改变验证码图片的功能,使用js改变img的src,代码如下 $("#cerificationCodeImg").attr("src" ...
- hdoj1540 【线段树的表示】
大牛blog 这题的题解写给自己看-- 总结(瞎扯一点): 之前只会思考,len,sum,然后GG,如果只是sum和len的去用的话,就是在mid的时候会GG.然后这次也是参考大牛的写法,其实还是蛮简 ...
- CF487E Tourists【圆方树+tarjan+multiset+树剖+线段树】
圆方树不仅能解决仙人掌问题(虽然我仙人掌问题也没用过圆方树都是瞎搞过去的),还可以解决一般图的问题 一般图问题在于缩完环不是一棵树,所以就缩点双(包括双向边) 每个方点存他所在点双内除根以外的点的最小 ...
- bzoj 3365: [Usaco2004 Feb]Distance Statistics 路程统计【容斥原理+点分治】
统计在一个root下的两个子树,每个子树都和前面的运算一下再加进去对于这种需要排序的运算很麻烦,所以考虑先不去同子树内点对的算出合法点对个数,然后减去每一棵子树内的合法点对(它们实际上是不合法的,相当 ...
- iOS NavigationBar 导航栏自定义
1. 设置导航栏NavigationBar的背景颜色: a) setBarTintColor : 设置NagivationBar的颜色 也可以用 : [[UINavigationBar appear ...
- Helvetic Coding Contest 2017 online mirror (teams allowed, unrated) A
Description Your search for Heidi is over – you finally found her at a library, dressed up as a huma ...
- 题解报告:hihoCoder #1050 : 树中的最长路
描述 上回说到,小Ho得到了一棵二叉树玩具,这个玩具是由小球和木棍连接起来的,而在拆拼它的过程中,小Ho发现他不仅仅可以拼凑成一棵二叉树!还可以拼凑成一棵多叉树——好吧,其实就是更为平常的树而已. 但 ...
- HBase表结构设计--练习篇
一.表结构操作 1.建立一个表scores,有两个列族grad和course [hadoop@weekend01 ~]$ hbase shell hbase(main):006:0> creat ...
- 01_C++学习笔记_入门
1.float类型只能表示数字里面的前6位或者前7位.也就是说c++只能保证float类型的数字的前6位是正确的.如果要求的精度更高的话,请使用double和long double. float精度是 ...
- Spring------自动化装配Bean(二)
上一篇是基于 @ComponentScan自动装配Bean的实现,这一篇将通过java手动装配bean来实现. 手动装配相对于自动装配的优点: 可以自行定义Bean的各个属性. 添加额外的方法调度. ...