介绍
代理模式就是多出一个代理类来,替原对象进行一些操作。比如:我们在需要租房子的时候回去找中介,因为我对该地区的房屋信息掌握的不够全面,希望找一个更 熟悉的人帮我找,此处就是代理的意思,中介就相当于代理类。
代理解决什么问题:当两个类需要通信的时候,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现可以让我们完成与另一个类之间关系的统一管理。代理类和委托类都要实现相同的接口,因为代理类真正调用的还是委托类的方法。
代理模式涉及的角色:
1.抽象角色:可以是接口,也可以是抽象类(代理方和委托方共同调用)
2.真正角色:代理角色所代理的真实对象,是我们最终要引用的对象,是业务的具体执行者(委托方)
3.代理角色:内部含有真实对象的引用,负责对真实对象的调用,并在真实对象处理前后做预处理和善后工作。
代理模式的UML
代理模式的优点
职责清晰:具体角色只需要关注业务逻辑的实现,非业务逻辑部分后期通过代理类完成。
代理模式的应用场景
如果已有的方法在使用的时候需要对原有的方法进行改进,有两种方法
- 对原有的方法进行改变,但是这样违反了 “ 对扩展开放,对修改关闭 ” 的原则。
- 就是采用一个代理类调用原有的方法,对产生的结果进行控制,这种方法就是代理模式。
采用代理模式就功能划分的更加清晰,便于后期的维护。
静态代理模式
静态代理:由程序员创建的代理类或特定工具自动生成源代码再对其编译,在程序运行前.class(字节码)文件已经存在了
抽象角色
Sourceable.java
public interface Sourceable {
void method();
}
具体角色
Source.java
public class Source implements Sourceable {//委托方
public void method(){
System.out.println("原始方法!");
}
}
代理角色
Proxy.java
public class Proxy implements Sourceable {//代理方
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
after();
}
private void after() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
测试
ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();//代理角色内部包含具体对象的引用
source.method();
}
}
缺点
代理角色和具体角色实现相同的接口,代理类通过具体角色类实现了相同的方法,这样就出现了大量重复的代码。如果接口增加了一个方法,除了所有具体角色类要实现这个方法外,所有代理角色类也需要实现这个方法,增加了代码维护的复杂度。
动态代理模式
动态代理:在程序运行时利用反射机制动态创建而成。也就是说代理类并不需要在Java代码中定义,而是在运行时动态生成的。
创建一个中介类(处于代理类与委托类之间本例为ProxyInvocationHandler),这个类必须实现InvocationHandler接口,在Java的动态代理机制中InvocationHandler接口和Proxy类是实现动态代理的核心。
每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理实例调用一个方法(该方法指的是具体角色中的方法)的时候,这个方法的调用就会转到实现了InvocationHandler接口类的 invoke() 方法中,然后当执行到 method.invoke(englishService,args)的时候,就会跳转到englishService的类中执行方法(这个方法是之前动态实例调用的具体角色的方法)
//抽象角色
public interface Service {
void sayHello();
void printName(String name);
}
//具体角色
public class EnglishService1 implements Service {
@Override
public void sayHello() {
System.out.println("嗨");
}
@Override
public void printName(String name) {
System.out.println("早上好,"+name);
}
}
1.类有一个成员变量,既目标对象(被代理对象)的引用,当程序运行的 时候它会被实例化
private Object englishService;
2.构造方法:实例化englishService
public ProxyInvocationHandler(Object englishService) {
this.englishService = englishService;
}
3.实现InvocationHandler接口的 invoke()方法
- Object proxy : 代理对象的引用
- Method method:被代理实例的方法
- Object[] args:方法的参数
public Object invoke(Object proxy, Method method, Object[] args){...}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ProxyInvocationHandler implements InvocationHandler {
//代理类内部包含具体角色的引用(目标对象)
private Object englishService;
public ProxyInvocationHandler(Object englishService) {
this.englishService = englishService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("--begin--");
//这个invoke()第一个参数是具体角色的实例,第二个方法是参数
method.invoke(englishService,args);
System.out.println("--end--");
return null;
}
}
测试:
public class ProxyInvocationHandlerTest {
public static void main(String[] args){
//要代理的真实对象
Service service = new EnglishService1();
//创建中介类实例,new一个被代理的对象作为参数传给 动态代理类
ProxyInvocationHandler handler = new ProxyInvocationHandler(service);
//获取类加载器
ClassLoader classLoader = service.getClass().getClassLoader();
//动态产生一个代理类
//newProxyInstance的三个参数:
// 1.类加载器:(ClassLoader:这个class loader在动态代理类中被使用,它可以从类或者接口中或得,比如 ProxyInvocationHandlerTest.class.getClassLoader()
//但是,如果我们已经拿到的实例,我们也可以从实例中获得:ob.getClass().getClassLoader())
// 2.代理实例接口:需要一个具体对象实现的接口(这样代理对象就能和具体对象一样调用接口中的所有方法)
// 3.动态中介对象:实现接口 java.lang.reflect.InvocationHandler 的类的实例
Service proxyService = (Service) Proxy.newProxyInstance(classLoader,service.getClass().getInterfaces(),handler);
proxyService.sayHello();
proxyService.printName("唐");
}
}