@Configuration : 配置类 == 配置文件,告诉Spring这是一个配置类标签:容器,Spring,最全,Bean,public,bean,详解,组件,class From: https://blog.51cto.com/u_15829196/5756186
@ComponentScan(value="com.atguigu",excludeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
@ComonentScan value : 指定要扫描的包(这个注解可以定义多个)
excludeFilters = Filter[] : 指定扫描的时候按照什么规则排除那些组件
includeFilters = Filter[] : 指定扫描的时候只需要包含那些组件
useDefaultFilters = false : 表示去掉默认扫描
FilterType.ANNOTATION : 按照注解类型 例如 : Controller.class
FilterType.ASSIGNABLE_TYPE : 按照给定的类型 例如 : UserService.class
FilterType.ASPECTJ : 使用ASPECTJ表达式
FilterType.REGEX : 使用正则指定
FilterType.CUSTOM : 使用自定义规则自定义规则需要实现TypeFilter接口
public class MyTypeFilter implements TypeFilter {
/**
metadataReader : 读取到的当前正则扫描的类的信息
metadataReaderFactory : 可以获取到其他任何类的信息
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadate = metadataReader.getAnnontationMetadata();
// 获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
// 获取这个类名
String className = classMetadata.getClassName();
// 自定义匹配规则
if(className.contains("er")) {
return true;
}
return false;
}
} @ComponentScans(
value {
@ComponentScan(value="com.atguigu",excludeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
}, useDefaultFilters = false)
}
) : 里面可以定义多个@ComponentScan@Bean : 给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Scope("") : 表示这个对象或者这个bean的创建作用域范围(调整作用域).
prototype : 多实例的,ioc容器时并不会调用方法创建对象放在容器中.每次获取的时候才会调用方法创建对象.
singleton : 单实例的(默认值),ioc容器启动会调用方法创建对象放到ioc容器中.以后每次获取就是直接从容器(map.get())中拿.
request : 同一次请求创建一个实例
session : 同一个session创建一个实例@Lazy : 懒加载
单实例bean,默认在容器启动的时候创建对象.
懒加载: 容器启动不创建对象,第一次调用(获取)Bean创建对象,并初始化.@Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean(可以放在@Bean上,或者类上)
例如 :
//放在类上统一设置
// 类中组件统一设置,满足当前条件,这个类中配置的所有bean注册才能生效.
@Conditional({WindowsCondition.class})
@Configuration
//实现接口Condition
public class LinuxCondition implements Condition {
/**
*/
public boolean matchs(CondititContext context, AnnotatedTypeMetadata metadada) {
// 是否linux系统
// 1. 能获取到ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2.获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 3.获取当前环境信息
Environment environment = context.getEnvironment();
// 4.获取到bean定义的注册类(可以判断容器中的bean注册情况,也可以给容器中注册bean)
BeanDefinitionRegistry registry = context.getRegistry();
// 判断容器中是否包含bean
boolean definition = registry.containsBeanDefinition("persion");
String property = environment.getProperty("os.name");
if (property.contains("linux")){
return true;
}
return false;
}
}
// 获取bean名称
String[] namesForType = applicatinContext.getBeanNamsForType();
// 动态获取环境变量的值,获取容器运行环境例如 : windows,liunx
ConfigurableEnvironment environment = applicationContext.getEnvironment();
// 获取操作系统的名称
String property = environment.getProperty("os.name");
给容器中注册组件 :
1 : 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
2 : @Bean[导入的第三方包里面的组件]
3 : @Import[快速给容器中导入一个组件]
1) : @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是组件的全类名(也可以导入多个组件)
2) : ImportSelector : 返回需要导入的组件的全类名数组.
3) : ImportBeanDefinitionRegistrar : 手动注册Bean到容器中
4 : 使用Spring提供的FactoryBean(工厂Bean)
1) : 默认获取到的是工厂bean调用getObject创建的对象
2) : 要获取工厂Bean本身,我们需要给id前面加一个&
&colorFactoryBean
@Import(要导入到容器中的组件 例如 : Color.class) : 方在类上,导入组件,容器中就会自动注册这个组件,id默认是组件的全类名(也可以导入多个组件)
(2) ImportSelector : 返回需要导入的组件的全类名数组.
例如 :
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值,就是导入到容器中的组件全类名
// AnnotationMetadata : 当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 方法不要返回null值
return new String[];
}
}
@Import({Color.class,Red.class,MyImportSelector.class})
(3) ImportBeanDefinitionRegistrar : 手动注册Bean到容器中
例如 :
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
// AnnotationMetadata : 当前类的注解信息
// BeanDefinitionRegistry : BeanDefinitionRegistry注册类;
// 把所有需要添加到容器中的bean : 调用 BeanDefinitionRegistry.registryBeanDefinition手工注册进来
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
// 通过bean名称,判断bean是否在容器中
booean definition = registry.containsBeanDefinition("red");
boolean definition1 = registry.containsBeanDefinition("blue");
if (definition && definition1) {
// 指定bean定义信息 : (Bean的类型,Bean...)
RootBeanDefinition bean = new RootBeanDefinition(RainBow.class);
// 注册一个Bean,指定Bean名
registry.registryBeanDefinition("rainBow", bean);
}
}
}4 : 例子 :
// 创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
//是单例?
//true : 这个bean是单例,在容器中保存一份
//false : 多实例,每次获取都会创建一个新的bean
@Override
public boolean isSingleton() {
return false
}
}
@Bean
public ColorFactoryBean getBean() {
return new ColorFactoryBean();
}
public void test() {
//工厂Bean获取的是调用getObject创建的对象;实际上获取的是Color对象
Object bean2 = applicatinContext.getBean("colorFactoryBean");
//这个是获取的ColorFactoryBean对象,因为在FactoryBean接口中定义了一个字符串,表示以&开头的就是获取实现FactoryBean接口的对象
Object bean3 = applicatinContext.getBean("&colorFactoryBean");
}
Bean的生命周期
bean创建 -- 初始化 -- 销毁的过程
容器管理bean的生命周期 :
我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁的方法
构造(对象创建)
单实例 : 在容器启动的时候创建对象
多实例 : 在每次获取的时候创建对象
BeanPostProcessor.postProcessBeforeInitialization
初始化 :
对象创建完成,并赋值好,调用初始化方法...
BeanPostProcessor.postProcessInitialization
销毁 :
单实例 : 容器关闭的时候
多实例 : 容器不会管理者bean,容器不会调用销毁方法.
遍历得到容器中所有的BeanPostProcessor : 挨个执行beforeInitialization,一旦返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessBeforeInitialization
BeanPostProcessor的原理
populateBean(beanName, mdb, instanceWrapper) : 给bean进行属性赋值
initializeBean
{
appliyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
}
1) : 指定初始化和销毁方法:
通过@Bean指定init-method和destory-method;
2) : 通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)
3) : 可以使用JSR250;
@PostConstruct : 在bean创建完成并且属性赋值完成;来执行初始化方法(在对象创建并赋值之后调用)
@PreDestroy : 在容器销毁bean之前通知我们进行清理工作.
例如 :
public class Dog{
public Dog() {
System.out.println("");
}
//对象创建并赋值之后调用
@PostConstruct
public void init() {
System.out.println("11");
}
//容器移除对象之前
@PreDestroy
public void detory(){
}
}
4) : BeanPostProcessor[interface] : bean后置处理器;
在bean初始化前后进行一些处理工作;
postProcessBeforeInitialization : 在初始化之前工作
postProcessInitialization : 在初始化之后工作
Spring底层对BeanPostProcessor的使用 :
bean赋值,注入其他组件,@Autowired,生命周期注解功能。@Async, xxx BeanPostProcessor;
@Value : 给属性赋值。
1. 基本数值 例如 : @Value("111")
2. 可以写SpEL. 例如 : @Value("#{20-2}")
3. 可以写${},取出配置文件中的值(在运行环境变量里面的值) 例如 : @Value("${name}")
@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值。
例如 :
AnnotationConfigApplicationContext applicatinContext = new AnnontationConfigApplicationContext();
pringBeans(applicationContext);
Person person = (Person)applicationContext.getBean("person");
ConfigurableEnvironment environment = applicatinContext.getEnvironment();
// 这是根路径下的.properties配置文件中的k/v值
Sting property = environment.getProperty("person.nickName);
applicatinContext.close();
自动装配 :
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值;
1) . 默认优先按照类型去容器中找对应的组件 : applicationContext.getBean(BookDao.class);找到就赋值
2) . 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
3) . @Qualifier("bookDao") : 使用@Qualifier指定需要装配的组件的id,而不是使用属性名。
4) . 自动装配默认一定要将属性赋值好,没有就会报错。(可以通过更改属性值.例如 : @Autowired(required=false);这样就在没有这个组件时,不会进行自动装配)
5) . @Primary : 让Spring进行自动装配的时候,默认使用首选的bean;
也可以继续使用@Qualifier指定需要装配的bean的名字。
applicatinContext.getBean("bookDao");
BookService{
@Autowired
BookDao bookDao
}
(2):Spring还支持使用@Resource(JSR250)和@Inject(JSR330)【Java规范的注解】
@Resource
可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
没有能支持@Primary功能没有支持 @Autowired(reqiured = false);
@Inject
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
@Autowired : Spring定义的; @Resouce和@Inject都是java规范。
Spring的@Autowired的底层使用AutowiredAnnotationBeanPostProcessor : 解析完成自动装配功能。
(3):@Autowired : 构造器,参数,方法,属性
@Componetnt : 默认加载ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
1): 标注在方法位置 : @Bean + 方法参数;参数从容器中获取;默认不写@Autowired效果是一样的,都能自动装配
2): 标注在构造器上 : 如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件换是可以自动从容中获取.
3):标注在参数位置
@Bean标注的方法创建对象的时候,方法参数的值从容器中获取。
(4) : 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware
把Spring底层一些组件注入到自定义的Bean中;
xxxAware: 功能使用xxxProcessor;
ApplicationContextAware ==> ApplicationContextAwareProcessor;
@Profile : 指定组件在哪个环境的情况下才能被注册到容器中。
1) : 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境。
2) : 写在配置上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效。
激活方式 :
(1) : 使用命令行动态参数 : 在虚拟机参数位置加载 -Dspring.profiles.active=test
(2) :代码的方式激活某种环境。
(3) : 没有标注环境标识的bean在任何环境下都是加载的。
例如 :
AnnotationConfigApplicationContext applicatinContext = new AnnontationConfigApplicationContext();
//1。创建一个applicationContext
//2. 设置需要激活的环境
applicatinContext.getEnvironment().setActiveProfiles("dev");
//3.注册主配置类
applicatinContext.registry(MainConfigOrProfile.class);
//4.启动刷新容器
applicatinContext.refresh();