新版博客升级完成

Flowable怎么通过委托表达式获取到Micronaut的bean

flowable Unknown property used in expression: ${testListener}

众所周知在spring中工作流引擎activiti/flowable是可以通过委托表达式获取到spring bean的。但在micronaut中会报这个异常,因为activiti/flowable官方已经集成好了spring,而micronaut并没有人帮你集成过,获取不到容器里的bean是理所当然的。

由于micronaut是个新框架,网上翻了一圈都没找到对路的文章,以及在micronaut中怎么解决。

无奈之下自能自己出手了,翻了翻flowable的源码,找到跟spring集成部分的代码,理解了一下,模仿着写了一个针对micronaut的简单集成,打通flowable和micronaut容器,经过一番调试之后问题解决。


在给任务节点设置监听器,运行到这个节点时会出现这个异常flowable Unknown property used in expression: ${testListener},监听器的设置如下图所示,设置了委托表达式delegateExpression,本意是想获取一个bean作为监听器。

对应的xml为:

<userTask id="task1" name="task1" flowable:formFieldValidation="true">
    <extensionElements>
        <flowable:taskListener event="complete" delegateExpression="${billGenerateListener}">
        </flowable:taskListener>
    </extensionElements>
</userTask>

监听器代码如下:

@Context
@Named("billGenerateListener")//监听器名称,跟委托表达式里的设置对应
public class BillGenerateTaskListener implements TaskListener {

    @Override
    public void notify(final DelegateTask delegateTask) {
        delegateTask.getTaskDefinitionKey();
        final Map<String, Object> variables = delegateTask.getVariables();
        final Object costPrice = variables.get("costPrice");
        log.warn("Generate Bill...");
        log.warn("costPrice:{}",costPrice);
    }
}

下面直接看怎么解决的吧,不扯什么工作原理、底层机制,我们只是为了解决问题,直接抄了能用最好!

我们知道flowable引擎对象ProcessEngine一般是通过ProcessEngineConfiguration#buildProcessEngine()创建的,这是使用引擎的入口。

那么需要在ProcessEngineConfiguration中设置一个自己扩展的ProcessExpressionManager

cfg.setExpressionManager(
                new MicronautExpressionManager(applicationContext, cfg.getBeans()));

MicronautExpressionManager:

public class MicronautExpressionManager extends ProcessExpressionManager {
    protected ApplicationContext applicationContext;//micronaut的context
    
    public MicronautExpressionManager(ApplicationContext applicationContext, Map<Object, Object> beans) {
        super(beans);
        this.applicationContext = applicationContext;
    }

    @Override
    protected ELResolver createElResolver(VariableContainer variableContainer) {
        CompositeELResolver compositeElResolver = new CompositeELResolver();
        compositeElResolver.add(createVariableElResolver(variableContainer));

        compositeElResolver.add(createMicronautElResolver());//主要是这里
        compositeElResolver.add(new ArrayELResolver());
        compositeElResolver.add(new ListELResolver());
        compositeElResolver.add(new MapELResolver());
        compositeElResolver.add(new JsonNodeELResolver());
        compositeElResolver.add(new BeanELResolver());
        compositeElResolver.add(new CouldNotResolvePropertyELResolver());
        return compositeElResolver;
    }

    protected ELResolver createMicronautElResolver() {
        if (beans != null) {
            return new ReadOnlyMapELResolver(beans);
        } else {
            // 还有这里,在表达式中暴露 application-context
            return new MicronautApplicationContextElResolver(applicationContext);
        }
    }
}

再看下MicronautApplicationContextElResolver:

public class MicronautApplicationContextElResolver extends ELResolver {
    protected ApplicationContext applicationContext;

    public MicronautApplicationContextElResolver(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    //主要就是这里通过bean名称获取micronaut的bean
    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        if (base == null) {
            String key = (String) property;

            final Object o = applicationContext.getBean( Object.class, Qualifiers.byName(key));
            if (o!=null) {
                context.setPropertyResolved(true);
                return o;
            }
        }
        return null;
    }

    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        return true;
    }

    @Override
    public void setValue(ELContext context, Object base, Object property, Object value) {
        if (base == null) {
            String key = (String) property;
            final Object o = applicationContext.getBean( Object.class, Qualifiers.byName(key));
            if (o!=null) {
                throw new FlowableException("Cannot set value of '" + property + "', it resolves to a bean defined in the micronaut application-context.");
            }
        }
    }

    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object arg) {
        return Object.class;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object arg) {
        return null;
    }

    @Override
    public Class<?> getType(ELContext context, Object arg1, Object arg2) {
        return Object.class;
    }
}

好了,抄完作业就阔以了。

原文发表于http://jenwang.me

更多交流请关注公众号:
-h200

[读书笔记]旅行的艺术

关于期待与实际的差异

实际的旅行跟我们对它的期待往往是有差异的。
因为艺术作品中的描述,以及我们的想象带有极强的简括性,而现实生活中,必须承受那些所忽略的环节。

继续阅读 →

SSH 隧道实现内网穿透

家里买了台 NETGEAR 路由器刷了梅林固件,可以在 USB 接口上挂个存储当 NAS 用,发现家里的移动宽带是没有公网 ip 的,于是在外网访问不到内网,ddns 也没法用了(不要给我推荐花生壳内网版😝)。那么,需求就是如何让外网访问到内网ip。

于是想到了从内网到公网上的某台机器建立一条 ssh 隧道,通过访问公网机器把数据穿透到内网,实现方法:
在内网 10.96.x.x 上执行 ssh -N -f -R 8088:10.96.x.x:443 root@167.88.x.x -b 0.0.0.0 "vmstat 30"
让外网可以通过公网的 167.88.x.x:8088 访问到内网10.96.x.x:443

备注:167.88.x.x上需要在 /etc/sshd/sshd_config 中修改 GatewayPorts noGatewayPorts yes ,
否则 8088 端口是绑定在 127.0.0.1 上的,只有本机能访问。


群晖的 quickconnect,以及内网版的 ddns 原理应该都类似,实现方式可能有所不同。这种可用性关键在于公网中转服务器的网速和通道的稳定性。

观察到许多公司常见的一种现象,出现了某些瓶颈,希望招个水平高的来突破。然而瓶颈往往是由于自身在某方面或全局的认知水平决定的,新招进来的人往往只能在原有的认知模式下工作,结果经常是这样的:要么融入其中但没啥作为,要么受不了而离开。

初创回忆之管理

一路磕磕绊绊,遇到不少问题,也有一点微小的心得,这次就写记录管理方面的。

逻辑一致

做为管理者不能逻辑混乱,前后逻辑要一致,「自洽」都做不到是很糟糕的,员工会一头雾水甚至抱怨。每次一遇到问题就想制定相应的措施,但后续的问题往往恰好相反,如果因某些小概率错误就大动干戈制定措施,只会给公司带来伤疤。

例如,

继续阅读 →

新时代职业个性

这个坑是去年挖的,后来想填发现内容想不起来了,今天想起来了就简略填一下吧。

经常听说90后的个性更加自我,崇尚自由,叛逆,不服从权威等,然后得出针对90后的管理方法应该转变,认为他们不像70、80后那样服从管理和隐忍,在做自己想做的事才能发挥最大的积极性,用僵硬的规则管理会让他们觉得缺乏自由而产生负面效果,应该以激发成就感和认同感为主,从而带动工作效率。

认同这种想法的人已经是善于接受新事物的了,没得出负面结论已经不错了。但是,有一点我并不认同…

继续阅读 →

[读书笔记]重新定义管理

本书主要讲了新时代科技企业的特点以及传统家长制、规划和控制等管理方式的弊端,提出了一种新的管理范式「合弄制」(这个翻译感觉怪怪的)。「合弄制」主要思想是分权自治,抛弃传统的层级管理结构采用圈子的形式作为组织结构,个人和圈子都具备自治权,同时伴随着责任,遵循章程办事,从人治到法治,既解放管理者又把权力交还给工作过程,发挥每个人的主动性和价值。

有人说这是「敏捷开发」的企业版,确实有异曲同工之处。与敏捷开发类似,认为一切都是在变化之中的,因而更倾向于根据实际反馈动态调整工作,而非以初始规划为重,然后提出一套实际操作手法,例如,章程制定、会议形式、人员组织形式等。

倘若完全照搬未免落入死板被动的境地,具体方法都套用的话也就落入了「重量级」的俗套,许多做法听起来是这么回事,但实际工作环境和团队情况千差万别,指某种使用「银弹」般的管理方法而后就可以安枕无忧的想法不现实。至于书本中提倡整个企业采取颠覆式的变动来推行,未免过于乐观,就像作者说的不愿放权的领导不愿合作的中层突然停止综合症等阻碍,还只是冰山一角,不具体展开。确实有非常好的理念,这种做事氛围也是自己认同和向往的,但也有鼓吹的成分,有些内容信服度不高。书中的一个重要观点是根据实际反馈动态调整,那么扩展一下可以同样用于「合弄制」本身,通过吸收令人反思的部分来改善管理工作,获得更加适合于自己的方式。

继续阅读 →

[读书笔记]合适

作者: [日] 坂井丰贵

开始看到这本书其实挺惊讶的,有种见微知著的感觉,看上去如此平常简单的事情,竟然可以通过理论体系和算法得到更合理的解决,而不是凭感觉行事。那么日常中,我们一直采用无知落后的方式来做决策而不自知的有多少?

深感市场设计的魅力,以及为经济学家思维的层次感与创造力所折服。制度和游戏规则设计绝不是件简单的事,有时甚至需要经过理论和公式的严密论证。良好的规则应该获得优化结果的同时还能防止策略性操作,使得表达真实意图和诚信的行为得到鼓励,消除碰运气和投机性而获利的可能。

以下记录本书的关键点,本书分三个部分,

继续阅读 →

[读书笔记]茶书

作者: [日] 冈仓天心

感想与摘录

原以为是介绍喝茶知识的书,然而并不是。一直对日本诸多侘寂之美的设计颇为赞赏,简朴亲切而又实用摒弃任何多余的干扰,然而对于这种审美的根基一无所知,看完后又刷新了对日本的一些认知,对简约朴素的审美和文化之渊源有了全新的了解,甚是惊喜。
译者的后记是本书不可忽视的篇章,更通透地解释了中日文化渊源和差异,视角非常有启发。中国艺术的背后主要是权力、审美的统治,而日本则是普通民众的力量,民艺运动通过改善民众的生活品质进而促进社会进步。日本茶道的建立是对这种审美统治的挑战,属于自己空间关系的审美而非复制中国的审美,我们总是提传统文化的保护却很少钻研传统文化的发展,传统不应该成为包袱,应该随时蜕去积攒千年的壳,关照事物纯粹的本质,没有创造无所谓传统,传统是创造的积淀。极为认同译者所言中国没有茶道,中国有饮茶的方法,近年来大陆的饮茶之风日盛,只是附庸风雅、软弱无力,是虚假和令人生厌的,没有文化的优雅是妖,再多的人声鼎沸也只能是繁华躁动且妖气弥漫。

我和工具

好的工具对于提升效率、提高生活品质起到重要作用。梳理和分享下我正在使用的,本列表长期更新。

继续阅读 →