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

2020-07-17 14:36:26 +08:00
 Vimax

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

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

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

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

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

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

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

3017 次点击
所在节点    Java
20 条回复
lemonEssence
2020-07-17 14:40:14 +08:00
如果你用的是 JPA 的话可以用 JPA 审计, @CreatedBy @LastModifiedBy
Vimax
2020-07-17 14:56:15 +08:00
@lemonEssence 谢谢了。用的是 mybatis 呢。
avk458
2020-07-17 15:19:34 +08:00
提供个获取信息的静态方法,多线程或者异步线程参考 DelegatingSecurityContext
rockyou12
2020-07-17 15:24:37 +08:00
使用自定义 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
2020-07-17 15:41:54 +08:00
其实并没有觉得这样算是干净了,java 里那一套,基本只能用楼上说的 spel 表达式来搞,但是后果就是失去了强类型的加持。
aop 当然可以给传递数据,但是这样就不干净了, 最好用 threadlocal 这种放在上下文 context 里。
cedoo22
2020-07-17 16:41:45 +08:00
之前 用自定义注解 然后 aop 注入到参数里,类似 validation 后加一个验证结果,就是很不面向 Java 编程。
lewis89
2020-07-17 18:04:29 +08:00
@optional 要是动态了 也轮不到 AOP 了..
lewis89
2020-07-17 18:05:43 +08:00
@optional AOP 本来就是给静态语言打补丁的 要是语言本身是动态类型的 根本不需要 AOP
lewis89
2020-07-17 18:07:35 +08:00
@optional 另外切面跟注解本身 并不是为了干净,而是为了可拔插,注解你想加就加 想移除就移除 不会对软件系统造成任何功能性的影响,类似缓存,你移除缓存注解 不会影响软件原本的业务逻辑行为
optional
2020-07-17 18:08:26 +08:00
@lewis89 其实我认为问题在 java/jvm 的 annotation 里只能放常量类型,不支持放表达式,否则会好很多。
lewis89
2020-07-17 18:08:39 +08:00
@optional 切面的逻辑也是类似的,可以通过注解来标识 那些方法需要拦截,这样设计代码模块 非常容易拔插
optional
2020-07-17 18:09:25 +08:00
哪怕是编译器常量都不行
optional
2020-07-17 18:11:27 +08:00
@lewis89 aop 本身是个 decorate 模式,这个没有问题问题,问题只支持常量类型,然后为了突破这个,搞出 spel 这种,就很脏。
magicdu
2020-07-17 18:14:45 +08:00
mybatisplus 有个 metaobjecthandler
optional
2020-07-17 18:15:42 +08:00
@lewis89 在 spring 这套体系里,aop 已经不能叫『可插拔』了,去掉注解和增加注解本质上是两个程序了。
aspect 里很脏的,还有 @Pointcut 这种,专门写个空方法来提供逻辑。。你说他不脏?
magicdu
2020-07-17 18:21:56 +08:00
@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;
}

}
```
totoro52
2020-07-17 18:33:16 +08:00
我采用的是注解拦截 自写了一个注解类 然后标注需要验证 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
2020-07-18 21:31:00 +08:00
请求用过滤器把 session 里面的对象保存好写到 ThreadLocal,然后 aop 直接取 ThreadLocal 里的 user 对象,写表的操作在 aop 里面来调用。
贴中的用 AOP 获取用户对象注入到方法参数里这个不太能理解,写表的操作是由被切的方法执行的?如果是这样的话,参数直接加个 HttpSession 再封一个工具类不就好了
EscYezi
2020-07-18 21:34:01 +08:00
抱歉抱歉,没有看到是要把用户信息放在属性里.....
siweipancc
2020-07-20 19:00:14 +08:00
:D 你这个只能在 Around 织入,官方有 demo, 深入的话建议看下 EntityListener 和 Spring Cache 的实现源码。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/690890

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX