配置元数据就是我们如何装配bean。我们在描述如何装配bean的时候,spring非常的灵活,它提供了三种主要的装配机制:

一、在xml中进行显示的配置
1、新建一个POJO类
public class BMWCar implements Car {
    @Override
    public String getName() {
        return "宝马汽车";
    }
}
2、创建一个配置文件,这里我们使用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 id="bMWCar" class="cn.szyrm.pojo.BMWCar"></bean>
</beans>
3、使用容器XmlBeanFactory
package cn.szyrm.beanFactory;


import cn.szyrm.pojo.Car;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class XmlBeanFactoryTest {
    public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("xmlBeanFactoryTest.xml"));
        Car bMWCar = beanFactory.getBean("bMWCar", Car.class);
        System.out.println(bMWCar.getName());

    }
}
4、测试

运行main方法,然后我们可以发现在控制台有如下输出

23:16:09.728 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [xmlBeanFactoryTest.xml]
23:16:09.732 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanFactory - Creating shared instance of singleton bean 'bMWCar'
宝马汽车

这里我们简单的演示了下BeanFactory的用法,在实际的生产环境中几乎不会使用BeanFactroy。而是使用其子接口ApplicationContext来做为Spring的容器。通过这个简单的例子和 spring 容器图 一起理解。这段代码的核心原理无非就是:

  • 1、XmlBeanFactory通过读取配置文件xmlBeanFactoryTest.xml
  • 2、通过xml文件中配置类并通过发射将类进行示例化
  • 3、将实例化话后对象存储在某个地方( 可能是map对象中)
  • 4、通过getBean方法从容器中将之前已经实例化好的对象取出
  • 5、调用实例化好的对象
二、在java中进行显示的配置
1、POJO还是使用上面的BMWCar
2、配置文件,这里采用基于Java的配置方式
import cn.szyrm.pojo.BMWCar;
import cn.szyrm.pojo.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public Car bMwCar(){
        return new BMWCar();
    }
}
3、使用AnnotationConfigApplicationContext 来读取配置类AppConfig
import cn.szyrm.pojo.Car;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ApplicationContextTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        Car car =   applicationContext.getBean(Car.class);
        System.out.println(car.getName());

    }
}
4、测试

运行main方法,我们会发现有如下的输出

23:47:16.699 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1786dec2
23:47:16.713 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
23:47:16.820 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
23:47:16.821 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
23:47:16.822 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
23:47:16.823 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
23:47:16.830 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
23:47:16.833 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bMwCar'
宝马汽车
三、隐式的bean发现和自动装配

spring是通过两个角度来实现自动化装配的:

  • 1、组件扫描:spring会自动的发现应用上下文中所创建的bean.
  • 2、自动装配:spring自动满足bean之间的依赖

新建一个可被发现的bean

package cn.szyrm.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class BenzCar implements Car {
    @Autowired
    private Engine engine;
    @Override
    public String getName() {
        return  "奔驰汽车" ;
    }

    @Override
    public void start() {
        engine.start();
    }
}
import org.springframework.stereotype.Component;

@Component
public class DCTEngine implements Engine{
    @Override
    public void start() {
        System.out.println("双离合发动机启动");
    }
}

这里我们注意到,POJO类BenzCarDCTEngine 上添加了一个@Component注解,这个就是告诉spring 当前类应该被当成一个bean来处理。

  • 在配置类上设置包扫描的路径
@Configuration
@ComponentScan("cn.szyrm.pojo")
public class AppConfig {

}
  • 初始容器
/**
 * 测试spring 的组件扫描及自动注入
 */
public class ComponentScanTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        Car car =   applicationContext.getBean("benzCar",Car.class);

        car.start();
    }
}
  • 测试使用
Connected to the target VM, address: '127.0.0.1:51781', transport: 'socket'
08:01:07.281 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1786dec2
08:01:07.293 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
08:01:07.341 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\learn\java\spring-source-learn\target\classes\cn\szyrm\pojo\BenzCar.class]
08:01:07.342 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\learn\java\spring-source-learn\target\classes\cn\szyrm\pojo\DCTEngine.class]
08:01:07.426 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
08:01:07.427 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
08:01:07.428 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
08:01:07.429 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
08:01:07.435 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
08:01:07.439 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'benzCar'
08:01:07.445 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'DCTEngine'
08:01:07.446 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bMwCar'
双离合发动机启动

上面是我们经常用到的三种配置方式,此外我们还可以通过@Import注解进入Bean的导入

四、通过@Import配置Bean

import的注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();

}

通过注释,我们可以知道我们可以配置一个普通的类,可以配置实现了ImportSelector 接口的类以及配置实现ImportBeanDefinitionRegistrar 接口的类。那么我们来试试每个配置的用法:

1、常规的Bean
package cn.szyrm.applicationContext;

/**
 * 用来测试通过@Import配置为spring bean。
 * 不实现任何特定的接口,即一个POJO对象
 */
public class RegularBean {
    private String name;

    public String getName() {
        return name;
    }

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

    public void say(){
        System.out.println("I'm a regular bean ");
    }
}
2、实现了ImportSelector 接口的类
package cn.szyrm.applicationContext;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class  ImportSelectorBean  implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{ChooseByImportSelector.class.getName()};
    }
}
package cn.szyrm.applicationContext;

/**
 * 通过被实现ImportSelector返回进行配置的bean
 */
public class ChooseByImportSelector {
    private String name;
    public void say(){
        System.out.println("I'm Choose By ImportSelector");
    }
}
3、实现了ImportBeanDefinitionRegistrar 接口的类
package cn.szyrm.applicationContext;

import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class ImportBeanDefinitionRegistrarBean implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("testImportBeanDefinitionRegistrar",new AnnotatedGenericBeanDefinition(TestImportBeanDefinitionRegistrar.class));
    }
}
package cn.szyrm.applicationContext;

/**
 * 这个类用来测试通过实现了 ImportBeanDefinitionRegistrar  接口的类导入Bean
 */
public class TestImportBeanDefinitionRegistrar {
}

4、配置类如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration

@Import({RegularBean.class, ImportSelectorBean.class,ImportBeanDefinitionRegistrarBean.class})
public class AppConfig {

}

5、测试用的相关类

public class ImportDemo {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        PrintUtil.printBeanDefinition(applicationContext);
    }
}
import org.springframework.context.ApplicationContext;

import java.util.stream.Stream;

public class PrintUtil {
    public static void printBeanDefinition(ApplicationContext applicationContext){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        Stream.of(beanDefinitionNames).forEach(System.out::println);
    }
}

6、运行测试类,发现输出如下:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig
cn.szyrm.applicationContext.RegularBean
cn.szyrm.applicationContext.ChooseByImportSelector
testImportBeanDefinitionRegistrar

可以看到我们三种配置都已经生效

五、在javaconfig中引入xml配置

如果之前的项目上的主要配置是基于xml的,我们想在基于注解的配置spring项目中如何引入这些配置呢?请看下面的示例:

1、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 id="bMWCar" class="cn.szyrm.pojo.BMWCar"></bean>
    <bean id="BenzCar" class="cn.szyrm.pojo.BenzCar"></bean>
</beans>

2、java配置类如下:

import org.springframework.context.annotation.*;
@Configuration

@ImportResource("classpath:xmlBeanFactoryTest.xml")
public class AppConfig {

}

3、测试类如下:

package cn.szyrm.applicationContext;

import cn.szyrm.PrintUtil;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class JavaConfigImportXmlTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        PrintUtil.printBeanDefinition(context);
    }
}

4、打印出来的bean的名称如下:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig
bMWCar
BenzCar

通过上面的输出,我们可以看到。通过java config配置类已经可以导入xml文件实验成功。