我封装了个实体
@Data
public UserDto{
private String userId;
private String userName
}
在设定中 userId是肯定有值
我想拦截 getUserName()方法
判断如果userName 为空
我根据userId去处理一个 userName值作为返回
但是 测试AOP 貌似拦截不到get方法
你是不是可以重写一下getUserName()方法,在方法里面增加userName为空的判断和处理逻辑。没必要非去拦截get方法啊
别想了,没交给容器管理,你就是个普通对象而非代理对象!只能将new一个对象,改成生成代理对象,这样才能执行aop操作!
写一个自定义注解,将该注解打在你的get方法上,然后aop切面该注解,这样也是清晰明了,后期无论多少个类,都可以使用该注解进行切面,但是切面的前提是该类必须是bean,你这个实体放进去没意义,建议做一个接口,接口定义一个方法,所有需要操作的实体都实现该接口,重写方法,方法中获取username 如果为空那么返回id
要用spring的aop切面,首先对象得是spring管理的bean,你这实体类不可能生成bean去让spring管理的;
如果只是单纯的想让name为空时根据id去设置name,实体类可以直接对name设置默认值 = id;
或者用反射去根据对象获取值再进行判断填充
Spring AOP 是在运行时动态为所定义的 bean 创建代理,拦截 bean 方法的执行,你定义的 DTO 不是 bean,因此拦截不到 DTO 方法的执行。
只能使用 AspectJ 在编译时生成字节码拦截 DTO 方法的执行。
这种的应该是将aop做在controller层面吧.可以在自定义个一个注解,然后将aop作用到这个注解上,然后在需要调用的地方加上自定义注解。dto再去处理感觉很不合理,要么在controller切面去处理,要么在service处理。切面实现类似如下
@RequestMapping()
@BeforeXxx
public void getXxx()
@before(@annotation(BeforeXxx))
public void beforeXxx(final JointPoint joinPoint){
//根据id设置name
}
如果是controller层的话定义一个注解,做restcontrolleradvice在读取body前拦截把实体转成map去get你要get的属性然后判断
建议换一个思路处理问题:
增加统一得BaseDto 把 xxxxId 和 xxxxName放到BaseDto里面,然后你其他的Dto继承BaseDto就行了
因为你的Bean不是交给spring托管的,想直接拦截你的实体是不可能的.可以拦截controller,在返回的时候拦截进行统一处理。下面的是我们进来和返回分别处理表情转码的例子
你可以在那个get属性的时候进行判断 在这个方法里写相关的操作
插个天眼
首先,这个想法是不对的,aop的前提是这个对象是容器创建的,很明显dto并不是容器创建的;
如果你想实现描述的功能,只能从侧面间接做到切面效果,比如在dao层做切面,检测返回结果中字段满足你的要求的情况下对返回结果做修订,这个相信你可以自己实现;
其他方式多少都需要对你的dto做修改,然而这恰恰不是你想要的。。。
突然想起一个东东貌似可以满足你的需求,不过有点麻烦,AspectJ你可以了解一下
感觉反射就能做
提供一下思路参考:
切记不要在方法内部出现
new XXXDTO()
的操作,会失效。如果是这种方式参考1解决。 如果是从数据库查询出来的,也是一样。必须得从方法拦截里面去处理这个DTO。
反射
和1有差不多的效果,编码式判断。不用继承。
非侵入的方式
稍微比较复杂,参考arthas产品。
创建注解
package com.zhao.annotationaop.annotation;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
//最高优先级
@Order(1)
public @interface InterceptGetName {
String value() default "getName";
}
通过AOP实现注解功能
package com.zhao.annotationaop.accomplish;
import com.zhao.annotationaop.annotation.InterceptGetName;
import com.zhao.annotationaop.entity.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Aspect
@Scope
@Component
public class accomplishGetNameAnnotation {
@Before("within(@org.springframework.web.bind.annotation.RestController *) && @annotation(getName)")
public void requestLimit(final JoinPoint joinPoint, InterceptGetName getName) throws IOException {
Object[] args = joinPoint.getArgs();
User user = (User) args[0];
String value = getName.value();
switch (value) {
case "getName": {
String name = user.getName();
if (name == null) {
user.setName("123");
}
}
;
break;
case "getId": {
String id = user.getId();
if (id == null) {
user.setId("456");
}
}
;
break;
default:
break;
}
}
}
编写测试Controller
package com.zhao.annotationaop.controller;
import com.zhao.annotationaop.annotation.InterceptGetName;
import com.zhao.annotationaop.annotation.RequestLimit;
import com.zhao.annotationaop.entity.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class TestController {
@InterceptGetName(value = "getId")
@RequestMapping("/test2")
public String test2(User user){
System.out.println(user);
return "123";
}
}
启动访问测试
返回结果
采用反射来直接获取吧,自己写的话可能有冲突
写一个自定义注解,将pointcut定义为该注解,然后把注解写在getxx上
封装一个方法,入参是UserDto的接口类型,然后在里面调用getUserName,根据返回结果统一做处理。
/**
*
* @param obj
* @param mapFiled userName ,userId 如果userName获取不到值 使用 userId 经过function进行处理,且userId 不为空
* @param function
* @param <T>
*/
public static <T> void doNameDeal(Object obj, Map<String,String> mapFiled, Function<String,T> function){
Class<?> aClass = obj.getClass();
if (mapFiled==null || mapFiled.isEmpty()|| function == null){
return;
}
for (Map.Entry<String, String> entry : mapFiled.entrySet()) {
String key = entry.getKey();
String val = entry.getValue();
try {
Field nameField = aClass.getDeclaredField(key);
nameField.setAccessible(true);
if (nameField.get(obj) == null){
Field idField = aClass.getDeclaredField(val);
idField.setAccessible(true);
// 题目意思是该值不为空,直接toString了
String id = idField.get(obj).toString();
T applyV = function.apply(id);
nameField.set(obj,applyV);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Data
public static class UserDto{
private String userId;
private String userName;
}
public static String getNameById(String id){
//模拟按照id获取名字
return id+"---";
}
public static void main(String[] args) {
UserDto dto = new UserDto();
dto.setUserId("1");
dto.setUserName("name");
Map<String,String> map = new HashedMap();
map.put("userName","userId");
doNameDeal(dto,map,AlipayFactory::getNameById);
System.out.println(dto);
dto.setUserName(null);
doNameDeal(dto,map,AlipayFactory::getNameById);
System.out.println(dto);
}
AlipayFactory.UserDto(userId=1, userName=name)
AlipayFactory.UserDto(userId=1, userName=1---)
如果所有的xxxID和xxxName 所有的dto处理的逻辑都一样,那么久这么写很方便
自定义注解不难解决
现代的开发集成工具IDE都有批量替换功能,我觉得你就不应该试图批量拦截实体类的getName方法,如果需要改,就做批量替换。
@After("execution(* com.test.entity..get(..))")
@After("execution( com.test.entity..*set(..))")