java高新技术-代理
程序中代理的概念与作用
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等。
代理架构图:
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类,还是使用代理类,
这样以后很容易切换,例如:想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易。
1.分析代理类的作用与原理及AOP概念
代理是实现AOP功能的核心和关键技术。
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面。
安全,事务,日志等功能要贯穿到好多个模块中,所以,他们就是交叉业务
用具体的程序代码描述交叉业务:
交叉业务的编程问题即为面向方面的变成(Aspect oriented program,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围。这与直接在方法中编写切面代码的运行效果是一样的。因为不可能去修改用户的代码。如下所示:
---------------------------------------------------- 切面
func1 func2 func3
{ { {
} } }
---------------------------------------------------- 切面
2.动态代理技术
> JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM动态生成的类不是代理,而是可以作为其他类的代理。
> JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理.
*如果目标类没有实现接口,要想生成代理使用 CGLIB
> CGLIB 库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的的类生成动态代理类,那么可以使用CGLIB库。
> 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
> 1.在调用目标方法之前
> 2.在调用目标方法之后
> 3.在调用目标方法前后
> 4.在处理目标方法异常的catch块中
void sayHello(){
.............. try{
target.sayHello();
}catch(Exception e){
...............
}
.................
}
创建动态类及查看其方法列表信息
分析JVM动态生成的类
创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数
static Class<?> |
getProxyClass(ClassLoader loader, Class<?>... interfaces) Returns the java.lang.Class object for a proxy classgiven a class loader and an array of interfaces. |
编码列出动态类中的所有构造方法和参数签名
编码列出动态类中的所有方法和签名
创建动态类的实例对象
> 用反射获得构造方法
> 编写一个最简单的InvocationHandler类
> 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
> 打印创建的对象和调用对象的没有返回值得方法和getClass方法,调用其他有返回值的方法会抛异常。
public class ProxyTest { public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//这里指定的classLoader 为任意一个ClassLoader,通常用与接口相同的
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println("-------------" + clazzProxy1.getName()); //-------------com.sun.proxy.$Proxy0
System.out.println("-------------Begin constructor list-----------");
Constructor[] constructors = clazzProxy1.getConstructors(); //返回所有的构造方法
for(Constructor constructor : constructors){
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzs = constructor.getParameterTypes(); //返回所有的参数列表类型
for(Class clazz: clazzs){
sBuilder.append(clazz.getName());
sBuilder.append(",");
}
if(clazzs != null && clazzs.length != 0){
sBuilder.deleteCharAt(sBuilder.length() - 1); //去掉最后一个 ','
}
sBuilder.append(')');
System.out.println(sBuilder.toString());
} System.out.println("-------------Begin method list-----------");
Method[] methods = clazzProxy1.getMethods(); //返回所有的方法
for(Method method : methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzs = method.getParameterTypes(); //返回所有的参数列表类型
for(Class clazz: clazzs){
sBuilder.append(clazz.getName());
sBuilder.append(",");
}
if(clazzs != null && clazzs.length != 0){
sBuilder.deleteCharAt(sBuilder.length() - 1); //去掉最后一个
}
sBuilder.append(')');
System.out.println(sBuilder.toString());
} System.out.println("--------------begin create instance----------------");
//以下为创建代理对象
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHandler implements InvocationHandler{ @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
} }
Collection proxy1 = (Collection) constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1); //null 这里为null是因为toString()返回为null
System.out.println(proxy1.toString());//null 这里为null 没有报错是因为调用了handler的toString(),handler的返回为null
proxy1.clear(); //调用没有返回值的就不会报错,无返回值为void invoke返回的是null 正确
// proxy1.size(); //如果collection为null, 调用有返回值的方法时会抛出异常 是由于调用handler的invoke方法,返回值为null, //以上为一种方式,还可稍微简化下
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){ @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}); }
}
> InvocationHandler 对象的内部功能
System.out.println("--------------begin create instance----------------");
//以下为创建代理对象
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHandler implements InvocationHandler{ @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
} }
Collection proxy1 = (Collection) constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1); //null 这里为null是因为toString()返回为null
System.out.println(proxy1.toString());//null 这里为null 没有报错是因为调用了handler的toString(),handler的返回为null
proxy1.clear(); //调用没有返回值的就不会报错,无返回值为void invoke返回的是null 正确
// proxy1.size(); //如果collection为null, 调用有返回值的方法时会抛出异常 是由于调用handler的invoke方法,返回值为null, //以上为一种方式,还可稍微简化下
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){ @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}); //可以使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 更为简单
Collection proxy3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class}, new InvocationHandler() { ArrayList target = new ArrayList<String>(); //为代理类挂上目标类,代理类需为前边接口的实现类
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/**
* 如果目标类出现在这,则proxy每次调用方法时都会调用invoke() 每次都会是一个全新的ArrayList对象
* 即后边的size() 总是0
*/
// ArrayList target = new ArrayList<String>(); //为代理类挂上目标类,代理类需为前边接口的实现类
long startTime = System.currentTimeMillis();
Object obj = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time " + (endTime-startTime));
return obj;
}
});
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
/**
* 在执行proxy3.getClass().getName()
* 返回的是com.sun.proxy.$Proxy0,调用的是代理的不是目标的getClass()
* 是由于 getClass() 是继承的Object的方法,其中Object中只有 hashCode,equals,toString这三个方法会委托给handler去调用
* 其余的Object中的其他方法 proxy自己有实现,不交给handler
* java API:
* If a proxy interface contains a method with the same name and parameter signature
* as the hashCode, equals, or toString methods of java.lang.Object, when such a method is invoked on a proxy instance,
* the Method object passed to the invocation handler will have java.lang.Object as its declaring class.
* In other words, the public, non-final methods of java.lang.Object logically precede all of the proxy interfaces for the determination of which Method object to pass to the invocation handler.
*/
System.out.println(proxy3.getClass().getName());//com.sun.proxy.$Proxy0
总结分许动态代理类的设计原理与结构
> InvocationHandler 对象的运行原理
Client程序调用objProxy.add("abc")方法时,涉及三个要素:objProxy对象、add方法、"abc"参数
Class Proxy${
add(Object object){
return handler.invoke(Object proxy, Method method, Object[] args);
}
}
动态代理的工作原理图
这里的log() 相当于一个系统功能,这个不应该一直是一个功能,而应该配置成为动态变化的,即可动态设置的系统功能。
> 面向切面编程:
把切面的代码,以对象的形式进行封装,以对象的形式传递给你,你只要执行这个对象,就等于执行了切面的代码
> 编写可生成代理和插入通告的通用方法
Collection proxy3 = (Collection)getProxy(new ArrayList<>(),new MyAdvice());
//每调用一次,都会调用 一次InvocationHandler 的 invoke方法
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
getProxy方法
private static Object getProxy(final Object target, final MyAdvice 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 {
advice.beforeMethod(method);
Object object = method.invoke(target, args);
advice.afterMethod(method);
return object;
}
});
return proxy3;
}
MyAdvice类:
public class MyAdvice implements Advice { long startTime = 0;
@Override
public void beforeMethod(Method method) {
startTime = System.currentTimeMillis(); } @Override
public void afterMethod(Method method) {
long endTime = System.currentTimeMillis();
System.out.println(method.getName()
+ " running time of " + (endTime - startTime));
} }
目的是把 代理 和系统功能都转义成了外边的 对象
完成Spring 只完成一件事:写MyAdvice, target是在配置文件中配置的
实现类似Spring的可配置的AOP框架
第一个类 FactoryBean bean工厂用于产生bean
public class BeanFactory { Properties properties = new Properties(); public BeanFactory(InputStream is) {
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
} public Object getBean(String name) throws Exception { //根据传入的名字判断为javaBean还是代理
String className = properties.getProperty(name);
Object bean = null;
Class clazz = Class.forName(className);
bean = clazz.newInstance();
if (bean instanceof ProxyFactoryBean) { //如果为一个代理对象,产生响应的代理bean
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
Advice advice = (Advice) Class.forName(properties.getProperty(name+".advice")).newInstance();
Object target = Class.forName(properties.getProperty(name+".target")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
Object proxy = proxyFactoryBean.getProxy();
return proxy;
}
return bean; //直接返回普通javaBean
} }
ProxyFactoryBean.java 产生代理的类 要求有两个参数 目标类target 以及Advice
public class ProxyFactoryBean { private Object target;
private Advice advice; /**
* @return the target
*/
public Object getTarget() {
return target;
} /**
* @param target
* the target to set
*/
public void setTarget(Object target) {
this.target = target;
} /**
* @return the advice
*/
public Advice getAdvice() {
return advice;
} /**
* @param advice
* the advice to set
*/
public void setAdvice(Advice advice) {
this.advice = advice;
} public Object getProxy() {
Object proxy3 = Proxy.newProxyInstance(target.getClass()
.getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
/**
* 如果目标类出现在这,则proxy每次调用方法时都会调用invoke()
* 每次都会是一个全新的ArrayList对象 即后边的size() 总是0
*/
// ArrayList target = new ArrayList<String>();
// //为代理类挂上目标类,代理类需为前边接口的实现类
advice.beforeMethod(method);
Object obj = method.invoke(target, args);
advice.afterMethod(method);
return obj;
}
});
return proxy3;
} }
存放配置的配置文件 config.properties
xxx=java.util.ArrayList
#xxx=com.java.javaenhance1.aopFrameWork.ProxyFactoryBean
xxx.advice=com.java.javaenhance1.MyAdivce
xxx.target=java.util.ArrayList
测试类:
public class AopFrameWorkTest { public static void main(String[] args) throws Exception { InputStream is = AopFrameWorkTest.class.getResourceAsStream("config.properties");
Object bean = new BeanFactory(is).getBean("xxx");
System.out.println(bean.getClass().getName());
} }
以上的例子实现了Spring的两大核心 IOC(Bean工厂) 及AOP(动态代理)
java高新技术-代理的更多相关文章
- 使用Java原生代理实现AOP
### 本文由博主柒.原创,转载请注明出处 ### 完整源码下载地址 [https://github.com/MatrixSeven/JavaAOP](https://github.com/Matri ...
- Java 动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- Java动态代理全面分析
代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1 主题:规定代理类和真实对象共同对外暴露的接口: 2 代理类:专门代理真实对象的类: 3 ...
- JAVA动态代理模式(从现实生活角度理解代码原理)
所谓动态代理,即通过代理类:Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联. java动态代理主要是使用java.lang.reflect包中的两个 ...
- 学习AOP之JAVA的代理机制
从一个输出日志的实例分析JAVA的代理机制 一.通用的日志输出方法 :需要在每个类里都增加对输出日志信息的代码 二.通过面向接口编程实现日志的输出(JAVA的静态代理):虽然实现了业务逻辑与输出日志 ...
- Java 动态代理作用是什么?
Java 动态代理作用是什么? 1 条评论 分享 默认排序按时间排序 19 个回答 133赞同反对,不会显示你的姓名 Intopass 程序员,近期沉迷于动漫ING 133 人赞同 ① 首先你 ...
- java动态代理原理
我们经常会用到Java的动态代理技术, 虽然会使用, 但是自己对其中的原理却不是很了解.比如代理对象是如何产生的, InvocationHandler的invoke方法是如何调用的?今天就来深究下Ja ...
- java 动态代理示例,带主要注释
Java proxy是基于反射,仅仅支持基于接口的动态代理. java 动态代理是一切架构的基础,必须了解. 废话少说,先上代码获得感性认识. 示例代码有主要注释. 接口: public interf ...
- java动态代理浅析
最近在公司看到了mybatis与spring整合中MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接口的动态代理类. 于是想起了java的动态代理,然后就有了这篇文章 ...
随机推荐
- js模拟高级语言的重载
js以递归的方式模拟高级语言的重载,我以添加元素节点为例子: //现有的子元素之前插入一个新的子元素 var before = function(elem,newElement,targetEleme ...
- IntelliJ Idea14 创建Maven多模块项目
Maven多模块项目的参考资料 Sonatype上的教程 http://books.sonatype.com/mvnex-book/reference/multimodule.html 在这个教程里, ...
- 在C#中将String转换成Enum:
一: 在C#中将String转换成Enum: object Enum.Parse(System.Type enumType, string value, bool ignoreCase); 所以,我 ...
- 动画制作库tween样例学习
mark: https://www.npmjs.com/package/tween
- AngularJS+Node的RESTful之基本实现
欢迎大家指导与讨论 : ) 一.前言 本文主要讲述RESTful在node端与AngularJS两端配合的基本实现方法,为了尽量做到大家易看易懂,因此文章也没有深入探讨(其实是小鹏我只学到目前这些 - ...
- Jboss EAP:native management API学习
上一节已经学习了CLI命令行来控制JBOSS,如果想在程序中以编码方式来控制JBOSS,可以参考下面的代码,实际上在前面的文章,用代码控制Jboss上的DataSource,已经有所接触了,API与C ...
- [MetaHook] BaseUI hook
Hook IBaseUI function. #include <metahook.h> #include <IBaseUI.h> IBaseUI *g_pBaseUI = ; ...
- #CSDN刷票门# 有没有人在恶意刷票?CSDN请告诉我!用24小时监控数据说话!
特别声明: 此次并非针对其他参与2013中国十大优秀开源项目的同行,体系有漏洞要谴责的是制定规则并从中获益但不作为的权贵,草根们制定不了规则但可发现和利用漏洞,这是程序员应有反叛精神没错.但被作为道具 ...
- 后记:Cookie安全大辩论总结
前天,我发布在博客园上的某知名电商网站的Cookie漏洞引发园友们的热议,学到了很多知识,现在整理一下其中比较激烈的技术讨论.谁对谁错每个人自己心中都有一把称,很多时候都是我无法说服你,你也无法说服我 ...
- RHEL每天定时备份Oracle
步骤: (1)创建脚本文件bak_112.sh,内容如下(自动按当前日期备份数据库): #!/bin/sh export ORACLE_BASE=/u01/app/oracle; ORACLE_HOM ...