springboot的自动装配源于SpringFramework的手动装备,SpringFramework手动装配来看起。
手动装配是按照SpringFramework的版本进行管理的,前提差不多都是基于基于注解驱动。
【什么是Spring模式注解】
一种用于声明在应用中扮演“组件”角色的注解
这里的应用:在Spring或者SpringBoot应用
组件:例如:@Component spring2.5,@Service:服务类注解 spring2.5,@Configuration 配置注解spring3
github上定义:A stereotype annotation is an annotation that is used to declare the role that a component plays within the application.
github关于模式注解(Stereotype Annotations)的解释中最为重要的一点就是:
凡是被 @Component 元标注(meta-annotated)的注解,如 @Service ,@Repository等,当任何组件标注它时,也被视作组件扫描的候选对象,可以理解为组件 是包括@Component及他的所有“派生”。
常用的模式注解
@Repository 数据仓储模式注解 起始版本2.0
@Component 通用组件模式注解 起始版本 2.5
@Service 服务模式注解 起始版本2.5
@Controller 控制器模式注解 起始版本2.5
@Configuration 配置类模式注解 起始版本 3.0
【装配的方式有2种】
1)xml方式:<context:component-scan> spring2.5提出的方案
base-package指明的这个包下所有的组件都会被扫描到
<meta charset="utf-8">
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
[http://www.springframework.org/schema/beans/spring-beans.xsd](http://www.springframework.org/schema/beans/spring-beans.xsd)
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-
context.xsd">
<!-- 激活注解驱动特性 -->
<context:annotation-config />
<!-- 找寻被 @Component 或者其派生 Annotation 标记的类(Class),将它们注册为 Spring Bean -->
<context:component-scan base-package="com.imooc.dive.in.spring.boot" />
</beans>
2)注解方式:@ComponentScan ,是spring3.1的方案
springboot1.0是基于spring4进行开发。
@ComponentScan(basePackages = "com.imooc.dive.in.spring.boot")
public class SpringConfiguration {
...
}
【Spring Framework手动装配自定义模式注解】
自定义模式注解,有两个性质@Component “派生性”和@Component “层次性”,我们从一个小栗子来看看这2个性质的体现
【@Component “派生性”】
注意这里的派生不是真正的派生就是一种理解
【栗子】
新建一个工程,工程下新建一个包annotation ,这个包下新建一个注解类FirstLevelRepository,作为一级仓储
/**
* 一级 {@link Repository @Repository}
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repository // 数据仓储模式注解
public @interface FirstLevelRepository {
String value() default "";
}
备注:
@Repository // 数据仓储模式注解
通过源码可以看到@Repository元标注的是@Component,
并且,他们的签名都保持这一致,属性方法value()
这就是所谓的“派生性”
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
@AliasFor(
annotation = Component.class
)
String value() default ""; //签名 一致性 :属性方法value
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
接下来,我们要将这个注解作用到一个类上面
新建一个包repository,下面新建一个类MyFirstLevelRepository,类上面添加我们刚才自定义的注解,通过value来添加一个bean的名称,注意名字的第一个字母要小写,在进行组件扫描后MyFirstLevelRepository这个类就成为一个bean。
/**
* 我的{@link FirstLevelRepository}
*/
@FirstLevelRepository(value = "myFirstLevelRepository") //Bean的名字
public class MyFirstLevelRepository {
}
我们新建一个bootstrap包,下面建一个引导类,就是启动类RepositoryBootstrap,这里我们不使用注解@SpringBootApplication来标注引导类,我们采用SpringApplicationBuilder()spring2.0写法。
可以指定类型web,我们定义为非web类型 ,一个普通类型,注解驱动
/**
* 仓储引导类
*/
@ComponentScan(basePackages = "com.cbt.diveinspringboot.repository")
public class RepositoryBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(RepositoryBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
// myFirstLevelRepository Bean是否存在
MyFirstLevelRepository myFirstLevelRepository = context.getBean("myFirstLevelRepository",MyFirstLevelRepository.class);
System.out.println("myFirstLevelRepository Bean:" + myFirstLevelRepository);
context.close();
}
}
说明:
1、run()返回的对象 ConfigurableApplicationContext
2、关闭上下文context
3、扫描MyFirstLevelRepository 这个类的方式:
1)可以包MyFirstLevelRepository这个bean丢进去
2)也可以把RepositoryBootstrap 这个bean丢进去
4、这里先采用注解@ComponentScan 的装配方式,这个@FirstLevelRepository组件可以用来识别我们的MyFirstLevelRepository类。
通过@ComponentScan 扫描包下所有的类,
类被@FirstLevelRepository标记的就会被认为是一个bean,我们可以通过Bean的名称进行获取。
通过上下文对象去获取到bean,第一个参数是bean的名称,就是通过类上的注解value获取的,第二个参数bean的类型,这种方式不用进行类型强转的
运行引导类:结果显示进行组件扫描后MyFirstLevelRepository这个类就成为一个bean了。
如果你采用@Component注解来替换我们自定义的注解@ FirstLevelRepository结果是一样的 ,你可以自己运行看看 ----这就是所谓的“派生性”
可以一直注解上去
@Component(value = "myFirstLevelRepository")
public class MyFirstLevelRepository {
}
【@Component “层次性”】就是自定义的注解还可以用来标注在其他自定义的注解上面
在annotation包下新建注解SecondLevelRepository
签名要保持一致
/**
* 二级{@link Repository }
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@FirstLevelRepository //标注自定义注解
public @interface SecondLevelRepository {
String value() default "";
}
将这个@SecondLevelRepository注解添加到MyFirstLevelRepository类
/**
* 我的{@link SecondLevelRepository}
*/
@SecondLevelRepository(value = "myFirstLevelRepository")
public class MyFirstLevelRepository {
}
层次关系:
- @Component
2.@Repository
3.@FirstLevelRepository
4.@SecondLevelRepository
运行引导类:结果和上面是一样的。
我们可以再看一个栗子:
注解:@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 {
看一下里面的:@SpringBootConfiguration这个注解,这个注解标注@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@Configuration注解又元标注了@Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
说明@SpringBootApplication也是属于模式注解