Spring 细节汇总(1)-AOP 的实现方式

1. AOP 的实现方式

AOP:面向切面编程,采用横向抽取的机制取代了传统的纵向继承体系

1.1 动态代理

Spring 中的 aop 通常是在运行时内存中临时生成代理类,故而又称作运行时增强。运行时增强其实就是动态代理,其底层实现有两种:

  1. 需增强的目标类有接口,采用 JDK 中的动态代理
    这种实现要求目标类必须有接口,因为 JDK 动态代理生成的代理类已经继承Proxy类,Java 的单继承特性决定了该方式只能通过接口来实现增强
  2. 目标类没有接口,采用 CGLIB 动态代理
    CGLIB 的原理是通过字节码处理框架ASM来转换字节码并生成目标类的子类,调用子类方法从而达到增强目的。该方式的缺陷在于,被代理类及被代理方法如果被 final 修饰则无法完成增强逻辑

在 JDK6、JDK7、JDK8 逐步对JDK动态代理优化后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率。只有当进行大量调用的时候,JDK6 和 JDK7 比CGLIB代理效率低一点,但到 JDK8 时JDK代理效率高于CGLIB代理

1.2 静态代理

在 Java 类加载期通过字节码转换,将增强逻辑织入切入点(目标类)完成增强的方式就是LTW(Load Time Weaving),即静态代理,也被称作编译时增强

这种方式主要依赖于Java探针技术,其核心为 java.lang.instrument 包。这个包在 JDK5.0 时引入,借助该包编写增强逻辑代码打成 jar 包,之后通过 -javaagent 参数来指定Java代理包即可启用,参数格式如下。代理包个数是不限的,指定多个则会按指定的先后执行,执行完各个 agent 后才会执行被代理类方法

-javaagent:<jarpath>[=options]

java.lang.instrument 包在 JVM 启动时会装配并应用 ClassTransformer,对类字节码进行转换,进而实现AOP的功能

  • Java 探针技术已经在开源框架 spring-loaded 中应用,可以使用该框架实现 jar 包热部署

Refer:Java agent 探针技术(1)-JVM 启动时 premain 进行类加载期增强
Java agent 探针技术(2)-JVM 启动后 agentmain 进行类运行时转换


2. @Value 注入 Map 类型数据

SpringBoot 提供的 @Value 注解可以很方便的完成常规属性的注入,但是在注入Map类型的数据需要一些特别的处理。通常 Map 与 List 类型的属性在 application.yml文件中配置如下

  • yml 配置
    需注意配置为 Map 的 value 要使用双引号包裹,否则无法正确解析

    nathan:
     topics: topic1,topic2,topic3
     maps: "{key1: 'value1', key2: 'value2'}"
    
  • Java 引用
    注入 Map 时使用了 #{}包裹目标key,其实是表示使用 EL 表达式

      @Value("#{${nathan.maps:{\"1\":\"2,3\"}}}")
      private Map<String, List<Long>> fundMatchFactor;
    
      @Value("${nathan.topics}")
      private List<String> topics;
    

3. Slf4j 日志框架打印堆栈

SpringBoot 项目集成 Slf4j日志框架打印 log 的时候,调用其 log.error() 方法 传入一个参数只会打印出很简略的错误描述,缺少足够的信息定位问题。查看源码发现其提供了多个方法重载,其中有如下方法声明

void error(String var1);

void error(String var1, Object var2);

void error(String var1, Object var2, Object var3);

void error(String var1, Object... var2);

void error(String var1, Throwable var2);

当使用两个参数的方法 error(String message, Throwable t),且第二个参数为 Throwable 类型时,才会将完整的异常堆栈打印出来,正确使用示例如下

@Slf4j
public class ExceptionTest {
    @Test
    public void test() {
        log.error("ExceptionDetail|",  new InternalException(ErrorFundRouteEnum.RC_ERROR_INNER_ERROR));
    }
}

4. Protobuf 的使用注意

在Java 中使用 Protobuf 时需要注意,当通过 build() 方法生成一个pb对象后,要再修改其中的内容需要调用 toBuilder()使其回到可编辑的状态,最后再调用build() 方法保存修改,否则修改不会写入到 pd 对象中。另外每调用一次build() 方法都会生成一个新的 pb 对象,这点尤其需要注意,因为当使用 pb 对象到方法内部获取数据时,build() 生成的新对象会导致传入的对象引用断开

 RequestBasic requestBasic = RequestBasic.newBuilder().setClientInfo("hhh").build();
 // 未调用 build() 方法保存修改,内容不会改变
 requestBasic.toBuilder().setClientInfo("ggg");
 System.out.println(requestBasic.toString());

5. Spring 非 web 应用模式启动

SpringBoot 框架内置了诸如 Tomcat 之类的 web 服务器,启动时默认为 web 应用模式,也就是会占用一个端口资源。但是有时候我们也会有使用 SpringBoot 框架但是自行实现服务器的需求,这就需要一些特殊的配置让框架以非 web 应用模式启动的。在 SpringBoot 2.0 及以上的版本中,以下两种方式可以实现应用模式的切换。需注意,如果使用非 web 应用模式启动 SpringBoot 后程序主线程没有持续运行,那么会立即运行结束退出

  1. application.yml 配置文件配置

    实际使用中,SpringBoot 是根据加载的相关类来实现应用模式的切换,这个可参考 WebFlux 服务启动流程 中的相关分析。另外 SpringBoot 中可以配置的属性其实都是由 meta 文件来定义的,这些属性可以在以下两个 json 文件中找到:

    • spring-configuration-metadata.json
    • additional-spring-configuration-metadata.json
    spring:
      main:
        web-application-type: none # none, servlet, reactive
    
  2. 应用主类中代码配置

    WebApplicationType 有 3 个枚举值:

    1. NONE:非 web 模式
    2. SERVLET:基于 Java Servlet 的 web 应用模式,也就是 Spring Mvc
    3. REACTIVE:基于响应式的 web 应用模式,也就是 Spring Webflux
    @SpringBootApplication
    public class WebFluxApplication {
    
     public static void main(String[] args) {
         new SpringApplicationBuilder(WebFluxApplication.class)
                 .web(WebApplicationType.NONE)
                 .run(args);
     }
    
    }
    

6. Spring RestTemplate 的请求 URL 经 urlencode 编码后异常的解决方案

使用 RestTemplate 时,如果 url 中存在特殊字符,可能存在编码前传进去不对,编码后传进去也不对的问题,解决步骤如下,具体可参考

  1. 首先对 requestUrl 完成 URL encode
  2. 再使用 URI.create(requestUrl) 保证 RestTemplate 不再对特殊字符转义
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值