1. Springboot 入门与原理
1.1 Springboot 简介
1.1.1 什么是Springboot
开发一个web
应用,从最初开始接触Servlet
结合Tomcat
, 跑出一个HelloWolrld
程序,是要经历特别多的步骤; 后来就用了框架Struts
,再后来是SpringMVC
,到了现在的SpringBoo
t
,SpringBoot
就是一个JavaWeb
的开发框架,和SpringMVC
类似,对比其他
JavaWeb
框架的好处,官方说是简化开发,约定大于配置, 能迅速的开发web应用,几行代码开发一个http
接口。
所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景衍生 一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。
随着 Spring
不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot
正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring
、更容易的集成各种常用的中间件、开源软件。
Spring Boot
基于 Spring
开发,Spirng Boot
本身并不提供 Spring
框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring
框架的应用程序。也就是说,它并不是用来替代 Spring
的解决方案,而是和 Spring
框架紧密结合用于提升 Spring
开发者体验的工具。Spring Boot
以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot
应用只需要很少的 Spring
配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),SpringBoot
应用中这些第三方库几乎可以零配置的开箱即用。
简单来说就是SpringBoot
其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot
整合了所有的框架 。
1.1.2 Springboot 主要优点
为所有Spring
开发者更快的入门
开箱即用,提供各种默认配置来简化项目配置
内嵌式容器简化Web项目
没有冗余代码生成和XML配置的要求
1.2 Springboot 相关注解
1.2.1 元注解
Java元注解是包括: @Retention、@Target、@Documented、@Inherited等。如下图:
1.2.1.1 @Target
@Target
用于指定注解的作用范围,它的取值包括:
1.2.1.2 @Retention
@Retention
用于指定注解的生命周期,它的取值包括:
1.2.2 @Configuration
添加了 @Configuration
的类是 SpringBoot
的配置类,这个配置类等价于 Spring
的 beans.xml
配置文件;在配置类中可以使用 @Bean
注解定义 Bean
对象。配置类中调用 Bean
对象定义方法可得到对应 Bean
对象,即使多次调用,返回的是同一个对象。
@Configuration public class MyConfiguration { @Bean public User user(){ return new User(); } }
进入 @Configuration
注解,发现该注解除了 value
属性,还有一个 proxyBeanMethods
属性,且默认值为 true
。该配置表示添加了 @Configuration
的配置类中的所有方法都是代理对象方法,代理对象调用 方法,SpringBoot
总会检查 该方法返回的 User
对象在容器中是否已经存在,如果存在就直接返回容器中的 Bean
对象。代码如下:打印结果一直为true
。
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); MyConfiguration myconfiguration = run.getBean("myConfiguration", MyConfiguration.class); System.out.println(myconfiguration.user()==myconfiguration.user()); } }
1.2.3 @Import
@Import
是可以通过配置来控制是否注入该Bean
,也可以通过条件来控制注入哪些Bean
到Spring
容器中。
1.2.3.1 直接注入
在@Configuration
标注的类上使用@Import
引入了一个类后,就会把该类注入容器中。类似于@Service
,@Component
等注解。代码如下:
@Configuration @Import({User.class}) public class MyConfiguration { }
1.2.3.2 实现 ImportSelector 注入
通过ImportSelector
的使用通过开关来控制注入哪些Bean
,来实现动态的Bean
对象的注入。
设置接口:
public interface FatherInterface { void test(); }
设置接口实现类:
public class A implements FatherInterface{ @Override public void test() { System.out.println("执行了A"); } }
public class B implements FatherInterface { @Override public void test() { System.out.println("执行了B"); } }
定义ImportSelector实现类
public class MySelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(Switch.class.getName()); //获取目标元素上某注解的属性值 String value = (String) annotationAttributes.get("value"); if(value.equals("A")) //将A类Bean对象注入Spring容器 return new String[]{A.class.getName()}; else //将B类Bean对象注入Spring容器 return new String[]{B.class.getName()}; } }
定义Switch注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(MySelector.class) public @interface Switch { String value() default ""; }
测试代码如下
@SpringBootApplication @Switch(value = "A") public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); A bean1 = run.getBean(A.class); System.out.println(bean1); B bean2 = run.getBean(B.class); System.out.println(bean2); } }
测试结果如下:
将value
值改为字符串B
,然后就会在Spring
容器中可以获得B
类Bean
对象,获得不了A
类Bean
对象,然后我们就可以进行动态的添加Bean
对象。
1.2.3.3 实现 ImportBeanDefinitionRegistrar 接口 注入
当配置类实现了ImportBeanDefinitionRegistrar
接口,你就可以自定义往容器中注册想注入的Bean
。
这个接口相比与 ImportSelector
接口的主要区别就是,ImportSelector
接口是返回一个类,你不能对这个类进行任何操作,但是ImportBeanDefinitionRegistrar
是可以自己注入 BeanDefinition
,可以添加属性之类的。
通过实现ImportBeanDefinitionRegistrar
的方式来完成注入。
public class MyBeanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).addPropertyValue("username", "admin") .addPropertyValue("password", "132") .getBeanDefinition(); registry.registerBeanDefinition("myuser",beanDefinition); } }
测试代码
@SpringBootApplication @Import(MyBeanRegistrar.class) public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); User myuser = run.getBean("myuser", User.class); System.out.println(myuser); } }
1.2.4 @Conditional
@Conditional
注解是用来匹配只有满足所有指定条件才能将Bean
注册到Spring
上下文中。例如:当某jar
包在类路径下,自动配置一个或多个bean
,这就是根据特定条件控制Bean
的创建行为,这样就可以利用这个特性进行一些自动配置。
如下图:
除此之外我们还可以自定义条件控制Bean对象的创建。代码如下:
自定义MyConditon类
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //此处编写业务逻辑 return true; } //true为通过,false为不通过不能创建bean }
Configuration类代码
@Configuration //@ConditionalOnMissingBean(name = "hello") //@EnableConfigurationProperties(User.class) public class MyConfiguration { @Bean @Conditional(MyCondition.class) public User user(){ return new User(); } }
1.2.4 @Conditional
@Conditional
注解是用来匹配只有满足所有指定条件才能将Bean
注册到Spring
上下文中。例如:当某jar
包在类路径下,自动配置一个或多个bean
,这就是根据特定条件控制Bean
的创建行为,这样就可以利用这个特性进行一些自动配置。
如下图:
除此之外我们还可以自定义条件控制Bean对象的创建。代码如下:
自定义MyConditon类
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //此处编写业务逻辑 return true; } //true为通过,false为不通过不能创建bean }
Configuration类代码
@Configuration //@ConditionalOnMissingBean(name = "hello") //@EnableConfigurationProperties(User.class) public class MyConfiguration { @Bean @Conditional(MyCondition.class) public User user(){ return new User(); } }
1.2.5 @ImportResource和@ConfigurationProperties
@ImportResource
主要用于原生配置文件引入。SpringBoot
是全注解开发,对于一些使用配置文件项目,SpringBoot
可以通过@ImportResource
将配置文件中的内容引入SpringBoot
项目中,不需要将配置文件中的内容一个一个的按照注解的方式改动。主要用于使用xml
文件向容器装配组件。这部分本作者用的不多,代码故在此省略。
有时候有这样子的情景,我们想把配置文件的信息,读取并自动封装成实体类,这样子,我们在代码里面使用就轻松方便多了,这时候,我们就可以使用@ConfigurationProperties
,它可以把同类的配置信息自动封装成实体类,主要用于Bean对象的属性注入。
在application.yml
文件中配置如下:
user: username: admin password: 123
自定义User类
@Component @ConfigurationProperties(prefix = "user") public class User { private String username; private String password; }
这里必须要加@Component
组件,因为只有Spring
容器中的Bean
对象才能使用Springboot
的强大功能。这里也可以不加@Component
组件,在Configuratio
n中添加如下:
@Configuration @EnableConfigurationProperties(User.class) public class MyConfiguration { }
1.2.6 @PropertySource+@Value
SpringBoot
默认能够读取resources
目录下的application
配置文件,当我们的配置文件不在全局配置文件中,该如何使用呢?
@PropertySource+@Value
可以读取其它配置文件内容。
新建test.yml
,内容如下:
user: username: admin
自定义MyUser类读取配置文件内容
@Component @PropertySource({"test.yml"}) public class MyUser { @Value("${user.username}") public String username; }
1.2.7 @SpringBootApplication
run
函数传入的当前启动类的字节码,最重要的是传入了@SpringBootApplication
,点开它,就能发现它是一个复合注解,有多个注解组成,如下图:
@SpringBootConfiguration
该注解源码,会发现本质是@Configuration
,定义该类是个配置类,功能等同于xml
配置文件。@Configuration
和@Bean
,两个注解合作创建一个简单的Spring
配置类,可以用来代替响应的xml
配置文件可以理解为创建了IOC
容器了。如下图:
@ComponentScan
这个注解对应的是Spring
的XML
配置中的ComponentSca
n
,也就是自动扫描并加载符合条件的组件,将他们加载到IOC
容器中。也可以通过basePackages
等属性来细粒度的定制@ComponentScan
自动扫描的范围,默认扫描引导类所在的包以及子包。
@EnableAutoConfiguration
点开源码发现本质是@Import
如下图:
@EnableAutoConfiguration
也是借助@Import
的帮助,将所有符合自动配置条件的bean
定义加载到IOC
容器。@EnbaleAutoConfiguration
会根据类路径中的jar
依赖为项目进行自动配置,如:添加了spring-boot-starter-
web
,就会自动添加Tomcat
和SpringMVC
的依赖,springboot
会对Tomcat和SpringMVC
进行自动配置。
springboot
是如何完成自动配置的呢?
启动类的run
方法执行,传入SpringBootApplication
注解的字节码
这个注解是个复合注解,其中有一个EnableAutoConfiguration
这个EnableAutoConfiguration
中又有一个Import
注解,这个注解可以用来导入其他类
通过这个Import
,导入的是AutoConfigurationImportSelector
,这个类内部有一个方法selectImport
s
。这个方法会扫描所有jar
包下的spring.factories
文件,解析其中的配置(key=value
的键值对形式),创建对应的类到Spring
容器中。
总结:
在Springboot
程序运行的时候:
1.SpringBoot
先加载所有的自动配置类xxAutoConfiguration
2.每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。从xxProperties
里面拿。xxProperties
和配置文件进行了绑定。
3.生效的配置类就会给容器中装配很多组件。
4.只要容器中有这些组件,相当于这些功能就有了。
5.我们可以定制化配置
1.用户直接自己@Bean
替换底层的组件。
2.用户去看这个组件是获取的配置文件什么值就去修改。在application全局配置文件中进行修改。
这就是Springboot
中的约定大于配置。
至此本篇文章到此结束。