Spring学习一、组件注册

Spring 组件注册的方式

1.XML-based metadata

1.1 XML Configuration

services.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->
    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

The id attribute is a string that identifies the individual bean definition.

The class attribute defines the type of the bean and uses the fully qualified classname.

daos.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

1.2 Using the Container

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

2.Annotation-based Configuration

Spring 2.5 introduced support for annotaion-based configruation metadata.

XML-based configuration metadata configures these beans as elements inside a top-level element. Java configuration typically uses @Bean - annotated methods with a Configuration classs.

2.1 Use Configuration Annotated

 @Configuration
 public class AppConfig {

     @Bean
     public MyBean myBean() {
         // instantiate, configure and return bean ...
     }
 }

2.2 Bootstrapping @Configuration classes

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);

2.3 使用CompentScan 扫描

	/**
	 * Alias for {@link #basePackages}.
	 * <p>Allows for more concise annotation declarations if no other attributes
	 * are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}
	 * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
	 */
	@AliasFor("basePackages")
	String[] value() default {};

上面是@CompentScan源码中的value说明,如果给默认值,就相当于扫描basePackages.

@ComponentScan(
        // 配置扫描包的根路径
        value = {"com.zuoyan"}
)

2.4 @compentScan 配置过滤扫描

	/**
	 * Specifies which types are not eligible for component scanning.
	   指定那些类型不适合进行组件扫描
	   
	   需要指定的是一个Filter数组
	 * @see #resourcePattern
	 */
	Filter[] excludeFilters() default {};
	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {

		/**
		 * The type of filter to use.
		 * 指定FilterType 使用的类型
		 */
		FilterType type() default FilterType.ANNOTATION;

		/**
		 * Alias for {@link #classes}.
		 * @see #classes
		 */
		@AliasFor("classes")
		Class<?>[] value() default {};

		
		@AliasFor("value")
		Class<?>[] classes() default {};

		
		String[] pattern() default {};

	}

FilterType 类型

public enum FilterType {

	/**
	 * Filter candidates marked with a given annotation.
	 * @see org.springframework.core.type.filter.AnnotationTypeFilter
	 */
	ANNOTATION,

	/**
	 * Filter candidates assignable to a given type.
	 * @see org.springframework.core.type.filter.AssignableTypeFilter
	 */
	ASSIGNABLE_TYPE,

	/**
	 * Filter candidates matching a given AspectJ type pattern expression.
	 * @see org.springframework.core.type.filter.AspectJTypeFilter
	 */
	ASPECTJ,

	/**
	 * Filter candidates matching a given regex pattern.
	 * @see org.springframework.core.type.filter.RegexPatternTypeFilter
	 */
	REGEX,

	/** Filter candidates using a given custom
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM

}

使用过滤

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.ComponentScans;

import com.atguigu.bean.Person;

//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类

@ComponentScans(
		value = {
				@ComponentScan(value="com.atguigu",includeFilters = 
                               {
						/*						
						@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
						@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
						*/
						@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
							},
                    useDefaultFilters = false)
				}
		)
//@ComponentScan  value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
public class MainConfig {
	
	//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
	@Bean("person")
	public Person person01(){
		return new Person("lisi", 20);
	}

}

2.5 @Scope 配置Bean的作用域

@Configuration
public class StudentBeanConfiguration {

    @Scope("prototype")
    @Bean
    Student student(){
        return new Student(10001L, "zuoyan");
    }
}
  @Test
    public void testBeanScope(){

        Student stu1 = (Student) applicationContext.getBean("student");
        Student stu2 = (Student) applicationContext.getBean("student");
        System.out.println(stu1 == stu2);
    }

输出结果

Spring Contation Initializer Ok ... 
Student Construct is exec ... 
Student Construct is exec ... 
false

可以看到创建了两次对象,而且两个对象不相等,因为这个是使用的多例的Bean作用域

更改bean的作用域

@Scope("singleton")

还是上面的代码查看控制台输出结果

Student Construct is exec ... 
Spring Contation Initializer Ok ... 
true

2.6 @Lazy 配置懒加载

@Configuration
public class StudentBeanConfiguration {

    @Scope("singleton")
    @Lazy
    @Bean
    Student student(){
        return new Student(10001L, "zuoyan");
    }
}

控制台输出内容

Spring Contation Initializer Ok ... 
Student Construct is exec ... 
true

可以通过和上面的控制台输出内容进行对比,发现Student的构造函数打印在 Spring Container 初始化完成之后调用的,这说明 Bean是在我们需要使用的时候才执行从初始化的。

2.7 @Conditional 条件配置

It is often useful to conditionally enable or disable a complete @Configuration class or even individual @Bean methods, based on some arbitrary system state. One common example of this is to use the @Profile annotation to activate beans only when a specific profile has been enabled in the Spring Environment (see Bean Definition Profiles for details).

大意就是条件注解对一个类或者独立的Bean注册方法的启用或者禁用非常有帮助,一个最常见的例子就是使用@Profile注解指定一个特定的配置文件来激活Bean

在需要添加的配置类上添加条件注解,控制粒度可以具体到某个方法上

@Conditional({SystemOSCondition.class})
@Configuration
public class StudentBeanConfiguration {

    @Scope("singleton")
    @Lazy
    @Bean
    Student student(){
        return new Student(10001L, "zuoyan");
    }


    @Bean
    Student windows(){
        return new Student(00001L, "Windows");
    }
    
    @Bean
    Student linux(){
        return new Student(00002L, "Windows");
    }
}

需要自己实现Spring-Framework 中的Condition接口

/**
 * @Author: ZuoYanCoder
 * @Description: 实现自定义的条件
 * @Date: 2020/4/17 10:50
 * @Version: 1.0
 */
public class SystemOSCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        Environment environment = context.getEnvironment();
        String systemOsNanme = environment.getProperty("os.name");
        System.out.println(systemOsNanme);
        return systemOsNanme.contains("Windows");
    }
}

测试方法代码类

@Test
    public void testConditionalAnnotated(){

        String[] beanNamesForType = applicationContext.getBeanNamesForType(Student.class);

        for (String name:beanNamesForType) {
            System.out.println(name);
        }

        Map<String, Student> studentMap = applicationContext.getBeansOfType(Student.class);

        System.out.println(studentMap);
    }

执行测试方法打印结果

Windows 10
Windows 10
Windows 10
Student Construct is exec ... 
Student Construct is exec ... 
Spring Contation Initializer Ok ... 
student
windows
linux
Student Construct is exec ... 
{student=Student{stuId=10001, name='zuoyan'}, windows=Student{stuId=1, name='Windows'}, linux=Student{stuId=2, name='Windows'}}

配置IDEA 运行时环境变量

-Dos.name=Linux

然后在运行打印结果

Linux
Spring Contation Initializer Ok ... 
{}

2.8 @Import导入组件使用

2.8.1@Import 导入所需组件

首先通过代码打印输出Spring容器中有的组件

@Test
    public void testImportAnnotation(){

        String[] names = applicationContext.getBeanDefinitionNames();

        for (String name: names) {
            System.out.println(name);

        }

    }

控制台输出

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
studentBeanConfiguration
student
windows
linux

通过使用@Import导入需要的组件

@Conditional({SystemOSCondition.class})
@Configuration
@Import({Animal.class})
public class StudentBeanConfiguration {
}

控制台输出

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
studentBeanConfiguration
com.zuoyan.bean.Animal
student
windows
linux

@Import可以导入多个类,默认的id是类的全路径

2.8.2 ImportSelector 导入组件

@Conditional({SystemOSCondition.class})
@Configuration
@Import({Animal.class, MyImportSelector.class})
public class StudentBeanConfiguration {}

MyImportSelector类的内容

public class MyImportSelector implements ImportSelector {

    /**
     *  方法返回值就是类的全路径名的数组
     *
     * @param importingClassMetadata  可以获取标有Import注解类的的所有注解
     *
     * @return
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

		// 方法可以返回空字符串数组,但是不能返回Null
        return new String[]{"com.zuoyan.bean.Cat","com.zuoyan.bean.Dog"};
    }
}

控制台打印输出

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
studentBeanConfiguration
com.zuoyan.bean.Animal
com.zuoyan.bean.Cat
com.zuoyan.bean.Dog
student
windows
linux

可以看到需要导入的组件已经导入到容器之中了。

2.8.3ImportBeanDefinitationRegistrar

在配置类上添加 ImportBeanDefinitationRegistrar

@Conditional({SystemOSCondition.class})
@Configuration
@Import({Animal.class, MyImportSelector.class, MyImportBeanDefinitationRegister.class})
public class StudentBeanConfiguration {}

MyImportBeanDefinitationRegister实现

public class MyImportBeanDefinitationRegister implements ImportBeanDefinitionRegistrar {

    /**
     *
     * @param importingClassMetadata  当前类的所有注解信息
     * @param registry BeanDefinition注册类,把所有需要添加的组件注册到容器中
     *                 调用BeanDefinitionRegistry.registerBeanDefinition手工注册来
     *
     */

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        boolean catFlag = registry.containsBeanDefinition("com.zuoyan.bean.Cat");
        boolean dogFlag = registry.containsBeanDefinition("com.zuoyan.bean.Dog");

        if(catFlag && dogFlag)
        {

            // 指定Bean的定义信息
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Elephant.class);
            // 指定注册Bean的名称
            registry.registerBeanDefinition("Elephant",beanDefinition);

        }
    }
}

测试类


    @Test
    public void testImportAnnotation(){

        String[] names = applicationContext.getBeanDefinitionNames();

        for (String name: names) {
            System.out.println(name);

        }

    }

控制台打印输出

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
studentBeanConfiguration
com.zuoyan.bean.Animal
com.zuoyan.bean.Cat
com.zuoyan.bean.Dog
student
windows
linux
Elephant

2.9 使用FactoryBean注册组件

首先通过自己定义一个类然后实现BeanFactory的接口

import org.springframework.beans.factory.FactoryBean;
import org.springframework.lang.Nullable;

/**
 * @Author: ZuoYanCoder
 * @Description:
 * @Date: 2020/4/23 14:17
 * @Version: 1.0
 */
public class CarFactoryBean implements FactoryBean<JeepCar> {


    /**
     *  需要管理的对象
     * @return
     * @throws Exception
     */
    @Nullable
    public JeepCar getObject() throws Exception {
        return new JeepCar();
    }

    /**
     * 返回类的类型
     * @return
     */
    @Nullable
    public Class<?> getObjectType() {
        return JeepCar.class;
    }

    /**
     * 是否为单例模式
     * @return
     */
    public boolean isSingleton() {
        return false;
    }
}

然后在配置中将注入进去

    @Bean
    CarFactoryBean carFactoryBean(){
        return new CarFactoryBean();
    }

通过测试

 /**
     * 通过对象工程类管理对象
     */
    @Test
    public void testBeanFactory(){
        Object carFactoryBean = applicationContext.getBean("carFactoryBean");
        System.out.println("获取Bean的类型是:" + carFactoryBean.getClass());
}

查看获取到的对象是

获取Bean的类型是:class com.zuoyan.bean.JeepCar

如果需要获取工厂类对象需要在前面加上 & 符号

获取Bean的类型是:class com.zuoyan.bean.CarFactoryBean

通过isSingleton() 方法可以设置放进容器的对象是单例还是多例。

posted @ 2020-04-23 14:38  梅花GG  阅读(413)  评论(0编辑  收藏  举报