o2o网站开发,集团企业网站建设方案,网站开发定位,广东佛山企业引言#xff1a;
当然#xff0c;在我们学习的过程中#xff0c;得知其然#xff0c;还得知其所以然。So理解了其原理#xff0c;更能让我们对其开发的理解#xff0c;遇到问题#xff0c;也更能快速找到解决办法#xff01;#xff01;#xff01;
1. SprngBoot-配…引言
当然在我们学习的过程中得知其然还得知其所以然。So理解了其原理更能让我们对其开发的理解遇到问题也更能快速找到解决办法
1. SprngBoot-配置优先级
1.1 属性配置的方式
在SpringBoot中支持以下三种格式的配置文件 1.2 配置的优先级
如果当配置三个文件同时配置了一个属性那么谁的优先级比较高 我们启动项目看端口是多少就知道了. 启动发现端口是8081说明当三分配置文件其Properties的优先较高。OK我们注释掉它的配置接下来继续比较另外2个优先级。 启动 发现是8082端口对应的Yml配置文件所以说
优先级结果Properties yml yaml
So虽然SpringBoot支持多种格式配置文件但是在项目开发时我们还是使用主流配置yml 到这里还没完呢其实在SpringBoot中为了增强程序的扩展性 除了支持配置文件属性配置还支持Java系统属性和命令行参数的方式进行属性配置
java系统属性格式为-Dkeyvalue (-D是固定的)
命令行参数 格式为--Keyvalue --是固定的后面加键值就可以了
如下 Idea中运行SpringBoot项目如果指定java系统属性和命令行参数的方式进行属性配置呢当然Idea中已经提供了可视化的界面提供操作了
如下点击编辑配置然后找到自己的项目启动类 进来点击选择参数 添加配置 点击右下角应用确定OK
然后我们这里注释掉以前的三种配置属性看看这两种的配置谁的优先级较高
欧克启动 发现端口是10010欧克我们可以得知命名行参数配置优先级大于Java系统属性配置
当然这只是在Idea中来设置属性配置如果我们没在Idea中比如我们打包得jar包上线了又该如何设置 java系统属性和命令行参数
欧克我们通过Maven打包运行一下来在启动得时候添加一下参数 默认不配置任何属性端口8080没问题欧克我们添加java系统属性和命名行参数 欧克CtrlC终止程序我们只配置命令行参数不出意外就是端口9000 欧克我们总结一下在来比较一下这2种配置命令行参数java系统属性和开始得3种配置(Properties yml yaml )优先级我们这边只需要拿 java系统属性来和另外三种配置来比较就可以了
欧克启动 端口9000没问题说明我们得java系统属性大于另外三种配置得综上 命令行参数java系统Properties yml yaml属性
So:配置 2. Bean 管理
Bean的声明
注解声明 Component及其派生注解这是最常用的声明Bean的方式。通过在类上添加Component、Service、Repository、Controller等注解Spring会自动扫描这些类并将其实例化为Bean。这些注解之间在功能上并无明显区别但通常遵循以下约定Controller用于控制层Service用于业务层Repository用于数据访问层Component用于其他组件。Bean注解在配置类Configuration标注的类中使用Bean注解来声明Bean。通过返回实例化的对象Spring会将其注册为Bean。这种方式在需要自定义Bean的实例化过程或引用第三方库中的类时特别有用。Component声明FactoryBeanFactoryBean是一个特殊的Bean它可以生成并返回其他Bean的实例。通过在类上添加Component注解并将类实现为FactoryBean接口可以声明一个FactoryBean类型的Bean。编程式声明 BeanDefinitionRegistryPostProcessor通过实现此接口可以在Bean定义加载到Spring容器之前动态地注册Bean定义。Import ImportBeanDefinitionRegistrar在配置类或Bootstrap启动类中使用Import注解导入实现了ImportBeanDefinitionRegistrar接口的类然后在该接口的实现类中注册Bean定义。
Bean的注入
自动装配 Autowired这是Spring提供的自动装配注解可以放在构造器、setter方法或字段上Spring会根据类型自动匹配并注入相应的Bean。如果存在多个同类型的Bean则需要通过Qualifier注解指定要注入的Bean的名称。Resource这是JDK提供的注解功能与Autowired类似但可以通过指定name属性来指定要注入的Bean的名称。构造方法注入 通过在类的构造方法中接收Bean参数Spring会在实例化该类时自动注入这些Bean。这种方式有助于确保Bean的不可变性因为一旦Bean被实例化其依赖的Bean就不能再被更改。Setter方法注入 在Bean的setter方法上使用Autowired或Resource注解Spring会在Bean实例化后调用这些setter方法来注入依赖的Bean。属性注入 直接在类的字段上使用Autowired或Resource注解Spring会在Bean实例化时通过反射将这些字段设置为相应的Bean实例。然而这种方式通常不推荐用于生产环境因为它可能导致字段的不可控访问和难以追踪的依赖关系。
获取Bean
默认情况下spring项目启动时会把声明扫描到的这些Bean都创建好放在IOC容器中如果想主动获取Bean可以通过如下方式 从IOC容器中获取到Bean当然在springBoot环境中直接注入IOC容器就可以了
根据name获取bean 当然里面有很多重载方法选择第一个就可以了顺便找一个我们项目的注入的Bean的名称来测试一下就可以了
这里我直接就拿第一个接口来试试
这里没有声明Bean的名称默认就是首字母小写的就是 loginController
欧克我们启动测试
实例;
SpringBootTest
public class getTheBean {Resourceprivate ApplicationContext applicationContext; // 自动注入IOC容器Testvoid testGetBean() {// 根据bean的名称获取beanLoginController loginController (LoginController) applicationContext.getBean(loginController);System.out.println(loginController loginController);}
}
效果 根据类型获取bean
根据name获取bean带类型转化
同理如下
实例
SpringBootTest
public class getTheBean {Resourceprivate ApplicationContext applicationContext; // 自动注入IOC容器Testvoid testGetBean() {// 根据bean的名称获取beanLoginController loginController (LoginController) applicationContext.getBean(loginController);System.out.println(loginController loginController);// 根据bean的类型获取LoginController loginController1 applicationContext.getBean(LoginController.class);System.out.println(loginController1 loginController1);// 根据bean的名称及类型获取LoginController loginController2 applicationContext.getBean(loginController, LoginController.class);System.out.println(loginController2 loginController2);}
}
效果 结果:So,我们可以看到三次地址都是一样的所以说明Ioc容器中这个Bean对象只有一个是单列的就是Bean的作用域了整个生命周期内只会被创建一次并且多个线程共享使用。
此时如果问:spring中的单列Bean是否有并发线程安全
答
Spring的单列Bean默认是非线程安全的但是只要我们避免多个Bean之间共享一些数据就不用害怕并发问题。
原因 单列Bean的生命周期Spring容器在初始化时会创建并管理单列Bean这些Bean整个生命周期内只会被创建一次并且多个线程共享使用。多线程访问如果单列Bean中包含共享可变状态如实例变量多个线程同时访问并修改这些共享状态时可能会导致并发安全问题如数据不一致脏读死锁等。 Bean的作用域
以下六种 默认的其实每次都会在springBoot启动的时候来创建我们测试一下 列 同时这些Bean会在SpringBoot启动就会被创建好方便测试弄个构造方法在创建完毕的时候我们可以观察Ok打上断点开始调试
效果 ok可以看到在springBoot启动的时候就已经创建好了
当然;如果我们需要达成我们某个条件还才开始创建有许多注解可以使用如下 Bean的延迟创建
1. 使用Lazy注解
Lazy 注解是最直接的方式来实现Bean的懒加载。通过在Bean的声明上添加Lazy注解可以指示Spring容器在第一次注入或使用时才创建该Bean。这个注解可以应用于类级别或方法级别在配置类中使用Bean注解声明Bean时。
// 类级别
Component
Lazy
public class MyLazyBean { // ...
}// 方法级别
Configuration
public class AppConfig { Bean Lazy public MyLazyBean myLazyBean() { return new MyLazyBean(); }
}
2. 使用Bean注解的lazyInit属性
在Spring的配置类中使用Bean注解声明Bean时可以设置lazyInit属性为true来实现懒加载。这是另一种在方法级别上实现懒加载的方式。
Configuration
public class MyConfig { Bean(lazyInit true) public MyLazyBean myLazyBean() { return new MyLazyBean(); }
}
3. 使用条件懒加载Conditional注解
虽然Conditional注解本身不直接用于实现懒加载但它可以根据条件来决定是否创建Bean。通过结合自定义条件可以在满足特定条件时才创建Bean这可以间接实现按需创建Bean的效果。然而它并不等同于懒加载因为它在容器启动时就会根据条件决定是否创建Bean。
最基础的就是根据IOC容器是否有某个类来决定是否加载这个类
Configuration
public class SomeConfiguration {BeanConditional(OnWebApplicationCondition.class)public SomeBean someBeanForWeb() {return new SomeBean();}BeanConditional(OnNotWebApplicationCondition.class)public SomeBean someBeanForNotWeb() {return new SomeBean();}
}4. 使用XML配置
如果你使用的是基于XML的配置方式可以通过在bean标签中设置lazy-init属性为true来实现懒加载。
bean idmyBean classcom.example.MyBean lazy-inittrue/ 欧克回归正题在spring中可以通过注解 Scope()来声明作用域 默认是singleton单咧的如果改成prototype则每次拿取都会创建新的对象的。
试试prototype每次获取都会实例化新的Bean启动测试
动效果 没问题每次都不一样把
总 第三方Bean
当然除了我们自定义了一些类比如我们加的这些Component注解以及衍生类注解Controller,Service,Repository 等。还有我们平常的第三方配置就是引入的第三方依赖所提供的
如果要管理的bean对象来自第三方不是自定义的无法用Component及衍生注解声明bean的就需要用到Bean注解
比如这里就随便测试一下
比如我们引入的是Spring Date Redis的依赖 当然它会自动配置RedisTemplate和StringRedisTemplate的Bean 这二种之间也有区别StringRedisTemplate默认传的字符串我们通常需要Json来转化但是相比某条件更节约内存OK我们这里不详细介绍后续补上这里我们就使用RedisTemplate当然这个东西我们一般都需要我们自定义的。根据需求来调整其行为包括选择序列化器、设置连接参数等。 欧克我们就来配置下为了方便集中管理
我们提供Configuration注解声明当前类为一个配置类任何在我们的方法上去加Bean就可以
如下
Configuration
Slf4j
public class RedisConfiguratiom {Bean //将当前方法的返回值对象交给IOC容器管理成为Ioc容器的Bean对象public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {// log.info(开始创建Redis模板...);RedisTemplate redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(new StringRedisSerializer());//redisTemplate.setValueSerializer(new StringRedisSerializer());return redisTemplate;}
}
插曲: 当然如果这里 第三方bean需要引入依赖注入的化只需要通过参数的形式来声明这个类型参数就可以了.SpringBoot会在这个容器中去找到这个Bean对象然后完成注入操作比如这里这个RedisConnectionFactory redisConnectionFactory参数是Spring自动注入的用于创建Redis连接。
当然其实同原理自然也可以在启动类下直接配置 因为我们进入启动类注解中可以看到他 自然当前类就是一个配置类自然也可以自己声明Bean当然一般不建议这样 当然也可以通过bean中的name和value来声明Bean的名称如果不设置默认就是方法名
总
项目中自定义的就使用Componet及其衍生注解
项目中引入的第三方的使用Bean注解 三 . springBoot-原理
原理就是通过扫描指定的依赖包下的文件的配置类任何封装到String【】数组里面实现自动 装配
2.7之前的版本 Spring常用的注解
SpringBootConfiguration //声明当前启动类也是一个注解
EnableAutoConfiguration //声明哪些第三类配置当前类中
ComponentScan(excludeFilters { Filter(type FilterType.CUSTOM, classes TypeExcludeFilter.class),Filter(type FilterType.CUSTOM, classes AutoConfigurationExcludeFilter.class) })//默认扫描当前类和其子类 配置条件 三.SpringBoot自动配置的原理
Spring框架的配置相对复杂需要手动配置大量的XML文件或注解。而Spring Boot通过自动配置和约定优于配置的原则大大简化了配置过程。之所以SpringBoot框架更加简单快捷是因为其底层提供了2个非常重要的功能
起步依赖 --就是通过Maven的依赖传递解决Spring中依赖配置的繁琐
自动配置 --大大简化框架的使用过程中Bean的声明和配置通过起步依赖常用的配置基本也就有了
起步依赖
起步依赖本质上是一个Maven或Gradle的依赖描述符它包含了构建特定类型应用程序所需的所有依赖项。例如spring-boot-starter-web包含了构建Web应用程序所需的所有Spring MVC和Tomcat的依赖项。通过引入起步依赖开发人员可以轻松地集成所需的组件而无需手动添加每个依赖项。 自动配置 Spring Boot的自动配置是另一个核心特性。它基于Spring框架的条件化配置功能根据应用程序中声明的依赖项和类路径中的资源自动配置Spring应用程序。自动配置会尝试猜测开发人员可能需要的配置并自动应用这些配置。如果开发人员需要自定义配置可以通过配置文件如application.properties或application.yml或Java配置类来覆盖自动配置。
我们启动一个程序 可以看到除了我们自定义的配置还有很多配置类。这些配置加载进来就会生成很多的Bean对象了我们都可以直接DI注入使用了,这就是SpringBoot启动的自动就帮我们配置好了的效果
OK哪我们就了解下SpringBoot自动配置的原理它是如何把我们引入的这些依赖定义的配置类以及Bean如何加载到我们的SpringIOC容器中。
这里新建一个模块做一些配置充当第三方依赖然后在其他项目引入这个模块的坐标
模块中 当然我们测试看看能不能获取到这个Bean对象 按理我们引入了这个依赖并且也声明了Component注解然后应该会加载到SpringBoot的容器中 Ok,不出意外找不到这个Bean说明是没有加载到的其实spring启动的时候默认会扫描当前包及其子包下才可以的需要扫描到才可以交到Ioc容器中实现不是声明了注解就一定会成为Ioc容器的Bean对象的这里可以通过注解在启动类声明 ComponentScan 里面是数组看源码然后指定包名就可以了 欧克:可以看到拿到Bean对象 当然底层肯定不是这样字的不然我们引入第三方依赖哪不得爆炸这种很繁琐
另一种就是import注解实现得其实这个就是关键后面得实现其实也是套用这个了得。
使用import导入得类会被Spring加载到Ioc容器中导入形式有以下几种
导入 普通类 -- 这个类就会交到Ioc容器中
导入 配置类 -- 这个配置类包括下得所以bean对象都会就会交到Ioc容器中
导入ImportSelector 接口实现类 关键得重点后面其实底层就是通过这个接口来扫描得文件
import源码也声明了可以导入这些类 返回的是数组呢
如
//Import({MyImportSelector.class})
//Import({TokenParser.class}) // 导入普通类 交给IOC容器
Import({HeaderConfig.class}) // 导入配置类 交给IOC容器
SpringBootApplication
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}}当然第三种就是 导入ImportSelector接口实现类看下源码: 返回值就是类的全类名欧克我们只需要实现这个接口重写这个方法然后添加一些我们想引入的Bean的全类名就ok了 public class MyImportSelector implements ImportSelector {public String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{com.example.HeaderConfig};}
}Configuration
public class HeaderConfig {Beanpublic HeaderParser headerParser(){return new HeaderParser();}Beanpublic HeaderGenerator headerGenerator(){return new HeaderGenerator();}
}Import({MyImportSelector.class})
SpringBootApplication
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}}当然上面这些方法有些鸡肋的当我们引入第三方依赖是需要清晰知道我们要导入第三依赖的哪些配置类哪些包的。 还是繁琐的当然需要第三方自己自己来封装需要到那些类然后通过第三方提供的Enablexxxx注解然后在通过Import注解来指定哪些需要声明的Bean来实现
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.TYPE)
Import(MyImportSelector.class)
public interface EnableHeaderConfig {
}public class MyImportSelector implements ImportSelector {public String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{com.example.HeaderConfig};}
}Configuration
public class HeaderConfig {Beanpublic HeaderParser headerParser(){return new HeaderParser();}Beanpublic HeaderGenerator headerGenerator(){return new HeaderGenerator();}
}-------EnableHeaderConfig
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}}
只需要在启动类声明这个注解就可以了层层套娃我们不需要关注第三方怎么实现的。 就可以把对于的配置类bean加载到IOC容器里面了这也是SpringBoot所采用的
3.1 SpringBoot自动配置源码启动
我们从启动类点击进去可以看到封装了许多注解 我们不需要关注其他进入EnableAutoConfiguration注解中去一探究竟 其实关键就是找到这个接口这个方法就完事了 欧克进入实现类看看找到这个方法 跟紧 注此版本是2.7版本之后的
可以看到其实就是扫描这2个文件的配置类容的。把这2个配置文件的信息加载出来就会封装到这个LlstString集合当中然后通过返回给给这个String【】只会spring就会导入这些配置和bean了
注: 2.7版本之前其实只有META-INF/spring.factories文件,如下 注: 从Spring Boot 2.7开始虽然META-INF/spring.factories文件仍然是自动配置的一个重要组成部分但Spring Boot引入了一种新的自动配置机制即META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。这个文件提供了一种更灵活的方式来指定自动配置类但它并不是完全替代spring.factories文件而是作为一种补充。 说简单点就是通过SpringBootApplication封装的EnableAutoConfiguration注解然后封装的Import来扫描指定文件通过这些文件获取到配置类Bean的全类名啊这些在封装到这个String【】数组对象就ok
其实这些文件在我们引入的起步依赖中都有 进去看看 就是这些全类名通过读取这些配置文件全类名后通过import把这些配置类Bean加载到Ioc容器中 。当然你也就可以自己定义一个启动类了
进入一个实例看看其实就是一个配置类并且下面的加了Bean所以我们ioc容器加载到这些自然就有了Bean了我们就可以注入使用了 总结 就是启动了注解底层封装了三个核心的注解
1) SpringBootConfiguration -- 其注解又封装了Configuration就是声明当前也是一个配置类 2) EnableAutoConfiguration - 其又封装了Import注解 其注解又指定了ImportSelector实现类其类有实现了selectImports的方法其返回值就是String【】这个数组封装的内容就是我们要导入到SpringIoc容器中的类的全类名然后这个方法其实就会去扫描加载2个文件的这些全类名这些全类名就是一个一个配置类1个是spring.factories 的文件2.7版本之前早期使用的3.0版本之后就没了在此期间会兼容另一个是spring下的org.springframework.boot.autoconfigure.AutoConfiguration.imports文件2.7.x版本之后一种新的自动配置机制,3.0版本之后主导使用。然后这些配置类下其实就是声明了一个一个的Bean对象然后就会把这些配置类加载通过String数组全部封装返回然后通过Import注解把这些全部交给Spring的Ioc容器当中。 3) ComponentScan -- 组件扫描默认扫描当前引导类所在的包及其子包
当然这里并不是全部就会交给Ioc容器成为Bean.其实还是有条件的比如底层有些Bean是加了Conditionxxx的条件注解满足某些条件之后才会加载到IOc容器中的 Conditional 注解可以作用于 Bean 方法、配置类或其他组件类上当 Spring 容器扫描到 Conditional 注解 时会调用其 Condition 实现类的 matches 方法根据返回的布尔值来决定是否实例化对应的 Bean。 当然这里扩展一下常用的Conditional的字注解使用