基于AOP的插件化(扩展)方案

在项目迭代开发中经常会遇到对已有功能的改造需求,尽管我们可能已经预留了扩展点,并且尝试通过接口或扩展类完成此类任务。可是,仍然有很多难以预料的场景无法通过上述方式解决。修改原有代码当然能够做到,但是这会增加许多附加成本,回归测试带来大量工作和一些潜在的未知风险。特别是一些极其重要的公共模块,可谓牵一发而动全身,稍有不慎都将引发重大的故障。这里分享一下自己在项目开发中的一点实践,一种基于AOP的插件化(扩展)方案。

假设一个场景:

现有一个可获取人员信息的服务:UserService。

public class UserService {

    public User getUserById(String id) {
        // ...
    }
}

该服务作为一个基础服务一直运行良好,但是客户出于对数据安全的考虑需要对人员信息中某些进行属性进行脱敏。该需求总体来说不是很复杂,解决方案也不止一个,那么来看看基于AOP的实现方式是怎么样的。

插件点注解(定义一次,多处可用):

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface PluginPoint {

    Class<?> service();

    String method();

    PluginType type();

    enum PluginType {
        BEFORE, AFTER, OVERRIDE;
    }
}

插件服务接口(后期所有的插件都必须实现这个接口):

public interface PluginService {

    Object process(Object[] args, Object result) throws Exception;
}

处理插件业务的切面类:

@Aspect
@Component
public class PluginServiceAspect {
    private static Logger logger = LoggerFactory.getLogger(PluginServiceAspect.class);
    private static ConcurrentHashMap<String, PluginService> pluginConfigMap = new ConcurrentHashMap<String, PluginService>();
    private static volatile Boolean initStatus = false;

    @Around("execution(* com.lichmama.demo..service.*Service.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        PluginService pluginService = getPluginService(point);
        if (pluginService == null) {
            return point.proceed();
        }
        PluginPoint pluginPoint = pluginService.getClass().getAnnotation(PluginPoint.class);

        Object result = null;

        // before
        if (pluginPoint.type() == PluginType.BEFORE) {
            pluginService.process(point.getArgs(), null);
        }
        // override
        if (pluginPoint.type() == PluginType.OVERRIDE) {
            result = pluginService.process(point.getArgs(), null);
        }
        // after
        if (pluginPoint.type() == PluginType.AFTER) {
            result = pluginService.process(point.getArgs(), point.proceed());
        }

        return result;
    }

    private PluginService getPluginService(ProceedingJoinPoint point) {
        // initialize pluginConfigMap first
        initPluginConfigMap();

        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        String serviceMethod = className + "#" + methodName;
        return pluginConfigMap.get(serviceMethod);
    }

    private void loadPluginService() {
        Map<String, Object> plugins = SpringContextHolder.getBeansWithAnnotation(PluginPoint.class);
        if (plugins != null && plugins.size() > 0) {
            Iterator<Map.Entry<String, Object>> iter = plugins.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String, Object> entry = iter.next();
                PluginService pluginService = (PluginService) entry.getValue();
                PluginPoint pluginPoint = pluginService.getClass().getAnnotation(PluginPoint.class);
                Class<?> service = pluginPoint.service();
                String method = pluginPoint.method();
                String serviceMethod = service.getName() + "#" + method;
                pluginConfigMap.put(serviceMethod, pluginService);
            }
        }
        logger.info("pluginConfigMap initialized ...");
    }

    private void initPluginConfigMap() {
        if (initStatus == false) {
            synchronized (initStatus) {
                if (initStatus == false) {
                    loadPluginService();
                    initStatus = true;
                }
            }
        }
    }
}

当然还有最重要的插件实现类:

@PluginPoint(service = UserService.class, method = "getUserById", type=PluginType.AFTER)
public class UserServicePlugin implements PluginService {

    @Override
    public Object process(Object[] args, Object result) throws Exception {
        User user = (User) result;
        // 增加脱敏的业务逻辑
        desensitive(user);
        return user;
    }

    private void desensitive(User user) {
        //TODO ...
    }
}

至此我们的工作基本就算完成了,启动项目时只要UserServicePlugin被加载到,那么再次调用getUserById方法时,返回的就是脱敏后的人员信息了。

一点总结:

软件开发过程中需求难免会发生变化,今日精巧的设计,明天就可能变成拦路的淤泥。

原文地址:https://www.cnblogs.com/lichmama/p/11143662.html

时间: 2024-10-17 23:14:20

基于AOP的插件化(扩展)方案的相关文章

Android基于代理的插件化思路分析

前言 正常的App开发流程基本上是这样的:开发功能-->测试--->上线,上线后发现有大bug,紧急修复---->发新版本---->用户更新----->bug修复.从发现bug到修复bug花了很长时间.我们希望bug的修复是立马生效的,用户无感知就能自动修复bug.当然,Android端的动态修复bug已经有不少框架了,不过我们今天讲的是另一个话题:Android的插件化.Android插件化有很多好处:热插拔.静默升级.bug动态修复.代码解耦等.正是因为如此,才有越来越多

Android Small插件化框架源码分析

Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github.com/wequick/Small 插件化的方案,说到底要解决的核心问题只有三个: 1.1 插件类的加载 这个问题的解决和其它插件化框架的解决方法差不多.Android的类是由DexClassLoader加载的,通过反射可以将插件包动态加载进去.Small的gradle插件生成的是.so包,在初始

热门前沿知识相关面试问题-android插件化面试问题讲解

插件化由来: 65536/64K[技术层面上]随着代码越来越大,业务逻辑越来繁杂,所以很容易达到一个65536的天花板,其65536指的是整个项目中的方法总数如果达到这个数量时则不无法创建新的方法了,所以基于这个原因插件化就产生了. 功能层面的解耦.维护团队的分离,这也是大势所趋,每个团队会维护一个APK中的不同的业务模块,如果每个业务模块升级都需要对整个APK进行升级,代价实在太大,虽说目前有H5的方式能解决这个问题,但是体验上肯定是没法中Native的APP进行比较的.虽说来自Faceboo

Asp.Net MVC 插件化开发简化方案

Web 管理系统可以庞大到不可想像的地方,如果想就在一个 Asp.Net MVC 项目中完成开发,这个工程将会变得非常庞大,协作起来也会比较困难.为了解决这个问题,Asp.Net MVC 引入了 Areas 的概念,将模块划分到 Area 中去--然而 Area 仍然是主项目的一部分,多人协作的时候仍然很容易造成 .csproj 项目文件的冲突. 对于这类系统,比较好的解决办法是采用 SOA 的方式,把一个大的 Web 系统划分成若干微服务,通过一个含授权中心的 Web 集散框架组织起来.不过这

基于.NET MVC的高性能IOC插件化架构

基于.NET MVC的高性能IOC插件化架构 最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博文是我的各大IOC框架的性能测试:http://www.cnblogs.com/gengzhe/p/4370979.html 我先分析下我的系统架构: 这是整体结构图,先简单介绍下:Sun.Core是系统核心,包含了系统必要组件及基础设施的所有接口及必要拓展类.Sun.Framework是整个系统的

基于.NET MVC的高性能IOC插件化架构(二)之插件加载原理

上一篇博文简单介绍了下插件化的代码组成部门:http://www.cnblogs.com/gengzhe/p/4390932.html 这篇博客主要讲解下插件化实现的原理,先面先讲解几个概念: 一.契约 插件与系统必须有契约,系统才能发现插件并正确加载插件,我采用的是所有插件都实现Sun.Core里面的IPlugin接口. 二.自述 插件在被加载的时候,需要告诉系统,我是什么类型的插件,我的guid,我依赖的程序集,我的状态与权限,我的配置信息等等,这个行为是插件的自我描述,简称自述. 三.配置

Android 开源项目android-open-project工具库解析之(二) 高版本向低版本兼容,多媒体相关,事件总线(订阅者模式),传感器,安全,插件化,文件

六.Android 高版本向低版本兼容 ActionBarSherlock 为Android所有版本提供统一的ActionBar,解决4.0以下ActionBar的适配问题 项目地址:https://github.com/JakeWharton/ActionBarSherlock Demo地址:https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demos APP示例:太多了..现在连google都

从ExtensionLoader看Dubbo插件化

欢迎加入 DUBBO交流群:259566260 之前很多人问我Dubbo插件化是怎么实现的,我都是简单回答SPI.了解SPI的人知道,它只是提供一种协议,并没有提供相关插件化实施的接口,不像OSGI那样有一成套实施插件化API.它只是规定在META-INF目录下提供接口的实现描述文件,框架本身定义接口.规范,第三方只需要将自己实现在META-INF下描述清楚,那么框架就会自动加载你的实现,至于怎么加载,JDK并没有提供相关API,而是框架设计者需要考虑和实现的,并且在META-INF下面对实现描

360手机卫士插件化RePlugin今日开源

写在前面 "RePlugin将在6月底开源,这将是我们献给安卓世界最好的礼物."当我们宣布这一消息时,心中的激动,无以言表.是的,三年的"厚积",如今的"薄发",看似平凡的话,实际上却饱含了我们太多的激动.辛酸与泪. 那么今天,我们就来详细的和您聊一聊,这个从2014年中旬,正式在手机卫士上启用,并即将开源的360 RePlugin,究竟能为我们,更为您能带来什么. GitHub地址:https://github.com/Qihoo360/ReP