17学Java之AOP以及装饰器模式 (1)

本文深入探讨了AOP(面向切面编程)的概念及其与OOP(面向对象编程)的区别,通过装饰器模式实现方法拦截,展示了如何在不修改原始代码的情况下为对象增加额外功能,适用于日志记录、缓存管理和权限验证等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是AOP

AOP: Aspect-Oriented Programming 面向切面编程。

AOP编程是相对于OOP编程的,在OOP编程中,我们经常会像下面这么写:

因为OOP方法是面向对象编程的,所以我们经常调用一个对象的方法,然后这个方法中又会调用另外一个对象的run方法。

在AOP编程中,我们将OOP编程里的方法进入之前和结束时候的点称之为切面,也就是说咱们想在进入一个方法的前后都预先做一些事情,

比如我们想打LOL,那我们就要先打开电脑,然后才可以打开LOL,最后打完了我们再关闭电脑。

如下图,就是我们在执行一个方法(动作)前,进行一些拦截动作,称之为before(之前),after(之后)切面。

AOP适合用于哪些场景

下面列举几个需要使用AOP的场景,但是它们的核心都是方法拦截:

  1. 日志: 在访问一个方法的时候,将方法的调用参数和返回结果都打印出来。
  2. 缓存: 某个方法只被调用一次后,将结果返回并保存起来,之后不再调用这个方法,直接返回之前保存的结果。
  3. 鉴权: 在调用一个方法前,判断是否有权限调用它。

首先我们先用OOP的模式实现一下方法拦截的场景,然后再用AOP的方式来实现一下。

使用OOP的话,我们就需要用到装饰器模式

装饰器模式

什么是装饰器模式?

  1. 装饰器模式:Decorator pattern
  2. 动态的为一个对象增加功能,但是不改变其结构
  3. 本质上是一个"包装"

打个比方,我们现在有一个方法是撸铁健身,但是在这之前我们可能需要先吃饭等等,才有力气去健身,这时候我们有两种选择,

一是修改这个方法的代码,在这个方法刚开始的地方添加吃饭的代码

改造前

public class DataService {
   public void 撸铁健身() {
	  System.out.println("开始健身");   
   }
}

改造后

public class DataService {
   public void 撸铁健身() {
   	  System.out.println("开始吃饭"); 
	  System.out.println("开始健身");   
   }
}

二是使用一个包装类,包装一下当前方法的类

首先写出一个撸铁健身方法的接口类和实现类:

public interface DataService {
	String 撸铁健身(String name);
}

public class DataServiceImpl implements DataService {
	public String 撸铁健身(String name) {
    	System.out.println(name + "正在健身中");
    	return "健身完毕";
    }
}

static DataService service = new DataServiceImpl();

public static void main(String [] args) {
	service.撸铁健身("张三");
}

随后我们添加一个装饰器类,开始我们的装饰器模式

public class EatDataServiceImpl implements DataService {
	// 委托类,真正的撸铁健身类
	DataService delegate;
    
    public EatDataServiceImpl(DataService dataService) {
    	this.delegate = dataService;
    }
    
    public String 撸铁健身(String name) {
    	System.out.println(name + "正在吃饭中");
    	return delegate.撸铁健身(name);
    }
}

static DataService service = new EatDataServiceImpl(new DataServiceImpl());

public static void main(String [] args) {
	service.撸铁健身("张三");
}

这时候输出的结果就是
张三正在吃饭中
张三正在健身中

装饰器模式的好处就是可以不改动原本类的任何代码,在外面套一层就可以增加额外的功能,

当然这种实现也依赖于接口这个特性,我们定义好一个没有实现的接口,真正实现的时候,不用关心到底是 DataServiceImpl 还是 EatDataServiceImpl ,

只要真正的实现类实现了我们的接口,就可以正常使用。

利用装饰器模式,我们就可以实现在调用真正的核心类DataServiceImpl.撸铁健身方法前做一些日志、鉴权以及缓存的功能了。

下面我们实现一个缓存的装饰类:

public class CacheDataServiceImpl implements DataService {
    // 缓存
	private Map<String,String> cache;

	// 委托类,真正的撸铁健身类
	DataService delegate;
    
    public CacheDataServiceImpl(DataService dataService) {
    	this.delegate = dataService;
        this.cache = new HashMap<>();
    }
    
    public String 撸铁健身(String name) {
    	// 如果张三已经健身过就直接从缓存中返回健身完毕
    	String cacheResult = cache.get(name);
    	if (cacheResult == null) {
          cacheResult = delegate.撸铁健身(name);
          cache.put(name,cacheResult);
        }
    	return cacheResult;
    }
}

static DataService service = new CacheDataServiceImpl(new EatDataServiceImpl(new DataServiceImpl()))

public static void main(String [] args) {
	System.out.println(service.撸铁健身("张三"));
    System.out.println(service.撸铁健身("张三"));
    System.out.println(service.撸铁健身("张三"));
}

这时候第一次输出的结果就是
张三正在吃饭中
张三正在健身中
健身完毕

因为张三已经健身完了,所以结果被缓存起来了,其余调用都是
健身完毕

源代码在:https://github.com/qiaomengnan16/DecoratorPatternDemo

下图是MyBatis的一个类,可以看见,MyBatis也使用了装饰器模式,在 CachingExecutor 类中有一个 delegate,

用于执行真正业务方法,

CachingExecutor 这个类作用于一些缓存操作,因为数据库的IO操作开销比较大,所以某些情况下一些结果被MyBatis放在了缓存中,

当缓存中没有或者需要刷新缓存的时候,调用委托类 delegate 的方法。

最后总结一下就是,当多个不同的实现类实现同一个接口的时候,我们可以使用装饰器模式为他们增加额外的功能。

AOP的实现在下一篇写,下面是链接地址:https://juejin.im/post/6865916387471310856

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值