Spring注册Bean的几种方式

通过XML配置注册Bean

spring-config.xml

    <!--方式一:声明自定义的bean,没有设置id,id默认为全类型#编号-->
    <bean id="cat" class="com.rzg.entity.Cat"/>
    <bean  class="com.rzg.entity.Cat"/>
public class SpringApp {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}

使用@Component注解创建bean

用@Component注解创建beanm,前提是开启包扫描,开启包扫描有两种方式:xml注解开启 配置类开启

xml注解开启

spring-config.xml

<context:component-scan base-package="com.rzg"/>

Dog.java

@Component
public class Dog {
    private String dog;
    
}

启动类

public class SpringApp {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}

Spring配置类开启

SpringConfig.java 配置类

@ComponentScan("com.rzg")//开启包扫描
public class SpringConfig2 {
}

Dog.java

@Component
public class Dog {
    private String dog;
    
}

启动类

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}

AnnotationConfigApplicationContext(SpringConfig2.class);方式启动的IOC容器,SpringConfig2可以不添加@Configuration注解和@Component注解,Spring会自动吧SpringConfig2加载到IOC容器中

@Component修饰的类或者类似上面SpringConfig配置的类,都可以在类中使用@Bean配置一个新的Bean,但是在这个新创建的Bean所在类中,无法再使用@Bean。
这里的@Component可以使xml注解开启的包扫描,也可以是XML配置开启的包扫描,用Spring配置类来开启包扫描,举例如下:

使用配置类的方式创建IOC容器

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}

在SpringConfig2中,使用@Bean创建了一个Bean。毫无疑问,配置类中使用@Bean可以帮我们创建一个bean。

@ComponentScan("com.rzg")
public class SpringConfig2 {
    @Bean
    public Rabbit rabbit(){
        return new Rabbit();
    }

}

由于这个配置类开启了ComponentScan,下面我们使用@Component创建一个Dog Bean

@Component
public class Dog {
    private String dog;
    @Bean
    public Cat cat(){
        return new Cat();
    }
}

在这个@Component创建的Bean的所在类中,仍然可以使用@Bean创建新的Bean。那么在这个Cat Bean所在类中,是否还能用@Bean创建bean呢?答案是不能

补充

FactoryBean

spring提供了一个接口FactoryBean,也可以用于声明bean,只不过实现了FactoryBean接口的类造出来的对象不是当前类的对象,而是FactoryBean接口泛型指定类型的对象。如下列,造出来的bean并不是DogFactoryBean,而是Dog。有什么用呢?可以在对象初始化前做一些事情,下例中的注释位置就是让你自己去扩展要做的其他事情的。

启动类

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println(context.getBean("dogFactory").getClass().getName());

    }
}
输出结果:
springConfig2
dogFactory
com.rzg.entity.Dog

配置类 定义了一个DogFactory

public class SpringConfig2 {
    @Bean
    public DogFactory dogFactory(){
        return new DogFactory();
    }
}

DogFactory.java

public class DogFactory implements FactoryBean<Dog> {

    @Override
    public Dog getObject() throws Exception {
        Dog dog = new Dog();
        dog.setName("小黑");
        /*  其他的操作 */
        return dog;
    }

    @Override
    public Class<?> getObjectType() {
        return Dog.class;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

FactoryBean也是一个bean,只不过它用于生产其他的bean。需要注意的是,IOC容器中并没有这个工厂bean,只有其生产的Bean

@ImportResource

​ 原有的xml配置的Spring项目怎么转换成Java配置类转换的项目。使用@ImportResource放在配置类上,传入XML的文件路径即可
配置类

@ImportResource("spring-config.xml")
public class SpringConfig2 {

}

启动类

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}

@Configuration

前面的例子中用到了@Configuration这个注解,当我们使用AnnotationConfigApplicationContext加载配置类的时候,配置类可以不添加这个注解。但是这个注解有一个更加强大的功能,它可以保障配置类中使用方法创建的bean的唯一性。为@Configuration注解设置proxyBeanMethods属性值为true即可,由于此属性默认值为true,所以很少看见明确书写的,除非想放弃此功能。

看下面的例子

public class Cat {
}

@Configuration(proxyBeanMethods =  true)
public class SpringConfig2 {
    @Bean
    public Cat cat(){
        return new Cat();
    }
}
public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);
        SpringConfig2 bean = context.getBean(SpringConfig2.class);
        System.out.println(bean.cat());
        System.out.println(bean.cat());
        System.out.println(bean.cat());
    }
}
输出结果:
com.rzg.entity.Cat@7fe8ea47
com.rzg.entity.Cat@7fe8ea47
com.rzg.entity.Cat@7fe8ea47

输出结果为什么会不一样呢。

如果配置类是一个普通类SpringConfig2 ,那么每调用一次cat方法,会产生一个新的cat对象。但是SpringConfig2作为普通类,并将proxyBeanMethods 设置为true(这也是默认值),那么获取SpringConfig2的Bean后调用cat方法,会由Spring代理,每次调用都会返回同一个IOC容器中的bean。如果proxyBeanMethods设置为false,则不使用代理,每次调用cat方法会像普通方法一样,返回一个全新的cat对象。

@Import注入Bean

在配置类上使用@Import可以精确导入指定的Bean,只需要给@Import传入指定的类对象数组,同样也可以导入工厂类,只不过这个工厂生产出来的Bean Name为工厂类的全类名。

public class Cat {
}
public class DogFactory implements FactoryBean<Dog> {

    @Override
    public Dog getObject() throws Exception {
        Dog dog = new Dog();
        dog.setName("小黑");
        /*  其他的操作 */
        return dog;
    }

    @Override
    public Class<?> getObjectType() {
        return Dog.class;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}


@Import({Cat.class,Cat.class,DogFactory.class})
public class SpringConfig2 {

}

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");
        Object bean = context.getBean("com.rzg.entity.DogFactory");
        System.out.println(bean.getClass().getName());


    }
}

输出结果:
springConfig2
com.rzg.entity.Cat
com.rzg.entity.DogFactory
----------------
com.rzg.entity.Dog

如果生产多个相同类型的Bean,后面的Bean会把前面的Bean覆盖掉

还需要注意 @Import导入的类可以作为一个配置类。看下面例子

我们知道在通过配置类启动的Spring程序,可以在配置类上省略@Configuration注解,Spring仍然可以作为一个配置类使用。比如在配置类上使用@ComponentScan标签。

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");

    }
}

@ComponentScan("com.rzg.entity")
public class SpringConfig2 {

    @Bean Cat cat(){
        return new Cat();
    }
}

使用@Component方式加载的bean和@Import加载的bean仍然可以作为配置类,像下面这样

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");

    }
}

@ComponentScan("com.rzg.entity") //会将Rabbit 扫描到,但是不会扫描到SpringConfig
public class SpringConfig2 {

}

@Component//使用@Component方式导入的bean,仍然可以用作配置类
@ComponentScan("com.rzg.config")//会扫描到SpringConfig
public class Rabbit {

}

@Component
public class SpringConfig {


}
输出结果:
springConfig2
rabbit
springConfig

@Import方式

	public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");

    }
}

@Import({Rabbit.class})
public class SpringConfig2 {

}

@Component
@ComponentScan("com.rzg.config")
public class Rabbit {

}
@Component
public class SpringConfig {
}

输出结果:
springConfig2
springConfig
com.rzg.entity.Rabbit


暂且将@Bean方式产生的bean作为普通bean,@Component和@Import方式产生的Bean作为配置Bean。因为@Component和@Import方式产生的bean可以放@ComponentScan(“”)注解,@Bean方式产生的Bean,放@ComponentScan没有作用。
那么@Import导入的类可以作为配置类的话,在这个类上是否可以继续用@Import导入其他Bean,答案是当然可以。

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");

    }
}

public class SpringConfig2 {
    @Bean
    public  Rabbit rabbit(){
        return new Rabbit();
    }
}

@Component
@ComponentScan("com.rzg.config")
public class Rabbit {

}

@Component
public class SpringConfig {
//在com.rzg.config包下,但是不会产生类,因为给@Bean普通类添加@ComponentScan没作用
}

输出结果:
springConfig2
rabbit

编程方式加载Bean

在ioc容器产生之后,可以动态的添加bean

public class SpringConfig2 {

}
public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");

        context.register(Rabbit.class);

        names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

    }
}
主要输出结果:
springConfig2
----------------
springConfig2
rabbit

ImportSelector

实现了ImportSelector的类,作为一个ImportSelector,可以一次性导入多个Bean。实现这个接口中的selectImports方法,返回String数组,数组中放置要导入的全类名。ImportSelector和工厂类似
,但是注意实现ImportSelector接口的类,只能通过@Import注解来导入。@Import应该是和ImportSelector配合使用的,@Import导入/ImportSelector导入时选择嘛。

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");


    }


@Import(MyImportSelector.class)
public class SpringConfig2 {

}


public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    //importingClassMetadata存放了一些配置类SpringConfig2的一些信息,包括是否有某些注解
        return new String[]{"com.rzg.entity.Cat","com.rzg.entity.Dog"};
    }

    @Override
    public Predicate<String> getExclusionFilter() {
        return ImportSelector.super.getExclusionFilter();
    }
}

主要输出结果:
springConfig2
com.rzg.entity.Cat
com.rzg.entity.Dog
----------------

ImportBeanDefinitionRegistrar接口

bean的加载不是一个简简单单的对象,spring中定义了一个叫做BeanDefinition的东西,它才是控制bean初始化加载的核心。BeanDefinition接口中给出了若干种方法,可以控制bean的相关属性。说个最简单的,创建的对象是单例还是非单例,在BeanDefinition中定义了scope属性就可以控制这个。如果你感觉方式六没有给你开放出足够的对bean的控制操作,那么方式七你值得拥有。我们可以通过定义一个类,然后实现ImportBeanDefinitionRegistrar接口的方式定义bean,并且还可以让你对bean的初始化进行更加细粒度的控制,不过对于新手并不是很友好。忽然给你开放了若干个操作,还真不知道如何下手。

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();

        registry.registerBeanDefinition("cat",beanDefinition);
    }
}

@Import(MyRegistrar.class)
public class SpringConfig2 {

}

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");

    }
}
主要运行结果:
springConfig2
cat
----------------

BeanDefinitionRegistryPostProcessor

如果在一个项目开发中,很多开发者在自己模块定义一些Bean,那么造成一些冲突怎么办,Bean的定义有前后顺序的话,岂不是很难控制。BeanDefinitionRegistryPostProcessor就是帮我们在一些bean定义之后再去定义一些Bean,他的定义是在以上几种Bean定义方式之后。

public class SpringApp02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig2.class);

        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------");


    }
}

@Import(MyPostProcessor.class)
public class SpringConfig2 {

}


public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        BeanDefinition beanDefinition =
                BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("cat",beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}


springConfig2
com.rzg.entity.MyPostProcessor
cat
----------------
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值