5.@Import 给容器导入一个组件
给容器中注册组件
一、包扫描 + 组件标注注解(@Controller/@Service/@Repository/@Component) [自己写的类]
二、@Bean [导入的第三包里面的组件]
三、@Import [快速给容器中导入组件] (@Import{Color.class,Red.class})
1.@Import(要导入到容器中的组件),容器中就会自动注册这个组件,id默认是组件全名
2. @ImportSelector: 返回需要导入组建的全类名数组
// 自定义逻辑,返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值就是要导入到容器中的组件的全类名
// AnnotationMetadata:当前标注@Import注解类的所有信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.lv.pojo.Blue", "com.lv.pojo.Yellow"};// 返回的类注入容器
}
}
3.使用ImportBeanDefinitionRegister 手动注册bean到容器中
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:注册类
* 把所有需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanDefinition手动注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 查询容器中是否存在red组件
boolean red = registry.containsBeanDefinition("com.xjhqre.pojo.Red");
if (red) {
// 指定bean的定义信息(bean的类型,bean的scope)
BeanDefinition beanDefinition = new RootBeanDefinition(Purple.class);
// 第一个参数指定bean的id
registry.registerBeanDefinition("purple", beanDefinition);
}
}
}
四、使用Spring提供的FactoryBean (工厂Bean)
1.默认获取到的是工厂bean调用getObject创建的对象
2.要获取工厂Bean本身,我们需要给id前面加一个& 即 &colorFactoryBean
// 创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 返回是否为单例,
// true 单实例 在容器中保存一份
// false,则每次创建时调用getObject()方法
@Override
public boolean isSingleton() {
return true;
}
}
在配置类中导入这个组件!!!
@Test
public void test5() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
// 工厂bean获取的是调用getObject创建的对象
Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
System.out.println(colorFactoryBean.getClass());
// 如果想要拿到colorFactoryBean对象,则需要在传入的id前加一个&标识
Object colorFactoryBean2 = applicationContext.getBean("&colorFactoryBean");
System.out.println(colorFactoryBean2.getClass());
}
6.Bean生命周期
即bean从创建 ----> 初始化 -----> 销毁的过程
容器管理bean的生命周期;
我们可以自定义初始化和销毁方法,容器在bean进行进行到当前生命周期可以自定义初始化和销毁方法
构造(对象创建)
- 单实例:在容器启动的时候创建对象
- 多实例:在每次获取的时候创建对象
初始化
- 对象完成创建,并赋值好后,调用初始化方法
销毁
-
单实例:在容器关闭的时候销毁
-
多实例:容器不会管理这个bean,所以不会调用销毁方法(初始化会在第一次使用这个对象时才会创建)
初始化和销毁方法:
- 通过@Bean注解指定init-method和destroy-method
-
实现InitializingBean和DisposableBean接口,重写里面的destroy和afterPropertiesSet方法
- 通过让bean实现
InitializingBean
接口来定义初始化逻辑 - 通过让bean实现
DisposableBean
接口来定义销毁逻辑 - 在
InitializingBean
中有一个方法afterPropertiesSet
,该方法在bean创建并赋值后调用
- 通过让bean实现
-
使用@PostConstruct和@PreDestroy注解
- @PostConstruct:在bean创建完成并且属性赋值完成后进行初始化
- @PreDestroy:在容器销毁bean之前执行
-
BeanPostProcessor:接口,bean后置处理器,在bean初始化前后进行一些处理
- postProcessBeforeInitialization:在初始化之前执行
- postProcessAfterInitialization:在初始化之后执行
BeanPostProcessor执行原理
1.执行populateBean(beanName, mbd, instanceWrapper);
给bean进行属性赋值
2.开始initializeBean
初始化bean
1.先执行applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);,遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
2.然后执行invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
3.最后执行applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
7.@Value 属性赋值
8.自动装配
Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
-
@Autowired:自动注入
(1)默认优先按照类型去容器中去找对应的组件: applicationContext.getBean(BookDao.class);如果找到了则进行赋值;
(2)如果找到了多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean(“bookDao”)
(3)@Qualifier(“bookDao”):使用 @Qualifier 指定需要装配的组件的id,而不是使用属性名
(4)自动装配默认一定要将属性赋值好,没有就会报错,可以使用@Autowired(required = false);来设置为非必须的
(5)可以利用@Primary:让Spring在进行自动装配的时候,默认使用首选的bean,也可以继续使用@Qualifier(“bookDao”)来明确指定需要装配的bean的名字 -
Spring还支持使用**@Resource(JSR250)**和@Inject(JSR330) [Java规范注解]
-
@Resource 可以和@Autowired一样自动装配功能,默认是按照组件名称进行装配的;没有能支持@primary功能没有支持@Autowired(required = false)
-
@Inject:需要导入java.inject的包,和@Autowired的功能一样
-
-
@Autowired:Spring定义的 @Resource和@Inject都是java规范的AutowiredAnnotationBeanPostProcessor:解析完成自动装配 以上注解都装配进去了
-
方法、构造器位置的自动装配
-
放在方法上,@Bean+方法参数:参数从容器中获取 ,默认不写@Autowired
-
标在构造器上只有一个有参构造器,这个有参构造器的@Autowired可以省略,其参数位置的组件还是可以自动从容器中获取 默认加在ioc容器中的组件,容器会调用无参构造器创建对象,再进行初始化赋值等操作
-
-
Aware注入Spring底层组件 原理
- Aware 接口,提供了类似回调函数的功能
- 自定义组件想要使用Spring 容器底层的一些组件(ApplicationContext,BeanFactory);自定义组件需要实现xxxAware接口;在创建对象的时候,会调用接口规定的方法注入相关组件
- 原理:通过对应的Processor进行处理的 ApplicationContextAware => ApplicationContextAwareProcessor 后置处理器,在创建完bean后,看见bean实现了相关Aware接口,将组件传进来
9.@Profile环境搭建
Spring我们提供的可以根据当前环境,动态的激活和切换一系列组建的功能
开发、生产、测试环境 数据源的不同
@Profile 指定组件在那个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这组件
我们需要在项目的src/main/resources目录下新建一个配置文件,例如dbconfig.properties,在其中写上数据库连接的相关信息,如下所示。
db.user=root
db.password=123456
db.driverClass=com.mysql.jdbc.Driver
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
*
* @author liayun
*
*/
@PropertySource("classpath:/dbconfig.properties") // 加载外部的配置文件
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String dirverClass;
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(dirverClass);
return dataSource;
}
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
dataSource.setDriverClass(dirverClass);
return dataSource;
}
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
dataSource.setDriverClass(dirverClass);
return dataSource;
}
// 继承EmbeddedValueResolverAware接口
// 他的实现就是直接用${db.driverClass}获取配置文件的值
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
dirverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}