设计模式与面向切面编程:代理模式实现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;
};

装饰器模式:更灵活的增强方式

代理模式有个局限——一个代理类通常只做一种增强。如果你要同时加日志、权限、缓存,就得嵌套代理。这时候装饰器模式就派上用场了。

装饰器模式的核心是:每个装饰器只做一件事,然后层层包装。我曾经用装饰器模式实现过一个权限校验链,每个装饰器检查一种权限,组合起来非常灵活。

我的经验:装饰器模式比代理模式更适合「增强链」的场景。每个装饰器只关注一个横切点,组合起来就是完整的AOP切面。
// 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面向切面编程 代理模式实现拦截 + 装饰器实现增强链 + 模板方法定义流程 三者协作,实现完整的AOP框架 三种设计模式各司其职,共同支撑AOP的实现

实战中的选择建议

这三种模式怎么选?我根据项目经验总结了一个表格:

场景 推荐模式 原因
需要拦截所有方法调用 代理模式 统一入口,适合框架级AOP
需要灵活组合多种增强 装饰器模式 每个装饰器独立,可随意组合
需要固定增强执行顺序 模板方法 骨架固定,子类只关心具体逻辑
三者都需要 组合使用 代理做拦截,装饰器做增强,模板方法定流程
避坑指南:我曾经在一个项目里只用代理模式做所有增强,结果代理类越来越臃肿。后来改成装饰器模式,每个增强独立一个类,维护起来轻松多了。记住:单一职责原则在这里同样适用。

最后说一句,设计模式不是银弹。但理解了代理、装饰器、模板方法这三种模式在AOP中的角色,你就能写出更优雅、更易维护的代码。下次遇到需要加日志、权限、缓存的场景,不妨想想这三种模式怎么配合。


公众号:蓝海资料掘金营,微信deep3321