package com.itheima.proxyExam;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.TreeMap;
/**
* AOP
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),
AOP的目标就是要使交叉业务模块化。
可以采用将切面代码移动到原始方法的周围,
这与直接在方法中编写切面代码的运行效果是一样的,
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务
重要原则:不要把供货商暴露给你的客户
动态代理
要为系统中的各种接口的类增加代理功能,
那将需要太多的代理类,全部采用静态代理方式,
将是一件非常麻烦的事情!写成百上千个代理类,基本不现实。
JVM可以在运行期动态生成出类的字节码,
这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,
所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,
所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,
还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
分析JVM动态生成的类
创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。
编码列出动态类中的所有构造方法和参数签名
编码列出动态类中的所有方法和参数签名
创建动态类的实例对象
用反射获得构造方法
编写一个最简单的InvocationHandler类
调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进 去
打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回
值的方法报告了异常。
将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
产生的类字节码必须有个一个关联的类加载器对象;
生成的类中的方法的代码是怎样的,也得由我们提供。
把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,
即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,
它是在创建动态类的实例对象的构造方法时传递进去的。
在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。
用Proxy.newInstance方法直接一步就创建出代理对象。
猜想分析动态生成的类的内部代码
一、动态生成的类实现了Collection接口(可以实现若干接口),
生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
二、构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?
该方法内部的代码会是怎样的呢?
三、实现Collection接口的动态类中的各个方法的代码又是怎样的呢?
InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:
Client程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数
Class Proxy$ {
add(Object object) {
return handler.invoke(Object proxy, Method method, Object[] args);
}
}
分析先前打印动态类的实例对象时,结果为什么会是null呢?
调用有基本类型返回值的方法时为什么会出现NullPointerException异常?
分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,
代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
*
*/
public class ProxyExam
{
/**
* 做Map的代理
*
* @param args
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
Class clazzProxy = Proxy.getProxyClass(Map.class.getClassLoader(),
Map.class);
System.out.println(clazzProxy.getName());
Constructor[] constructors = clazzProxy.getConstructors();
for (Constructor con : constructors)
{
System.out.println(con);
}
// 代理类的创建方式1
System.out
.println("map1______________________________________________________");
Constructor constructor = clazzProxy
.getConstructor(InvocationHandler.class);
Map map1 = (Map) constructor.newInstance(new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
// TODO Auto-generated method stub
return null;
}
});
map1.put("a", "b");
System.out
.println("map2______________________________________________________");
// 代理类的创建方式2
// InvocationHandler 的实现类,为下面带参数的构造方法传参
class MyInvocationHandler implements InvocationHandler
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
// TODO Auto-generated method stub
return null;
}
}
MyInvocationHandler handler = new MyInvocationHandler();
Map map2 = (Map) constructor.newInstance(handler);
map2.put("a", "b");
// 强悍的代理类的创建方式3
System.out
.println("map3______________________________________________________");
Map map3 = (Map) Proxy.newProxyInstance(
//接口的类加载器
Map.class.getClassLoader(),
//接口的字节码文件,这里可能有多个所以用数组
new Class[]
{ Map.class },
//InvocationHandler 参数
new InvocationHandler()
{
TreeMap tree = new TreeMap();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable
{
System.out.println(method.getName()+" 我的前插入");
Object reVal=method.invoke(tree, args);
System.out.println(method.getName()+" 我的后插入");
return reVal;
}
});
map3.put("a", "b");
map3.put("c", "d");
map3.put("e", "f");
System.out.println(map3.size());
}
}
|
动态代理基础理论
最新推荐文章于 2020-05-25 21:40:13 发布