Spring注解驱动(一)IOC容器注册方式

一、配置类

我们想将一个类注册到IOC容器中首先要有个配置,一般有基于xml配置文件的方式和注解的方式。

已知Person类

package com.tan.bean;

public class Person {

    private String name;

    private Integer age;
}

1、xml配置的方式(配置文件为:bean.xml)

<bean id="person" class="com.tan.bean.Person">
    <property name="name" value="张三"></property>
    <property name="age" value="18"></property>
</bean>  

2、基于注解的方式的配置(等于xml的配置文件)

  •  @Configuration:告诉spring这是一个配置类。

二、注册方式(给容器中注册组件的方式)

  1. 包扫描 + 组件标注注解(@Controller,@Service, @Repository,@Component)
  2. @Bean[导入的第三方包里面的组件]
  3. @Import[快速给容器中导入一个组件]

        1)@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名

         2)@ImportSelector 返回需要导入的组件的全类名数组

         3)@ImportBeanDefinitionRegistrar 手动将bean注册到容器中

  1. 使用spring提供的FactoryBean(工厂bean)

1、包扫描 + 组件标注注解(@Controller,@Service, @Repository,@Component)

@ComponentScan:包扫描

@ComponentScan(value = "com.tan", excludeFilters = {
        //@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
        //type:按什么类型排除,以上是按注解去排除
        //classes:指定扫描的时候按照什么规则排除哪些组建
        // includeFilters + useDefaultFilters=false 只扫描classes中的组建
        //@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
})

参数解析

value:指定要扫描的包

包过滤扫描:

excludeFilters = Filter[]:指定扫描的时候按照什么规则排除哪些组件

includeFilters = Filter[]:指定扫描的时候只需要包含哪些组件,(需要关闭默认全部扫描的设置,useDefaultFilters=false)

@Filter中参数解析:

type:按什么类型排除

 FilterType.ANNOTATION       按照注解进行扫描
 FilterType.ASSIGNABLE_TYPE  按照给定的类型
 FilterType.ASPECTJ          使用ASPECTJ表达式方式
 FilterType.REGEX            使用正则表达式方式
 FilterType.CUSTOM           使用自定义规则

classes:指定扫描的时候按照什么规则排除哪些组件

自定义过滤扫描包

@ComponentScan(value = "com.tan", excludeFilters = {

        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})

})

自定义扫描规则类实例

package com.tan.config;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

/**
 * 自定义包扫描过滤规则
 */
public class MyTypeFilter implements TypeFilter{

    /**
     * @param metadataReader 读取到的当前正在扫描的类的信息
     * @param metadataReaderFactory  可以获取到其他任何类信息
     * @return
     * @throws IOException
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        //获取当前类资源信息(类的路径)
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println("--->" + className);
        if (className.contains("er")) { //bean名字中包含er字符的返回true
            return true;
        }
        return false;
    }
}

2、@Bean[导入的第三方包里面的组件]

   @Conditional({MacConditional.class})
   @Scope("prototype")
   @Lazy  //懒加载模式,容器在启动的时候不会创建,当获取一次的时候会加载,第二次再进来就不会在new 
            了
    @Bean(value = "person02")
    public Person person() {
        return new Person("张三", 10);
    }
@Bean("person"):给容器注册一个bean (类型为返回值的类型,id默认用方法名作为id)。
@Lazy:懒加载模式,容器在启动的时候不会创建,当获取一次的时候会加载,第二次再进来就不会在new了。

Scope 注解调整作用域:

  • @Scope("singleton"):单实例的(默认是单实例的)   ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是                                          直接从容器(map.get())中拿。
  • @Scope("prototype"):多实例的ioc启动并不会去调用方法创建对象放在容器中,而是在每次获取bean的时候,才会                                          调用方法创建对象,每次获取的时候都会调一遍。
  • @Scope("request"):同一次请求创建一个实例(基于web的)。
  • @Scope("session"):同一个session创建一个实例(基于web的)。
@Conditional({Conditional}) :按照一定的条件进行判断,满足条件给容器中注册bean。

   类中组件统一设置,满足当前条件,这个类中配置的所有bean注册才能生效,可以放在类上也可以放在方法上。

Conditional实例:

    /**
     * @Conditional({Conditional}) 按照一定的条件进行判断,满足条件给容器中注册bean
     * 如果系统是mac,放这个bean
     * 如果是linux系统就放linus这个bean
     */
    @Conditional({MacConditional.class})
    @Bean(value = "mac")
    public Person person02() {
        return new Person("mac", 40);
    }

    @Conditional({LinuxConditional.class})
    @Bean(value = "linus")
    public Person person03() {
        return new Person("linus", 50);
    }

条件过滤类:

/**
 * 判断是否是mac系统
 */
public class MacConditional implements Condition{

    /**
     *
     * @param context  判断条件,能使用的上下文(环境)
     * @param metadata 注释信息
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //是否mac系统
        //1、能获取到ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

        //2、获取类加载器
        ClassLoader classLoader = context.getClassLoader();

        //3、获取当前环境信息
        Environment environment = context.getEnvironment();

        //4、获取bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");
        if (property.contains("Mac OS X")) {
            return true;
        }

        return false;
    }
}
MacConditional实现了Condition接口,实现了matches方法,方法中的参数类(ConditionContext)可以获取到ioc容器的beanfactory,可以获取类加载器,可以获取当前环境,可以获取bean定义的注册类。

LinuxConditional类同理(略)........

3、@Import[快速给容器中导入一个组件]

导入组件,id默认是组件的全类名

@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})

 1)@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。

 2)、@ImportSelector 返回需要导入的组件的全类名数组。

3)@ImportBeanDefinitionRegistrar 手动将bean注册到容器中。

@ImportSelector注册方式:

/**
 * 自定义逻辑返回需要导入的组件
 */
public class MyImportSelector implements ImportSelector{


    /**
     * 返回值,就是导入到容器中的组件全类名
     * @param importingClassMetadata  当前标注@Import注解的类的所有注解信息
     * @return
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"com.tan.bean.Blue", "com.tan.bean.Yellow"};
    }
}

@ImportBeanDefinitionRegistrar手动注册方式

/**
 * 手动注册
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{

    /**
     * @param importingClassMetadata  当前类的注解信息
     * @param registry                BeanDefinition注册类
     *                                把所有需要添加到容器中的bean调用
     *                                BeanDefinitionRegistry.registerBeanDefinition()手工注册进来
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        boolean definition1 = registry.containsBeanDefinition("com.tan.bean.Red");
        boolean definition2 = registry.containsBeanDefinition("com.tan.bean.Blue");
        if (definition1 && definition2) {
            //指定bean的定义信息(bean的类型,作用域啥的都可以在这指定)
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Black.class);
            //注册一个bean,指定bean名
            registry.registerBeanDefinition("black", rootBeanDefinition);
        }
    }
}

4、使用spring提供的FactoryBean(工厂bean)

@Bean public ColorFactoryBean colorFactoryBean() {

return new ColorFactoryBean();

}

实现类:

/**
 * 创建一个spring定义的FactoryBean
 */
public class ColorFactoryBean implements FactoryBean<Color>{

    /**
     * 返回color对象,这个对象会添加到容器中
     * @return
     * @throws Exception
     */
    public Color getObject() throws Exception {

        System.out.println("ColorFactoryBean...getObject...");
        return new Color();
    }

    public Class<?> getObjectType() {

        return Color.class;
    }

    /**
     * 是单实例?
     * true:  这个bean是单实例,在容器中保存一份
     * false: 多实例,每次获取都会创建一个新的bean
     * @return
     */
    public boolean isSingleton() {
        return false;
    }
}

分析:

使用spring提供的FactoryBean(工厂bean), 默认获取到的是工厂bean调用getObject创建的对象,以上获取就是Color的bean

要获取bean本身,我们需要给id前面加一个& (&colorFactoryBean)才能回去。

springboot中大量使用了这种方式。

三、bean的获取方式

1、基于xml的方式的获取

 /*********基于spring xml配置文件式开发**********/
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);

2、基于注解配置文件的方式获取

/*********基于spring 注解式开发**********/
        ApplicationContext applicationContext= new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);

        //通过类名称去找bean的名称
        String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章