问题:
在java中如何实现对程序的扩展,对程序的扩展有那几种方式?
面对这种问题的时候,不由得想起了java封装,继承,多态等。其实也就三种方式,面向对象的方式,包装模式,动态代理。今天重点介绍一下动态代理。
动态代理:
代理类具用以下属性:
- 代理类是公共的、最终的,而不是抽象的。
- 未指定代理类的非限定名称。但是,以字符串
"$Proxy"
开头的类名空间应该为代理类保留。- 代理类扩展
java.lang.reflect.Proxy
。- 代理类会按同一顺序准确地实现其创建时指定的接口。
- 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
- 由于代理类将实现所有在其创建时指定的接口,所以对其
Class
对象调用getInterfaces
将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其Class
对象调用getMethods
将返回一个包括这些接口中所有方法的Method
对象的数组,并且调用getMethod
将会在代理接口中找到期望的一些方法。- 如果
Proxy.isProxyClass
方法传递代理类(由Proxy.getProxyClass
返回的类,或由Proxy.newProxyInstance
返回的对象的类),则该方法返回 true,否则返回 false。- 代理类的
java.security.ProtectionDomain
与由引导类加载器(如java.lang.Object
)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予java.security.AllPermission
。- 每个代理类都有一个可以带一个参数(接口
InvocationHandler
的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用Proxy.newInstance
方法(将调用Proxy.getProxyClass
的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。
动态代理类:
(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler
。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke
方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method
对象以及包含参数的 Object
类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。
JDK动态代理:
Proxy
类
jdk的动态代理是有Proxy
类实现的,
Proxy
提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
创建一个接口的代理:
ClassLoader loader | 类加载器 , 用来加载代理对象 |
Class<?>[] interfaces | 目标类的字节码对象数组. 因为代理的是接口,需要知道接口中所有的方法 |
InvocationHandler h | 执行句柄, 代理对象处理的核心逻辑就在该接口中 |
接口 InvocationHandler:
| 在其上调用方法的代理实例 |
|
|
| 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 |
案例流程分析:
功能:完成用户登录,记录日志
核心代码:
//1.声明目标对象 UserDaoImpl
private UserDaoImpl target;
//2.创建有参构造
public ProxyBeanFactory(UserDaoImpl target) {
this.target = target;
}
//3.创建一个获取代理对象的方法
public Object getProxyBean(){
/**
* Proxy.newProxyInstance 返回一个代理对象
* 参数1:类加载器
* 参数2:接口的字节码数组对象
* 参数3: InvocationHandler 接口的匿名内部类对象 代理对象执行的核心逻辑
*/
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 当代理对象调用login方法的时候,就会进入invoke方法
* 参数1:Object proxy 代理对象 在内存中生成
* 参数2:执行方法的对象
* 参数3:方法传递的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.调用原方法
method.invoke(target);
//2.模拟记录日志
System.out.println("记录日志完毕");
return null;
}
});
}
Cglib动态代理:(maven环境)
该实现代理的方式是spring提供.,所以需要引入spring的依赖 。Enhance 是cglib的核心类,主要用来创建代理对象
setSuperClass() | 设置目标类的字节码对象 |
setCallback() | 设置回调函数, 目的让代理对象执行的时候进入回调函数执行具体的逻辑 ; 该方法的参数 |
Callback 接口 | |
create() | 创建代理对象的方法 |
案例分析:
功能:完成用户登录,记录日志
核心代码:
//1.声明目标类对象
private PersonService target;
//2.提供有参构造
public ProxyBeanFactory(PersonService target) {
this.target = target;
}
//3.创建方法获取代理对象
public Object getProxyBean(){
//1)创建Enhance核心对象
Enhancer enhancer = new Enhancer();
//2)设置SuperClass
enhancer.setSuperclass(target.getClass());
//3)设置回调函数
/**
* 给该回调函数中传递一个Callback的子接口 MehtodInterceptor 的匿名内部类
* MehtodInterceptor 接口中有intercept方法拦截
*/
enhancer.setCallback(new MethodInterceptor() {
/**
* intercept 函数当代理对象执行目标方法时,进入该拦截方法,执行里面的内容
* 参数1:Object proxy 代理对象
* 参数2:Method method 当前执行的方法对象
* 参数3:原目标方法的实际传入的参数Object[] objects
* 参数4: MethodProxy methodProxy 代理方法对象
*/
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
method.invoke(target);
System.out.println("记录日志");
return null;
}
});
//4.返回create的代理对象
return enhancer.create();
}
环境准备:(强调)
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
总结:
动态代理:jdk侧重于接口,cglib侧重于类。