一、将Bean注册到Spring容器的方式:
Spring容器组件添加五大法宝:
1. 包扫描+组件标注注解
2. 包扫描+@Bean
3. @Import / @ImportResource
4. 包扫描+FacotoryBean
5. 包扫描+ BeanFactoryPostProcessor
只有@Import是不需要包扫描(ComponentScan / @SpringBootApplication(scanBasePackages = {"com.example.demo1"})),可以直接引入其他模块或者第三方jar包的类,该类可以添加了component系列注解的类,或者是个普通的实体类也可以加载到容器中。
@ComponentScan 的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中。
如果需要注入容器的组件(类)不在@ComponentScan的扫描范围内(如:在其他的模块中, 或者引入的第三方jar包),则需要显示的把路径添加进来,如:
@SpringBootApplication(scanBasePackages = {"com.example.demo1", "com.guanyu.common"}))
以SpringBoot工程举例:
DemoApplication启动类在根目录,则该目录及其子目录内的所有通过@Conponent系列标注的类都可以被扫描到,注入到Spring容器中。
这时候,如果是和com.example.demo并列的包,或者更上一层的包内通过@Conponent系列标注的类无法被DemoApplication扫描到的,也就无法注入Spring容器中,这时候,需要在启动类中手动添加扫描路径:@SpringBootApplication(scanBasePackages = {“com.example.demo1”, “com.guanyu.common”})),即可将其他模块或者路径下的类扫描进容器中。
如果是第三方jar包中的类,也是无法直接扫描进容器的
解决方法1:如果第三方jar中的类已经添加了@Compoent系列注解,可以将路径添加扫描路径,使其可以被扫描到,也就可以进入Spring容器。
解决方法2:如果第三方jar中的类没有添加@Compoent系列注解,可以使用@Import进行注入,当然如果第三方jar中添加了@Component注解,也是可以使用@Import进行注入。
二、详细介绍
1、静态注册
方式1: xml配置方式
<bean id="xxxx" class="xxxx.xxxx"/>
方式2: 注解
(1)使用@Component系列
这几个注解都是同样的功能,被注解的类将会被Spring 容器创建单例对象。
@Component : 侧重于通用的Bean类
@Service:标识该类用于业务逻辑
@Controler:标识该类为Spring MVC的控制器类
@Repository: 标识该类是一个实体类,只有属性和Setter,Getter
(2)@Configuration系列
@Configuration + @Bean
@Configuration + @Import
@Configuration + @Import + ImportSelector
当我们设计一个 class,注册给 ioc 容器来管理对象的生命周期时,会使用 @Component, @Repository , @Controller , @Service 标识到。但是这样模式下,只能限于自己编写的类。如果依赖到一个第三方库的 class,也想交由 Ioc 机制来管理和使用,由于我们没有源码无法加入 @Component 等注解声明,就需要 @Bean 或 @Import 来把三方 class 注册 (导入) 给 Ioc 容器。
@Bean 和 @Import的区别是:@Bean用在方法上,@Import用在类上
【1】@Import
参数value接收一个Class数组,将你传入的类以全类名作为id加入IOC容器中
【2】ImportSelector
ImportSelector强调的是复用性,使用它需要创建一个类实现ImportSelector接口,实现方法的返回值是字符串数组,也就是需要注入容器中的组件的全类名。id同样也是全类名。
@Import示例
public class PiggUser {
private String name;
private Integer age;
public PiggUser() {
System.out.println("初始化了PiggUser()");
}
//省略get,set
}
public class PiggPet {
private String name;
private Integer age;
public PiggPet() {
System.out.println("初始化了PiggPet()");
}
//省略get,set
}
@Configuration
@Import({PiggUser.class, PiggPet.class})
public class PiggMainConfig {
public PiggMainConfig() {
System.out.println("初始化了PiggMainConfig()");
}
}
@Import + ImportSelector 示例
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"info.pigg.dict.entity.PiggPet","info.pigg.dict.entity.PiggUser"};
}
}
@Configuration
@Import(MyImportSelector.class)
public class PiggMainConfig {
public PiggMainConfig() {
System.out.println("初始化了PiggMainConfig()");
}
}
@Import + ImportSelector 示例
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(Person.class.getName());
MutablePropertyValues values = beanDefinition.getPropertyValues();
values.addPropertyValue("age", 10);
values.addPropertyValue("name", "ZhangSan");
//这里注册bean
registry.registerBeanDefinition("person666", beanDefinition );
}
}
@Configuration
@Import(MyImportSelector.class)
public class PiggMainConfig {
public PiggMainConfig() {
System.out.println("初始化了PiggMainConfig()");
}
}
方式3:@ImportResource + < bean id=“”>
这种方法实际上是通过读取xml文件来配置bean,首先我们定义一个bean.xml,并在其中定义bean:
然后我们创建一个配置类,类上标注@Configuration告诉spring这是个注解类,再用@ImportResource注解类,并告诉spring我们需要读取的xml文件位置:
执行结果:
2、条件注册bean
使用Condition,实现步骤:
(1)定义一个接口
package com.example.demo.service;
public interface UserService{
void register();
}
(2)定义一个实现类
public class UserServiceImpl implements UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Override
public void register(){
UserDTO userDTO = new UserDTO(this);
userDTO.setAge(10);
userDTO.setUserId(1001);
System.out.println("register user");
eventPublisher.publishEvent(userDTO);
}
}
(3)定义一个MyCondition 实现 Condition
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 项目名称:
* 文件名称:
* 功能描述:
* 创建时间: 2022/8/19
* @author Top
* @version v1.0
*/
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//实现具体的逻辑判断,然后返回true 或者false,这里也就是条件注册bean的关键点
return true;
}
}
(4)定义配置类
import com.example.demo.service.impl.User2ServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* 项目名称:
* 文件名称:
* 功能描述:
* 创建时间: 2022/8/19
* @author Top
* @version v1.0
*/
@Configuration
public class MyConfig {
@Bean
@Conditional(MyCondition.class)
public User2ServiceImpl user2Service(){
return new User2ServiceImpl();
}
}
@Conditional的扩展注解
@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。
@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
@ConditionalOnMissingClass:某个class类路径上不存在的时,才会实例化一个Bean。
@ConditionalOnNotWebApplication:不是web应用时,才会实例化一个Bean。
@ConditionalOnBean:当容器中有指定Bean的条件下进行实例化。
@ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。
@ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。
@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnExpression:基于SpEL表达式的条件判断。
@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。
3、动态注册bean
方法1: 使用FactoryBean
- FactoryBean也叫做Bean工厂,是用于生产Bean对象的类
FactoryBean通常是用来创建比较复杂的Bean,但是在ioc容器启动的时候不会创建实例,只有在使用的时候才会创建对象
(1)定义
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class MyFactoryBean implements FactoryBean {
@Override
public Person getObject() throws Exception {
Person person = new Person();
person.setAge(20);
person.setName("guoguo");
return person;
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
}
(2)调用
@Autowired
private Person person;
@GetMapping("/myPage")
@ResponseBody
public Map<Object, Object> expenseStatement(Object str) throws IOException {
System.out.println(person);
return null;
}
方法2: 使用BeanDefinitionRegistryPostProcessor
(1)定义接口
public interface UserService{
void register();
}
(2)定义实现类
public class UserServiceImpl implements UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Override
public void register(){
UserDTO userDTO = new UserDTO(this);
userDTO.setAge(18);
userDTO.setName("精灵王jinglingwang.cn");
userDTO.setUserId(1001);
System.out.println("register user");
eventPublisher.publishEvent(userDTO);
}
}
(3)定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CoreAnnotation {
String[] value() default {};
}
(4)动态注册bean
package com.example.demo.common;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@Component
public class DefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, EnvironmentAware {
private Environment environment;
private ResourceLoader resourceLoader;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//用扫描器根据指定注解进行扫描获取BeanDefinition
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, false, environment, resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(CoreAnnotation.class));
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents("com");
try {
registerCandidateComponents(beanDefinitionRegistry, candidateComponents);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* 注册 BeanDefinition
*/
private void registerCandidateComponents(BeanDefinitionRegistry registry, Set<BeanDefinition> candidateComponents) throws ClassNotFoundException {
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
Map<String, Object> customImportAnnotationAttributesMap = annotationMetadata.getAnnotationAttributes(CoreAnnotation.class.getName());
AnnotationAttributes customImportAnnotationAttributes = Optional.ofNullable(AnnotationAttributes.fromMap(customImportAnnotationAttributesMap)).orElseGet(AnnotationAttributes::new);
String[] values = customImportAnnotationAttributes.getStringArray("value");
String className = annotationMetadata.getClassName();
Class<?> clazzName = Class.forName(className);
// AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(CustomImportFactoryBean.class)
// .addPropertyValue("type", clazzName)
// .addPropertyValue("beanName", beanName)
// .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
// .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
// .getBeanDefinition();
// registry.registerBeanDefinition(beanName, beanDefinition);
Arrays.asList(values).forEach(m ->{
RootBeanDefinition mbean = null;
try {
mbean = new RootBeanDefinition(clazzName);
} catch (Exception e) {
e.printStackTrace();
}
registry.registerBeanDefinition(m, mbean);
});
}
}
}
}
方法3: 使用ImportBeanDefinitionRegistrar
(1)定义接口
package com.example.demo.service;
public interface UserService{
void register();
}
(2)定义实现类
public class UserServiceImpl implements UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Override
public void register(){
UserDTO userDTO = new UserDTO(this);
userDTO.setAge(10);
userDTO.setUserId(1001);
System.out.println("register user");
eventPublisher.publishEvent(userDTO);
}
}
(3)定义import
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableCustomImport {
String[] packages() default {};
}
(4)动态注册bean
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private Environment environment;
private ResourceLoader resourceLoader;
@SneakyThrows
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean enableCustomImport = importingClassMetadata.hasAnnotation(EnableCustomImport.class.getName());
//@Import不是在这个EnableCustomImport注解上的不执行
if (!enableCustomImport) {
return;
}
Map<String, Object> annotationAttributesMap = importingClassMetadata.getAnnotationAttributes(EnableCustomImport.class.getName());
AnnotationAttributes annotationAttributes = Optional.ofNullable(AnnotationAttributes.fromMap(annotationAttributesMap)).orElseGet(AnnotationAttributes::new);
// 获取需要扫描的包
String[] packages = retrievePackagesName(importingClassMetadata, annotationAttributes);
// useDefaultFilters = false,即第二个参数 表示不扫描 @Component、@ManagedBean、@Named 注解标注的类
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false, environment, resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(CoreAnnotation.class));
// 扫描包
for (String needScanPackage : packages) {
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(needScanPackage);
try {
registerCandidateComponents(registry, candidateComponents);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
* 获取需要扫描的包
*/
private String[] retrievePackagesName(AnnotationMetadata annotationMetadata, AnnotationAttributes annotationAttributes) {
String[] packages = annotationAttributes.getStringArray("packages");
if (packages.length > 0) {
return packages;
}
//如果不存在,则默认第一个包开始
String className = annotationMetadata.getClassName();
return new String[]{className.split("\\.")[0]};
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
三、使用场景
(1)最常见的注册方式
(2)最灵活的注册方式
(3)更加自定义注册方式