讲到代理,好像在之前的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. CodeForces 719B Anatoly and Cockroaches (水题贪心)

    题意:给定一个序列,让你用最少的操作把它变成交替的,操作有两种,任意交换两种,再就是把一种变成另一种. 析:贪心,策略是分别从br开始和rb开始然后取最优,先交换,交换是最优的,不行再变色. 代码如下 ...

  2. Android Studio新建类头部注释和添加函数注释模板及快捷键

    一,Android Studio新建类头部注释 是不是有时候看到这个很心烦 其实Studio中有设置修改这些注释模板的信息的功能 其实很简单,只需要两步: 1.打开Setting设置面板,找到File ...

  3. poj3249【拓扑排序】

    //题意:   给出一个有向无环图,每个顶点都有一个权值. //         求一条从入度为0的顶点到出度为0的顶点的一条路径, //         路径上所有顶点权值和最大. //我觉得只要明 ...

  4. ubuntu 18 安装virtulenv以及virtualenvwrapper

    转自: https://www.jianshu.com/p/06533f19c4ad 首先安装pip,如果用的python3版本要安装pip3(自行google) sudo apt install v ...

  5. vs2015未能正确加载“ProviderPackage”包

    出现以下错误的解决方案 ---------------------------Microsoft Visual Studio---------------------------未能正确加载“Prov ...

  6. jmeter(二十二)jmeter测试Java请求

    目的:对Java程序进行测试 目录 一.核心步骤 二.实例 三.JMeter Java Sampler介绍 四.自带Java Request Sampler 一.核心步骤 1.创建一个Java工程: ...

  7. c语言读取一个文件夹下的全部文件(jpg / png 文件)

    #include <cstdio> #include <cstring> #include <unistd.h> #include<dirent.h> ...

  8. Linux--NiaoGe-Service-01

    安装环境介绍 CentOS 6.9_x86_64 我们选择的是基本安装,也即“Basic Server”. 安装完成后重启来到(runlevel 3)纯文本界面. 例题 批量创建账号:假设有5个账号x ...

  9. RHEL7.2安装及配置实验环境

    截图太多了,就不一一上传了,请查看这个分享网址 http://pan.baidu.com/s/1kVeYANH 什么时候博客更新下能直接把图一下复制进来多好!省事.

  10. PHP pack和unpack函数详解

    pack 压缩资料到位字符串之中. 语法: string pack(string format, mixed [args]...); 返回值: 字符串 函数种类: 资料处理 内容说明 本函数用来将资料 ...