Spring bean的几种注册方式

bean的注册有很多中方式,我们一样一样来讲解。

首先搭建一个java项目,这里就不多说了,需要使用的jar包,这里选用的是比较新的5.1.7.RELEASE:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.7.RELEASE</version>
</dependency>

 通过@Bean等注解创建

通过包扫描和对应的注解(@Bean,@Controller,@Service,@Component,@Repository)来将bean放到容器中。

一般用来注入自己创建的类或者数量比较少的jar包里面的配置类,为了测试方便我们创建一个自定义类。

 

package com.tr.demos.spring.annotion.bean;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
public class Food {
    String name;

    int num;

    public String getName() {
        return name;
    }

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

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Food() {
    }

    public Food(String name, int num) {
        this.name = name;
        this.num = num;
    }

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

然后我们需要通过配置类去将自定义类注入到容器中:

 

package com.tr.demos.spring.annotion.config;

import com.tr.demos.spring.annotion.bean.Food;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
@Configuration
public class MainConfig {

    @Bean
    public Food niceFood(){
        return new Food("鱼豆腐",18);
    }
}

@Configuration告诉springMainConfig是一个配置文件;

@Bean将该类配置到spring容器中,@Bean为默认值时,容器中bean的名字是配置函数的名字。

编写一个测试运行类进行测试,在这里说明一下,因为我创建的是spring boot项目,spring-boot本身有依赖junit单元测试,所以我就没有重新依赖,有需要的可以自行添加:

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>compile</scope>
      <optional>true</optional>
    </dependency>

 

package com.tr.demos.spring.annotion.test;

import com.tr.demos.spring.annotion.bean.Food;
 import com.tr.demos.spring.annotion.config.MainConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
public class Test1 {
    @Test
    public void test01(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        Food name = context.getBean(Food.class);
        System.out.println(name);

        String[] names = context.getBeanNamesForType(Food.class);
        for (String s : names) {
            System.out.println(s);
        }
    }
}

AnnotationConfigApplicationContext 后面输入配置类,获取配置类的Application-上下文;

通过指定Food.class,获取指定类的bean内容;

通过getBeanNamesForType这个方法获取bean名称。

 

测试结果如下: 

当给Bean赋值的时候,容器中bean的名字就是你赋值的名字:

 

@Bean(value = "food")

 

通过扫描包注解@ComponentScan

@ComponentScan 可以扫描配置包下面的@Controller,@Service,@Repository,@Component注解。

创建几个带有注解的类

 

package com.tr.demos.spring.annotion.dao;

import org.springframework.stereotype.Repository;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
@Repository

public class FoodDao {
}

 

package com.tr.demos.spring.annotion.service;

import org.springframework.stereotype.Service;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
@Service
public class FoodService {
}

 

package com.tr.demos.spring.annotion.controller;

import org.springframework.stereotype.Controller;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
@Controller
public class FoodController {
}

在配置类上面加上注解@ComponentScan,写一个测试类 测试一下

 

package com.tr.demos.spring.annotion.test;

import com.tr.demos.spring.annotion.config.MainConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author: glg
 * @Date: 2019/5/30
 */
public class Test2 {
    @Test
    public void test01(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

测试结果如下:

因为我创建的是spring boot ,所以bean有点多,可以看到上面的都是IOC容器自己的组件,中间框起来的是我们创建的,有一个乱入的请忽略,下面的都是springboot需要的bean。

接下来应该是介绍@ComponentScan注解的参数使用了

 

@ComponentScan(value = "com.tr.demos.spring.annotion",excludeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Component.class})
})

excludeFilters 不包括哪些标签,我们去掉了所有带@Component标签的bean,里面参数形式为@Filter(),Filter的参数有type和calsess,这里我们讲一下Filter

@Filter有很多种type

 

ANNOTATION,  //注解
ASSIGNABLE_TYPE, //按照给定的类型,过滤给定类型及其子类
ASPECTJ, //一种类型,没接触过
REGEX,  //正则
CUSTOM;  //自定义

classes就是注解的类

我们在测试下,看看结果如何:

世界清静了好多,就剩IOC容器组件需要的bean和我们的配置bean mainConfig和@Bean注入的food,

那么其他的Bean去哪里了?为什么不过滤service,controller之类的其他文件呢?

 

我打开注解看了一下,service是依赖@Conponent的,其他的注解也是,所以我们过滤controller和service或者Repository的时候,是过滤不掉的。

为什么会导致这样的情况呢,这个还要赖我使用了springboot了,我是用的springboot是2.1.5.RELESE版本的,本身有依赖spring-context,而且是最新的5.1.7.RELESE版本,我添加的依赖也是这个版本,刚开始的时候没有注意到,后来改掉了spring-context版本的时候,发现jar包冲突了,才发现的问题,所以各位使用springboot的时候一定要好好的斟酌一下。

这里安利一款插件--maven helper。

 这是一款对你pom里面jar包做分析的插件,使用起来时这样的,十分方便,不仅可以帮你分析jar包,还可以帮你快速解决jar包冲突的问题,具体的用法可以自行百度。

好了,继续,我们回归到主题,虽然jar包版本确实导致了一些问题使我们的解释结果没有那么清晰,但是不影响我们使用这个功能,并且我们也依然得出了结论。

 

@ComponentScan(value = "com.tr.demos.spring.annotion",includeFilters = {
        @Filter(type = FilterType.ANNOTATION,classes = {Component.class})
},useDefaultFilters = false)

同样的includeFilters参数,包含什么注解,因为service等注解都加上了@Component,所以我们使用它添加任何一个注解都会使所有的注解回来,达不到过滤注解的目的。

另外注意一点,使用includeFilters,一定要设置useDefaultFilters =false,useDefaultFilters默认是true,默认扫描全部,如果不设置为false,你的includeFilters将不会生效。

通过@Import注入

用来批量注入bean:在导入外部包的时候,有大量的bean需要创建,@Bean一个一个添加是很麻烦的,这个时候就可以使用@Import注解,批量导入。

我们首先先创建一个新的实体类Bike

 

package com.tr.demos.spring.annotion.bean;

/**
 * @Author: glg
 * @Date: 2019/6/3
 */
public class Bike {
}

接下来是配置类:

 

package com.tr.demos.spring.annotion.config;

import com.tr.demos.spring.annotion.bean.Bike;
import com.tr.demos.spring.annotion.bean.Food;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @Author: glg
 * @Date: 2019/6/3
 */
@Configuration
@Import(Bike.class)
public class MyConfig3 {
    @Bean(value = "food")
    public Food niceFood() {
        return new Food("鱼豆腐", 18);
    }
}

我们重新开一个测试类,对MyConfig3进行测试:

 

package com.tr.demos.spring.annotion.test;

import com.tr.demos.spring.annotion.config.MyConfig3;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author: glg
 * @Date: 2019/6/3
 */
public class Test4 {
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig3.class);

    @Test
    public void test01() {
        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

结果如下:

这里可以看一下BIke的Bean的名称,是它的全类名,@Import在导入Bean的时候,Bean的名称是全类名,如果导入多个类的话,输入格式是这样:

 

@Import({Bike.class,Car.class})

@Import的参数参数也是丰富多样的,@Import的参数可以是ImportSeclector的实现类,也可以是ImportBeanDefinitionRegistrar的实现类,我们这里先讲一下ImportSeclector的实现类:

 

package com.tr.demos.spring.annotion.condition;

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

/**
 * @Author: glg
 * @Date: 2019/6/3
 */
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.tr.demos.spring.annotion.bean.Food"};
    }
}

配置是这样的:

 

@Import({Bike.class,Car.class, MyImportSelector.class})

然后我们测试一下:

可以看到1是我们@Import注解直接导入的两个bean,2是我们通过实现ImportSeclector接口导入的bean,可能会有人会问,@Import注解中也有MyImportSeclector.class,为什么没有对应的bean,这里简单解释下,因为它实现了ImportSeclector接口,不是普通的类,返回的结果是实现方法中返回的数组,所以没有它对应的bean,当然具体的实现就不清楚了。

需要注意的一个地方是,在一些版本里面实现ImportSeclector接口的时候,默认的返回值是null,会报NullPointerException,是因为在底层会取返回的类的数组的长度,在新的版本里面这个地方已经改掉了,默认的返回是返回new String[0]。

接下来是ImportBeanDefinitionRegistrar的实现类:

首先,按照惯例,我们先创建一个实体类备用:

 

package com.tr.demos.spring.annotion.bean;

/**
 * @Author: glg
 * @Date: 2019/6/3
 */
public class Wheel {
}

我们要写一个自定义类去实现ImportBeanDefinitionRegistrar:

 

package com.tr.demos.spring.annotion.condition;

import com.tr.demos.spring.annotion.bean.Wheel;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Set;

/**
 * @Author: glg
 * @Date: 2019/6/3
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param annotationMetadata     当前类的注解信息
     * @param beanDefinitionRegistry BeanDefinotion注册类
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
        System.out.println("注解类:" + annotationTypes);

        boolean definition = beanDefinitionRegistry.containsBeanDefinition("com.tr.demos.spring.annotion.bean.Bike");
        boolean definition1 = beanDefinitionRegistry.containsBeanDefinition("com.tr.demos.spring.annotion.bean.Car");
        if (definition  definition1){
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Wheel.class);
            beanDefinitionRegistry.registerBeanDefinition("wheel", rootBeanDefinition);
        }
    }
}

继承的方法有两个参数:AnnotationMetadata,BeanDefinitionRegistry;

beanDefinitionRegistry.containsBeanDefinition这个方法,可以判断注册类中是否有参数名称的实例;

registerBeanDefinition("wheel",rootBeanDefinition) 方法是注册名称为wheel的实例,rootBeanDefinition是参数类,在上面指定了bean的实体类。

 

测试结果1那里,我们在自定义实现类中有打印当前注解类的注解信息,很明显成功了,前面是@Configuration,后面是@Import

我们在下面注册类的地方,加了一个判断,Bike和Car如果存在一个,就会注册3,实例wheel,根据我们的打印结果,我们有Bike和Car,所以成功注册wheel。

另外还有一种实现FactoryBean注入,个人感觉太麻烦,实用性不高,不多作介绍,如果有感兴趣的,可以自行百度。

 

 

简单的见解,简单的介绍,新手入门,如果有不正确的地方,希望大家多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值