Spring编程常见错误(一)

Spring的核心是围绕Bean进行的,无论是Spring Boot 还是 Spring Cloud ,凡是带Spring的技术,都离不开bean的定义,所以就显得尤为重要。

当然这么重要的工作,Spring给我们提供了多种简单的定义bean的方法,全得益于“约定大于配置”,但我们可能不是对所有的约定都了如指掌,仍然会在bean的定义上犯一些经典的错误。

接下来我们就了解一下那些经典错误并了解一下原理,你也可以对照自己是否犯过,想想自己当初是如何解决的。

隐式扫描不到bean的定义

通常我们为了快速定义一个web工程,通常使用spring boot来构建,下图是简单的一个web服务

 其中,定义SringLearningAplication的代码定义如下:

package net.zyy.learning.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringLearningApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringLearningApplication.class, args);
    }

}

定义HelloController的代码定义如下:

package net.zyy.learning.application;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {

    @ResponseBody
    @GetMapping("/hello")
    public String hello(){
        return "Hello,World";
    }
}

上述即实现了一个小功能,访问http://localhost:8080/hello即可返回HelloWorld,两个类在application同一个包中,因为HelloController添加了@Controller注解,则被Spring注册到容器中,但是,如果有一天,我们需要添加更多的Controller,并要求层次分明的包结构来进行管理,我们可能单独创建一个controller包,并调整类的位置,如图下:

实际上我们并没有改变代码,仅仅更改了类的位置,web应用就失效了,原因就在于我们找不到HelloController这个类了,这是为何?

 解析

要想知道HelloController现在为什么会找不到,首先得知道Spring之前是如何找到的,对于Spring Boot而言,关键点在于@SpringBootApplication注解上,而这个注解使用了其他的注解,具体定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

定义我们就可以看出,关键点就在于@ComponentScan注解上,当SpringBoot启动时,Spring就会去扫描出所有的bean,那具体扫描什么位置呢,这是由@ComponentScan注解的basePackages属性来定义的,参考如下:

public @interface ComponentScan {

	/**
	 * Alias for {@link #basePackages}.
	 * <p>Allows for more concise annotation declarations if no other attributes
	 * are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}
	 * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
	 */
	@AliasFor("basePackages")
	String[] value() default {};

而在我们的代码中,我们并没有定义value的值,所以默认为{},此时扫描的是哪个包,不如带着这个问题我们去debug一下,调试位置参考 ComponentScanAnnotationParser.parse 方法,debug如下:

从上图可以看出,当basePackages为空时,则默认选择declaringClass所在的包,所有之所以没有找到HelloController的原因我们就找到了,他已经离开了我们可以扫描到的最大范围。

问题修正

有了源码的剖析,我们自然而然就能找到解决方案,具体修改方式如下:

@SpringBootApplication
@ComponentScan("net.zyy.learning")
public class SpringLearningApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringLearningApplication.class, args);
    }
    
}

我们可以扩大扫描范围,则包里的bean就都会被我们扫描到了。或者我们直接更换启动类的位置,则默认扫描启动类所在的包里的bean:

 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有个金丝熊叫老许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值