本文最后更新于:2024年5月10日 下午
介绍
在对象中可能会有一些公共的行为,例如日志记录、权限验证等,如果每个对象都写上这些就会造成冗余。对此AOP【Aspect Oriented Programming面向切面编程】提供了一种方式:将通用的方法抽离出来并封装,定义为独立的切面,在合适的时机将其横向切入业务流程指定的位置中
因为Spring的AOP主要作用就是通过不修改源代码的方式,将非核心的功能代码织入,来实现对方法的增强,而它实现的原理,关键就在于使用代理模式
代理模式的分类:静态代理;动态代理
静态代理
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
1、创建一个抽象角色,比如一些基础的增删改查业务
1 2 3 4 5 6
| public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
|
2、一个完成这些业务操作的真实对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class UserServiceImpl { public void add(){ System.out.println("增加用户!"); } public void delete(){ System.out.println("删除用户!"); } public void update(){ System.out.println("修改用户!"); } public void select(){ System.out.println("查询用户!"); } }
|
3、对于需求的变更,比如要新增一个日志功能,实现调用每个函数都有对应的日志输出:
- 实现1:在原来的接口和实现类上去加。这样太过麻烦
- 实现2:使用代理实现,在不改变原来业务的情况下增加
4、设置代理类处理日志,即代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class UserServiceProxy { private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) { this.userService = userService; }
public void add(){ log("add"); userService.add(); } public void delete(){ log("delete"); userService.delete(); } public void update(){ log("update"); userService.update(); } public void select(){ log("select"); userService.select(); }
public void log(String msg){ System.out.println("[debug] 使用了"+msg+"方法"); } }
|
5、编写测试代码,即客户
1 2 3 4 5 6 7 8 9 10 11 12
| public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService);
proxy.add(); } }
|
通过代理类处理日志, 我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
动态代理
动态代理和静态代理的角色都是用到了代理,区别就在于:动态代理是动态生成的,而静态代理是提前把代理类写好了。动态代理分类:一种是基于接口动态代理;一种是基于类动态代理
以JDK的代理为例,JDK的动态代理需要了解两个类 : InvocationHandler
和Proxy
, 打开JDK帮助文档
【InvocationHandler:调用处理程序】
1 2 3 4 5
| Object invoke(Object proxy, 方法 method, Object[] args);
|
【Proxy : 代理】
1 2 3 4 5
| public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this); }
|
代码实现
以租房子为例,现在我们租房买房都是通过第三方中介来实现,中介就相当于代理角色,我们和房东就相当于真实角色,而租房这一操作就是客户
1、抽象角色,定义一个租房接口
1 2 3 4 5
| public interface Rent { public void rent(); }
|
2、真实角色,要出租房子的房东
1 2 3 4 5 6
| public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子!"); } }
|
3、代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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); seeHouse(); fare(); return result; }
public void seeHouse(){ System.out.println("中介带你看房子!"); } public void fare(){ System.out.println("中介费!"); } }
|
客户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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(); } }
|
【总结】:一个动态代理往往代理一类业务,他代理的是业务接口
使用动态代理可以抽离公共的代码,降低冗余、简化我们的开发,使真实角色更加存粹