spring去XML配置,使用JavaConfig实现IOC

 2023-02-09
原文作者:Jony_zhang 原文地址:https://juejin.cn/post/7074754056358461447

一、概述

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

二、准备

首先将之前文章准备好的基于Maven搭建的Spring项目已完成。

三、基于Java的容器配置

3-1、@Bean和@Configuration的基本概念

在之前,我们将Bean注入到IOC容器中,主要使用spring.xml文件,一种是直接将bean注入直接写在xml文件中,另外一种是在xml中配置context:component-scan进行包的扫描,然后在需要注入的bean上面加上@Controller @Service @Repsoitory @Component这四个注解来注入IOC容器中,需要注意的是IOC对于这四个注解在注入的时候是不进行区分的,只是我们在写代码的时候根据习惯以及方便代码管理进行分成注解。

javaconfig可以抛弃之前基于xml进行配置注入IOC的方式。

3-2、@Configuration 标记配置类

通过给Bean设置@Configuration就可以标记为一个配置类,相当于之前spring的xml,通过这个配置就可以将之前的sping.xml文件删掉了。

3-3、@ComponentScan 配置扫描包路径

通过给Bean添加@ComponentScan(basePackages="com.jony"),basePackages="com.jony"就是指定扫描包的路径,相当于我们之前使用的context:component-scan

3-4、@PropertySource 加载外部资源文件

通过@PropertySrouce可以加载我们在resource中的资源文件,相当于我们之前使用的context:property-placeholder

    package com.jony;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    
    //@Configuration用户标记一个配置类,之前根据xml启动spring上下文
    //相当于 xml文件 <bean></bean>
    @Configuration
    @ComponentScan(basePackages = "com.jony")//可以配置包的扫描路径,相当于之前xml里面的<context:component-scan>
    @PropertySource("user.properties") //可以配置加载外部资源文件,相当于之前的<context:property-placeholder
    public class IocJavaConfig {
    }

3-4、加载spring容器

之前我们在使用IOC容器的时候,通过使用如下来进行加载spring的配置

    ClassPathXmlApplicationContext ioc=new ClassPathXmlApplicationContext("spring_ioc.xml");

再回之前提到加载spring容器一共有以下几种方式

    // 加载spring容器 
    //ApplicationContext spring的顶层核心接口
    ClassPathXmlApplicationContext 根据项目路径的xml配置来实例化spring容器 // 
    FileSystemXmlApplicationContext 根据磁盘路径的xml配置来实例化spring容器 // 
    AnnotationConfigApplicationContext 根据javaconfig 来配置实例化spring容器 // 
    在容器实例化的时候 就会加载所有的bean 
    ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-ioc.xml");

3-4-1、使用AnnotationConfigApplicationContext加载spring容器

通过使用AnnotationConfigApplicationContext加载之前我们创建的IocJavaConfig来进行容器的加载

    import com.jony.IocJavaConfig;
    import com.jony.controller.UserController;
    import org.junit.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class TestIocJavaConfig {
    
        @Test
        public void test01(){
            AnnotationConfigApplicationContext ioc=new AnnotationConfigApplicationContext(IocJavaConfig.class);
            UserController userController=ioc.getBean("userController",UserController.class);
            userController.getBean();
        }
    
    }

3-4-2、测试结果

通过下图,我们可以发现,在没有spring.xml的情况下,使用JavaConfig的方式使用
@Configuration,设置一个配置类
@CompentScan配置包的扫描路径
@PropertySource设置加载外部资源文件
AnnotationConfigApplicationContext来进行容器的初始化加载

202301012036005081.png

四、基于@Bean设置第三方Bean

@Bean是一个方法级别的注解,它与XML中的 元素类似。注解支持 提供的一些属性,例如 * init-method * destroy-method * autowiring * name开发者可以在@Configuration类或@Component类中使用@Bean注解。

在此处我们可以看到@Bean可以在@Configuration或者@Component中使用,实际上@Configuration也是基于@Component实现的。

4-1、使用@Bean注入外部类(数据库连接池)

    package com.jony;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    
    //@Configuration用户标记一个配置类,之前根据xml启动spring上下文
    //相当于 xml文件 <bean></bean>
    @Configuration
    @ComponentScan(basePackages = "com.jony")//可以配置包的扫描路径,相当于之前xml里面的<context:component-scan>
    @PropertySource("user.properties") //可以配置加载外部资源文件,相当于之前的<context:property-placeholder
    public class IocJavaConfig {
    
        @Bean
        public DruidDataSource dataSource(){
            DruidDataSource dataSource=new DruidDataSource();
            dataSource.setName("root");
            dataSource.setPassword("root");
            dataSource.setUrl("dbc:mysql://localhost:3306/jony");
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            return dataSource;
        }
    }

4-1-1、测试一下是否可以连接

从IOC容器获取dataSource,使用了两种方式,一个是类.class ,一个是通过方法名。

202301012036012152.png

4-2、@Bean设置初始化及销毁

    @Bean(initMethod = "",destroyMethod = "")

4-3、使用@Bean(name="")设置bean的Id

    @Bean("db")

或者使用数组

    @Bean(name={"db1","db2"})

4-4、使用Scope设置Bean的作用域

    @Scope("prototype")

五、使用@Import

上面的示例中,我们创建了一个IocJavaConfig配置类,但是随着业务的发展,要是把所有的配置都放在一个配置类中,那无疑增加了代码的阅读性,因此后面我们会创建多个配置类,这样就可以使用Import引入,方便加载了。

5-1、使用@import导入其他配置类

5-1-1、创建第二个配置类

    package com.jony;
    
    import com.jony.entity.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class IocJavaConfig2 {
    
        @Bean
        public User user2(){
            return new User();
        }
    }

5-1-2、在主配置文件引入

    @Import(IocJavaConfig2.class)

5-1-3、测试

202301012036019013.png

5-2、使用@Import 导入其他Bean

    @Import(User.class)

5-3、使用@Import 一次性导入多个bean

5-3-1、首先创建两个类

Cat.java

    package com.jony.entity;
    
    import org.springframework.beans.factory.annotation.Value;
    
    public class Cat {
        @Value("cat")
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

Dog.java

    package com.jony.entity;
    
    import org.springframework.beans.factory.annotation.Value;
    
    public class Dog {
        @Value("dog")
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

5-3-2、创建一个类并实现ImportSelector接口

实现ImportSelector,重写selectImports方法

    package com.jony.entity;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector implements ImportSelector {
        @Override
        //数组中必须使用类的完整限定名,在获取的时候也根据类型获取,无法根据名称获取
        //下面举例了两种方式
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.jony.entity.Cat",Dog.class.getName()};
        }
    }

可以看到返回的是一个String数组,数组中传入类的完整限定名。

5-3-3、在加载器中导入

    package com.jony;
    
    import com.jony.entity.MyImportSelector;
    import com.jony.entity.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration
    @Import(MyImportSelector.class)
    public class IocJavaConfig2 {
    
        @Bean
        public User user2(){
            return new User();
        }
    }

5-5-4、测试

在通过IOC容器获得bean的时候,一定要使用类.class,因为我们在使用importSelector的时候,使用的是类的完整限定名。

202301012036025084.png

5-3、使用import导入ImportBeanDefinitionRegister实现bean定义

5-3-1、首先创建一个类实现ImportBeanDefinitionRegistrar

    package com.jony.entity;
    
    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 MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
            GenericBeanDefinition genericBeanDefinition=new GenericBeanDefinition();
            genericBeanDefinition.setBeanClass(Dog.class);
            registry.registerBeanDefinition("dog",genericBeanDefinition);
        }
    
    }

通过以上代码,我们就可以将Dog类注册为一个bean,并且不同于ImportSelector的是,ImportBeanDefinitionRegistrar可以设置注册bean的名称,这样在使用bean的时候就可以通过bean获取了。

5-3-2、修改配置类,使用Improt导入上面的类

    package com.jony;
    
    import com.jony.entity.MyImportBeanDefinitionRegister;
    import com.jony.entity.MyImportSelector;
    import com.jony.entity.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration
    //@Import(MyImportSelector.class)
    @Import(MyImportBeanDefinitionRegister.class)
    public class IocJavaConfig2 {
    
        @Bean
        public User user2(){
            return new User();
        }
    }

这样就完成了bean的注入。

5-3-3、测试结果

202301012036035805.png

总结

本篇文章主要介绍了通过JavaConfig实现注册Bean的方式。

首先通过@Congruation 设置一个bean为配置。

然后通过@ComponentScan 设置扫描包的路径。

其次使用@PropertySrouce 设置引入外部资源文件。

然后使用@Bean注册一个bean

1、同时可以设置bean的名称如:@Bean(name="user")

2、使用@Bean,设置类的初始化及销毁监听。

如:@Bean(initMethod = "",destroyMethod = "",name = "db")

使用@Scope 设置Bean的作用域

单例:@Scope("singleton")--默认
多例:@Scope("prototype")

然后使用@Import导入bean,有以下几种

1、导入其他配置类@Import(MyJavaConfig.class)

2、导入类注册为Bean,如:@Import(User.class)

这样User类就不需要设注解(@Controller,@Service,@Repository,@Component),让spring主动注入IOC容器了。

3、导入ImportSlector实现类,可以一次性注册多个bean

需要注意的是,在通过ioc容器获得bean的时候,必须通过类型来获取

4、导入ImportBeanDefinitionRegistrar实现类。注册为bean

需要注意的是,在通过ioc容器获得bean的时候,必须使用name来获取