赞
踩
背景:
一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又比如找女朋友、找保姆、找工作等都可以通过找中介完成。
代理模式的定义:
代理模式的目的:
代理模式的作用:
代理模式的主要优点有:
其主要缺点是:
那么如何解决以上提到的缺点呢?答案是可以使用动态代理方式
代理模式的主要角色如下。
其结构图如图所示。
在这里我们举一个租房的例子,用来理解代理模式
在这个例子里面,租房是抽象主题类,房产中介是代理类,房东是真实主题类,租房的人就是客户类,房东就是中介所代表的的真实对象。
根据代理的创建时期,代理模式分为静态代理和动态代理。
静态代理代码实现租房:
1.接口
//租房
public interface Rent {
public void rent();
}
2.真实角色
//房东,被代理的真实角色 实现租房Rent这个接口
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要把房子租出去");
}
}
3.代理角色
package com.cheng.demo01; //中介代理 public class Proxy implements Rent{ //多用组合,少用继承 private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } @Override public void rent() { host.rent(); seeHost(); charge(); } //看房,这件事中介能实现,房东不能实现 public void seeHost(){ System.out.println("中介带你看房子"); } //收中介费,这件事也只有中介能实现 public void charge(){ System.out.println("中介收你中介费"); } }
4.客户类
//要租房的人
public class Client {
public static void main(String[] args) {
Host host = new Host();//获得房东
//因为房东不能直接出租房子,所以我们要找中介(代理),通过代理访问房东
//代理一般会有一些附属操作,例如看房,收中介费
Proxy proxy = new Proxy(host);
proxy.rent();//本质还是调用了房东的rent方法
}
}
静态代理代码实现二:
1.接口,只有增删改查方法
//抽象角色
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
2.真实角色,实现了增删改查方法
package com.cheng.demo02; import java.util.Random; //真实角色 public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("修改了一个用户"); } @Override public void query() { System.out.println("查询了一个用户"); } }
要求:如果在不改变原有业务代码的情况下,想要在执行接口中的方法的时候,打印出日志,这时候最好的方法就是用静态代理实现。
3.代理类
package com.cheng.demo02; //代理类 public class UserServiceProxy implements UserService{ private UserServiceImpl userService; //使用set注入对象 public void setUserService(UserServiceImpl userService){ this.userService = userService; } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } //日志的方法 public void log(String msg){ System.out.println("使用了"+msg+"方法"); } }
4.客户类
//客户类
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
动态代理:在程序运行时,运用反射机制动态创建而成
动态代理分类:
基于接口的动态代理(如InvocationHandler)
基于类的动态代理
java动态代理机制中有两个重要的类和接口InvocationHandler
(接口)和Proxy
(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心
InvocationHandler
是由proxy代理实例的调用处理程序实现的接口 。
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke
方法。
invoke方法详细信息:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
Proxy类提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
这个方法的作用就是创建一个代理类对象,它接收三个参数:
实现一:租房
接口
//租房
public interface Rent {
public void rent();
}
真实角色
//房东,被代理的真实角色 实现租房Rent这个接口
public class Host implements Rent {
public void rent() {
System.out.println("房东要把房子租出去");
}
}
由proxy代理实例的调用处理程序
package com.cheng.demo03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们将会用这个类自动生成代理类,这个类(ProxyInvocationHandler)只是proxy代理实例的调用处理程序 public class ProxyInvocationHandler implements InvocationHandler { private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成代理类 public Object getProxy(){ //this.getClass().getClassLoader()代表加载类 rent.getClass().getInterfaces()代表要实现的接口 this代表当前InvocationHandler接口对象 return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHose(); fare();//这两个方法会通过反射动态的加载到代理类中04 //动态代理的本质还是反射机制 Object result = method.invoke(rent,args); return result; } public void seeHose(){ System.out.println("中介带你看房子"); } public void fare(){ System.out.println("收中介费"); } }
客户类
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色,暂时还没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过处理程序来实现接口对象 这里用了多态,相当于 Rent rent = new Host();
pih.setRent(host);
//动态生成的代理类
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
测试
实现二:用动态代理优化4.1静态代理中的代码实现二
接口
//抽象角色
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
由proxy代理实例的调用处理程序,我们可以把它封装成一个工具类
package com.cheng.demo04; import com.cheng.demo03.Rent; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们将会用这个类自动生成代理类,这只是一个处理程序 public class ProxyInvocationHandler implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } //生成代理类 public Object getProxy(){ //this.getClass().getClassLoader()代表加载类 rent.getClass().getInterfaces()代表要实现的接口 this代表当前InvocationHandler接口对象 return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质还是反射机制 log(method.getName());//用反射动态的获取方法名 Object result = method.invoke(target,args); return result; } //增加日志功能 public void log(String msg){ System.out.println("[Debug]:使用了"+msg+"方法"); } }
客户类
public class Client {
public static void main(String[] args) {
//获得真实角色
UserServiceImpl userService = new UserServiceImpl();
//获得代理角色,暂时没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//处理程序处理接口对象
pih.setTarget(userService);
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
相比静态代理,动态代理的优点:
例如:在上面的例子中,只要实现了UserService这个接口的类,都可以被动态代理类代理。
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
前面分析了代理模式的结构与特点,现在来分析以下的应用场景。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。