1:SpringBoot的简介:
SpringBoot是由Spring4.0版本出来以后诞生出来一个新的框架,它崇尚的是一个简易的配置和零配置的一种做法,比如刚开始学习Spring的时候需要做很多很多的配置,而用SpringBoot的话这些的配置就不需要我们自己去做,它的内部给我们都做好了,归根揭底它用的就是注解,把Spring5提出的新特征和java提出的注解的方式把它用的非常的棒,做了很多的解析器对它来进行处理,全部都是用注解来做的,所以在开发中都是用一个@+一个注解就可以把所有要管理的对象纳入到Spring容器中去管理。SpringBoot是依赖于Spring4.X版本以上来研发的。
1.1:快速的构建SpringBoot项目:
注意:
在我们创建maven项目或者把项目从一个远程的仓库里面迁到自己的机器上,有时会报错的情况下,归根揭底:1:可能环境不匹配。2:依赖不存在。就是说很多的时候会自己把一些依赖安装到本地仓库里面,但是想要在本地中 运行的话还需要自己手动安装一遍。把本地的jar文件安装到本地的仓库里面去。
如果这里不改1.8的话默认是1.5,用一些的高级特征是用不了的。
<properties><!--源文件的编码格式--><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!--编译源文件的jdk的版本--><maven.compiler.source>1.8</maven.compiler.source><!--编译后目标文件的jdk的版本--><maven.compiler.target>1.8</maven.compiler.target></properties>
1:引入springBoot的依赖:
第一种引入方式:引入SpringBoot项目的starter。
<!--引入boot项目的starter--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.0.5.RELEASE</version></dependency>
第二种引入方式:采用父模块来引入:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.5.RELEASE</version></parent><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
2:构建一个SpringBoot项目的启动类:
@SpringBootApplication:是一个复合的注解:@SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan
@SpringBootConfiguration:是一个@Configuration注解,这个注解是由Spring 框架提供的,把配置的东西配置到Spring容器中去。@ComponentScan:扫描当前包,及其下面子包的标注着Spring能识别的的类上面注解的,类将会纳入到Spring容器中去管理。统统来源于Spring4.X以上的版本来演变的。
@EnableAutoConfiguration:这个注解是自动装配的意思,下次写的时候会写这个特性。
package com.weizhaoyang.boot;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** 快速构建一个SpringBoot的项目* 1:@SpringBootApplication:代表SpringBoot的启动类* 2:提供应用程序的入口去执行* run方法是静态方法,里面的第一个参数是主要的源,第二个参数是一个可变的参数*/@SpringBootApplicationpublic class App {public static void main( String[] args ) {SpringApplication.run(App.class,args);}}
在源代码中:Class primarySource:这个参数是主要加载的类,这个方法返回的结果就是Spring容器对象:ApplicationContext。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);}
拿到PrimarySource这个类的主要作用是什么呢?在run里面还有一个静态方法:run。这个方法相当于创建了一个SpringApplication的对象
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);}
点击SpringApplication再往下面走:这里是调用自己本身的构造函数来对这个资源进行装载:ResourceLoader是一个资源装载器
public SpringApplication(Class<?>... primarySources) {this((ResourceLoader)null, primarySources);}public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.sources = new LinkedHashSet();this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = new HashSet();this.isCustomEnvironment = false;this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));this.webApplicationType = this.deduceWebApplicationType();this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = this.deduceMainApplicationClass();}
而在资源装载器的接口里面:有一个getResource方法:
Resource getResource(String var1);
Resource接口里面又继承了InputStreamSource接口。
public interface Resource extends InputStreamSource
在InputStreamSource的接口里面又提供了:getInputStream方法。
InputStream getInputStream() throws IOException;
上面的就是Spring的源码。
SpringBoot其实还是用ResourceLoader来把当前的传过来的对象转成流,转成资源来进行处理,这个资源必须是标注着@SpringBootApplication的类才可以的。
如果把这个注解去掉的话:无法去驱动一个Servlet的Web的应用程序。启动不了一个web Server
因为这个注解标上以后相当于通过run方法去驱动servlet容器,从而让内置的tomcat去把Servlet的应用程序给发布出去。
注意:1:@SpringBootApplication这个注解不一定标在App类上,可以标在任何的类上它都可以的。
这样写仍然可以驱动应用程序的。
多个类标上这个注解也是可以同时驱动的。例如:这个就是加载多个源
package com.weizhaoyang.boot;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import java.util.Arrays;/*** 快速构建一个SpringBoot的项目* 1:@SpringBootApplication:代表SpringBoot的启动类* 2:提供应用程序的入口去执行* run方法是静态方法,里面的第一个参数是主要的源,第二个参数是一个可变的参数*///@SpringBootApplicationpublic class App {public static void main( String[] args ) {SpringApplication application = new SpringApplication();application.addPrimarySources(Arrays.asList(Application.class,Application1.class));application.run(args);}}
运行的结果如下:这样也是可以驱动的,这就是多个Spring的入口。但是在SpringBoot中是不建议这么做的,一般在多环境的在做测试的时候这么做的。
3:SpringBoot的思想的来源
3.1:引入Spring的依赖:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.14.RELEASE</version></dependency>
3.2:Spring4以前是要写一些配置文件,而在Spring4以后就不用写配置文件了,原有的方式想要把一个类交给Spring容器管理的话:加上一个@Component注解,代码如下:
package com.weizhaoyang.spring;import org.springframework.stereotype.Component;@Componentpublic class User {}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** Hello world!**/public class App {public static void main( String[] args ) {//创建上下文对象AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext("com.weizhaoyang.spring");//获取User类的实例User user = context.getBean(User.class);System.out.println(user);context.close();}}
运行的结果如下:这就得到了User对象的实例,所以这个类就装载到Spring容器里面去了。
但是如何去做到像SpringBoot那样玩呢?步骤如下:
1:创建UserConfig的类:一但类上标注了@Configuration的类将会被Spring容器管理起来。因为@Configuration=+@Target({ElementType.TYPE}) +@Target({ElementType.TYPE})+@Retention(RetentionPolicy.RUNTIME) +@Documented+@Component
package com.weizhaoyang.spring;import org.springframework.context.annotation.Configuration;@Configurationpublic class UserConfig {}
3.3 上面的User类不想要用@Component来装载,想要用这个配置对象来装载:代码如下:在Spring容器中对象都是单例的。
package com.weizhaoyang.spring;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class UserConfig {@Beanpublic User createUser(){return new User();}}
运行结果如下:
但是一旦在User类上标上了@Component注解的话,在运行的话:会报错,这个名字不是唯一的,发现了两个。解释:因为@Component的注解或者@Service或者@Controller标着的注解的类它们默认的别名是当前类首字母小写。
如果按照名称去装配的话:
User user = context.getBean("user",User.class);
在配置类中,如果把方法返回的类类型装配到Spring容器中的话,别名应为方法名。
User user1 = context.getBean("createUser",User.class);
运行的结果如下:user和user1不是同一个对象,因为他们的别名不一样。
Spring4里面的单例怎么做的呢?这时在Bean的后面加上user,代码如下:
package com.weizhaoyang.spring;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class UserConfig {@Bean("user")public User createUser(){return new User();}}package com.weizhaoyang.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** Hello world!**/public class App {public static void main( String[] args ) {//创建上下文对象AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext("com.weizhaoyang.spring");//获取User类的实例User user = context.getBean("user",User.class);User user1 = context.getBean("user",User.class);System.out.println(user+","+user1);context.close();}}
运行的结果如下:这时拿到的是同一个对象。在Spring中的单例是通过Key来进行覆盖的,Key相同就认为是同一个对象。
通过Spring容器也可以拿到UserConfig类的实例,代码如下:
System.out.println(context.getBean(UserConfig.class));
运行的结果如下:由于UserConfig是一个普通的类,并没有实现接口,所以这里生成了EnhancerBySpringCGLIB的代理,去生成一个代理类,通过代理类动态的去调用createUser方法去创建User对象的实例。
综上所述:不管怎么变,SpringBoot中只是把@Configuration做了一次封装而已。
4:在SpringBoot中如何去读取配置文件,SpringBoot中有属性的配置文件和结构化的配置文件两种。首先在属性的配置文件中写上相应的配置信息 例如:
local.ip.addr =192.168.1.110
现在想把上面的配置信息给读取出来:1):通过Enviroment获取 在主函数run的时候先得到得是监听器,创建了环境对象,这里给自动得创建了一个环境对象Enviroment这个接口得实现类会放到Spring容器中去管理。
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();this.configureHeadlessProperty();SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();Collection exceptionReporters;try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);Banner printedBanner = this.printBanner(environment);context = this.createApplicationContext();exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);this.refreshContext(context);this.afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}listeners.started(context);this.callRunners(context, applicationArguments);} catch (Throwable var10) {this.handleRunFailure(context, var10, exceptionReporters, listeners);throw new IllegalStateException(var10);}try {listeners.running(context);return context;} catch (Throwable var9) {this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}}
SpringApplication.run方法得返回值是ConfigurableApplicationContext 这个接口继承了ApplicationContext,所以说还是一个容器。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable
通过这个容器可以获取到Spring中装配得对象,代码如下:
Environment env=context.getEnvironment();
通过这个配置对象可以获取到属性配置文件里得配置信息:因为所有得配置信息都会存储到环境对象里面去。
package com.boot.config.configdemo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.core.env.Environment;@SpringBootApplicationpublic class ConfigDemoApplication {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run(ConfigDemoApplication.class, args);Environment env=context.getEnvironment();String property = env.getProperty("local.ip.addr ");System.out.println(property);context.close();}}
运行结果如下:
还可以通过环境变量获取当前文件的目录:user.dir是系统的环境变量。
System.out.println(env.getProperty("user.dir"));
运行结果如下:
SpringBoot默认装载属性的配置文件默认是有四个顺序的。优先级从高到低 resource/config/xxx.properties>resource/config/xxx.yml>resource/xxx.properties>resource/yyy.yml 比如:在resources文件下建个config文件夹:
这时读取的信息运行结果如下:
比如:在Connfig文件下创建一个xxx.yml的文件:
再次运行这时也是读取的是config/xxx.properties的配置文件的信息:
这时把config/application.properties的文件的名字改为application1.properties,再次运行 :这时读取的是config/xxx.yml配置文件里的内容。
如果这时把config的文件名改为conf的话:这时只会读取resource下的application.properties文件里的内容,如果都没找到的话就为null。
总结:先从config的文件下去查找xxx.properties如果找不到再去confgi下的xxx.yml文件里去查找,如果都查不到再去resource下的根目录去查找,默认的只会去读取一个配置文件里的内容,默认只会读取前缀为application名字的配置文件,默认找的是config里面的文件。
上面的方式在实际的开发中用的不多,只是在框架中用的很多。2):通过@Value获取 新建一个类:RedisConfig:在Spring容器中如何去读取redisConfig类中的属性
这样运行 的话会报错的:因为这个类是交给Spring容器管理的,而这几个属性的值在属性的配置文件里面是读取不出来的,所以会报错:
解决办法:1):给设置默认值:
再次运行的话:这时是没有报错的。
运行的结果如下:
但是有的时候端口不一定是6379,有可能会变得话:2):在属性得配置文件里面去配置redis的一些信息:
运行的结果如下:这时就不会读取默认的值了,而是读取配置文件里的信息了。
在Spring中上面的方式是经常用的,但是在SpringBoot中还有更友好的方式去使用它。现在在conf的文件下的xxx.yml文件里加上redis的配置信息,想要读取conf/application.yml文件里的内容:
1):指定配置文件的路径及名称
然后再次运行:这就是从conf/app.yml文件里面去读取redis的配置信息了。
现在想读取conf/app1.properties和conf/app.yml文件里的内容:
换下环境的配置信息:如果在VM options写-D也是一样的效果。
运行的结果如下:
2):通过@ConfigurationProperties(prefix="xx")上面的方式是通过环境的参数来配置,现在可以通过代码的方式去配置:这个注解的意思是把属性配置文件中的属性的配置装配到这个类里面去。
package com.boot.config.configdemo;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@ConfigurationProperties(prefix = "weizhaoyang.datasource")@Componentpublic class DateSourceProperties {private String driverClassName;private String url;private String username;private String password;public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "DateSourceProperties{" +"driverClassName='" + driverClassName + '\'' +", url='" + url + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +'}';}}
运行的结果如下:
3):正常的用法是在配置类里面或者主类里面配置上面的信息:这种写法方便调试。注释掉这两个注解,然后在主类里面去写:
这样写也是ok的。
运行的结果如下:
或者按照下面的写法创建一个config文件夹也是ok的。config文件夹统统都是配置项的一些信息,这个就是项目开发中正常这么做的。
这个运行的结果也是ok的。
4):如果想要统一集中化的去管理配置信息的路径:这样也是可以的。这样就可以不用在运行的时候参数配置了。但是不支持xxx.yml的配置。这样的配置会把所有的路径进行综合,然后放入到环境对象里面去的,会进行一个合并的。
package com.boot.config.configdemo;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;@Configuration@PropertySource("classpath:conf/app.yml")//不支持@PropertySource("classpath:conf/app1.properties")@PropertySource("file:/E:/temp/app.properties")public class FileConfig {}