代理模式是SpringAOP的底层,在了解AOP之前,需要先了解代理模式是个什么样的东西。
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式分为以下两种:
- 静态代理
- 动态代理
下面先从静态代理入手,了解学习。
1. 静态代理 1.1 简 述
前面关于代理模式的定义,如果没有一个例子进行阐述,理解起来,有些抽象。这里用租房的例子,来详细说说这个代理模式是个什么玩意。
毕业后进入社会参加工作,第一件事要考虑的,就是住哪里?这个时候,就有租房的需求了。
要租房子,最简单的方法莫过于找房东直接租,但房东又岂是那么容易找到的!往往都是神龙见首不见尾。
就目前的租房市场而言,能直接找到房东租房的,少得可怜,往往都是中介人员在打理,这也就意味着我们去租房所遇到的人,十之八九都是中介人员。只有中介人员才可能直接联系到房东,并且中介人员在房屋出租之前,并不会给你房东的联系方式。
结合上面的图和描述,我们可以知道,在整个租房过程中,中介其实充当的就是房东的代理人,目的就是代表房东将房子出租出去,那房东自己干嘛去了?当然是找地方度假快活去了。所以租房子就是代理模式的一个体现了。
1.2 静态代理实现角色分析
- 抽象角色:一般会使用接口或者抽象类来解决(如上面的共同目标)
- 真实角色:被代理的角色(如上面的房东)
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作(如上面的中介)
- 客户:访问代理角色的人(如上面的租房子的人)
代码实现
- 新建一个接口 Rent ,作为抽象角色,代表出租房子
package com.yuhuofei.proxy;
public interface Rent {
void rent();
}
- 新建一个 Host.java 类,作为真实角色,代表房东
package com.yuhuofei.proxy;
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子!");
}
}
- 新建一个 HostProxy.java 类,作为代理角色,表示中介
package com.yuhuofei.proxy;
public class HostProxy implements Rent{
private Host host;
public HostProxy(Host host){
this.host=host;
}
//帮房东出租房子
@Override
public void rent() {
host.rent();
}
//看各个地方的房子
public void seeHouse(){
System.out.println("中介带你看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
//签租房合同
public void signContract(){
System.out.println("签租房合同");
}
}
- 新建一个 Client.java 类,作为客户,代表要租房子的人
package com.yuhuofei.proxy;
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理,代理一般会有其它的附加操作
HostProxy hostProxy = new HostProxy(host);
//直接找中介租房
hostProxy.rent();
}
}
静态代理优缺点:
- 可以使得真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务交给代理角色处理,实现业务分工
- 公共业务发生扩展的时候,方便集中管理
- 一个真实角色就要产生一个代理角色,代码里会翻倍,开发效率会变低
动态代理和静态代理的角色分类一样,但不同的是动态代理动态生成的代理角色,所代理的是接口,而不再是某一个具体实现类。
动态代理分类:
- 基于接口的动态代理
- 基于类的动态代理
基于接口实现动态代理,一般是使用 JDK 动态代理
基于类实现动态代理,一般是使用 cglib
还有一种用得比较多的动态代理实现方式是基于 java 字节码实现,使用 javasist 这个类库包。
在进行代码实现动态代理前,需要了解一个类和一个接口,一个类是 Proxy (代理)类,一个接口是 InvocationHandler (调用处理程序)接口,同时还要了解反射的知识,因为动态代理本质是通过反射实现的
2.1 示例1——动态地代理指定的接口- 新建一个 Rent 接口,表示出租房子这个目标,即抽象角色
package com.yuhuofei.dynamic;
public interface Rent {
void rent();
}
- 新建一个 Host.java 类,代表真实角色
package com.yuhuofei.dynamic;
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子!");
}
}
- 新建一个调用处理程序的类,ProxyInvocationHandler.java,用于动态生成代理角色
package com.yuhuofei.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//处理代理类实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理本质是使用反射机制实现的
seeHouse();
Object result = method.invoke(rent, args);
return result;
}
public void seeHouse() {
System.out.println("中介带着看房子!!!");
}
}
- 新建一个 Client.java 类,用于代表客户
package com.yuhuofei.dynamic;
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//使用ProxyInvocationHandler,用于生成代理角色
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
//设置要代理的真实角色
proxyInvocationHandler.setRent(host);
//生成代理角色,这里要注意,动态代理代理的是接口,不是具体实现类
Rent proxy = (Rent) proxyInvocationHandler.getProxy();
proxy.rent();
}
}
- 项目结构即测试结果如下
- 新建一个接口 UserService ,代表抽象角色
package com.yuhuofei.demo;
public interface UserService {
void add();
void delete();
void query();
void update();
}
- 新建一个 UserServiceImpl.java 类,作为真实角色
package com.yuhuofei.demo;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("新增用户信息");
}
@Override
public void delete() {
System.out.println("删除用户信息");
}
@Override
public void query() {
System.out.println("查询用户信息");
}
@Override
public void update() {
System.out.println("更新用户信息");
}
}
- 新建一个泛型类 UserProxyInvocationHandler,用于动态地生成代理角色
package com.yuhuofei.demo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class UserProxyInvocationHandlerimplements InvocationHandler { //被代理的接口 private T target; //构造方法 public UserProxyInvocationHandler(T target) { this.target = target; } //生成得到代理类 public Object getProxy(T target) { return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } //处理代理类实例,并返回结果 @Override 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 name) { System.out.println("调用了 " + name + " 方法"); } }
- 新建一个 Client.java 类,代表客户
package com.yuhuofei.demo;
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl user = new UserServiceImpl();
//UserProxyInvocationHandler,用于生成代理角色
UserProxyInvocationHandler handler = new UserProxyInvocationHandler<>(user);
//生成代理角色,这里要注意,动态代理所代理的是接口,不再是具体实现类
UserService proxy = (UserService) handler.getProxy(user);
//调用接口中的方法
proxy.delete();
}
}
- 测试结果及项目结构
-
方法执行顺序说明:
proxy.delete()——>Object invoke(Object proxy, Method method, Object[] args)——>log(method.getName())——>输出结果——>method.invoke(target, args)——>void delete()——输出结果;对于这种不能一眼看出执行顺序的程序,可以打断点执行,一步一步跟踪代码运行步骤,找到正确逻辑。
动态代理相比于静态代理的好处:动态代理,它代理的是接口,因此也就可以代理实现了这个接口的所有类;而静态代理只能代理指定的一个类。



