讲到代理,好像在之前的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. Design with the User in Mind--从用户角度进行设计

    Back to Human Interface Design Design with the User in Mind 从用户角度进行设计 The success of a Mac app depen ...

  2. 简述网站、B/S架构与C/S架构

    一.什么是网站? 定义:网站是指在因特网上根据一定的规则,使用HTML等工具制作的用于展示特定内容相关网页的集合. 简单地说,网站是一种沟通工具(或者说是一种软件——建设网站也是软件开发的一种),我们 ...

  3. C#中自定义类数组和结构数组的使用

    如有雷同,不胜荣幸,若转载,请注明 C#中自定义类数组和结构数组的使用 最近在很多项目中发现很多时候给定的数组要实现某个逻辑或处理很是麻烦,一维数组,二维数组,,,等等需要经过n多转换,还不如自己写一 ...

  4. Appium问题记录

    1.Appium 提示覆盖安装Appium Android Input Manager for Unicode 问题 安卓手机在新版本中Appium 总是提示覆盖安装Appium Android In ...

  5. AtCoder Regular Contest 076 E - Connected?

    题目传送门:https://arc076.contest.atcoder.jp/tasks/arc076_c 题目大意: 给定一个\(R×C\)的矩阵,然后给定\(N\)对点,每对点坐标为\((X_{ ...

  6. Hdu 3966 Aragorn's Story (树链剖分 + 线段树区间更新)

    题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2 ...

  7. B.华华教月月做数学

    链接:https://ac.nowcoder.com/acm/contest/392/B 题意: 找到了心仪的小姐姐月月后,华华很高兴的和她聊着天.然而月月的作业很多,不能继续陪华华聊天了.华华为了尽 ...

  8. Cannot call sendRedirect()/forward after the response has been committed的问题

    问题其实已经很明确了,说明就是不能重定向,因为已经有response了. 然后一检查,是前面已经用servlet的printWriter打印东西了. 所以,重定向前 必须先保证没有任何的输出,包括:1 ...

  9. Nagios安装与部署

    Nagios概述: Nagios是一款开源免费(也有收费版的Nagios XI)的监控工具,可以用以监控Windows.Linux.Unix.Router.Switch,可以监控指定主机的物理基础资源 ...

  10. Hadoop工作流概念学习系列总述(一)

    不多说,这里,直接上干货!从这篇博客起,逐步分享如下: 1.工作流 2.Hadoop工作流(内置) 3.第三方框架--Azkaban(推荐外安装)