前言
使用Idea搭建微服务开发环境时,我们多使用嵌套module来组织项目.如果配置不合适,会出现各种莫名其妙的问题.在此记录一下,避免以后重复趟坑.
问题1
- SpringBoot started failed:Failed to introspect Class [org.springframework.boot.autoconfigure.xxx]
- 分析: SpringCloud依赖SpringBoot,不同SpringCloud版本依赖特定的SpringBoot版本.因为我们使用嵌套的module来组织,所以很容易导致父子版本之间的版本冲突. 举例来说如果父pom文件引入了2.2.5 SpringBoot
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>
我们知道parent项目包含了多个dependency,如果某个子module中单独引用了其中一个dependency,而且版本跟parent版本不一致,就会导致上面的问题.
- 解决: 通过上面的异常信息我们可以看出是org.springframework.boot.autoconfigure相关问题.此时你可以删除这个依赖,默认使用父pom中的autoconfigure依赖
问题2
- Failed to execute goal org.springframework.boot:spring-boot-maven-plugin
- 分析: 一旦引入 spring-boot-maven-plugin,当打包时会扫描项目下的main方法,也就是说引入该配置,你就必须在项目src/main/java/下创建一个spring-boot启动类.
- 解决: 如果父module下面没有代码,可以在pom中去掉plugin配置
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
问题3
- Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.2.5.RELEASE
- 分析: 注意dependencyManager中的dependencies只是声明,不会真的下载到本地. 如果子模块想引用某个依赖,需要在自己的dependencies里再次添加, 而且不用添加version信息,此时版本继承父模块版本. 当子模块明确需要跟父模块版本不一致是才需要添加version信息
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
默认情况下spring-boot-starter-xxx版本继承于spring-boot-starter-parent的版本,如果你不想继承parent版本,可以将scope设置为import. 这样你就可以使用自己的版本,子模块也将继承你自定义的版本.
注意: < scope> import < /scope> 只能在dependencyManagement 中使用
- 解决: 在子模快的pom中显示添加需要的依赖,如果没有必要自定义版本可以不写版本,这样就继承了父pom中的版本.
问题4
- 在UT中
autowired标记的对象返回空 - 分析
autowired需要依赖Spring context才能运行,如果只是普通的测试类并不能加载Spring context - 解决: 为测试类添加如下注解
- @RunWith(SpringRunner.class) : 让Spring容器来启动该类
- @SpringBootTest: 将该测试类注册成bean
- 示例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestPaymentDao {
@Autowired
private PaymentDao paymentDao;
@Test
public void testAdd(){
int result=paymentDao.add(new Payment(1L,"123"));
Assert.assertTrue(result>0);
}
}
问题5
- org.yaml.snakeyaml.scanner.ScannerException: while scanning a simple key
- 分析: 如果使用yml来作为配置文件,那么冒号后面必须有一个空格
- 解决: 冒号后添加一个空格
- 示例:
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/blog?characterEncoding=utf-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
在使用yml时一定要注意缩进,同一级的配置缩进保持一致,否则也会导致异常
问题6
- SqlSession XXX was not registered for synchronization because synchronization is not active
- 分析: 如果可以排除配置原因的话,可以尝试加上
@Transactional注解 - 解决: 为方法添加事务注解
- 示例:
@Update("update payment set sequence_num=#{SequenceNum} where id=#{Id}")
@Transactional
int update(Payment payment);
问题7
- no converter found for return value of type
- 分析: 使用RestTemplate将json转换为指定对象时,需要为指定对象的属性添加getter.因为我是用的是lombok,但是忘记了添加
@getter,所以报以上错误 - 解决: 添加
@Getter或@Data - 示例:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResult<T> {
private int status;
private String message;
private T data;
public ApiResult(int status,String message){
this(status,message,null);
}
}
问题8
- RestApi之间相互调用的时候一般使用RestTemplate来传参,此时可能会出现被调用的api接收到的参数为空的情况
- 举例: 订单服务的add方法会调用支付服务的add方法,并且需要向支付服务的add传递payment对象,此时支付服务接收到的payment可能为空
解决: 为支付服务add方法参数添加
@RequestBody方法 示例:
@PostMapping(value = "add")
public ApiResult add(@RequestBody Payment payment){
if(payment==null || payment.getId()==0)
return new ApiResult(405,"invalid parameters");
if(paymentService.add(payment)>0)
return new ApiResult(200,"succeeded");
return new ApiResult<>(500,"failed");
}
问题9
- Connect to localhost:8761 time out
- 分析: 使用eureka搭建微服务时,需要通过
defaultZone来指定注册中心的地址, 如果没有配置会发生上面的异常. 查看了我的配置,我的application.yml里包含了defaultZone,但是顶层的eureka写成了eurake,这样也间接导致找不到eureka.client.service-url.defaultZone - 解决: 将
eurake改成eureka.What a stupid mistake