——Java培训、Android培训、iOS培训、.Net培训,期待与您交流! ——-
一、静态代理
1、生活中的代理:
四川人从成都代理商那里购买联想电脑,直接到北京传智播客去联想总部购买电脑。您认为最终的主要业务目标有什么不同吗?基本一样,都解决了核心问题动态代理中 匿名内部类,但是没有区别吗?从代理那里购买真的没有好处吗?虽然贵了几百元,但不用开车去北京,省时又省钱。
2、程序中的代理:
需求:在同一个接口的多个目标类的现有方法中添加一些系统功能,如异常处理、日志记录、计算方法的运行时间、事务管理等,你打算怎么做?
解决方案:编写一个与目标类具有相同接口的代理类动态代理中 匿名内部类,代理类的每个方法调用目标类的相同方法,并在调用方法时添加系统函数代码。
如果使用工厂模式和配置文件进行管理,无需修改客户端程序,在配置文件中配置使用目标类还是代理类,方便切换以后,比如想要日志功能的时候就配置代理类,否则配置目标类。这样既方便添加系统功能,也方便运行一段时间后删除系统功能。
代码示例:
[java] view plaincopy
package cn.itcast.proxy;
public class Test {
public static void main(String[] args) {
Subject subject = new ProxyRealSubject();
subject.action();
}
}
//接口
interface Subject {
void action();
}
//被代理类
class RealSubject implements Subject {
@Override
public void action() {
System.out.println("------------");
System.out.println("------------");
System.out.println("这是被代理的类");
System.out.println("------------");
System.out.println("------------");
}
}
//代理类:
class ProxyRealSubject implements Subject{
Subject subject;
public ProxyRealSubject() {
System.out.println("这是代理类");
subject = new RealSubject();
}
@Override
public void action() {
System.out.println("代理类开始");
subject.action();
System.out.println("代理类结束");
}
}
面向方面的编程:
1.系统中有交叉服务,交叉服务是切入系统的一个切面
2.跨业务的编程问题是面向方面的程序(AOP),AOP的目标是使跨业务模块化。可以在原始方法周围移动切面代码,这与直接在方法中编写切面代码相同。
3.使用代理技术正好可以解决这个问题。 Proxy是实现AOP功能的核心和关键技术。
二、动态代理:
知名框架spring中的核心技术aop采用动态代理技术。
出现原因:给系统中的各种接口类添加代理功能,需要的代理类太多了,所有的静态代理方法都会是一件很麻烦的事情!写成百上千个代理类是不是太累了!
概念:JVM可以在运行时动态生成类的字节码。这个动态生成的类经常被用作代理类,即动态代理类。
1、JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能作为具有相同接口的目标类的代理。
2、CGLIB库可以动态生成类的子类,类的子类也可以作为类的代理,所以如果要为类生成动态代理类不实现一个接口,你可以使用CGLIB库。
3、代理类的各个方法中,除了调用目标的对应方法并返回目标返回的结果外,还可以在代理方法中的以下四个位置添加系统功能代码:
1.调用目标方法之前
2.调用目标方法后
3.调用目标方法前后
4.在处理目标方法异常的catch块中
创建动态类的实例对象的步骤:
1、使用反射获取构造函数
2、编写一个简单的 InvocationHandler 类
3、调用构造函数创建动态类的实例对象,并传入其中写的InvocationHandler类的实例对象
4、打印创建的对象并调用该对象的无返回值方法和getClass方法,并演示调用其他有返回值的方法报异常。
5、将创建动态类的实例对象的代理改为匿名内部类,训练大家习惯匿名内部类。
总结思考:创建动态类及其实例对象需要提供哪些信息给jvm?
三个方面:
1、生成的类中有哪些方法,它实现了哪些接口来告知;
2、生成的类字节码必须有关联的类加载器对象;
3、生成类中方法的代码也是我们提供的。把我们的代码写在一个约定好的接口对象的方法里,把对象传给它,它就调用我的方法,相当于插入我的代码。提供执行代码的对象是InvocationHandler对象,在创建动态类的实例对象的构造函数时传入。在上面InvocationHandler对象的invoke方法中添加一点代码,可以看到这些代码被调用并运行了。
题目:为ArrayList类编写一个代理,实现与ArrayList完全相同的功能,并且可以计算每个方法的运行时间。
package com.itheima;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class Test4 {
/**
* 4、 写一个ArrayList类的代理,实现和ArrayList中完全相同的功能, 并可以计算每个方法运行的时间。
*
* 思路: 1.写一个获得代理类的通用方法: public static Object getProxy(final Object target,
* final Advice advice){} target是被代理的类,Advice是需要插入系统中的功能接口。
*
* 2.定义一个接口Advice,为插入的系统功能定义规则interface Advice{}
*
* 3. 实现Advice接口,实现具体的系统功能,即:计算每个方法运行的时间。 class MyAdvice implements
* Advice{实现方法}
*
* @author 鲁中需
*/
public static void main(String[] args) {
// 获取ArrayList的代理
final ArrayList list = new ArrayList();
Collection proxy = (Collection) getProxy(list, new MyAdvice());
// 演示效果:
proxy.add("flx");
proxy.add("bxd");
proxy.add("zxx");
proxy.add("lxz");
System.out.println(list.size());
}
// 获得代理类的通用方法:
public static Object getProxy(final Object target, final Advice advice) {
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用方法前,调用beforeMethod(Method method)
advice.beforeMethod(method);
Thread.sleep(1000);
Object retVal = method.invoke(target, args);
// 在调用方法后,调用afterMethod(Method method)
advice.afterMethod(method);
return retVal;
}
});
return proxy;
}
}
// 定义一个接口Advice,为插入的系统功能定义规则
interface Advice {
public abstract void beforeMethod(Method method);
public abstract void afterMethod(Method method);
}
// 实现Advice接口,实现具体的系统功能,即:计算每个方法运行的时间。
class MyAdvice implements Advice {
long beginTime = 0;
@Override
public void beforeMethod(Method method) {
System.out.println("调用方法前的功能");
beginTime = System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) {
System.out.println("调用方法后的功能");
long costTime = System.currentTimeMillis() - beginTime;
System.out.println("方法:" + method + " running time of " + costTime + "毫秒!");
}
}
1、动态生成的类实现了Collection接口(可以实现几个接口)。生成的类具有Collection接口中的所有方法和一个接受InvocationHandler参数的构造函数,如下所示。
2、构造函数接受一个 InvocationHandler 对象。接受对象的目的是什么?这个方法里面的代码是什么样子的?
动态类中实现Collection接口的各个方法的代码是什么? InvocationHandler接口中定义的invoke方法接受的三个参数是什么意思?
3.为什么分析之前打印的动态类的实例对象时结果为null?为什么在调用具有原始返回值的方法时会收到 NullPointerException?
分析为什么动态类的实例对象的getClass()方法返回正确的结果?
调用继承自Object类的代理对象的hashCode、equals、toString方法时,代理对象将调用请求转发给InvocationHandler对象。其他方法不转发调用请求。
暂无评论内容