springboot小结

@ConfigurationProperties

ConfigurationProperties(prefix="nxj")

ignoreInvalidFields

@Value

@Value("${person.name}")
@Value("#{11*2+1}")
@Value("你好")
@Value("true")
 @ConfigurationProperties@Value
松散绑定 支持 不支持
全部赋值 支持 不支持(只能一个个赋值)
JSR303 支持 不支持
SPLE 不支持 支持

@ImportResource

@ImportResource(value={"classpath:applicationContext.xml"})

导入Spring的配置文件,让其生效

 

还有一些注解比如说@Configuration,@Bean等等等等,这些在Spring注解版中都很清楚了,这里就不需要再记录出来了。

 

配置文件中:

在application.yml或者properties中直接使用${}取值,`name: ${person.name1:hello}`意思就是如果能取到person.name1则取,否则赋值为hello【就是hello字符串】

Profiles

多环境之间需要切换的时候
1.使用application-xxx.properties
2.直接在application.yml中使用 '---' 多文档块分割


使用 spring.profiles.active=dev 进行切换
spring:
profiles:
  active: dev
#最上面文档快指定激活那个哪个文档块
server:
port: 8081
---
spring:
config:
  activate:
    on-profile: dev
server:
port: 8082

🐾自动配置原理

起源:

@SpringBootApplication
@EnableAutoConfiguration
@Import(AutoConfigurationImportSelector.class)
其中AutoConfigurationImportSelector是一个selector,为我们加载了类路径下:META-INF、spring.factories
这就是自动配置的起源,正因为把这些AutoConfiguration自动加入到了容器中,后面的自动配置才会开始。

示例:

以其中的一个HttpEncodingAutoConfiguration为例进行说明

//这是一个springboot的配置类
@Configuration(proxyBeanMethods = false)

//将ServerProperties加入到ioc容器中,并且这个类是绑定springboot的配置文件的
@EnableConfigurationProperties(ServerProperties.class)

//判断当前是不是一个Web应用,如果是则导入,否则不
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)

//判断当前是否存在CharacterEncodingFilter这个类【是mvc中用于字符编码的】,如果是则导入,否则不
@ConditionalOnClass(CharacterEncodingFilter.class)

//判断配置文件中是否存在server.servlet.encoding.enabled=true,当然我们指定如果配置文件中没有指定server.servlet.encoding.enabled,我们默认为true
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;

//我们只有一个有参构造器,因此,properties会自动从ioc容器中取值(也就是隐式调用@Autowired,这个在Spring注解版中了解的已经很清楚了)
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}

//其中方法体中的properties就是上方的属性
@Bean
//如果容器中没有这个组件则加载
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
//绑定配置文件
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

所以我们能在配置文件中配置的,都是来自于**Properties这些。

精髓:

  1. springboot在启动的时候会加载大量的自动配置类(这些配置类加载来源于类路径下/META-INF/spring.factories的org.springframework.boot.autoconfigure.EnableAutoConfiguration下配置)

  2. 我们看我们需要的功能springboot有没有默认写好的自动配置类

  3. 如果有的话,我们看下自动配置类中配置了那些组件,有没有我们需要的,如果有且满足我们的要求,我们就不需要再来自动配置了

  4. 自动配置类在添加组件的时候,会从properties中获取属性,我们可以在配置文件中指定这些属性

xxxAutoConfiguration:自动配置类

xxxProperties:封装配置文件中相关属性

日志

稍后回头再看

整合Mybatis

Mybatis注解版
  1. springboot配置数据源(spring.datasource等)

  2. 创建mapper,其中mapper上方标注执行sql方法,并将其加入到容器中@Component

  3. 然后在ioc容器中获取【@Autowired】即可使用

//这是一个操作数据库的mapper
@Mapper //或者在启动类上标注@MapperScan("")
@Component
public interface UserMapper {
 @Select("select * from z1")
 public List<User> queryAllUser();

 @Select("select * from z1 where id=#{id}")
 public User queryUserById(Integer id);
	
 //@Options用来设置id是我的自增主键,我插入的时候,直接返回给我的带自增主键的,否则不写的话,就返回给我null
 @Options(useGeneratedKeys = true,keyProperty = "id")
 @Insert("insert into z1 (name,age) values (#{name},#{age})")
 public int insertUser(User user);
}
//这是controller类里面的内容
@Autowired
UserMapper userMapper;

@ResponseBody
@GetMapping("/user")
public List<User> queryAllUser(){
 return userMapper.queryAllUser();
}

@ResponseBody
@GetMapping("/use/{id}")
public User queryAllUser(@PathVariable("id") Integer id){
 return userMapper.queryUserById(id);
}

@ResponseBody
@GetMapping("/userinsert")
public User insertUser(User user){
 userMapper.insertUser(user);
 return user;
}

 

Mybatis配置文件版
  1. springboot配置数据源(spring.datasource等)

  2. 创建mapper

  3. 可以创建mybatis的配置文件

  4. 创建mapper对应实现的xml

  5. 在springboot的配置文件中绑定mabatis的配置文件与mapper.xml文件

  6. 从ioc中获取即可调用

1.springboot配置数据源(spring.datasource等)

spring:
datasource:
 url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
 username: root
 password: 123456
 driver-class-name: com.mysql.cj.jdbc.Driver

2.创建mapper

@Component
@Mapper
public interface UserXmlMapper {
 public List<User> queryAllUser();

 public User queryUserById(Integer id);

 public int insertUser(User user);
}

3.可以创建mybatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!--这是mybatis的主配置文件-->
</configuration>

4.创建mapper对应实现的xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springmybtis.mapper.UserXmlMapper">
 <!--这里就是让这个xml配置文件绑定UserXmlMapper,作为它的实现,我只要把它与Mybatis的配置文件加入到ioc中即可使用了-->

 <select id="queryAllUser" resultType="com.example.springmybtis.bean.User">
     select * from z1
 </select>

 <select id="queryUserById" resultType="com.example.springmybtis.bean.User">
     select * from z1 where id=#{id}
 </select>

 <insert id="insertUser">
     insert into z1 (name,age) values (#{name},#{age});
 </insert>
</mapper>

5.在springboot的配置文件中绑定mabatis的配置文件与mapper.xml文件

mybatis:
config-location: classpath:mybatis/mybatis-config.xml  #指定全局配置文件的位置
mapper-locations: classpath:mybatis/mapper/*.xml	#指定sql映射文件的位置

6.从ioc中获取即可调用

@Controller
public class UserXmlController {
 @Autowired
 UserXmlMapper userXmlMapper;

 @GetMapping("/all")
 @ResponseBody
 public List<User> getAllUser(){
     return userXmlMapper.queryAllUser();
 }
}

 

springboot启动配置原理

只要看到getSpringFactoriesInstances这个方法,它就是从类路径下META-INF/spring.factories下获取相关信息

Bootstrapper
ApplicationContextInitializer
ApplicationListener

1.创建SpringApplication对象

//分别获取ApplicationContextInitializer与ApplicationListener在META-INF/spring.factroies中配置的类的全路径名集合

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){
	
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");

  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  //推断是是一个WebApplicationType什么类型的(这是个枚举)
  this.webApplicationType = WebApplicationType.deduceFromClasspath();

  this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
  //获取类路径下META-INF/spring.factories下ApplicationContextInitializer配置的Initializer
     setInitializers((Collection) 				getSpringFactoriesInstances(ApplicationContextInitializer.class));
 //获取类路径下META-INF/spring.factories下ApplicationListener配置的相关的Listener
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
}

2.执行run方法

public ConfigurableApplicationContext run(String... args) {
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 DefaultBootstrapContext bootstrapContext = createBootstrapContext();
 ConfigurableApplicationContext context = null;
 
 //设置java.awt相关的,一般我们也不用
 configureHeadlessProperty();
 
 //从类路径下的META-INF/spring.factories中获取所有的SpringApplicationRunListener
 SpringApplicationRunListeners listeners = getRunListeners(args);
 
 //回调所有SpringApplicationRunListener的starting方法
 listeners.starting(bootstrapContext, this.mainApplicationClass);
 try {
     
     //封装命令行参数
     ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     
     //准备环境
     ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
     	//获取或者创建环境,然后配置管径
     	//回调SpringApplicationRunListener的environmentPrepared方法
     
     //设置环境中需要忽略的组件信息
     configureIgnoreBeanInfo(environment);
     
     Banner printedBanner = printBanner(environment);
     
     //创建ioc容器
     context = createApplicationContext();
     
     //设置context的applicationStartup属性
     context.setApplicationStartup(this.applicationStartup);
     
     //准备上下文
     prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
     	//设置环境
     	//注册一些内部组件
     	//获取所有的ApplicationContextInitializer【上方】,并调用其initialize方法
     	//回调SpringApplicationRunListener的contextPrepared方法
     	//全部执行完最后回调SpringApplicationRunListener的contextLoaded方法
     
     //刷新容器,这里就是和spring原理一样,BeanPostProcessors或者组件注册等都在这里
     refreshContext(context);
     
     //内部无任何实现,留给子类扩充
     afterRefresh(context, applicationArguments);
     
     stopWatch.stop();
     
     if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
     }
     
     //回调SpringApplicationRunListener的started方法
     listeners.started(context);
     
     //从ioc容器中获取ApplicationRunner与CommandLineRunner组件
     //然后将获取出来这两类组件排序后
     //先调用ApplicationRunner的run方法
     //再调用CommandLineRunner的run方法
     callRunners(context, applicationArguments);
 }
 catch (Throwable ex) {
     handleRunFailure(context, ex, listeners);
     throw new IllegalStateException(ex);
 }

 try {
     //回调SpringApplicationRunListener的running方法
     listeners.running(context);
 }
 catch (Throwable ex) {
     handleRunFailure(context, ex, null);
     throw new IllegalStateException(ex);
 }
 //将创建好的ioc容器返回
 return context;
}

上方小结

可以看出以下需要在类路径下:META-INF/spring.factories中配置

Bootstrapper

*ApplicationContextInitializer

ApplicationListener

*SpringApplicationRunListener

*ApplicationRunner

*CommandLineRunner

是从ioc容器中取得【也就是对象创建完了,我们去拿】

调用过程

SpringApplicationRunListener.starting()
↓
SpringApplicationRunListener.environmentPrepared()
↓
ApplicationContextInitializer.initialize()
↓
SpringApplicationRunListener.contextPrepared()
↓
SpringApplicationRunListener.contextLoaded()
↓
SpringApplicationRunListener.started()
↓
ApplicationRunner.run()
↓
CommandLineRunner.run()
↓
SpringApplicationRunListener.running()
应用测试

1.分别创建ApplicationContextInitializerSpringApplicationRunListenerApplicationRunnerCommandLineRunner的实现类

public class MyApplicationContextInitializer implements ApplicationContextInitializer {

 @Override
 public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
     System.out.println("MyApplicationContextInitializer...initialize"+configurableApplicationContext.getApplicationName());
 }
}
public class MySpringApplicationRunListener implements SpringApplicationRunListener {

 public MySpringApplicationRunListener(SpringApplication application, String[] args) {
     System.out.println("chushihua");
 }

 @Override
 public void starting(ConfigurableBootstrapContext bootstrapContext) {
     System.out.println("starting");
 }

 @Override
 public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
     System.out.println("environmentPrepared");
 }

 @Override
 public void contextPrepared(ConfigurableApplicationContext context) {
     System.out.println("contextPrepared");
 }

 @Override
 public void contextLoaded(ConfigurableApplicationContext context) {
     System.out.println("contextLoaded");
 }

 @Override
 public void started(ConfigurableApplicationContext context) {
     System.out.println("started");
 }

 @Override
 public void running(ConfigurableApplicationContext context) {
     System.out.println("running");
 }

 @Override
 public void failed(ConfigurableApplicationContext context, Throwable exception) {
     System.out.println("failed");
 }
}
@Component
public class MyApplicationRunner implements ApplicationRunner {
 @Override
 public void run(ApplicationArguments args) throws Exception {
     System.out.println("MyApplicationRunner...run"+ Arrays.asList(args));
 }
}
@Component
public class MyCommandLineRunner implements CommandLineRunner {
 @Override
 public void run(String... args) throws Exception {
     System.out.println("MyCommandLineRunner...run"+ Arrays.asList(args));
 }
}

2.在类路径下META-INF下创建spring.factories,内容如下:

org.springframework.context.ApplicationContextInitializer=\
com.example.springmybtis.test.MyApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.example.springmybtis.test.MySpringApplicationRunListener

启动主程序,输出顺序如下:

chushihua
starting
environmentPrepared

.   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
'  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::                (v2.4.0)

MyApplicationContextInitializer...initialize
contextPrepared

contextLoaded

started
MyApplicationRunner...run[org.springframework.boot.DefaultApplicationArguments@f446158]
MyCommandLineRunner...run[]
running

自定义starter

【参考WebMvcAutoconfiguration】
@Configuration //指定这是一个配置类
@ConditionalOnXXX //指定条件生效时自动配置类才生效
@AutoConfigureAfter //指定自动配置类的顺序(这里是在这些写自动配置加载完后,这个类再加载)
@Bean //给容器中添加组件

@ConditionalOnProperty //结合xxProperties来绑定属性赋值
@EnableConfigurationProperties //让xxProperties生效并加入到ioc容器中

自动配置类要能生效,源于我们在META-INF/spring.factories下的自动配置,如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
命名时,我们最好xxx-spring-boot-starter

【启动器只用于一来到日,专门写一个自动配置模块】(这是spring在书写时stater时的规范)
1.我们得代码写在xxx-spring-boot-starter-configure下
2.xxx-spring-boot-starter依赖xxx-spring-boot-starter-configure
3.我们使用xxx-spring-boot-starter

1.创建pom工程起名为nxj-spring-boot-starter

注意这里千万不要因为在其他文件夹下,从而出现parent节点,很容易出问题,导致其他pom引入这个stater出错(unknown)。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <artifactId>nxj-spring-boot-starter</artifactId>
 <groupId>org.jd.nxj</groupId>
 <version>1.0.0</version>
 <dependencies>
     <!--引入我的自动配置模块-->
     <dependency>
         <groupId>com.jd.nxj</groupId>
         <artifactId>nxj-spring-boot-starter-configure</artifactId>
         <version>0.0.1-SNAPSHOT</version>
     </dependency>
 </dependencies>

</project>

2.编写nxj-spring-boot-starter-configure

和配置文件属性赋值绑定的XXXProperties

@ConfigurationProperties(prefix = "nxj")
public class NxjProperties {
 private String prefix;
 private String suffix;

 public String getPrefix() {
     return prefix;
 }

 public void setPrefix(String prefix) {
     this.prefix = prefix;
 }

 public String getSuffix() {
     return suffix;
 }

 public void setSuffix(String suffix) {
     this.suffix = suffix;
 }
}

3.写一个NxjService

public class NxjService {

 private NxjProperties nxjProperties;

 public NxjProperties getNxjProperties() {
     return nxjProperties;
 }

 public void setNxjProperties(NxjProperties nxjProperties) {
     this.nxjProperties = nxjProperties;
 }

 public String sayHello(String name){
     return nxjProperties.getPrefix()+"-"+name+"-"+nxjProperties.getSuffix();
 }
}

4.自动配置类

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(NxjProperties.class)
public class NxjServiceAutoConfig {
 @Autowired
 NxjProperties nxjProperties;
 @Bean
 public NxjService nxjService(NxjProperties nxjProperties){
     NxjService nxjService = new NxjService();
     nxjService.setNxjProperties(nxjProperties);
     return nxjService;
 }
}

5.要让自动配置类能生效,必须在类路径下:META-INF/spring.factories中配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jd.nxj.autoconfig.NxjServiceAutoConfig

引入:

1.pom文件引入

<dependency>
 <artifactId>nxj-spring-boot-starter</artifactId>
 <groupId>org.jd.nxj</groupId>
 <version>1.0.0</version>
</dependency>

2.配置文件中写值,我们可以赋予上去

nxj.prefix=前缀
nxj.suffix=后缀

3.编写controller测试

@Controller
public class HelloController {
 @Autowired
 NxjService nxjService;

 @GetMapping("/nxj")
 @ResponseBody
 public String hello(){
     return nxjService.sayHello("nxj");
 }
}

结果:前缀-nxj-后缀

 

posted @ 2020-12-06 21:40  程序杰杰  阅读(146)  评论(0编辑  收藏  举报