一、什么是代理
我使用一个比较形象的例子,假如说你要租房子,那么有两种方式,第一种方式:可以直接找房主,和房主进行协商;第二种方式:直接找中介,那么这里的中介就相当于是房主的一个代理。
在代理中一般会涉及以下几个角色:
- 抽象角色:一般会使用抽象类和接口来解决
- 真是角色:得代理的角色
- 代理角色:代理真实角色,代理这真实角色后,一般会做一些负数操作
- 客户角色:访问代理角色的人
二、为什么需要代理
代理的目的是为了使被代理类的功能更加纯粹,不用考虑其他的问题,其他的一些操作都可以在代理类中完成。
在javaweb中就用一个典型的例子,我们可以将service层看做是dao层的代理,dao层需要专注于纯粹的数据库交互。
代理的好处:
- 可以使真实角色的操作更加纯粹,不用关注一些公共的业务。
- 公共的业务直接交给代理角色,实现了业务的分工。
- 公共业务发生扩展时,可以集中操作。
三、静态代理
所谓的静态代理,就是对于一个实体类。我们需要为每一个实体类创建一个代理类。
静态代理对于有一定java基础的人而言,还是很简单的。
实体类(实体类的原代理就不写了):
代理类Host01Proxy :
public class Host01Proxy {
private Host01 host;
public Host01Proxy() {
}
public Host01Proxy(Host01 host) {
this.host = host;
}
public void getAddress(){
host.address();
}
public void getprice(){
host.price();
}
public void getarea(){
host.area();
}
}
代理类Host02Proxy :
public class Host02Proxy {
private Host02 host;
public Host02Proxy() {
}
public Host02Proxy(Host02 host) {
this.host = host;
}
public void getAddress(){
host.address();
}
public void getprice(){
host.price();
}
public void getarea(){
host.area();
}
}
代理类Host03Proxy :
public class Host03Proxy {
private Host03 host;
public Host03Proxy() {
}
public Host03Proxy(Host03 host) {
this.host = host;
}
public void getAddress(){
host.address();
}
public void getprice(){
host.price();
}
public void getarea(){
host.area();
}
}
测试:
@Test
public void test(){
Host01Proxy host = new Host01Proxy(new Host01());
host.getAddress();
host.getarea();
host.getprice();
System.out.println("---------------------------------");
Host02Proxy host1 = new Host02Proxy(new Host02());
host1.getAddress();
host1.getarea();
host1.getprice();
System.out.println("---------------------------------");
Host03Proxy host2 = new Host03Proxy(new Host03());
host2.getAddress();
host2.getarea();
host2.getprice();
System.out.println("---------------------------------");
}
四、动态代理
本质上是通过反射实现的,反射是万能的。
动态代理的分类:
- 基于接口的动态代理
- JDK动态代理(我们在这里使用此种方式)
- 基于类的动态代理
- cglib
- java字节码实现
- JAVAssist
在实现动态代理之前,需要了解的两个类:
Proxy :代理类
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
我可以通过.newProxyInstance为某个接口创建代理,关于这个方式的详细说明,如下图:
InvocationHandler :调用处理程序
InvocationHandler是由代理实例的调用处理程序实现的接口.
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
该接口中之后一个抽象方法invoke,具体介绍如下:
实体类(实体类的原代理就不写了):
代理类(基本就是固定写法):
public class ProxyInvocationHandler implements InvocationHandler {
public Rent rent;
//设置实现了rent接口的被代理类
public void setRent(Rent rent) {
this.rent = rent;
}
//获取代理类
public Object getProxy(){
return Proxy.newProxyInstance(
this.getClass().getClassLoader(), //类加载器
rent.getClass().getInterfaces(), //被代理类的公共接口
this);//因为本类实现了InvocationHandler,所以就是this
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是代理类,下面是你想要的信息:");
//通过Method调用invoke
Object invoke = method.invoke(rent, args);
return invoke;
}
}
测试:
public class MyTest {
@Test
public void test(){
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(new Host01());
Rent proxy = (Rent)pih.getProxy();
proxy.address();
proxy.area();
proxy.price();
System.out.println("----------------------------------");
pih.setRent(new Host02());
Rent proxy2 = (Rent) pih.getProxy();
proxy2.address();
proxy2.area();
proxy2.price();
System.out.println("----------------------------------");
pih.setRent(new Host03());
Rent proxy3 = (Rent) pih.getProxy();
proxy3.address();
proxy3.area();
proxy3.price();
System.out.println("----------------------------------");
}
}
动态代理的好处:
- 可以使真实角色的操作更加纯粹,不用关注一些公共的业务
- 公共的业务直接交给代理角色,实现了业务的分工
- 公共业务发生扩展时,可以集中操作
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可