SpringBoot系列:SpringBootApplication详解
软件环境
- Spring Boot 2.0.4.RELEASE
- JDK 1.8.0_181
- Maven 3.5.4
- IntelliJ IDEA 2018.1.6
疑问
在快速入门中,我们只需要启动main方法,整个spring容器就可以正常运行,这是如何做到的呢?
查看SpringBootApplication这个注解源码,发现是一个复合注解,包括@ComponentScan,和@SpringBootConfiguration,@EnableAutoConfiguration
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class,
attribute = "exclude"
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class,
attribute = "excludeName"
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
@SpringBootConfiguration
说明
@SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。
package com.itxiaoer.demo.config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import java.util.HashMap;
import java.util.Map;
/**
* @author : liuyk
*/
@SpringBootConfiguration
public class BootConfiguration {
/**
* 代表在spring容器中创建了一个名字为userMap的对象,值为该方法的返回值。
*
* @return map对象
*/
@Bean
public Map<String, String> userMap() {
Map<String, String> map = new HashMap<>(2);
map.put("id", "id");
map.put("name", "name");
return map;
}
}
package com.itxiaoer.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Map;
/**
* @author : liuyk
*/
@SpringBootApplication
public class DemoSpringBootSpringBootConfigurationApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoSpringBootSpringBootConfigurationApplication.class, args);
Map<String, Object> userMap = (Map<String, Object>) applicationContext.getBean("userMap");
userMap.forEach((k, v) -> System.out.println(k + "=" + v));
}
}
@ComponentScan
- 有以下目录结构程序
---- package1
-------- Package1.java
-------- Package1Application.java
---- package2
-------- Package2.java
-------- Package2Application.java
---- package3
-------- Package3.java
-------- Package3Application.java
---- Application.java
- Application.java
package com.itxiaoer;
import com.itxiaoer.package1.Package1;
import com.itxiaoer.package2.Package2;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
/**
* @author : liuyk
*/
@ComponentScan
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
Package2 package2 = context.getBean(Package2.class);
System.out.println("package2 = " + package2);
Package1 package1 = context.getBean(Package1.class);
System.out.println("package1 = " + package1);
// 执行结果,都有值说明都扫描到了
// package2 = com.itxiaoer.package2.Package2@750fe12e
// package1 = com.itxiaoer.package1.Package1@f8908f6
}
}
- Package1Application.java
package com.itxiaoer.package1;
import com.itxiaoer.package2.Package2;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
/**
* @author : liuyk
*/
@ComponentScan
public class Package1Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Package1Application.class, args);
Package1 package1 = context.getBean(Package1.class);
System.out.println("package1 = " + package1);
Package2 package2 = context.getBean(Package2.class);
System.out.println("package2 = " + package2);
// 执行结果 package1对象扫描到了,package2没有扫描到
// package1 = com.itxiaoer.package1.Package1@34cdeda2
// No qualifying bean of type 'com.itxiaoer.package2.Package2' available
}
}
- Package2Application.java
package com.itxiaoer.package2;
import com.itxiaoer.package1.Package1;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
/**
* @author : liuyk
*/
@ComponentScan
public class Package2Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Package2Application.class, args);
Package2 package2 = context.getBean(Package2.class);
System.out.println("package2 = " + package2);
Package1 package1 = context.getBean(Package1.class);
System.out.println("package1 = " + package1);
// 执行结果 package1对象扫描到了,package2没有扫描到
// package2 = com.itxiaoer.package2.Package2@305a0c5f
// No qualifying bean of type 'com.itxiaoer.package1.Package1' available
}
}
- Package3Application.java
package com.itxiaoer.package3;
import com.itxiaoer.package1.Package1;
import com.itxiaoer.package2.Package2;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
/**
* @author : liuyk
*/
@ComponentScan("com.itxiaoer.package1,com.itxiaoer.package2")
public class Package3Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Package3Application.class, args);
Package1 package1 = context.getBean(Package1.class);
System.out.println("package1 = " + package1);
Package2 package2 = context.getBean(Package2.class);
System.out.println("package2 = " + package2);
Package3 package3 = context.getBean(Package3.class);
System.out.println("package3 = " + package3);
// 执行结果 指定扫描的包都扫描到了,没有指定的包无法扫描
// package1 = com.itxiaoer.package1.Package1@1ecee32c
// package2 = com.itxiaoer.package2.Package2@4535b6d5
// No qualifying bean of type 'com.itxiaoer.package3.Package3' available
}
}
总结
- @ComponentScan默认只当前包以及子包下被@Component,@Controller,@RestController,@Service,@Repository等标记的类
- @ComponentScan若想当前包以及子包以外的包,需要显示的指定
- @ComponentScan和以前的<context:component-scan/>(以前使用在xml中使用的标签,用来扫描包配置的平行支持)
- @ComponentScan若指定了包名,那么就只会扫描指定的包以及指定包的子包