spring学习笔记(spring注解驱动开发之容器注册组件)

组件注册

一、@Configuration和@Bean注解:
  • @Configuration:标注该类是一个Spring的配置类
  • @Bean:将方法的返回值注入spring容器中。

代码演示:
1、创建一个person类,该类只有名称和年龄两个简单的属性。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String userName;
    private int age;
}

2、因为是纯注解开发,所以舍弃了spring的配置文件。没有配置文件,如何向容器中注入实例呢?
@Configuration注解标注在类上,这类将成为spring的配置类,可以取代于配置文件。原本用 <bean 标签去配置一个实例到容器中,现在可以用@bean注解标注在方法上。

@Configuration
public class MainConfig {

    @Bean
    public Person person(){
        return new Person("张三",18);
    }
}

测试:

 private ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

    @Test
    public void test1(){
        Person person = context.getBean("person", Person.class);
        //按照类型查找容器中bean的名称
        String[] names = context.getBeanNamesForType(Person.class);
        for (String name : names){
            System.out.println(name);
        }
        System.out.println(person);
    }

测试结果:

person
Person(userName=张三, age=18)

说明:默认情况下,使用@bean标注的方法,在容器中唯一标识为方法名,可以利用valuename属性来指定名称。

@Bean(value = "peron01")
    public Person person(){
        return new Person("张三",18);
    }
二、@ComponentScan

@ComponentScan :自动扫描组件。

<!--包扫描-->
    <context:component-scan base-package="com.wjh">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

这是配置文件中配置spring扫描规则。
可以用@ComponentScan来代替,把@ComponentScan注解标注在配置类中

@Configuration
@ComponentScan(value = "com.wjh")
public class MainConfig {

    @Bean(value = "peron01")
    public Person person(){
        return new Person("张三",18);
    }
}

新建dao、srevice、controller类,并用注解将其加入容器中。

@Repository
public class BookDao {
}
@Service
public class BookService {
}

@Controller
public class BookController {
}

测试:

 @Test
    public void test2(){
        String[] names = context.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
mainConfig
bookController
bookDao
bookService
peron01

因为配置了扫描规则,所有容器中有对应的实例。

@ComponentScan的属性:

  • String[] value() default {} :指定要扫描的包
  • Filter[] includeFilters() default {} :指定要扫描的时候要按照扫描规则去排除哪些组件
  • Filter[] excludeFilters() default {} :指定要扫描的时候要按照扫描规则去包含哪些组件
@Configuration
@ComponentScan(value = "com.wjh",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
})
public class MainConfig {

    @Bean(value = "peron01")
    public Person person(){
        return new Person("张三",18);
    }
}

结果:

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
mainConfig
bookDao
bookService
peron01

因为配置了排除controller注解,所以标注了Controller注解的类没有加入到容器中。

@Configuration
@ComponentScan(value = "com.wjh",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
},useDefaultFilters = false)
public class MainConfig {

    @Bean(value = "peron01")
    public Person person(){
        return new Person("张三",18);
    }
}

结果:

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
mainConfig
bookController
peron01

说明:useDefaultFilters = false,如果不设置,扫描规则为扫描全部。

扩展:

  • FilterType.ASSIGNABLE_TYPE :按照给定的类型
@Configuration
@ComponentScan(value = "com.wjh",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookService.class)
},useDefaultFilters = true)

结果:

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
mainConfig
bookController
bookDao
bookService
peron01

说明:BookService类的实例也都会加载进容器中。

  • FilterType.ASPECTJ : 按照ASPECTJ表达式
  • FilterType.REGEX : 按照正则表达式
  • FilterType.CUSTOM : 自定义规则【重点】

实现方式:
创建一个自定义类实现TypeFilter接口,并在自定义扫描规则。

public class MyTypeFilter implements TypeFilter {

    /**
     *
     * @param metadataReader    :读取到当前正在扫描的类的信息
     * @param metadataReaderFactory     :可以获取其他任何其他类的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        //获取当前类的资源信息(类的路径)
        Resource resource = metadataReader.getResource();
        //扫描到的类只有包含Dao的才加入到容器中
        if (className.contains("Dao")){
            return true;
        }
        return false;
    }
}

主配置:

@Configuration
@ComponentScan(value = "com.wjh",includeFilters = {
//        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
//        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookService.class)
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)
},useDefaultFilters = false)
//FilterType.ASSIGNABLE_TYPE:按照给定的类型
//FilterType.ASPECTJ : 按照ASPECTJ表达式
//FilterType.REGEX : 按照正则表达式
public class MainConfig {

    @Bean(value = "peron01")
    public Person person(){
        return new Person("张三",18);
    }
}

结果:

--->com.wjh.PersonTest
--->com.wjh.bean.Person
--->com.wjh.config.MyTypeFilter
--->com.wjh.controller.BookController
--->com.wjh.dao.BookDao
--->com.wjh.service.BookService
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
mainConfig
bookDao
peron01
三、@Scope和@Lazy

如果从容器中获取同一个bean实例,是否为同一个实例?

Person person = context.getBean("person", Person.class);
        Person person2 = context.getBean("person", Person.class);
        System.out.println(person == person2);

结果为

true

@Scope:bean的作用范围。

  • singleton:单实例的(默认)。ioc容器启动会调用方法创建对象放到容器中,以后每次获取就是直接从容器中(map.get())取。
@Configuration
public class MainConfig2 {

    @Bean
    @Scope(value = "singleton")
    public Person person(){
        System.out.println("调用方法把person实例放到容器中");
        return new Person("张三",18);
    }
}

测试:

  @Test
    public void test3() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        System.out.println("容器创建完成。。。");
        Person person = context.getBean("person", Person.class);
        Person person2 = context.getBean("person", Person.class);
        System.out.println(person == person2);
    }

结果:

调用方法把person实例放到容器中
容器创建完成。。。
true
  • prototype:多实例的。ioc容器启动的不会调用方法创建对象放到容器中,而是获取的时候才会调用方法创建放到容器中。
  @Bean
    @Scope(value = "prototype")
    public Person person(){
        System.out.println("调用方法把person实例放到容器中");
        return new Person("张三",18);
    }

测试:

  @Test
    public void test3() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        System.out.println("容器创建完成。。。");
        Person person = context.getBean("person", Person.class);
        Person person2 = context.getBean("person", Person.class);
        System.out.println(person == person2);
    }

测试结果:

容器创建完成。。。
调用方法把person实例放到容器中
调用方法把person实例放到容器中
false

@Lazy:懒加载

  • 单实例bean:会在容器启动的时候创建对象。
  • 懒加载:容器启动不创建对象,在第一次使用的时候创建实例,并初始化。
@Configuration
public class MainConfig2 {

    @Bean
    @Scope(value = "singleton")
    @Lazy
    public Person person(){
        System.out.println("调用方法把person实例放到容器中");
        return new Person("张三",18);
    }
}

测试:

 @Test
    public void test3() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        System.out.println("容器创建完成。。。");
        Person person = context.getBean("person", Person.class);
        Person person2 = context.getBean("person", Person.class);
        System.out.println(person == person2);
    }

测试结果:

容器创建完成。。。
调用方法把person实例放到容器中
true

说明:标注**@Lazy**注解后,只有在获取(使用)实例的才会创建加入到容器中。但还是只创建一次。

四、@Conditional按照条件注册bean

场景:如果当前系统环境为windows,注入bill,如果为linux,注入linus。

@Configuration
public class MainConfig2 {

    @Bean
    @Scope(value = "singleton")
    @Lazy
    public Person person(){
//        System.out.println("调用方法把person实例放到容器中");
        return new Person("张三",18);
    }

    @Bean(value = "bill")
    @Conditional(value = {WindowCondition.class})
    public Person person01(){
        return new Person("Bill Gates",68);
    }

    @Bean(value = "linus")
    @Conditional(value = {LinuxCondition.class})
    public Person person02(){
        return new Person("Linus",48);
    }
}

说明:@Conditional的属性值为实现了Condition接口的类,在该类中过滤条件。

WindowCondition

/判断是否为windows系统环境来注入bean
public class WindowCondition implements Condition {

    /**
     *
     * @param context   :判断能使用的上下文(环境)
     * @param metadata  :注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //能获取到ioc使用到的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //能获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        //能获取到类加载器
        ClassLoader classLoader = context.getClassLoader();
        //能获取到当前环境
        Environment environment = context.getEnvironment();
        //判断是否为Windows环境,并且容器中有person实例才通过
        String name = environment.getProperty("os.name");
        if (name.contains("Windows") && registry.containsBeanDefinition("person")){
            return true;
        }
        return false;
    }
}

LinuxCondition

//判断是否为Linux系统环境来注入bean
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //能获取到ioc使用到的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //能获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        //能获取到类加载器
        ClassLoader classLoader = context.getClassLoader();
        //能获取到当前环境
        Environment environment = context.getEnvironment();
        //判断是否为Windows环境,并且容器中有person实例才通过
        String name = environment.getProperty("os.name");
        if (name.contains("Linux") && registry.containsBeanDefinition("perosn")){
            return true;
        }
        return false;
    }
}

测试:

 @Test
    public void test4() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        Environment environment = context.getEnvironment();
        System.out.println(environment.getProperty("os.name"));
        Map<String, Person> beans = context.getBeansOfType(Person.class);
        System.out.println(beans);
    }

测试结果:

Windows 10
调用方法把person实例放到容器中
{person=Person(userName=张三, age=18), bill=Person(userName=Bill Gates, age=68)}

在虚拟机参数上带上linux环境参数来测试

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

linux
{person=Person(userName=张三, age=18), linus=Person(userName=Linus, age=48)}
五、@Import导入组件

总结一下给容器导入组件的方法:

  1. 包扫描+组件标注解(@Controller、@Service、@Repository、@Compent)【自己定义的组件】
  2. @Bean【导入的第三方包里面的组件】
  3. @Import【快速给容器导入一个组件】
    1)、@Import(要导入的组件):容器就会自动注册这个组件,id默认为全限定类名。
    2)、ImportSelector:返回需要导入的组件的全限定类名数组。
    3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
  4. 使用spring提供的FactoryBean(工厂bean)。

@Import

案例:用@Import注解给容器中注入Color和Red类。


@Configuration
@Import({Red.class, Color.class})
public class MainConfig2 {

    @Bean
    @Scope(value = "singleton")
    @Lazy
    public Person person(){
//        System.out.println("调用方法把person实例放到容器中");
        return new Person("张三",18);
    }

    @Bean(value = "bill")
    @Conditional(value = {WindowCondition.class})
    public Person person01(){
        return new Person("Bill Gates",68);
    }

    @Bean(value = "linus")
    @Conditional(value = {LinuxCondition.class})
    public Person person02(){
        return new Person("Linus",48);
    }
}

public class Color {
}

public class Red {
}

测试:

 @Test
    public void test5() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] names = context.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
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
person
bill

说明:用 @Import注册的组件的bean名称为全限定类名

ImportSelector
实现:自定义一个实现了ImportSelector接口的类,定义需要注入到容器的组件
MyImportSelector


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

    //返回值就是要导入到容器的组件的类名
    //AnnotationMetadata :  当前标注@Import注解的类的所有注解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.wjh.bean.Blue","com.wjh.bean.Yellow"};
    }
}

@Configuration
@Import({Red.class, Color.class, MyImportSelector.class})
public class MainConfig2 {

测试结果:

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
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
com.wjh.bean.Blue
com.wjh.bean.Yellow
person
bill

FactoryBean
实现:自定义一个类,并实现FactoryBean接口,在实现的getObject()中定义要加入到容器的组件。


public class ColorFactoryBean implements FactoryBean<Color> {

    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

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

把factoryBean加入到容器中:

 @Bean
    public ColorFactoryBean factoryBean(){
        return new ColorFactoryBean();
    }

测试:

    @Test
    public void test6() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] names = context.getBeanDefinitionNames();
        for (String name : names){
            System.out.println(name);
        }

        Object factoryBean =  context.getBean("factoryBean");
        System.out.println("bean的类型:"+factoryBean);
    }

测试结果:

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
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
com.wjh.bean.Blue
com.wjh.bean.Yellow
person
bill
factoryBean
rainBow
bean的类型:com.wjh.bean.Color@4b0b0854 

说明:容器中会把自定义的factory类和color类加入到组件中,并且自定义的factory类的实例类型为com.wjh.bean.Color@4b0b0854 。如果想要得到真实的factoryBean,需要在根据名称获取bean的时候在名称前加上"&"。

    @Test
    public void test6() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] names = context.getBeanDefinitionNames();
        for (String name : names){
            System.out.println(name);
        }

        Object factoryBean =  context.getBean("&factoryBean");
        System.out.println("bean的类型:"+factoryBean);
    }

结果:

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
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
com.wjh.bean.Blue
com.wjh.bean.Yellow
person
bill
factoryBean
rainBow
bean的类型:com.wjh.bean.ColorFactoryBean@4b0b0854
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值