概述
将Java对象转义成Bean交由IOC容器进行管理,减少开发者对对象的生命周期相关操作
Bean的生命周期
默认项
每个Bean默认都是**饿汉式**的实例化,可以使用@Lazy描述使用**懒汉式**实例化
Bean的默认作用域为Singleton 单例,可以使用@Scope进行指定作用域
测试时允许覆盖Bean,如果定义多个相同的Bean,那么会得到最后一个Bean
SpringBoot 2.1 开始默认禁用,不允许覆盖Bean,可以通过设置进行允许覆盖
spring.main.allow-bean-definition-overriding=true
相关注解方法
用来指定Bean的初始化开始及销毁之前的方法
@Bean(initMethod = "" ,destroyMethod ="")
@PreDestroy 指定Bean销毁之前的方法
@PostConstruct 执行Bean初始化开始之前的方法
@Scope("作用域") 用于指定Bean的作用域
@Lazy 用于表述该类使用懒加载的方式进行实例化,可以避免依赖循环问题
@DependsOn("BeanName") 指定创建Bean的顺序,被该注解描述的类需要等指定的Bean创建完成后在进行创建
配置Bean
显示配置
@Configuration + @Bean
//标识该类为配置类,Spring会自动加载
@Configuration //作用域类上
public class ConfigBean {
//方法的返回值将由Ioc容器进行管理,BeanName默认为方法名-首字母小写 - 可以通过@Bean(name="BeanName")来指定
@Bean // @Bean作用与方法与注解上
public ExampleService exampleService(){
return new ExampleService();
}
//Spring会自动注入已经存在的exampleService Bean
@Bean
//当ExampleService类存在时Spring才执行该方法
@ConditionalOnBean(ExampleService.class)
public ExampleTwoService exampleTwoService(ExampleService exampleService){
ExampleTwoService exampleTwoService = new ExampleTwoService();
exampleTwoService.setExampleService(exampleService);
return exampleTwoService;
}
}
class ExampleService{
}
class ExampleTwoService{
private ExampleService exampleService;
public void setExampleService(ExampleService exampleService) {
this.exampleService = exampleService;
}
}
隐式配置
@SpringBootApplication //组件扫描通过@componentScan("path") 用于扫描被@Component注解描述的类,将扫描到的类加载到IOC
public class KafkaStudyApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaStudyApplication.class, args);
}
}
@Component //由该注解描述的类为IOC的组件类,下面几种注解是开发者人为分层便于开发者用于快速理解该类的作用,但以下注解都有@Component元注解 -- BeanName默认为类名首字母小写
class ExampleComponent{
}
@Configuration //配置类
class ExampleConfig{
}
@Controller //控制层
class ExampleController{
}
@Service //业务层
class ExampleService{
}
@Repository //持久层
class ExampleRepository{
}
作用域
@Scope("作用域") 用于指定Bean的作用域
Singleton 单例,全局唯一对象,存活时间与ApplicationContext对象一致 **默认的作用域**
prototype 原型 ,每次返回一个新的对象,存活时间取决与持有该对象引用的时间,没有引用后会被GC回收
request 存活时间与用户的HTTP请求一致
session 存活时间与用户的HTTP会话一致
Web Socket 存活时间与websocket一致
Refresh 可以超过其ApplicationContext的重新加载时间,但不一定由效果, 我也不知道为啥
可以自定义作用域
注入Bean
1. 摆脱解析依赖的负担
2. 简化代码,提高代码的利用性
3. 隐藏依赖的实现细节
4. 依赖关系可以很容易被存根出来,用于单元测试
5. 允许几种控制对象的生命周期
构造
构造方法需要唯一,如果不唯一需要用@Autowired注解进行描述 (没有实验瞎猜的)
可以不用添加注解
字段
Setter
注解
这两个注解默认如果没有注入项则会抛出异常
@Autowired
1. Spring框架提供的注解
2. 首先根据BeanType进行查找再根据BeanId ,如果找到多个注入项,则会抛出异常,可以通过@Qualifer("BeanId") 配合进行指定BeanId
3. @Autowired(required = false) 如果没有注入项则不进行注入,而不是抛出异常
4. 作用域为 构造 | 字段 | Setter 上方, 如果构造方法只有一个则可以不用使用该注解
@Resource
5. Java提供的注解
6. 首先根据BeanId进行查找,如果没有找到则根据BeanType进行查找
7. 作用域 字段 |Setter 上方
8. 可以通过@Resource(name = "BeanId")用来指定
动态代理
SpringBoot 2.x之前 根据目标对象是否实现了接口自动选择使用CGLIB 或JDK原生代理
SpringBoot 2.x之后 默认都是用CGLIB进行代理
JDK
实现方式:通过实现目标类实现的接口,与目标类成为兄弟类进行代理
提供方:API内置与JDK中
CGLIB
实现方式:通过继承的目标类,进行代理,所以目标类不能使用final关键字
提供放:Spring的Jar中
Application Context
上下文对象可以理解为环境,包含了所有的Bean
ApplicationContext 支持任何环境进行引导,包括不限于
1. JUnit系统测试
2. Web应用程序
3. 独立的应用程序
获取Bean的三种重载方法
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(KafkaStudyApplication.class, args);
//如果没有找到指定的Bean会抛出异常 NoSuchBeanDefinitionException: No bean named 'BeanId' available
context.getBean("BeanId"); //返回指定BeanName的Bean
context.getBean(Bean.class); //返回指定类型的Bean
context.getBean("BeanId",Bean.class); //返回指定BeanId和指定类型的Bean
}
多配置文件
使用@Import关联或引入其他配置
1. 避免出现一个非常大的Conflguration类
2. 更好的区分每个配置类的功能
3. 便于维护
@Configuration
@Import(ConfigOne.class,ConfigTwo.class)
public class MasterConfig{
}
@Conflguration
public class ConfigOne{}
@Configuration
public class ConfigTwo{}
环境变量
Environment 对象存放Spring程序中所有的环境变量
Environment Bean从运行时环境中加载属性,属性来源依次为
1. JVM系统属性 --- System.getProperty()
2. 系统环境变量 --- System.getenv()
3. Java Properties 外部配置文件
JVM 和系统环境变量 会自动填充到Environment Bean中
外部配置文件可以使用@PropertySource("")进行填充,该注解的属性可以为
4. classpath: --- @PropertySource("classpath:/cn/org/config/app.properties")
5. file: --- @ProertySource("file:config/local.properties")
6. http: --- @PropertySource("http:www.baidu.com/config/acc.properties") //没有实验
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.io.support.PropertySourceFactory;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
//Bean名字,如果不指定则根据文件名按照一定规则进行生成
String name() default "";
//指定扫描文件的路径,可以配置多个
String[] value();
//是否忽略文件,如果文件没有找到,默认false -> 报错
boolean ignoreResourceNotFound() default false;
//指定文件的编码
String encoding() default "";
//指定文件的解析工厂,通过提供工厂让该Bean支持读取Yml/或更多类型的配置文件
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
支持读取Yml类型的文件
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Properties;
public class YAMLPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
//1,创建一个YAML文件的解析工厂。
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
//2,设置资源。
factory.setResources(encodedResource.getResource());
//3,获取解析后的Properties对象
Properties properties = factory.getObject();
//4返回。此时不能像默认工厂那样返回ResourcePropertySource对象 ,要返回他的父类PropertiesPropertySource对象。
return name != null ? new PropertiesPropertySource(name, properties) :
new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties);
}
}
//使用
@PropertSource(value = "classpath:app.yml",factory = YAMLPropertySourceFactory.class)
@Profile
通过@Prifile可以指定程序运行时读取其他配置文件
配置文件名称规范
application-dev.yml
application-test.yml
application-xxx.yml
重要接口
BeanFactoryPostProcessor
可以在任何Bean创建 <之前>,修改Bean的定义
可以通过实现该接口,Spring会自动调用,如果实现该接口最好将@bean方法使用**static**进行修饰,因为他需要在创建Bean之前运行
配置Bean的作用域等其他信息
PropertySourcesPlaceholderConfigurer
用于读取配置文件,修改bean的定义
BeanPostProcessor
Bean的后处理接口
对每个Bean进行增强
可以在初始化之前和/或初始化之后使用任何方式修改Bean实例
实现该接口在初始化之前和之后增强Bean
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Nullable
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//在FooBean初始化之前给属性A进行赋值
if("foo".equals(beanName)){
Foo foo =(Foo)bean;
foo.setA(110);
}
return bean; //需要将Bean进行返回,否则会丢失
}
@Nullable
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//在FooBean初始化之后打印A属性的值
if("foo".equals(beanName)){
Foo foo =(Foo)bean;
System.out.println(foo.getA());
}
return bean; //需要将Bean进行返回,否则会丢失
}
}
@Configuration
class MyConfig{
@Bean
public Foo foo(){
return new Foo();
}
}
class Foo{
int a;
public int getA(){
return this.a;
}
public void setA(int a) {
this.a = a;
}
}
BeanFactory
BeanFactory接口定义了Ioc容器的最基本形态,Spring IOC所遵守的最底层和最基本的编程规范,但不负责具体实现,Spring容器提供了很多具体实现,这些实现都有不同的侧重点或者说功能
ApplicationConext
FactoryBean
Spring 提供的一种用于生成Bean的工厂Bean接口,实现该接口后,Spring会自动调用getObject方法,无需开发者进行调用,但只是提供了最基本的三个方法,getObject()将放回的对象放入到IOC中,getObjectType()返回Bean类型,isSingletion()判断是否为单例默认为true,该接口由70余种实现,每种实现都是为了隐藏实例化一些复杂Bean的细节,给上层较少工作。
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
# 工厂类用于生产AccountService对象
public class AccountServiceFactoryBean implements FactoryBean<AccountService> {
@Override
public AccountService getObject() throws Exception {
return new AccountService();
}
@Override
public Class<?> getObjectType() {
return AccountService.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
# 隐式配置FactoryBean 并注入实例
@Configuration
class ServiceConfig{
@Bean
public AccountServiceFactoryBean accountServiceFactoryBean(){
return new AccountServiceFactoryBean();
}
@Bean
public void getClassName(AccountService accountService){
accountService.getName();
}
}
# 实例
class AccountService{
public void getName(){
System.out.println("注入对象成功-");
}
}