程序中的代理:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如 异常处理,日志功能,等等。
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect orientedprogram ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
安全,事物,日志等功能要贯穿到好多个模块中,所以他们就是交叉业务
重要原则,不要把供货商暴露给客户
JVM可以在运行期间动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
动态生成的类往往被用作代理。但动态生成的类不是代理
Jvm生成的动态类必须实现一个或者多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理。
如果不知道目标类实现的接口,则JVM无法实现动态类,需通过CGLIB库实现
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理。所以,如果需要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
代理类的各个方法中通常除了要调用目标的相应方法和对外返目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码。
1. 在调用目标方法之前
2. 在调用目标方法之后
3. 在调用目标方法前后都有
4. 在调用目标方法异常的catch块中
分析JVM动态生成的类
Proxyà 有个方法
public static Class<?> getProxyClass(ClassLoader loader,
创建的时候一定要指定一个类加载器
Class<?>... interfaces)
实现了哪些接口
在java虚拟机内存中造出了Class 字节码,没有通过ClassLoader加载. 所以创建的时候一定要指定一个类加载器
返回代理类的java.lang.Class
对象,并向其提供类加载器和接口数组。该代理类将由指定的类加载器定义
public static void main(String[] args) {
//代理类的字节码
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy1.getName());
//获取所有的构造方法
Constructor[] constructors =clazzProxy1.getConstructors();
for(Constructor constructor : constructors){
String name = constructor.getName();
StringBuilder sb = new StringBuilder(name);
//获取参数
Class[] clazzParams =constructor.getParameterTypes();
//取出参数
for(Class clazzParam : clazzParams ){
String name2 = clazzParam.getName();
sb.append(name2);
}
System.out.println(sb);
}
}
结果
$Proxy0
$Proxy0java.lang.reflect.InvocationHandler
创建动态类的实例对象
public static void main(String[] args) throws Exception {
//InvocationHandler的参数,但它为接口,所以自己定义一个类实现他
class MyInvocationHandler implementsInvocationHandler{
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable {
// TODO Auto-generatedmethod stub
return "abc";
}
}
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1);
}
}
Jvm创建动态类及其实例对象需要哪些信息。 三个方面
1. 类实现了哪些接口
2. 类加载器是谁 这样就可以得到类的字节码,并且只有一个参数类型的构造方法。InvocationHandler为接口,所以需要创建一个实现它的类的对象
3. 创建对象,需要传递InvocationHandler()对象
分析InvocationHandler对象的运行原理
//把创建动态类和实例对象合二为一Proxy.newProxyInstance方法
Collection proxy2 = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable {return null;}
}
);
public Object invoke(Object proxy, Method method, Object[]args)
invoke方法中接受的三个参数意义:
Client程序调用objProxy.add(“abc”)方法时:涉及三要素:
objProxy对象,add()方法,”abc”参数