【Spring基础之IOC】Spring IOC创建bean的几种方式

【Spring基础之IOC】Spring IOC创建bean的几种方式

一. 简介:

Spring是一个轻量开源级别的框架。
Spring的出现是为了解决企业级业务逻辑层面和其它层面之间的耦合。
Spring是一个基于IOC和AOP的框架,这两者是spring中最核心的功能。
			AOP: 面向切面编程,对代码层面进行增强,如spring事务
			IOC:  控制反转,避免过于复杂的对象创建,基于依赖倒置原则引入的一种设计思想。
			DI:  依赖注入,IOC的具体实现。
			容器: 负责集中化管理bean。

二.注册/创建bean的几种方式。

准备工作:
1.创建新的maven项目:在这里插入图片描述
在这里插入图片描述
2.引入相关的maven依赖

   <!-- 引入spring相关的依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <!-- junit测试相关的依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

由于spring-context内部已经依赖了spring-beans,spring-core, spring-expression, spring-jcl 等相关jar包,所以只需要暂时引入spring-context包即可。

(一).基于纯xml创建bean的方式。

使用 XML 配置,可以让 bean 定义分布在多个 XML 文件上,这种方法直观优雅清晰明显. 通常,每个单独的 XML 配置文件代表架构中的一个逻辑层或模块. 这种配置方式在spring2.5之前非常常用。

实体类与测试类:

public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


public class Test {

    @org.junit.Test
    public void test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-ioc.xml");
        Student bean = context.getBean(Student.class);
        System.out.println(bean);
    }
}


xml配置:

<?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">
    <bean class="spring.ioc.Student" id="student"></bean>
</beans>

输出结果:
在这里插入图片描述

(二).基于xml的componentScan加上注解的方式

Spring 2.5 支持基于注解的元数据配置,通过配置componentScan来扫描标注了@Controller @Service @Repository @Component的类,将这些类注册成bean组件。

 @Controller
//@Service
//@Repository
//@Component
public class StudentController {
    @Value("李雷")
    private String name;

    public void say(){
        System.out.println("My name is " + name);
    }
   }

public class Test {
    @org.junit.Test
    public void test02(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-ioc.xml");
        StudentController bean = context.getBean(StudentController.class);
        bean.say();
    }
}

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="spring.ioc"></context:component-scan>
</beans>

测试:
在这里插入图片描述

(三).基于javaConfig(@Configuration)+ @Bean的方式注册bean

从 Spring 3.0 开始, 由 Spring JavaConfig 项目提供的功能已经成为 Spring 核心框架的一部分. 因此,你可以使用 Java 配置来代替 XML 配置定义外部 bean . 要使用这些新功能,请参阅 @Configuration, @Bean, @Import, 和 @DependsOn 注解.

Spring 的 Bean (至少一个) 由容器来管理,基于 XML 的元数据配置将这些 bean 配置为 元素.并放置于 元素内部. 基于 Java 的配置通常是使用 @Configuration 注解过的类中,在它的方法上使用 @Bean 注解.

这些 bean 定义会对应到构成应用程序的实际对象. 通常你会定义服务层对象,数据访问对象(DAOs) ,表示对象(如Struts Action 的实例),基础对象(如 Hibernate 的 SessionFactories, JMS Queues,) . 通常,不会在容器中配置细粒度的 domain 对象,因为创建和加载 domain 对象通常是 DAO 和业务逻辑的职责. 但是,你可以使用 Spring 与 AspectJ 集成独立于 IoC 容器来创建的对象,请参阅 Spring 使用 AspectJ 进行依赖注入 domain 对象.

通常在项目中使用到第三方的bean时,常常会使用这种方式,这种方式可以人工干预创建bean的过程,给开发带来了一定的便利性。

@Configuration
public class JavaConfigApp {
    @Bean
    public Student student(){
        return new Student();
    }
}


public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


测试:


public class Test {
    @org.junit.Test
    public void test03(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        Student bean = context.getBean(Student.class);
        System.out.println(bean);
    }

}

结果:
在这里插入图片描述

(四). 通过实现FactoryBean 来注册bean。

FactoryBean区别于BeanFacotory,它也是一个bean,通过getObject方法来手动创建真实的bean。实现BeanFactory接口需要实现三个方法。
getObject(): 自定义创建bean的方法。
getObjectType(): 注册返回bean的类型。
getSingleton(): 是否为单例bean。

public class Teacher {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
     @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}


@Component
public class TeacherBeanFactory implements FactoryBean<Teacher> {

    public Teacher getObject() throws Exception {
        Teacher t = new Teacher();
        t.setName("小明");
        return t;
    }

    public Class<?> getObjectType() {
        return Teacher.class;
    }

    public boolean isSingleton() {
        return true;
    }
}


@Configuration
@ComponentScan(basePackages = "spring.ioc")
public class JavaConfigApp {
}

测试:

 @org.junit.Test
    public void test05(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        Teacher bean = (Teacher) context.getBean("teacherBeanFactory");
        TeacherBeanFactory bean1 = (TeacherBeanFactory) context.getBean("&teacherBeanFactory");
        System.out.println(bean);
        System.out.println(bean1);
    }

结果:
在这里插入图片描述

重点注意: 如果根据注解标注类名首字母小写来获取bean,context.getBean(“teacherBeanFactory”),这样获取到的是getObject创建的bean,如果需要获取原型bean,那么需要通过&teacherBeanFactory
方式,context.getBean(“&teacherBeanFactory”)。

(五).通过JavaConfig的(@Configuration) + @ComponentScan + 注解的方式注册bean

@Configuration
@ComponentScan(basePackages = "spring.ioc")
public class JavaConfigApp {

}

@Controller
//@Service
//@Repository
//@Component
public class StudentController {
    @Value("李雷")
    private String name;

    public void say(){
        System.out.println("My name is " + name);
    }
    }

测试:

    @org.junit.Test
    public void test04(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        StudentController bean = context.getBean(StudentController.class);
        System.out.println(bean);
    }

结果:
在这里插入图片描述

(六).通过@Configuration + @Import注解的方式创建bean

可以在@Configuration配置类中使用 @Import 注解直接导入需要注册成bean的类。这种方式不能人工干预bean的创建, 并且这种方式注册的bean名字并不是类名首字母小写,所以获取bean的时候需要使用类的全限定名或者根据类型获取 , 在需要注册成bean的类上不需要标注任何注解

@Configuration
@Import(Teacher.class)
public class JavaConfigApp {
}


public class Teacher {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}

测试:

public class Test {
//基于@Import + 需要注册的组件
    @org.junit.Test
    public void test06(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        Teacher bean = context.getBean(Teacher.class);
        System.out.println(bean);


         bean = (Teacher) context.getBean("spring.ioc.Teacher");
        System.out.println(bean);
    }
 }

结果:
在这里插入图片描述

(七).通过@Configuration配置类中的@Import + ImportSelector方式注册多个bean实例。

这种方式比较起上一种方式来说更加方便,可以同时注册多个bean的实例,同样的不需要在@Import引入的组件类中加任何注解,同时获取bean的时候,需要根据类的全限定名或者类型获取,不能直接使用类的首字母小写进行获取。

@Configuration
@Import(TeacherImportSelector.class)
public class JavaConfigApp {
}



public class TeacherImportSelector  implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"spring.ioc.Teacher", Student.class.getName()};
    }
}

测试:

 //基于@Import + ImportSelector注册多个bean实例
    @org.junit.Test
    public void test07(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        //根据类型获取
        Teacher teacher = context.getBean(Teacher.class);
        System.out.println(teacher);
        Student student =  context.getBean(Student.class);
        System.out.println(student);

        Teacher teacher1 = (Teacher) context.getBean("spring.ioc.Teacher");
        System.out.println(teacher1);
        Student student1 = (Student) context.getBean("spring.ioc.Student");
        System.out.println(student1);


        Teacher teacher2 = context.getBean("teacher", Teacher.class);
        System.out.println(teacher2);
        Student student2 =  context.getBean("student", Student.class);
        System.out.println(student2);
    }

结果:
在这里插入图片描述
根据类名首字母小写是无法获取bean的。

(八). 通过@Configuration + @Import + ImportBeanDefinitionRegistrar来注册bean

这种方式需要自己实现ImportBeanDefinitionRegistrar类,并复写registerBeanDefinitions()方法,相比前两种更加的灵活,可以在生产bean之前对bean的一些属性做一系列的修改,比如作用域,bean的名字,自动注入的模式等 , 最后通过registry.registerBeanDefinition()将bean定义放到beanDefinitionMap中统一管理。


@Configuration
@Import({TeacherBeanDefinetionRegistry.class})
public class JavaConfigApp {
}




public class TeacherBeanDefinetionRegistry implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        String name = Teacher.class.getName();
        beanDefinition.setBeanClass(Teacher.class);
        beanDefinition.setBeanClassName(name);
        beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        registry.registerBeanDefinition(name, beanDefinition);

        RootBeanDefinition bd = new RootBeanDefinition(Student.class);
        bd.setScope(BeanDefinition.SCOPE_SINGLETON);
        registry.registerBeanDefinition("student", bd);
    }
}



public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}




public class Teacher {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}

测试:

  //基于@Import + ImportBeanDefinitionRegistrar来手动注册
    @org.junit.Test
    public void test08(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        //根据名字获取
        Teacher teacher = context.getBean(Teacher.class.getName(), Teacher.class);
        System.out.println(teacher);
        Student student =  context.getBean("student" ,Student.class);
        System.out.println(student);

        //根据类型获取
        Teacher teacher1 = context.getBean(Teacher.class);
        System.out.println(teacher1);
        Student student1 =  context.getBean(Student.class);
        System.out.println(teacher1);
    }

结果:
在这里插入图片描述

(九). 通过@ComponentScan + BeanDefinitionRegistryPostProcessor来注册bean

实现了该结构就有注册和获取Bean定义的能力,初始化ioc容器的过程中,会多次调用实现了BeanDefinitionRegistryPostProcessor
接口的postProcessBeanDefinitionRegistry()方法注册到容器之中。
所以在这里只需要将BeanDefinitionRegistryPostProcessor想办法注
册成bean即可。

1.可以使用@Import 直接将其注册成bean
2.可以使用@ComponentScan + 注解的方式将其注册成bean

@Configuration
@ComponentScan(basePackages = "spring.ioc")
//@Import(TeacherBeanDefinitionRegistryPostProcessor.class)
public class JavaConfigApp {
}



public class Teacher {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}



@Component
public class TeacherBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        beanDefinitionRegistry.registerBeanDefinition("teacher", new RootBeanDefinition(Teacher.class));
    }

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

    }
}

测试:

 @org.junit.Test
    public void test09(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        System.out.println(context.getBean("teacher", Teacher.class));
    }

结果:

在这里插入图片描述

(十). 通过@ComponentScan + BeanFactoryPostProcessor来完成bean的注册。

通过结构类型关系图可以看出,BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor有注册bean的功能,然而BeanFactoryPostProcessor拥有修改bean的功能。
1、 BeanDefinitionRegistryPostProcessor:侧重于bean的注册。
2、 BeanFactoryPostProcessor:侧重于对已经注册的bean的属性进行修改,但是可以通过将方法内部的ConfigurableListableBeanFactory向下转型,实现BeanDefinitionRegistry有注册功能的DefaultListableBeanFactory

@Component
public class TeacherBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
      DefaultListableBeanFactory factory = (DefaultListableBeanFactory) configurableListableBeanFactory;
      factory.registerBeanDefinition("teacher", new RootBeanDefinition(Teacher.class));
    }
}


@Configuration
@ComponentScan(basePackages = "spring.ioc")
public class JavaConfigApp {
}


public class Teacher {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}



测试:

public class Test {
@org.junit.Test
    public void test09(){
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfigApp.class);
        System.out.println(context.getBean("teacher", Teacher.class));
    }
}

结果:
在这里插入图片描述

总的来说,创建bean的方式有很多种,对于实际应用往往使用比较多的就是javaConfig方式的@Import导入组件,其它的方式并不常用,可以在适当的时候择优选择。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值