Spring Bean的8种加载方式,90%的程序员都说不全

前言

以前学习Spring框架的时候,总结了几种Bean的加载方式,不过老师说还有其它的加载方式,以下八种并不是全部,但也足以用来做很多事情了。
注意以下是Spring中Bean的加载方式,不是SpringBoot,但其中的很多东西是相通的,尤其是Bean的注入方式、适用场景等,在本文中也有介绍的。

1.xml+<bean>

被配置的bean需要有无参数的构造函数

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- xml方式声明自己开发的bean --> <bean id="user" class="cn.sticki.blog.pojo.domain.User" /> <!-- xml方式声明第三方bean --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/> </beans> 

2.xml:context+注解(@Component+4个@Bean)

使用组件扫描,指定加载bean的位置,spring会自动扫描这个包下的文件。

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
    "> <!-- 组件扫描,指定加载bean的位置 --> <context:component-scan base-package="cn.sticki.bean,cn.sticki.config"/> </beans> 

然后在需要被加载的类名上添加@Component注解。也可以使用@Controller、@Service、@Repository定义bean。

@Service publice class UserServiceImpl implements UserService {
}

使用@Bean定义第三方bean,并将所在类定位为配置类或Bean

@Configuration // 或使用@Component public class DBConfig { @Bean public DruidDataSource dataSource(){
        DruidDataSource ds = new DruidDataSource(); return ds;
    } 
}

3.配置类+扫描+注解(@Component+4个@Bean)

使用 AnnotationConfigApplicationContext(SpringConfig.class); 来获取
ApplicationContext

public class AppObject { public static void main() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        SpringConfig config = ctx.getBean("Config", SpringConfig.class); // 两次获取的是同一个对象 config.user();
        config.user();
    }
}

和上面的第二种有点类似,就是包的扫描方式有所改变。

@ComponentScan({"cn.sticki.bean","cn.sticki.config"}) public class SpringConfig { @Bean public DogFactoryBean dog(){ return new DogFactoryBean();
    }
}

3.1FactoryBean接口

初始化实现FactoryBean接口的类,实现对bean加载到容器之前的批处理操作。

public class DogFactoryBean implements FactoryBean<Dog> { @Override public Dog getObject() throws Exception { return new Dog();
    } @Override public Class<?> getObjectType() { return Dog.class;
    }
}

在下面的bean中,显示的表示为配置DogFactoryBean ,但实际上配置的是 Dog 。

@Component public class SpringConfig { @Bean public DogFactoryBean dog(){ return new DogFactoryBean();
    }
}

3.2@ImportResource注解

用于加载配置类并加载配置文件(系统迁移)

@Configuration @ComponentScan("cn.sticki.bean") @ImportResource("applicationContext.xml") public class SpringConfig {
}

3.3proxyBeanMethods属性

使用 proxyBeanMethods = true 可以保障调用此类中的方法得到的对象是从容器中获取的,而不是重新创建的,但要求必须是通过此类调用方法获得的bean。

@Configuration(proxyBeanMethods = true) public class SpringConfig { @Bean public User user() {
        System.out.println("user init..."); return new User();
    }
}

4.@Import导入bean的类

使用@Import注解导入要注入的bean对应的字节码

@Import(User.class) public class SpringConfig {
}

而被导入的bean无需使用注解声明为bean

public class User{ }

这种形式可以有效的降低源代码与spring技术的耦合度(无侵入),在spring技术底层及诸多框架的整合中大量使用。
使用这种方法可以加在配置类,且也可以加在配置类当中的bean。

5.AnnotationConfigApplicationContext调用register方法

在容器初始化完毕后使用容器对象手动注入bean

public class App { public static void main() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        ctx.register(User.class); // 打印容器中当前的所有bean String[] names = ctx.getBeanDefinitionNames(); for (String name: names) {
            System.out.println(name);
        }
    }
}

必须在容器初始化之后才能使用这种方法。如果重复加载同一个bean,后面加载的会覆盖前面加载的。

6.@Import导入ImportSelector接口

导入实现了ImportSelector接口的类,实现对导入源的编程式处理

public class MyImportSelector implements ImportSelector { public String selectImports(AnnotationMetadata metadata) { // 使用metadata可以获取到导入该类的元类的大量属性,通过对这些属性进行判断,可以达到动态注入bean的效果 boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Import"); if(flag) { return new String[]{"cn.sticki.pojo.User"};
        } return new String[]{"cn.sticki.pojo.Dog"};
    }
}

调用处:

@Import(MyImportSelector.class) public class SpringConfig {
}

7.@Import导入ImportBeanDefinitionRegistrar接口

导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的绑定,例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public String registrarBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 使用元数据去做判定,然后再决定要注入哪些bean BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        registry.registerBeanDefinition("user",beanDefinition);
    }
}

调用处和上面第六种方式差不多。

8.@Import导入BeanDefinitionRegistryPostProcessor接口

导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的最终裁定。其在@Import中的加载顺序为最后一个加载,可以用来做bean覆盖的最终裁定。

public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 注意这里注册的是BookServiceImpl4 BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

调用处:

// 按照先后顺序加载,但 MyPostProcessor.class 最后才加载 @Import(BookServiceImpl1.class,MyPostProcessor.class, BookServiceImpl.class, MyImportSelector.class) public class SpringConfig {
}

后记

这八种加载方式几乎可以满足我们所需要的全部场景了,但一般场景下,我们用的其实也就只有那两三种,真正掌握这八种加载方式的朋友,肯定是大佬了

#Java##Spring#
全部评论
学会了!mark
点赞 回复 分享
发布于 2023-01-11 13:23 湖北

相关推荐

06-13 17:33
门头沟学院 Java
顺序不记了,大致顺序是这样的,有的相同知识点写分开了1.基本数据类型2.基本数据类型和包装类型的区别3.==和equals区别4.ArrayList与LinkedList区别5.hashmap底层原理,put操作时会发生什么6.说出几种树型数据结构7.B树和B+树区别8.jvm加载类机制9.线程池核心参数10.创建线程池的几种方式11.callable与runnable区别12.线程池怎么回收线程13.redis三剑客14.布隆过滤器原理,不要背八股,说说真正使用时遇到了问题没有(我说没有,不知道该怎么回答了)15.堆的内存结构16.自己在写项目时有没有遇见过oom,如何处理,不要背八股,根据真实经验,我说不会17.redis死锁怎么办,watchdog机制如何发现是否锁过期18.如何避免redis红锁19.一个表性别与年龄如何加索引20.自己的项目的QPS怎么测的,有没有真正遇到大数量表21.说一说泛型22.springboot自动装配原理23.springmvc与springboot区别24.aop使用过嘛?动态代理与静态代理区别25.spring循环依赖怎么解决26.你说用过es,es如何分片,怎么存的数据,1000万条数据怎么写入库中27.你说用limit,那么在数据量大之后,如何优化28.rabbitmq如何批次发送,批量读取,答了延迟队列和线程池,都不对29.计网知不知道smtp协议,不知道写了对不对,完全听懵了30.springcloud知道嘛?只是了解反问1.做什么的?短信服务,信息量能到千万级2.对我的建议,基础不错,但是不要只背八股,多去实际开发中理解。面试官人不错,虽然没露脸,但是中间会引导我回答问题,不会的也只是说对我要求没那么高。面完问我在济宁生活有没有困难,最快什么时候到,让人事给我聊薪资了。下午人事打电话,问我27届的会不会跑路,还在想办法如何使我不跑路,不想扣我薪资等。之后我再联系吧,还挺想去的😭,我真不跑路哥😢附一张河科大幽默大专图,科大就是大专罢了
查看30道真题和解析
点赞 评论 收藏
分享
评论
2
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务