java之代理
来源:互联网 发布:淘宝行业数据哪里看 编辑:程序博客网 时间:2024/06/01 14:52
一、概述
1、 背景:有一个已经开发好的类,但是现在要为其增加异常处理、日志、统计方法的运行时间、事务处理等功能,但是开发好的类不能修改或者没有办法修改,该如何做?
2、 代理类和目标类
a) 目标类
已经开发好且不能被修改的类称为目标类
b) 代理类和目标类的关系
1) 代理类的功能与目标类的功能一样
2) 只是在执行目标类相关功能的前或者后增加了新的辅助功能。
1 代理类的每个方法名与目标类的每个方法名都一样
2 代理类的每个方法都要调用目标类的每个方法
3 代理类的每个方法在调用目标类的每个方法的前或者后都会加上系统的额外功能
c) 客户端不直接使用目标类,而是直接使用代理类,代理类实际上是在调用目标类的功能。
3、程序中的代理
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换。
4、AOP
处理学生、课程和管理的三种业务,有各自的C、R、U、D。但是无论这些类的各自的业务如何不同,都会涉及到安全、事务管理和日志三个方面的内容。、
交叉业务:就是不同模块具有的共同的业务。
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
用代码描述交叉业务如下所示:
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采
用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示
注意:
安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务。
重要原则:不要把供货商暴露给你的客户。
1) 对交叉业务编程的问题就称为AOP(Aspect Oriented Programming)
a) AOP与代理联系的纽带
思想:将和切面有关的代码移动到原始方法的周围,这和直接在方法中编写切面代码之后的整体运行效果是一致的。
b) AOP与代理的关联
如果将切面的代码移到原始方法的周围,原始方法看做目标类的方法,那么移动以后的切面代码+原始代码就是代理类对应的方法
c) 可以采用代理的方式来实现AOP
代理是实现AOP的核心和关键技术。只要是AOP,就一定会涉及代理技术。
d) 代理技术的分类
按照是否是在程序运行期间产生代理类可以将代理分为静态代理和动态代理
1 静态代理:就是手动为每一个目标类的每一个方法都增加交叉业务,也就是手动为每一个目标类增加代理类
缺点:如果目标类数量非常多或者目标类中的功能非常多,直接使用静态代理的方式来为目标类增加交叉业务会非常的繁琐。
2 动态代理:通过特定的设置,在程序运行期间指示JVM动态地生成类的字节码。这种动态生成的类往往被用作代理类,即动态代理类。也就是运行时做编译的事情并且把生成的字节码加载成这个类的Class对象
5、动态代理
为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将会非常麻烦。
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现
接口的类生成动态代理类,那么可以使用CGLIB库
操作步骤如下:
(1). 在调用目标类的目标方法之前
(2). 在调用目标类的目标方法之后
(3). 在调用目标类的目标方法之前和之后
(4). 在调用目标类的目标方法异常的catch块中
a) 创建动态类及查看其方法列表信息
如下所示:
imp
imp
imp
imp
imp
imp
public class proxyTest {
public static void main(String[] args) throws NoSuchMethodException, Exception {
Class clazzproxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(
), Collection.class);
System.out.println(clazzproxy1.getName());
// 查看clazzproxy1中的所有构造方法
System.out.println("-------begain constructor list-------");
Constructor [] constructors = clazzproxy1.getConstructors();
for(Constructor constructor:constructors){
String name = constructor.getName();
StringBuilder sb = new StringBuilder(name);
sb.append("(");
Class[] clazzPrams=constructor.getParameterTypes();
for(Class clazzPram:clazzPrams){
sb.append(clazzPram.getName()).append(",");
}
if(clazzPrams!=null&&clazzPrams.length!=0){
sb.deleteCharAt(sb.length()-1);
}
sb.append(")");
System.out.println(sb.toString());
}
// 查看clazzproxy1中的所有方法
System.out.println("-------begain method list-------");
Method [] methods = clazzproxy1.getMethods();
for(Method method:methods){
String name = method.getName();
StringBuilder sb = new StringBuilder(name);
sb.append("(");
Class[] clazzPrams=method.getParameterTypes();
for(Class clazzPram:clazzPrams){
sb.append(clazzPram.getName()).append(",");
}
if(clazzPrams!=null&&clazzPrams.length!=0){
sb.deleteCharAt(sb.length()-1);
}
sb.append(")");
System.out.println(sb.toString());
}
}
}
运行结果如下:
-------begain constructor list-------
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
-------begain method list-------
add(java.lang.Object)
remove(java.lang.Object)
equals(java.lang.Object)
toString()
hashCode()
clear()
contains(java.lang.Object)
isEmpty()
iterator()
size()
toArray([Ljava.lang.Object;)
toArray()
spliterator()
addAll(java.util.Collection)
stream()
forEach(java.util.function.Consumer)
removeAll(java.util.Collection)
containsAll(java.util.Collection)
retainAll(java.util.Collection)
parallelStream()
removeIf(java.util.function.Predicate)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait()
wait(long,int)
wait(long)
getClass()
notify()
notifyAll()
知识点复习:StringBuffer与StringBuilder的区别及运用。
答:StringBuilder相对于StringBuffer效率高些,StringBuffer线程安全,StringBuilder线程不安全,StringBuffer用于单线程,StringBuildr用于多线程。
b) 创建动态类的实例对象及调用其方法
代码如下:
public class proxyTest {
public static void main(String[] args) throws NoSuchMethodException, Exception {
System.out.println("-------begain creat instance--------");
// 创建该构造方法的方法
Constructor constructor = clazzproxy1.getConstructor(InvocationHandler.class);
// InvocationHandler为一个接口,需创建一个类来实现它
class myInvocationHander1 implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
Collection proxy1 = (Collection) constructor.newInstance(new myInvocationHander1());
System.out.println(proxy1);//结果显示为null
proxy1.clear();//结果无返回值,不出异常
proxy1.size();//抛出异常
}
}
c) 完成InvocationHandler对象的内部功能
public class proxyTest {
public static void main(String[] args) throws NoSuchMethodException, Exception {
Collection proxy3 = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler() {
ArrayList target = new ArrayList();
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retval = method.invoke(target, args);
return retval;
}
}
);
proxy3.add("abc");
proxy3.add("bbc");
proxy3.add("qqtv");
System.out.println(proxy3.size());
}
}
运行结果为3
d) 分析InvocationHandler对象的运行原理
动态生成的类实现了Collection接口生成的Collection类中的所有方法和一个
InvocationHandler参数的构造方法。
实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的
三个参数又是什么意思?
Client 程序调用objProxy.add(“abc”)方法,涉及三要素:objProxy对象,add方法,”abc”参数
class proxy${
add(Object object){
return handler invoker(Object proxy,Method method, Object[]args);
}
}
其中objProxy对象对应Object proxy,add方法对应Method method,”abc”参数对应Object[]args。
代码如下:
imp
imp
imp
imp
imp
public classProxyTest {
@SuppressWarnings("unchecked")
public static void main(String[] args)throws Exception {
@SuppressWarnings("rawtypes")
Collection proxy = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] {Collection.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,
Object[] args)throws Throwable{
return null;
}
});
System.out.println(proxy.getClass());
System.out.println(proxy);// 结果:null
proxy.clear();// 无返回值的方法,不会报告异常
proxy.size();//有返回值的方法,会报告异常
}
}
小结:
分析上面打印动态类的实例对象时,结果是null,因为打印动态类的示例对象实际上就是打印proxy的toString方法;
调用有基本类型返回值的方法时出现NullPointerException异常,是因为执行的代码为:
int size(){
return handler.invoke(this,this.getClass().getMethod("size"),null);
}
调用invoke方法返回的是null,将null转换为int类型时,则会报异常。
总结分析动态代理类的设计原理与结构:
- Java代理之代理模式
- Java代理之静态代理
- Java 代理之 动态代理
- Java 代理之静态代理
- java代理之静态代理
- Java 之代理模式
- java之动态代理
- java之动态代理
- java学习之代理
- Java高新技术之代理
- Java之代理
- java之代理
- Java之动态代理
- java之动态代理
- java之动态代理
- java 之代理模式
- java之代理
- java之代理模式
- Android Api Demos登顶之路(六十三)Content-->Resources Smallest Width
- 关于JS单选按钮与复选按钮验证是否选中
- 软件体系结构网站
- C++Primer第五版 2.3.2节练习
- LIBTIFF配置方法总结
- java之代理
- poj 2186 Popular Cows 有向图强连通分量 tarjan
- 数组和面向对象(封装)
- ubuntu14.04更新源出错解决
- Android学习——其他View
- Android 酷炫来袭:制作属于你自己的音频播放器(综合运用MediaPlayer、Service、Broadcast、ListView、SeekBar)
- c++中字符数组与字符串的转换
- 数据结构之双向循环链表操作4-(插入,删除,建立等)
- Teapot