Spring AOP 如何将参数传递给需要织入的方法 - V2EX
Vimax
V2EX    Java

Spring AOP 如何将参数传递给需要织入的方法

  •  
  •   Vimax Jul 17, 2020 4042 views
    This topic created in 2124 days ago, the information mentioned may be changed or developed.

    后台管理系统,每个表有对应的创建者,更新者字段。

    当在页面进行操作时,需要先获取将操作者的信息,然后记录到表。

    现在用户的信息是存在 session 中,每次执行方法的时候都要从 session 中获取操作者信息,然后将操作者的信息放入更新的对象中,然后插入 /更新到表中。

    能否借用 AOP 来完成获取登入用户的信息,然后将操作者信息的属性,通过 AOP 传参给方法,然后在方法中直接通过对象来接收。然后再插入 /更新到表中。

    看了 spring AOP 可以通过 args 等方法,向通知传递数据。

    那么反过来,aop 可以给需要织入的方法传递数据吗?

    或者有什么其他方法可以简化获取用户信息,并记录到表中。

    20 replies    2020-07-20 19:00:14 +08:00
    lemonEssence
        1
    lemonEssence  
       Jul 17, 2020
    如果你用的是 JPA 的话可以用 JPA 审计, @CreatedBy @LastModifiedBy
    Vimax
        2
    Vimax  
    OP
       Jul 17, 2020
    @lemonEssence 谢谢了。用的是 mybatis 呢。
    avk458
        3
    avk458  
       Jul 17, 2020
    提供个获取信息的静态方法,多线程或者异步线程参考 DelegatingSecurityContext
    rockyou12
        4
    rockyou12  
       Jul 17, 2020
    使用自定义 spel 非常好,不过稍微有点复杂,我之前写的基于 aop 的 rbac 框架就是这样做的,效果如下。

    @GetMapping("/{id}")
    @Audit(resType = ResType.shop, resId = "#id", resOpt = ResOpt.READ)
    public ShopViewDto getViewById(String id){
    ShopViewDto shopViewDto = shopService.selectDetailById(id);
    return shopViewDto;
    }

    这个 #id 就是读取的方法上的参数,你要读其他的只要反射得出来都可以,甚至是其他 context 里的参数也可以写在 spel 中,但定义 spel 稍微有点复杂
    optional
        5
    optional  
       Jul 17, 2020
    其实并没有觉得这样算是干净了,java 里那一套,基本只能用楼上说的 spel 表达式来搞,但是后果就是失去了强类型的加持。
    aop 当然可以给传递数据,但是这样就不干净了, 最好用 threadlocal 这种放在上下文 context 里。
    dr1q65MfKFKHnJr6
        6
    dr1q65MfKFKHnJ6  
       Jul 17, 2020 via iPhone
    之前 用自定义注解 然后 aop 注入到参数里,类似 validation 后加一个验证结果,就是很不面向 Java 编程。
    lewis89
        7
    lewis89  
       Jul 17, 2020
    @optional 要是动态了 也轮不到 AOP 了..
    lewis89
        8
    lewis89  
       Jul 17, 2020
    @optional AOP 本来就是给静态语言打补丁的 要是语言本身是动态类型的 根本不需要 AOP
    lewis89
        9
    lewis89  
       Jul 17, 2020
    @optional 另外切面跟注解本身 并不是为了干净,而是为了可拔插,注解你想加就加 想移除就移除 不会对软件系统造成任何功能性的影响,类似缓存,你移除缓存注解 不会影响软件原本的业务逻辑行为
    optional
        10
    optional  
       Jul 17, 2020
    @lewis89 其实我认为问题在 java/jvm 的 annotation 里只能放常量类型,不支持放表达式,否则会好很多。
    lewis89
        11
    lewis89  
       Jul 17, 2020
    @optional 切面的逻辑也是类似的,可以通过注解来标识 那些方法需要拦截,这样设计代码模块 非常容易拔插
    optional
        12
    optional  
       Jul 17, 2020
    哪怕是编译器常量都不行
    optional
        13
    optional  
       Jul 17, 2020
    @lewis89 aop 本身是个 decorate 模式,这个没有问题问题,问题只支持常量类型,然后为了突破这个,搞出 spel 这种,就很脏。
    magicdu
        14
    magicdu  
       Jul 17, 2020 via Android
    mybatisplus 有个 metaobjecthandler
    optional
        15
    optional  
       Jul 17, 2020
    @lewis89 在 spring 这套体系里,aop 已经不能叫『可插拔』了,去掉注解和增加注解本质上是两个程序了。
    aspect 里很脏的,还有 @Pointcut 这种,专门写个空方法来提供逻辑。。你说他不脏?
    magicdu
        16
    magicdu  
       Jul 17, 2020
    @magicdu #14
    ```
    /**
    * 处理新增和更新的基础数据填充,配合 BaseEntity 和 MyBatisPlusConfig 使用
    */
    @Component
    public class MetaHandler implements MetaObjectHandler {


    /**
    * 新增数据执行
    * @param metaObject
    */
    @Override
    public void insertFill(MetaObject metaObject) {

    UserDetails user;
    try {
    user = SecurityUtils.getUserDetails();
    this.setFieldValByName("crtUserName", user.getUsername(), metaObject);
    this.setFieldValByName("crtUserId", SecurityUtils.getUserId(), metaObject);
    this.setFieldValByName("updUserName", user.getUsername(), metaObject);
    this.setFieldValByName("updUserId", SecurityUtils.getUserId(), metaObject);
    } catch (Exception e) {

    }
    this.setFieldValByName("crtTime", new Date(), metaObject);
    this.setFieldValByName("updTime", new Date(), metaObject);


    }

    /**
    * 更新数据执行
    * @param metaObject
    */
    @Override
    public void updateFill(MetaObject metaObject) {
    UserDetails user;
    try {
    user = SecurityUtils.getUserDetails();
    this.setFieldValByName("updUserName", user.getUsername(), metaObject);
    this.setFieldValByName("updUserId", SecurityUtils.getUserId(), metaObject);
    } catch (Exception e) {

    }
    this.setFieldValByName("updTime", new Date(), metaObject);
    }
    }
    ```
    配合 BaseEntity 和 MyBatisPlusConfig 使用
    ```
    @Configuration
    public class MyBatisPlusConfig {

    /**
    * 自动填充功能
    * @return
    */
    @Bean
    public GlobalConfig globalConfig() {
    GlobalConfig globalCOnfig= new GlobalConfig();
    globalConfig.setMetaObjectHandler(new MetaHandler());
    return globalConfig;
    }

    }
    ```
    MIUIOS
        17
    MIUIOS  
       Jul 17, 2020
    我采用的是注解拦截 自写了一个注解类 然后标注需要验证 token 的方法 如果这个方法参数需要一个 user 类的话 就注入一个用户实体类 不知道符不符合楼主的需求

    if(method.getAnnotation(TokenAccess.class).userHold()){
    for (int i = 0;i<args.length;i++) {
    // 需要做进一步判断,判断这个方法是否需要这个 user
    if(args[i]!=null && args[i].getClass() == User.class){
    args[i] = user;
    }
    }
    }
    EscYezi
        18
    EscYezi  
       Jul 18, 2020 via iPhone
    请求用过滤器把 session 里面的对象保存好写到 ThreadLocal,然后 aop 直接取 ThreadLocal 里的 user 对象,写表的操作在 aop 里面来调用。
    贴中的用 AOP 获取用户对象注入到方法参数里这个不太能理解,写表的操作是由被切的方法执行的?如果是这样的话,参数直接加个 HttpSession 再封一个工具类不就好了
    EscYezi
        19
    EscYezi  
       Jul 18, 2020 via iPhone
    抱歉抱歉,没有看到是要把用户信息放在属性里.....
    siweipancc
        20
    siweipancc  
       Jul 20, 2020 via iPhone
    :D 你这个只能在 Around 织入,官方有 demo, 深入的话建议看下 EntityListener 和 Spring Cache 的实现源码。
    About     Help     Advertise     Blog     API     FAQ     Solana     1033 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 51ms UTC 22:13 PVG 06:13 LAX 15:13 JFK 18:13
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86