关于aop:because-it-is-a-JDK-dynamic-proxy-that-implements-问题

6次阅读

共计 2854 个字符,预计需要花费 8 分钟才能阅读完成。

景象形容

  • 以下的 SpringBoot 工程在启动的时候会启动失败。(SpringBoot 1.5.7-RELEASE)

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);
    }

}

package com.example.demo.controller;

import com.example.demo.service.DemoServiceImpl;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class DemoController {

    @Resource
    private DemoServiceImpl demoService;

    @RequestMapping("/test")
    public String test() {return demoService.demo();
    }

}
package com.example.demo.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("demoService")
public class DemoServiceImpl implements DemoService{

    @Override
    @Transactional
    public String demo() {return "DemoServiceImpl";}
}

package com.example.demo.service;

public interface DemoService {String demo();

}

报错信息

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'demoService' could not be injected as a 'com.example.demo.service.DemoServiceImpl' because it is a JDK dynamic proxy that implements:
    com.example.demo.service.DemoService


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.

如果只是解决

  • 如果不去深究起因,只是须要解决这个异样。那么有以下几种形式。只需实现其中任意一个即可。

1. 批改 EnableTransactionManagement 注解参数

DemoApplication 上的注解 @EnableTransactionManagement 变成 @EnableTransactionManagement(proxyTargetClass = false)

2. 在 application.properties 配置文件中减少配置

spring.aop.proxy-target-class=true

3. 注入 bean 的时候变更为应用其接口(最举荐)

@Resource
private DemoServiceImpl demoService;

改成:

@Resource
private DemoService demoService;

4. 降级 SpringBoot 版本至 2.0.0 以上

  • 这个其实不是很举荐,因为改的可能很多很多。然而因为后续会提到 SpringBoot 的一些默认设置,所以这里还是写进去了。

起因剖析

一些前置常识

  • AOP 的动静代理有 JDK dynamic proxy 和 CGLIB 两种。
  • JDK dynamic proxy 要求代理的类至多实现一个接口。CGLIB 不必。

问题的剖析

  • 当咱们应用 @Transactional 润饰了 DemoServiceImpl 类中的办法时,Spring 会创立一个代理。而且在咱们的 SpringBoot 版本(1.5.7)中,默认的是 jdk proxy,所以 Bean 的真正的 type 是 DemoServiceImpl 的接口类型 DemoService,所以当咱们应用

@Resource
private DemoServiceImpl demoService;

来注入的时候就无奈找到 DemoServiceImpl 类型的 bean。

解决方案的原理

1. 批改 EnableTransactionManagement 注解参数

  • 这种形式将动静代理的办法强制指定为 CGLIB,所以防止了只有接口类型 bean 的问题。

2. 在 application.properties 配置文件中减少配置

  • 同上。

3. 注入 bean 的时候变更为应用其接口

  • 这种形式 @Resource 的时候用的也是接口的类型,所以能够失常的注入 bean,也比拟举荐这种形式来解决问题。如果 interface 存在多个实现类,那么能够思考应用注解中的 name 参数来进行管制。

4. 降级 SpringBoot 版本至 2.0.0 以上

  • 这个其实大部分人应该都不会这么做,这里列出来次要是为了阐明,在 SpringBoot 2.0.0 之后,动静代理由默认的 jdk proxy 变成了 CGLIB,所以降级 SpringBoot 版本能够解决这个问题。

参考的材料

  • Stack Overflow 上的答复:https://stackoverflow.com/que…
  • 另外一篇文章比拟详尽的测试:https://blog.csdn.net/weixin_…
  • JDK 代理和 CGLIB 的区别:https://www.cnblogs.com/doubl…
正文完
 0