1 代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
关于代理的举例:
2 静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色: 代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户: 访问代理对象的人。
我们再举一个例子,MVC模式中,用户访问Controller层,进而Service层,进一步调用dao层实现具体操作。现假设我们已经开发好了一个可以正常运行的代码块(可实现CRUD操作),而客户此时又提出一个新需求:想要追踪方法的调用,即显示日志。
Solution:
面对这样的问题,通常我们想的就是,直接在原来的代码中更改,但是这样做很不安全,可能我们改出了新的功能,旧的功能出现了bug,让问题变得越来越复杂。
此时我们引入代理的思想,不动原来的代码,就像增加了一个代理商一样增加了一个类,通过该类增加功能,同时完成原来的操作,这样做不会影响原来的代码逻辑,可以更好地适应需求变更。
以下代码可以辅助我们理解上面举的例子:
UserService~~接口:
public interface UserService {
public void insert();
public void delete();
public void update();
public void query();
}
UserServiceImpl~~实现类:
public class UserServiceImpl implements UserService{
public void insert() {
System.out.println("增加成员!");
}
public void delete() {
System.out.println("删除成员!");
}
public void update() {
System.out.println("修改成员信息!");
}
public void query() {
System.out.println("查询成员信息!");
}
}
UserServiceProxy~~代理类:
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void log(String msg) {
System.out.println("【新增语句】使用了"+msg+"方法");
}
public void insert() {
log("insert");
userService.insert();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
}
Client~~客户类:
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService); //代理原先的类
proxy.update(); //通过代理对象来实现原先的操作
}
}
运行结果:
图解:
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务放给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍~开发效率变低。
3 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是自动生成的,不是我们直接写好的!
- 动态代理可以分为两大类:基于接口的动态代理;基于类的动态代理
- 基于接口:JDK动态代理【本文中展示该方法】
- 基于类:cglib
- java字节码实现:javassist
举例说明:拿一开始的例子来说明,假设有租客(Client)要租房,而房东(Host)正好要出租,租客和房东都找到了代理中介。我们参照这个故事来编写一个实现动态代理的代码:
Rent~~接口:
public interface Rent {
public void rent();
}
Host~~房东类:
public class Host implements Rent {
public void rent() {
System.out.println("房东出租房屋!");
}
}
ProxyInvocationHandler~~使用该类自动生成代理类:
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);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(rent, args);
return result;
}
}
Client~~客户类:
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//创建代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//传入要代理的接口对象!
pih.setRent(host);
//获取代理对象!
Rent proxy = (Rent) pih.getProxy();
//使用代理对象来完成操作
proxy.rent();
}
}
动态代理其实挺简单,需要特别补充的是:
-
生成代理类Proxy.newProxyInvocation(参数1, 参数2, 参数3)
- 参数1:表明要用哪个类加载器去加载生成的代理类。
- 参数2:说明要生成的代理类的接口
- 参数3:实现了InvocationHandle接口的类(本例中是this),这个类只有一个方法需要实现,即invoke();
-
invoke(Object proxy, Method method, Object[] args){}
- 第一个参数:生成的代理类,目前没发现用处,不管他
- 第二个参数:执行的方法(利用反射的原理)
- 第三个参数:执行某方法需要的参数
- 返回值:执行某方法返回的值
第二个第三个参数解释执行的方法,意思是:代理类代理的是某个对象,然后增强里面的方法,代理类会将几乎所有方法都增强,除非在这里做判断。
写在最后
Early to bed, early to rise, make a man healthy, wealthy, and wise.