设计模式与面向切面编程:代理模式实现AOP、装饰器实现增强、模板方法定义切面
面向切面编程(AOP)这个概念,很多朋友一听就觉得高大上。其实说白了,它就是把那些散落在各个业务方法里的「横切关注点」给抽出来。比如日志、权限、事务这些,你想想看,它们是不是每个方法都要写一遍?
我个人习惯用设计模式来理解AOP。因为Spring AOP底层就是代理模式,而装饰器模式可以做更灵活的增强,模板方法模式则帮我们定义切面的骨架。今天我们就把这三种模式串起来,看看它们怎么配合实现AOP。
代理模式:AOP的基石
代理模式实现AOP,核心思想就是:不修改目标对象,而是通过代理对象来增强它。我在项目中遇到过好几次这样的场景——老系统要加日志,但代码不能动,那就用代理。
Java里分静态代理和动态代理。静态代理就是手动写一个代理类,动态代理用JDK Proxy或者CGLIB。我建议你优先掌握动态代理,因为Spring AOP默认用的就是它。
// Java动态代理示例:实现AOP日志切面
public class LogProxy implements InvocationHandler {
private Object target;
public LogProxy(Object target) {
this.target = target;
}
public Object createProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("[AOP] 调用方法: " + method.getName());
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
// 后置增强
long elapsed = System.currentTimeMillis() - start;
System.out.println("[AOP] 方法耗时: " + elapsed + "ms");
return result;
}
}
// 使用
UserService service = new UserServiceImpl();
UserService proxy = (UserService) new LogProxy(service).createProxy();
proxy.addUser("张三");
C++里实现代理模式,可以用模板和函数对象。嗯,这里要注意,C++没有内置的动态代理,但我们可以用std::function和模板来模拟。
// C++代理模式实现AOP风格增强
template<typename Func>
class LogProxy {
public:
LogProxy(Func f) : func(f) {}
template<typename... Args>
auto operator()(Args&&... args) {
std::cout << "[AOP] 调用函数" << std::endl;
auto start = std::chrono::high_resolution_clock::now();
auto result = func(std::forward<Args>(args)...);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "[AOP] 耗时: " << duration << "ms" << std::endl;
return result;
}
private:
Func func;
};
装饰器模式:更灵活的增强方式
代理模式有个局限——一个代理类通常只做一种增强。如果你要同时加日志、权限、缓存,就得嵌套代理。这时候装饰器模式就派上用场了。
装饰器模式的核心是:每个装饰器只做一件事,然后层层包装。我曾经用装饰器模式实现过一个权限校验链,每个装饰器检查一种权限,组合起来非常灵活。
// Java装饰器模式实现AOP增强链
interface Handler {
void handle(String request);
}
class BaseHandler implements Handler {
@Override
public void handle(String request) {
System.out.println("处理请求: " + request);
}
}
// 日志装饰器
class LogDecorator implements Handler {
private Handler wrapped;
LogDecorator(Handler handler) {
this.wrapped = handler;
}
@Override
public void handle(String request) {
System.out.println("[日志] 开始处理: " + request);
wrapped.handle(request);
System.out.println("[日志] 处理完成");
}
}
// 权限装饰器
class AuthDecorator implements Handler {
private Handler wrapped;
AuthDecorator(Handler handler) {
this.wrapped = handler;
}
@Override
public void handle(String request) {
if (!request.contains("admin")) {
System.out.println("[权限] 拒绝访问");
return;
}
System.out.println("[权限] 校验通过");
wrapped.handle(request);
}
}
// 使用:层层包装
Handler handler = new BaseHandler();
handler = new LogDecorator(handler);
handler = new AuthDecorator(handler);
handler.handle("admin_request");
C++实现装饰器模式,可以用继承加组合,也可以用std::function链。我个人更喜欢用函数式的方式,代码更简洁。
// C++装饰器模式实现增强链
using HandlerFunc = std::function<void(const std::string&)>;
HandlerFunc withLogging(HandlerFunc next) {
return [next](const std::string& request) {
std::cout << "[日志] 开始: " << request << std::endl;
next(request);
std::cout << "[日志] 结束" << std::endl;
};
}
HandlerFunc withAuth(HandlerFunc next) {
return [next](const std::string& request) {
if (request.find("admin") == std::string::npos) {
std::cout << "[权限] 拒绝" << std::endl;
return;
}
std::cout << "[权限] 通过" << std::endl;
next(request);
};
}
// 使用
HandlerFunc base = [](const std::string& r) {
std::cout << "处理: " << r << std::endl;
};
auto enhanced = withLogging(withAuth(base));
enhanced("admin_request");
模板方法模式:定义切面骨架
模板方法模式在AOP里扮演什么角色?它定义了切面的执行流程。比如「前置通知→目标方法→后置通知→异常处理」,这个骨架就是模板方法。
我习惯把模板方法看作AOP的「剧本」,代理模式和装饰器模式是「演员」。剧本定好了顺序,演员去执行具体的增强逻辑。
// Java模板方法定义AOP切面骨架
abstract class AbstractAspect {
// 模板方法:定义切面执行流程
public final Object execute(ProceedingJoinPoint pjp) throws Throwable {
before();
try {
Object result = pjp.proceed();
afterReturning(result);
return result;
} catch (Throwable t) {
afterThrowing(t);
throw t;
} finally {
after();
}
}
// 抽象方法:子类实现具体增强
protected abstract void before();
protected abstract void afterReturning(Object result);
protected abstract void afterThrowing(Throwable t);
protected abstract void after();
}
// 具体切面:日志切面
class LogAspect extends AbstractAspect {
@Override
protected void before() {
System.out.println("[日志] 方法开始");
}
@Override
protected void afterReturning(Object result) {
System.out.println("[日志] 方法返回: " + result);
}
@Override
protected void afterThrowing(Throwable t) {
System.out.println("[日志] 异常: " + t.getMessage());
}
@Override
protected void after() {
System.out.println("[日志] 方法结束");
}
}
C++实现模板方法,可以用虚函数加模板。嗯,这里要注意,C++的模板方法可以用CRTP模式来避免虚函数开销。
// C++模板方法定义切面骨架(CRTP模式)
template<typename Derived>
class AspectBase {
public:
void execute(std::function<void()> target) {
static_cast<Derived*>(this)->before();
try {
target();
static_cast<Derived*>(this)->afterReturning();
} catch (...) {
static_cast<Derived*>(this)->afterThrowing();
throw;
} finally {
static_cast<Derived*>(this)->after();
}
}
};
class LogAspect : public AspectBase<LogAspect> {
public:
void before() { std::cout << "[日志] 开始" << std::endl; }
void afterReturning() { std::cout << "[日志] 成功" << std::endl; }
void afterThrowing() { std::cout << "[日志] 异常" << std::endl; }
void after() { std::cout << "[日志] 结束" << std::endl; }
};
三种模式的协作关系
这三种模式不是孤立的。在实际的AOP框架里,它们经常一起出现。我画了一张图,帮你理清它们的关系。
实战中的选择建议
这三种模式怎么选?我根据项目经验总结了一个表格:
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 需要拦截所有方法调用 | 代理模式 | 统一入口,适合框架级AOP |
| 需要灵活组合多种增强 | 装饰器模式 | 每个装饰器独立,可随意组合 |
| 需要固定增强执行顺序 | 模板方法 | 骨架固定,子类只关心具体逻辑 |
| 三者都需要 | 组合使用 | 代理做拦截,装饰器做增强,模板方法定流程 |
最后说一句,设计模式不是银弹。但理解了代理、装饰器、模板方法这三种模式在AOP中的角色,你就能写出更优雅、更易维护的代码。下次遇到需要加日志、权限、缓存的场景,不妨想想这三种模式怎么配合。
公众号:蓝海资料掘金营,微信deep3321