切面编程遇到的注入service为null的问题分析

切面编程遇到的注入service为null的问题分析


最近学习并运用了AOP切面编程,主要是在所有的controller方法中增加权限校验、时间统计。步骤如下:
1、引入依赖包
在这里插入图片描述

2、增加AOP配置类,并定义切入点@PointCut以及切入后要做的事情。
在这里插入图片描述

3、由于并不是controller中所有的api接口,需要进行权限校验,所以,我增加了一个自定义注解@CheckAuth,对controller下配有该注解的方法才进行权限校验。
在这里插入图片描述

3、增加了一个接口,进行了测试。
在这里插入图片描述

/testAop接口可以正常运行,以为就完成了。结果出现了问题,某些api接口中,通过@Autowired引入的service对象,出现了null的情况,导致接口异常。

原来,这个与Springboot的动态代理有关。Springboot默认cglib动态代理模式。动态代理是什么?Cglib又是怎么代理的?AOP切面编程跟cglib又有什么关联?

通俗地讲,代理proxy就像是代理人,代表他人办理事务,他人就是被代理人。如果被代理人固定为一个,就是静态代理,反之,就是动态代理。动态代理分为JDK动态代理和CGLIB动态代理。

JDK动态代理,是jre内部提供的,可以直接使用,最关键的两个词儿就是proxy、invoke,就是Proxy创建一个对象实例,实现InvocationHandler的invoke方法,同时InvocationHandler包含了被代理对象,就像是被代理对象的一个包装。如下图,Proxy代理了InvocationHandler,InvocationHandler代理了Target,两级代理,也是对象之间的代理。开发人员可在InvocationHandler的invoke()方法内部,写自己的代码,实现某些操作,即代码增强。

在这里插入图片描述

Ciglib动态代理,本质是使用了继承,并且子类重写父类的方法(方法名与父类方法一致)。最关键的词儿就是MethodInterceptor、intercept。B(代理类)继承了Target类、实现了MethodInterceptor,且子类B中有重写方法、cglib方法(名叫“CGLIB”+“ 父 类 方 法 名 父类方法名 ”的方法)、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法是有映射关系的。Ciglib代理,就是通过这个intercept,去调用了ciglib方法,继而去调用代理类中的重写方法(因为invoke反射机制无法直接调用父类的方法)。我们可以在intercept方法中写增强代码。

AOP切面编程,默认就是用了ciglib的动态代理。其中代理类中的ciglib方法只能构建父类的非private方法,controller中的private对象和方法,是无法被动态代理继承的。所以AOP切入的controller中接口是必须要声明为public的。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud 项目的切面中根据数据字典格式化返回值,你可以使用 AOP(面向切面编程)来实现。下面是一个示例的 Java 代码: 首先,你需要创建一个数据字典的实现类,用来管理具体的键值对。这里使用 `HashMap` 作为数据字典的示例: ```java import org.springframework.stereotype.Component; import java.util.HashMap; @Component public class DataDictionary { private HashMap<String, String> dictionary; public DataDictionary() { dictionary = new HashMap<>(); // 添加键值对到数据字典 dictionary.put("name", "John"); dictionary.put("age", "25"); dictionary.put("city", "New York"); } public String getValue(String key) { // 检查数据字典中是否包含指定的键 if (dictionary.containsKey(key)) { // 获取指定键的值 return dictionary.get(key); } else { return null; } } } ``` 接下来,你需要创建一个切面类,用来拦截方法并处理返回值的格式化: ```java import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Aspect @Component public class ReturnValueFormattingAspect { @Autowired private DataDictionary dataDictionary; @Pointcut("execution(* com.example.YourService.*(..))") public void serviceMethods() {} @AfterReturning(pointcut = "serviceMethods()", returning = "returnValue") public void formatReturnValue(Object returnValue) { if (returnValue instanceof String) { String formattedValue = dataDictionary.getValue((String) returnValue); if (formattedValue != null) { // 根据需要进行返回值的格式化操作 // 这里只是简单地替换为数据字典中的对应值 formattedValue = "Formatted value: " + formattedValue; // 将格式化后的值设置为方法的返回值 ((String) returnValue).replace((String) returnValue, formattedValue); } } } } ``` 在上述代码中,我们首先通过 `@Autowired` 注解将 `DataDictionary` 类注入切面类中。然后,使用 `@Pointcut` 注解定义了一个切点,用来匹配需要拦截的方法。接着,使用 `@AfterReturning` 注解定义了一个后置通知,当匹配的方法执行完毕并返回值时,会进入该通知进行处理。在通知方法中,我们首先检查返回值是否为 `String` 类型,然后调用 `DataDictionary` 类中的 `getValue` 方法获取对应的格式化值,并进行自定义的格式化操作(在这里只是简单地替换为数据字典中的对应值)。最后,我们将格式化后的值设置为方法的返回值。 请注意,上述代码中的 `com.example.YourService.*(..)` 部分需要替换为你实际项目中的服务类和方法的包名和方法名。 希望这个示例能帮助到你在 Spring Cloud 项目中根据数据字典格式化返回值的需求。请根据你的具体场景进行调整和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值