代理模式
代理模式的定义:为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用应一个对象,需要实现相同的接口,而代理对象可以在客户端和目标对象之间起到中介的作用。
就好比销售。代理商是受别人的委托,帮助委托人销售指定的商品。而这件商品原本是委托人,而不是代理商的,代理商可以把商品说的天花乱坠,但商品原本是不变的,这就类似于“拓展”。
Java中的开闭原则:对拓展是开放的,修改是封闭的,不得修改源代码。当我们对一个类进行拓展时,这时就需要使用代理模式了,将拓展的代码写在代理类中。
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类,在程序运行前就编译好的。以膨胀空间来节省时间!
package Demo;
//接口
interface IShow {
void show();
}
//用户类 被代理类
class User implements IShow{
@Override
public void show() {
System.out.println("520秀一下!");
}
}
//代理类
class Agency implements IShow{
private User user;
//被代理类作为参数传入
public Agency(User user) {
this.user = user;
}
@Override
public void show() {
System.out.println("****拓展代码****");
user.show();
System.out.println("****拓展代码****");
}
}
public class Test {
public static void main(String[] args) {
User user=new User();
Agency agency=new Agency(user);
agency.show();
}
}
结果:
****拓展代码****
520秀一下!
****拓展代码****
代理类和委托类需要实现同一个接口,并重写相应的方法。然后在代理类的构造方法中以委托类对象作为参数传入。在代理类重写的方法通过传入的委托类对象调用委托类的方法,并加上拓展的一些代码,就实现了代理。
动态代理
1. 代理对象 , 不需要实现接口2. 代理对象的生成 , 是利用 JDK 的 API, 动态的在内存中构建代理对象 ( 需要我们指定创建代理对象 / 目标对象实现的接口的类型 )
3. 动态代理也叫做 :JDK 代理 , 接口代理
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
· ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
· Class<?>[]interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
· InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
package Demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface IShow{
void show();
}
class User implements IShow{
@Override
public void show() {
System.out.println("520秀一下!");
}
}
/**
* 方法一
* 代理类 将newProxyInstance写在代理类中
*/
class Dynamic_Proxy{
private User user;
public Dynamic_Proxy(User user) {
this.user = user;
}
public Object getProxy(){// 获得代理类的类加载器
return Proxy.newProxyInstance(Dynamic_Proxy.class.getClassLoader(),
user.getClass().getInterfaces(),//得到委托类实现的接口
new InvocationHandler() {//将代理类与委托类建立联系
@Override// 委托类对象 方法 方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("****拓展代码****");
Object invoke = method.invoke(user, args);//调用委托类中的方法
System.out.println("****拓展代码****\n");
return invoke;
}
});
}
}
/**
* 方法二
* 代理类 实现InvocationHandler接口
*/
class DynamicProxy implements InvocationHandler{
private User user;
public DynamicProxy(User user) {
this.user = user;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("****拓展代码****");
//反射 调用方法 对象 参数
Object invoke = method.invoke(user, args);
System.out.println("****拓展代码****");
return null;
}
}
public class Test {
public static void main(String[] args) {
//方法一
User user=new User();
Dynamic_Proxy proxy=new Dynamic_Proxy(user);
IShow instance =(IShow) proxy.getProxy();
instance.show();
//方法二
User us=new User();
// 得到InvocationHandler对象
// 因为DynamicProxy实现了得到InvocationHandler对象 并重写了invoke方法
InvocationHandler handler = new DynamicProxy(us);
// 返回的是接口类型 代理类的类加载器
IShow proxyInstance =(IShow) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
us.getClass().getInterfaces(),//委托类实现的接口
handler);//将代理类与委托类建立联系
proxyInstance.show();
}
}
方法一和方法二的原理是类似的,一个无非是是在内部重写InvocationHandler接口实现Involve方法;另一个则将newProxyInstance方法写在外面,传入指定的参数。它们的结果是一样的。多次用到反射的知识点,通过反射调用方法,得到类的接口,类的加载器等,包括导的包都是基于Reflect下面的。
总结:
使用代理的好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
静态代理,以膨胀空间来节省时间(类在运行前就已经全被加载);动态代理,以膨胀时间来节省空间(使用反射,动态加载,需要用到的时候就加载)。
动态加载使用的会比较多,比如说:三大框架中的Spring框架的底层,使用过动态代理,用于解耦和,提高开发效率。在后面的框架中动态代理无处不在。
每日鸡汤:要按照你想的去生活,否则,你迟早会按照你生活的去想。
Over!