Spring注解驱动 组件注册(一)


Spring 中所有的组件都会被放到 IOC 容器当中,然后组件之间通过容器来进行自动装配,也就是我们所说的依赖注入。
下面介绍用纯注解的方式来进行组件注册的几种方法。

一、@ComponentScan包扫描注解

1、@ComponentScan基本使用示例

新建一个Dog类,然后用@Component标注为组件。

package com.example.demo.annotation;

import org.springframework.stereotype.Component;

@Component
public class Dog {

}

新建一个配置类,扫描Dog组件所在的包来加入Dog组件。

package com.example.demo.annotation;

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

@Configuration
@ComponentScan(value = "com.example.demo.annotation")
public class MainConfig {

}

写个main测试,看看容器中是否有了它。

package com.example.demo;

import com.example.demo.annotation.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class annoationTest {

    public static void main(String[] args) {
    	//导入配置类MainConfig,创建容器
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("容器创建完成。。。");
        String[] names=applicationContext.getBeanDefinitionNames();
        for (String name:names) {
            System.out.println(name);
        }
    }
}

运行测试方法,看控制台打印,发现刚才添加的配置类 MainConfig 和 dog 组件已经被放到 IOC 容器中了。
在这里插入图片描述

2、value属性

有时候我们想要扫描更多的包,比如再加上Controller包。
这个时候只需直接将要扫描的包名写到 ComponentScan 的 value 属性的数组中即可。

@ComponentScan(value = {"com.example.demo.annotation","com.example.demo.controller"})

再次运行测试,发现Controller包中的标注了@Controller的类也会被加入到容器中。
在这里插入图片描述
事实上,加了组件标注注解的类都会被加入到容器中。如@Controller,@Component,@Service等。

3、excludeFilters属性

这个 ComponentScan 里面还可以来指定扫描过滤规则。比如我在后面再加一句。
用excludeFilters 来排除标有Controller注解的类(type = FilterType.ANNOTATION表示按照注解过滤),如下:

@ComponentScan(value = {"com.example.demo.annotation","com.example.demo.controller"},excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION ,classes = Controller.class)
})

运行后看控制台。结果显示容器中没有Controller了,只剩下了dog。
在这里插入图片描述

4、includeFilters属性

与它相对应的还有 includeFilters,用来指定只包含的组件。
使用这个属性同时需要将useDefaultFilters 置为false,因为默认true是扫描所有的。
这样容器中就只包含标注有@Controller的组件了。代码如下:

@ComponentScan(value = {"com.example.demo.annotation","com.example.demo.controller"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION ,classes = Controller.class)
},useDefaultFilters = false)

控制台结果显示dog没有了,只剩下了Contoller。
在这里插入图片描述

5、直接指定一个类型加入到容器中

我们还可以用 type = FilterType.ASSIGNABLE_TYPE 直接指定一个类型的加入到容器中,比如我把这个dog加回来。

@ComponentScan(value = {"com.example.demo.annotation","com.example.demo.controller"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION ,classes = Controller.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE ,classes = Dog.class)
},useDefaultFilters = false)

运行后看控制台,dog又被加回来了。
在这里插入图片描述

6、自定义扫描过滤规则

另外还可以自定义扫描过滤规则。将type属性改为FilterType.CUSTOM,再写一个过滤规则的类即可。

@ComponentScan(value = {"com.example.demo.annotation","com.example.demo.controller"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM ,classes = MyTypeFilter.class)
},useDefaultFilters = false)

用于过滤的自定义类,规则为将扫描到的全类名中包含有Controller的都加入到IOC容器中。

package com.example.demo.annotation;

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 {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        AnnotationMetadata annotationMetadata=metadataReader.getAnnotationMetadata();
        ClassMetadata classMetadata=metadataReader.getClassMetadata();
        String name=classMetadata.getClassName();
        if(name.contains("Controller")){
            return  true;
        }
        return false;
    }
}

结果显示规则已经生效,类名中包含有 Controller 字符串的类都被加入到了 IOC 容器中。
在这里插入图片描述

二、@Bean导入

1、@Bean的基本使用示例

新建一个Person对象,加上有参和无参构造器。

package com.example.demo.annotation;

import lombok.Data;

@Data
public class Person {
    public  String name;

    public  Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

配置类:

package com.example.demo.annotation;

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

@Configuration
public class MainConfig {

    @Bean
    public Person person(){
        System.out.println("给容器中添加Person。。。");
        return  new Person("lisi",20);
    }
}

再次运行测试类,容器中出现了名为person的组件。
在这里插入图片描述

2、组件id名字修改

@Bean注解加入的bean默认id为方法名,我们要改id名字怎么搞呢?

(1)改方法名

修改方法名为 person111 。

@Bean
public Person person111(){
    System.out.println("给容器中添加Person。。。");
    return  new Person("lisi",20);
}

控制台结果:
在这里插入图片描述

(2)赋值Bean注解的name属性

将名字写在Bean注解的name属性中。

@Bean(name = "person")
public Person person111(){
    System.out.println("给容器中添加Person。。。");
    return  new Person("lisi",20);
}

控制台结果:
在这里插入图片描述

3、@Scope多实例

这样获得的组件是单实例的,测试一下看是也不是:

package com.example.demo;

import com.example.demo.annotation.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class annotationTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("容器创建完成。。。");
        String[] names=applicationContext.getBeanDefinitionNames();
        for (String name:names) {
            System.out.println(name);
        }
        Object bean=applicationContext.getBean("person");
        Object bean1=applicationContext.getBean("person");
        //判断是否为同一个实例
        System.err.println(bean==bean1);
    }
}

打印结果,确实是单实例的。
在这里插入图片描述
单实例的bean组件会在创建IOC容器的时候自动生成。

那么要怎么改成多实例的呢?有一个@Scope注解(prototype表示多实例),代码如下。

    @Scope("prototype")
    @Bean(name = "person")
    public Person person111(){
        System.out.println("给容器中添加Person。。。");
        return  new Person("lisi",20);
    }

再次运行测试,可以看到 Person 确实变成了多实例。
在这里插入图片描述
以这种方式注册的组件在创建对象的时候才会加入到容器中。

4、@Lazy懒加载

有时候我们不想在创建容器的时候就添加单实例组件,这个@Lazy懒加载可以帮我们做到:

    @Lazy
    @Bean(name = "person")
    public Person person111(){
        System.out.println("给容器中添加Person。。。");
        return  new Person("lisi",20);
    }

运行结果如下。
在这里插入图片描述
可以看见在创建对象的时候才创建单实例bean。

5、@Conditional条件注册

还有一个springboot底层使用非常多的注解@Conditional,该注解表示满足一定的条件才会在容器中注册bean。下面来实现一下。
先实现 Condition 接口,条件设置为容器中没有Dog组件则添加被@Conditional标注的bean组件。

package com.example.demo.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class LisiCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        BeanDefinitionRegistry definitionRegistry=conditionContext.getRegistry();
        boolean contain=definitionRegistry.containsBeanDefinition("dog");

        if(!contain){
            return true;
        }
        return  false;
    }
}

然后将@Conditional注解标注到bean上。

    @Conditional({LisiCondition.class})
    @Bean(name = "person")
    public Person person111(){
        System.out.println("给容器中添加Person。。。");
        return  new Person("lisi",20);
    }

最后将Dog组件扫描到容器中。

@ComponentScan(value = "com.example.demo.annotation")

来看看运行结果。
在这里插入图片描述
由于IOC容器中现在存在Dog组件,所以没有再注册Person组件。

三、@Import快速导入

1、@Import基本使用示例

在新建一个Cat类:

package com.example.demo.annotation;

public class Cat {
    
}

配置类,直接用@Import注解引入Cat组件:

package com.example.demo.annotation;

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

@Configuration
@Import(Cat.class)
public class MainConfig {

}

运行结果如下,我们发现这个Cat以全类名为id被导入到了IOC容器中:
在这里插入图片描述

2、实现ImportSelector接口

我们还可以用ImportSelector接口返回需要导入组件的全类名数组,下面换这种方式导入Cat组件。
先实现这个接口:

package com.example.demo.annotation;

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

public class MySelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.example.demo.annotation.Cat"};
    }
}

配置类,用@Import引入上面写的实现类:

package com.example.demo.annotation;

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

@Configuration
@Import(MySelector.class)
public class MainConfig {

}

然后运行,结果显示Cat这样也被导入进来了。
在这里插入图片描述

3、实现ImportBeanDefinitionRegistrar接口

我们还可以实现 ImportBeanDefinitionRegistrar 接口手动注册组件并导入,同时顺便自定义组件id为cat。

package com.example.demo.annotation;

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;

public class MyRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition=new RootBeanDefinition(Cat.class);
        registry.registerBeanDefinition("cat",beanDefinition);
    }
}

运行结果如下,我们自定义id名字的Cat也被导入到容器中了:
在这里插入图片描述

四、FactoryBean组件注册

示例如下:
实现FactoryBean接口,用 getObject() 方法返回要导入的对象Cat,用 isSingleton() 方法设置为是单实例。

package com.example.demo.annotation;

import org.springframework.beans.factory.FactoryBean;

public class MyFactroy implements FactoryBean<Cat> {
    
    //获取对象
    @Override
    public Cat getObject() throws Exception {
        System.out.println("factory...获取对象。。。");
        return new Cat();
    }

    @Override
    public Class<?> getObjectType() {
        return Cat.class;
    }

    //是否单实例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

在配置类中用 @bean 注册 MyFactroy 组件。

package com.example.demo.annotation;

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

@Configuration
public class MainConfig {

    @Bean
    public MyFactroy myFactroy(){
        return  new MyFactroy();
    }
}

修改测试方法,看看它在容器中是什么样子的:

package com.example.demo;

import com.example.demo.annotation.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class annotationTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig.class);
        System.out.println("容器创建完成。。。");
        String[] names=applicationContext.getBeanDefinitionNames();
        for (String name:names) {
            System.out.println(name);
        }
        Object bean=applicationContext.getBean("myFactroy");
        System.out.println(bean.getClass());
    }
}

控制台打印结果如下:
在这里插入图片描述
可以看到,虽然它的id名字是myFactroy,但是它里面实际的东西是工厂中的Cat对象。

发布了17 篇原创文章 · 获赞 1 · 访问量 304

猜你喜欢

转载自blog.csdn.net/weixin_43424932/article/details/104074104