a) singleton 单实例的 (默认), 单例的生命周期有 spring 容器来控制, 非懒加载时在 spring 实例化以后就产生了对象, 容器销毁则对象销毁
- package com.springIOC4b.config;
- import com.springIOC4b.bean.CarBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Lazy;
- import org.springframework.context.annotation.Scope;
- @Configuration
- public class MainConfig {
- @Bean
- @Scope(value = "singleton")
- public CarBean car(){
- return new CarBean();
- }
- }
- package com.springIOC4b;
- import com.springIOC4b.bean.CarBean;
- import com.springIOC4b.config.MainConfig;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- public class MainTest {
- /**
- * @Scope(value = "singleton") 单例, 默认也是 @Scope(value = "singleton")
- * @param args
- */
- public static void main(String[] args) {
- AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
- CarBean car = (CarBean)aca.getBean("car");
- CarBean car2 = (CarBean)aca.getBean("car");
- System.out.println(car == car2); // # true
- }
- }
输出结果为 true, 说明我们的对象是单例的, 单例的对象, 生命周期由 spring 来管理的.
b) prototype 多实例的
- package com.springIOC4c.config;
- import com.springIOC4c.bean.CarBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Scope;
- @Configuration
- public class MainConfig {
- @Bean
- @Scope(value = "prototype")
- public CarBean car(){
- return new CarBean();
- }
- }
- package com.springIOC4c;
- import com.springIOC4c.bean.CarBean;
- import com.springIOC4c.config.MainConfig;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- public class MainTest {
- /**
- * @Scope(value = "prototype") 多例
- * @param args
- */
- public static void main(String[] args) {
- AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
- CarBean car = (CarBean)aca.getBean("car");
- CarBean car2 = (CarBean)aca.getBean("car");
- System.out.println(car == car2); // # false
- }
- }
多例的不受 IoC 容器来管理, 销毁时是由 GC 来清理的, 还有 request 同一次请求和 session 同一个会话级别的, 这里就不一一演示了.
5.@Configuration 注解, 来判断是否注入 Bean 的.
- package com.springIOC5.config;
- import com.springIOC5.bean.CarBean;
- import com.springIOC5.bean.UserBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Conditional;
- import org.springframework.context.annotation.Configuration;
- @Configuration
- public class MainConfig {
- @Bean(value = "user")
- public UserBean userBean() {
- return new UserBean();
- }
- @Bean
- @Conditional(value = IOCConditional.class)
- public CarBean carBean() {
- return new CarBean();
- }
- }
- package com.springIOC5.config;
- import org.springframework.context.annotation.Condition;
- import org.springframework.context.annotation.ConditionContext;
- import org.springframework.core.type.AnnotatedTypeMetadata;
- public class IOCConditional implements Condition {
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- if (context.getBeanFactory().containsBean("user")) { // 这里必须和 Bean 的名称完全一致.
- return true;
- }
- return false;
- }
- }
上面的代码什么意思呢? 就是我们是否需要注入 carBean, 如果包含 user 这个对象, 就注入我们的 carBean, 不包含就不注入, 这里有个意思的事, Configuration 配置里类的注入是有顺序的, 我们必须把我们作为判断条件的 Bean 放在上面, 否则 Conditional 会识别你没有那个判断条件的 Bean.
6.@Import 引入方式注入 Bean
- package com.springIOC6.config;
- import com.springIOC6.bean.CarBean;
- import com.springIOC6.bean.UserBean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Import;
- @Configuration
- @Import({
- CarBean.class, UserBean.class
- })
- public class MainConfig {
- }
直接在注解内写入我们的要注入的类即可, 也可以使用接口的方式来实现, 我们来看换一下.
- package com.springIOC6b.config;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Import;
- @Configuration
- @Import({ImportSelector.class})
- public class MainConfig {
- }
- package com.springIOC6b.config;
- import org.springframework.core.type.AnnotationMetadata;
- public class ImportSelector implements org.springframework.context.annotation.ImportSelector {
- @Override
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- return new String[]{"com.springIOC6b.bean.CarBean","com.springIOC6b.bean.UserBean"};
- }
- }
实现 ImportSelector 类, 然后返回类名全路径即可. 自动装配就是基于 @Import 实现的.
实现 ImportBeanDefinitionRegistrar, 重写 registerBeanDefinitions 方法, 也是可以的.
- package com.springIOC6c.config;
- import com.springIOC6c.bean.CarBean;
- import org.springframework.beans.factory.support.BeanDefinitionRegistry;
- import org.springframework.beans.factory.support.RootBeanDefinition;
- import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
- import org.springframework.core.type.AnnotationMetadata;
- public class ImportSelectorRegister implements ImportBeanDefinitionRegistrar {
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(CarBean.class);
- registry.registerBeanDefinition("CarBean",rootBeanDefinition);
- }
- }
7. 通过 FactoryBean 注入
- package com.springIOC7.config;
- import com.springIOC7.bean.UserBean;
- import org.springframework.beans.factory.FactoryBean;
- public class IOCFactoryBean implements FactoryBean<UserBean> {
- @Override
- public UserBean getObject() throws Exception {// 指定对象
- return new UserBean();
- }
- @Override
- public Class<?> getObjectType() {// 指定类型
- return UserBean.class;
- }
- @Override
- public boolean isSingleton() {// 指定是否为单例
- return true;
- }
- }
- package com.springIOC7.config;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- @Configuration
- public class MainConfig {
- @Bean
- public IOCFactoryBean iocFactoryBean(){
- return new IOCFactoryBean();
- }
- }
- package com.springIOC7;
- import com.springIOC7.config.MainConfig;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- public class MainTest {
- /**
- * FactoryBean 注入
- * @param args
- */
- public static void main(String[] args) {
- AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
- Object carBean = aca.getBean("iocFactoryBean");// 取出 userBean
- System.out.println(carBean);
- Object iocFactoryBean = aca.getBean("&iocFactoryBean");// 取得 FactoryBean
- System.out.println(iocFactoryBean);
- }
- }
说到这所有往 IoC 容器中添加组件的方式就全部说完了, 简单总结一下:
@Bean 注入, 可以指定四种作用域, 单例, 多例 (生命周期不受 IoC 容器管理), 一次请求和一次会话, 也可以设置懒加载,
@ComponentScan 指定包扫描的方式来注入, 配合 @Controller,@Repository,@Service,@Component 注解来使用.
@Import 方式注入, 两种实现类 ImportSelector 和 ImportBeanDefinitionRegistrar 两种方式.
@FactoryBean, 工程 Bean 的方式也可以注入. 注意不带 & 是取得最终对象, 带 & 是取得真实 Bean. 三个方法, 一个指定对象, 一个指定类型, 一个指定是否为单例.
二, Bean 的生命周期 --- 初始化方法和销毁方法
针对单实例 bean 的话, 容器启动的时候, bean 的对象就创建了, 而且容器销毁的时候, 也会调用 Bean 的销毁方法.
针对多实例 bean 的话, 容器启动的时候, bean 是不会被创建的而是在获取 bean 的时候被创建, 而且 bean 的销毁不受 IoC 容器的管理.
1. 我们先来看个最简单的方法, 用 initMethod 和 destroyMethod 来指定我们的初始化方法和销毁方法
- package com.springlifeCycle1a.config;
- import com.springlifeCycle1a.bean.CarBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- @Configuration
- public class MainConfig {
- @Bean(initMethod = "init",destroyMethod = "destroy")
- public CarBean car() {
- return new CarBean();
- }
- }
我们在指定了我们的 init 初始方法, 销毁方法为 destroy 方法. 调用顺序是, Car 的构造方法, Car 的 init 方法, Car 的 destroy 方法, 也可以自己尝试使用 @Lazy 注解. 码云代码里有可以自己去尝试.
2. 通过 InitializingBean 和 DisposableBean 的两个接口实现 bean 的初始化以及销毁方法
- package com.springlifeCycle2a.bean;
- import org.springframework.beans.factory.DisposableBean;
- import org.springframework.beans.factory.InitializingBean;
- public class CarBean implements InitializingBean, DisposableBean {
- public CarBean() {
- System.out.println("我是 Car");
- }
- public void afterPropertiesSet() {
- System.out.println("我是初始化 init");
- }
- public void destroy() {
- System.out.println("我是销毁 destroy");
- }
- }
3. 通过 JSR250 规范 提供的注解 @PostConstruct 和 @ProDestory 标注的方法
- package com.springlifeCycle3.bean;
- import javax.annotation.PostConstruct;
- import javax.annotation.PreDestroy;
- public class CarBean {
- public CarBean() {
- System.out.println("我是 Car");
- }
- @PostConstruct
- public void init() {
- System.out.println("我是初始化 init--PostConstruct");
- }
- @PreDestroy
- public void destory() {
- System.out.println("我是销毁 destroy--PreDestroy");
- }
- }
4. 通过 Spring 的 BeanPostProcessor 的 bean 的后置处理器会拦截所有 bean 创建过程 (这个方法后面讲源码的时候会去讲内部实现, 自己觉得有必要看这个的源码)
- package com.springlifeCycle4.bean;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.config.BeanPostProcessor;
- import org.springframework.stereotype.Component;
- @Component
- public class LifeBeanPostProcessor implements BeanPostProcessor {
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("初始化方法" + beanName);
- return bean;
- }
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("销毁方法" + beanName);
- return bean;
- }
- }
这里也总结一下, 我们来指定容器内对象的初始化方法和销毁方法的方式一共有四种
1. 用 @Bean 的 initMethod 和 destroyMethod 来给予初始化方法和销毁方法.
2. 通过 InitializingBean 和 DisposableBean 的二个接口实现 bean 的初始化以及销毁方法.
3. 通过 JSR250 规范 提供的注解 @PostConstruct 和 @ProDestory 标注的方法.
4. 通过 Spring 的 BeanPostProcessor 的 bean 的后置处理器会拦截所有 bean 创建过程.
三, 给属性赋值
这里的东西不多, 我就尽快说一下啦, 赋值的方式有三种我们来看一下.
- package com.springValue.config;
- import com.springValue.bean.CarBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.PropertySource;
- @Configuration
- @PropertySource(value = "classpath:carBean.properties",encoding = "utf-8") // 指定外部文件的位置
- public class MainConfig {
- @Bean
- public CarBean carBean() {
- return new CarBean();
- }
- }
- import org.springframework.beans.factory.annotation.Value;
- public class CarBean {
- @Value("宝马")// 通过普通的方式
- private String name;
- @Value("#{5-2}")//spel 方式来赋值
- private int carNum;
- @Value("${carBean.realName}")// 通过读取外部配置文件的值
- private String realName;
- }// 自己记得加 get set 方法.
这里值得一提的就是导入文件最好设置一下 encoding = "utf-8", 不然汉字会乱码.
四, 自动装配:
主动装配平时是我们最熟悉的, 用的也是最多的, 我们来复习一下.
1.@Autowired 自动装配首先时按照类型进行装配, 若在 IoC 容器中发现了多个相同类型的组件, 那么就按照 属性名称来进行装配
- package com.springxAutowired1.config;
- import com.springxAutowired1.dao.AutowiredDao;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- @Configuration
- @ComponentScan(value = "com.springxAutowired1.service")
- public class MainConfig {
- @Bean
- public AutowiredDao autowiredDao1(){
- return new AutowiredDao(1);
- }
- @Bean
- public AutowiredDao autowiredDao2(){
- return new AutowiredDao(2);
- }
- }
- package com.springxAutowired1.service;
- import com.springxAutowired1.dao.AutowiredDao;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- @Service
- public class ServiceBean {
- @Autowired
- private AutowiredDao autowiredDao2;
- public ServiceBean() {
- System.out.println("我是 serviceBean");
- }
- @Override
- public String toString() {
- return "ServiceBean{" +
- "AutowiredDao=" + autowiredDao2 +
- '}';
- }
- }
在这里我们设置了两个 AutowiredDao 的对象, 一个标识为 1, 一个标识为 2,ServiceBean 默认是按照名字来装配的.
2. 我们也可以通过 @Qualifier 来指定装配名字.
- package com.springxAutowired1b.service;
- import com.springxAutowired1b.dao.AutowiredDao;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.stereotype.Service;
- @Service
- public class ServiceBean {
- @Autowired
- @Qualifier(value = "autowiredDao1")
- private AutowiredDao autowiredDao2;
- public ServiceBean() {
- System.out.println("我是 serviceBean");
- }
- @Override
- public String toString() {
- return "ServiceBean{" +
- "AutowiredDao=" + autowiredDao2 +
- '}';
- }
- }
3. 我们使用 Qualifier 是如果名字写错了, 可能装配错误会报错, 这时我们可以使用 required = false 来阻止异常的抛出
- package com.springxAutowired3.service;
- import com.springxAutowired3.dao.AutowiredDao;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.stereotype.Service;
- @Service
- public class ServiceBean {
- @Qualifier(value = "autowiredDaoError")
- @Autowired(required = false)
- private AutowiredDao autowiredDao2;
- public ServiceBean() {
- System.out.println("我是 serviceBean");
- }
- @Override
- public String toString() {
- return "ServiceBean{" +
- "AutowiredDao=" + autowiredDao2 +
- '}';
- }
- }
注意: 这里我并没有说 @Resource 注解, 这个注解其实不是 spring 里的, 是 JSR250 规范的, 但是不支持 @Primary 和 @Qualifier 的支持
五, 环境切换:
我们有时候需要通过不同的环境来切换我们的配置, 我们通过 @Profile 注解, 来根据环境来激活标识不同的 Bean,
@Profile 标识在类上, 那么只有当前环境匹配, 整个配置类才会生效
@Profile 标识在 Bean 上 , 那么只有当前环境的 Bean 才会被激活
- package com.springxAutowired4.config;
- import com.springxAutowired4.bean.Environment;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Profile;
- @Configuration
- public class MainConfig {
- @Bean
- @Profile(value = "test")
- public Environment environment_test() {
- return new Environment("test");
- }
- @Bean
- @Profile(value = "dev")
- public Environment environment_dev() {
- return new Environment("dev");
- }
- @Bean
- @Profile(value = "pro")
- public Environment environment_pro() {
- return new Environment("pro");
- }
- }
激活切换环境的方法
方法一: 通过运行时 jvm 参数来切换 -Dspring.profiles.active=test,dev,prod 多个参数表中间使用英文的逗号来分隔
方法二: 通过代码的方式来激活
- package com.springxAutowired4;
- import com.springxAutowired4.config.MainConfig;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- public class MainTest {
- /**
- * 环境切换
- * @param args
- */
- public static void main(String[] args) {
- AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext();
- aca.getEnvironment().setActiveProfiles("test","dev");// 方便演示, 我写了两个, 一般都是一个的.
- aca.register(MainConfig.class);
- aca.refresh();
- for (String beanDefinitionName : aca.getBeanDefinitionNames()) {
- System.out.println(beanDefinitionName);
- }
- }
- }
如果两个都写了, 按照代码中的来实现, 参数不再起作用
今天就说到这里, 大概就这么多吧. 后期我也会基于这篇博客来详细的扒一下源码, 中间会穿插一些 spring 的常见面试题.
代码地址: https://gitee.com/dwyui/springboke.git
来源: https://www.cnblogs.com/cxiaocai/p/11553020.html