Spring注解版

1、组件添加

1、组件添加

给容器中注册组件:
1、包扫描 + 组件标注注解(@Controller、@Service、@Repository、@Component)[局限于自己写的类]
2、@Bean[导入的第三方包里的组件]
3、@Import[快速给容器中导入一个组件]
    3.1、@Import[要导入容器中的组件,容器就会自动注入这个组件,默认id为全类名]
    3.2、ImportSelect导入选择器接口,里面有一个方法,返回字符串数组,由方法决定返回哪些组件
    3.3、ImportBeanDefinitionRegistrar手工注册bean,相比前两种方法可以自定义bean名
4、使用Spring提供的FactoryBean[工厂bean]
    4.1、默认获取到的是工厂bean调用getObject()创建的对象
    4.2、要获取工厂bean本身,需要给id前增加"&"

1.1、@Configuration + @Bean 为容器中注册组件

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lwj</groupId>
    <artifactId>spring-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties/>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

实体类:Person.java

package com.atguigu.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;
}

以前我们配置bean都是在applicationContext.xml配置文件中使用<bean>标签配置,现在使用JavaConfig的方式配置bean。

配置类:JavaConfig.java

package com.atguigu.config;

import com.atguigu.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//配置类(Java代码) == 配置文件(xml)
@Configuration
//告诉Spring,这是一个配置类
public class JavaConfig {

    @Bean(value = "person")
    //注册组件,<bean> -> @Bean,给容器中注册一个Bean,返回值类型为Bean类,id为方法名
    //1、方法名作为bean的id;2、使用@Bean的value属性设置bean的id
    //@Configuration + @Bean 给容器中注册组件
    public Person person1() {
        return new Person("李四", 22);
    }
}

测试类:测试Bean容器中是否存在id=person的Person对象

package com.atguigu.test;

import com.atguigu.bean.Person;
import com.atguigu.config.JavaConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;


public class MainTest {
    //1、@Configuration + @Bean 为容器中注册组件
    @Test
    public void testAnnotation() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        Person person = (Person) context.getBean("person");
        System.out.println(person);
        //Person(name=李四, age=22)
        String[] beanNamesForType = context.getBeanNamesForType(Person.class);
        System.out.println(Arrays.toString(beanNamesForType));
        //[person]
    }
}

1.2、@ComponentScan 自动扫描组件&指定扫描规则

以前在xml文件中的扫描方式:引入context约束,<context:component-scan base-package="">,指定要扫描的包。现在使用JavaConfig方式自动扫描。

controller层

package com.atguigu.controller;

import org.springframework.stereotype.Controller;

@Controller
public class BookController {
}

service层

package com.atguigu.service;

import org.springframework.stereotype.Service;

@Service
public class BookService {
}

dao层

package com.atguigu.dao;

import org.springframework.stereotype.Repository;

@Repository
public class BookDao {
}

Person实体类使用@Configuration + @Bean的方式注入,而@Controller、@Service、@Repository这些都是@Component注解标注的(包括@Configuration),使用@ComponentScan自动扫描注入。

@ComponentScan注解:@Target(ElementType.TYPE),只能标注在类上,比如JavaConfig配置类上,@Repeatable(ComponentScans.class)可重复注解(jdk1.8新注解),在JavaConfig类上可以写多个@ComponentScan注解,扫描多个包,还有一点,@ComponentScan注解和@ComponentScans注解的@Retention和@Target必须相同,关于这一点,多看下注解的基础,尤其是@Repeatable注解的使用。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
	ComponentScan[] value();
}

在主配置类JavaConfig.java上配置@ComponentScan注解

package com.atguigu.config;

import com.atguigu.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value = "com.atguigu.controller")
@ComponentScan(basePackages = {"com.atguigu.service", "com.atguigu.dao"})
//以上两种方式可以合并为@ComponentScan(value="com.atguigu"),上面的方式是为了体现@ComponentScan的元注解@Repeatable的作用
public class JavaConfig {

    @Bean()
    public Person person() {
        return new Person("李四", 22);
    }
}

测试类:测试容器中自动注入了哪些bean?

package com.atguigu.test;

import com.atguigu.bean.Person;
import com.atguigu.config.JavaConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;


public class MainTest {
    //<context:component-scan base-package="com.atguigu"> 在xml中配置包扫描,只要标注了
    //@Controller、@Service、@Repository、@Component注解,都会被添加到容器中
    //2、@ComponentScan,同样作用于配置类JavaConfig上
    @Test
    public void testComponentScan() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
            //javaConfig
            //bookController
            //bookService
            //bookDao
            //person
            //结果当然不包括IOC容器自动注入的一些bean
        }
    }
}

另外,在@ComponentScan自动扫描时,除了value和basePackages属性指定扫描哪些包以外,还可以制定扫描规则。

public @interface ComponentScan {
    Filter[] includeFilters() default {};
    Filter[] excludeFilters() default {};
}

excludeFilters属性指定扫描的时候排除哪些组件,类型为Filter数组。

这个Filter也是一个注解,可以指定type属性(按照什么规则,默认按照注解),value属性,是一个数组(注解.class)。

@interface Filter {
    FilterType type() default FilterType.ANNOTATION;

    @AliasFor("classes")
    Class<?>[] value() default {};
}
package com.atguigu.config;

import com.atguigu.bean.Person;
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.stereotype.Controller;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(value = {"com.atguigu"},
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})})
// 扫描com.atguigu的包,但是排除@Controller、@Service注解标注的类(当然可以不按照注解类型进行排除,默认是FilterType.ANNOTATION)
public class JavaConfig {
    
    @Bean()
    public Person person() {
        return new Person("李四", 22);
    }
}

测试@ComponentScan的excludeFilters属性,返回Filter类型的数组。

package com.atguigu.test;

import com.atguigu.bean.Person;
import com.atguigu.config.JavaConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;


public class MainTest {
    @Test
    public void testComponentScan() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
            //javaConfig
            //bookDao
            //person
        }
    }
}

从结果中可以看到excludeFilter的排除规则生效。

includeFilters:指定扫描的时候,只包含哪些组件。

现在重新修改JavaConfig.java配置类,让@ComponentScan自动扫描时只包含@Controller和@Service注解标注的类。

另外,在使用includeFilters属性只包含哪些注解时,要使默认扫描规则失效,(默认扫描所有组件),在ComponentScan中有一个属性,useDefaultFilters,默认为true,现在修改为false。

boolean useDefaultFilters() default true;
package com.atguigu.config;

import com.atguigu.bean.Person;
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.stereotype.Controller;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(value = {"com.atguigu"}, useDefaultFilters = false,
    includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Service.class})})
//只扫描标注了@Controller和@Service注解的类
public class JavaConfig {

    @Bean()
    public Person person() {
        return new Person("李四", 22);
    }
}

测试@ComponentScan的includeFilters属性:

package com.atguigu.test;

import com.atguigu.bean.Person;
import com.atguigu.config.JavaConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;

public class MainTest {
    @Test
    public void testComponentScan() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
            //javaConfig
            //bookController
            //bookService
            //person
        }
    }
}

从结果可以看到确实@ComponentScan只扫描了标注Controller和Service的类。

1.3、枚举类FilterType的其他过滤规则

在上一节中使用@Filter(type=FilterType.ANNOTATION)按照注解类型进行过滤,现在学习其他过滤规则。

public enum FilterType {
    ANNOTATION,

    ASSIGNABLE_TYPE,
	
    ASPECTJ,

    REGEX,

    CUSTOM
}

FilterType.ASSIGNABLE_TYPE:按照给定的类型

JavaConfig.java

package com.atguigu.config;

import com.atguigu.bean.Person;
import com.atguigu.service.BookService;
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.stereotype.Controller;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(value = {"com.atguigu"}, useDefaultFilters = false,
    includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
                      @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {BookService.class})})
//只扫描:1、@Controller注解标注的类;2、BookService的类及其子类
public class JavaConfig {

    @Bean()
    public Person person() {
        return new Person("李四", 22);
    }
}

测试:和@Filter(type=FilterType.ANNOTATION, value={Controller.class, Service.class})结果一致。

FilterType.ASPECTJ:按照ASPECTJ表达式(不常用)

FilterType.REGEX:按照正则表达式

FilterType.CUSTOM:按照自定义规则

要想自定义规则,那么必须实现TypeFilter接口。

package com.atguigu.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
     */
    @Override
    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);
        //return false;
        //如果返回false时,那么includeFilters使用此规则后,容器中只有javaConfig和person
        if (className.contains("er")) {
            return true;
        }
        return false;
    }
}

在JavaConfig中配置includeFilter的type类型为FilterType.CUSTOM,

package com.atguigu.config;

import com.atguigu.bean.Person;
import com.atguigu.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(value = {"com.atguigu"}, useDefaultFilters = false,
    includeFilters = {@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
public class JavaConfig {

    @Bean()
    public Person person() {
        return new Person("李四", 22);
    }
}

测试方法:

@Test
public void testComponentScan() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
        System.out.println(name);
        //javaConfig
        //bookController
        //bookService
        //person
        //myTypeFilter
    }
}

除了@Configuration标注的JavaConfig类和@Bean标注的Person类之外,只有类名中包含"er"的类才会被@ComponentScan扫描到,包括MyTypeFilter类,这次扫描规则为自定义,和@Controller这类注解没有关系。

1.4、@Scope设置组件作用域

新建JavaConfig2类,

package com.atguigu.config;

import com.atguigu.bean.Person;
import org.springframework.context.annotation.*;

@Configuration
public class JavaConfig2 {
    // prototype:多实例
    // singleton:单实例(默认)
    @Scope(value = "prototype")
    @Bean
    public Person person() {
        return new Person("张三", 21);
    }
}

@Scope("singleton") + @Bean 标注的person方法,IOC容器启动会调用方法创建对象,以后每次获取就是从容器中拿,每一次获取的都是同一个;

@Scope("prototype") + @Bean 标注的person方法,IOC容器启动不会调用方法创建对象,直到测试方法中getBean()主动获取时,才会创建对象,同时如果多次获取,每次不同。

1.5、@Lazy实现bean懒加载

懒加载针是对于单实例Bean来说,因为单实例Bean默认是在容器启动时创建对象。

所谓懒加载,即容器启动不创建对象,而在第一次使用/获取Bean时创建对象。

package com.atguigu.config;

import com.atguigu.bean.Person;
import org.springframework.context.annotation.*;

@Configuration
public class JavaConfig2 {
    @Lazy
    @Scope(value = "singleton")
    @Bean
    public Person person() {
        return new Person("张三", 21);
    }
}

1.6、@Condition按照条件注册bean

@Condition:按照一定的条件进行判断,满足条件给容器中注册Bean,不满足条件不注册,在SpringBoot底层大量使用。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    /**
     * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class<? extends Condition>[] value();
}
  • @Condition注解 1、作用在类上;2、作用在方法上;
  • value属性,类型为实现了Condition接口的类的class数组;

在IOC环境Environment对象中可以获取"os.name"操作系统名,可以根据操作系统名来决定将哪个bean注册到容器中。

WindowsCondition.java

package com.atguigu.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String property = context.getEnvironment().getProperty("os.name");
        if ("Windows 10".equals(property)) {
            return true;
        }
        return false;
    }
}

LinuxCondition.java

package com.atguigu.condition;

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

public class LinuxCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        BeanDefinitionRegistry registry = context.getRegistry();
        //获取容器中bean的注册情况
        boolean b = registry.containsBeanDefinition("person");
        //判断容器中是否注册了id = person的bean,也可以给容器中注册bean
        if ("Linux".equals(property)) {
            return true;
        }
        return false;
    }
}

JavaConfig2.java

package com.atguigu.config;

import com.atguigu.bean.Person;
import com.atguigu.condition.LinuxCondition;
import com.atguigu.condition.WindowsCondition;
import org.springframework.context.annotation.*;

@Configuration
public class JavaConfig2 {
    @Bean
    public Person person() {
        return new Person("张三", 21);
    }

    @Conditional(value = {LinuxCondition.class})
    @Bean(value = "shawnYue")
    public Person person01() {
        return new Person("ShawnYue", 32);
    }

    @Conditional(value = {WindowsCondition.class})
    @Bean(value = "john")
    public Person person02() {
        return new Person("john", 23);
    }
}

测试方法:

@Test
public void testConditional() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig2.class);
    ConfigurableEnvironment environment = context.getEnvironment();
    String property = environment.getProperty("os.name");
    System.out.println(property); //Windows 10

    String[] beanNamesForType = context.getBeanNamesForType(Person.class);
    for (String name : beanNamesForType) {
        System.out.println(name);
        //person
        //john
    }
    Map<String, Person> beans = context.getBeansOfType(Person.class);
    System.out.println(beans);
    //{person=Person(name=张三, age=21), john=Person(name=john, age=23)}
}

当@Condition注解作用于类上时,如果条件满足,那么该配置类下的bean可以注册;如果条件不满足,那么该配置类下的bean不可以注册,包括配置类本身。

@Conditional(value = {LinuxCondition.class})
@Configuration
public class JavaConfig2 {
}

测试方法:

String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));

结果:都是Spring容器本身的Bean。

[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]

1.7、@Import给容器中快速导入一个组件

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}
  • @Import注解只能作用于配置类上,属性value类型为class的数组。
package com.atguigu.bean;

//Color类模拟框架/第三方包提供的类
public class Color {
}
package com.atguigu.bean;


public class Red {
}

在配置类上标注@Import注解

package com.atguigu.config;

import com.atguigu.bean.Color;
import com.atguigu.bean.Red;
import org.springframework.context.annotation.*;

@Configuration
@Import(value = {Color.class, Red.class})
public class JavaConfig2 {

}

测试方法:

@Test
public void testImport() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig2.class);
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
        System.out.println(name);
        //javaConfig2
        //com.atguigu.bean.Color
        //com.atguigu.bean.Red
    }
}

1.8、@Import使用ImportSelector

package com.atguigu.condition;

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

//自定义逻辑,返回需要导入的组件
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.atguigu.bean.Blue", "com.atguigu.bean.Yellow"};
    }
}
package com.atguigu.bean;

//配合ImportSelector接口
public class Blue {
}
package com.atguigu.bean;


public class Yellow {
}

在配置类上添加ImportSelect

package com.atguigu.config;

import com.atguigu.bean.Color;
import com.atguigu.bean.Red;
import org.springframework.context.annotation.*;

@Configuration
@Import(value = {Color.class, Red.class, MyImportSelector.class})
public class JavaConfig2 {

}

测试方法:

@Test
public void testImport() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig2.class);
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
        System.out.println(name);
        //javaConfig2
        //com.atguigu.bean.Color
        //com.atguigu.bean.Red
        //com.atguigu.bean.Blue
        //com.atguigu.bean.Yellow
    }
}

从运行结果可以看到,导入容器的是返回的全类名数组,而不是MyImportSelector类。

1.9、@Import使用ImportBeanDefinitionRegistrar

package com.atguigu.condition;

import com.atguigu.bean.Rainbow;
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 MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("com.atguigu.bean.Red");
        boolean blue = registry.containsBeanDefinition("com.atguigu.bean.Blue");
        if (red && blue) {
            registry.registerBeanDefinition("rainbow", new RootBeanDefinition(Rainbow.class));
        }
    }
}
package com.atguigu.bean;


public class Rainbow {
}

配置类上标注ImportBeanDefinitionRegistrar

package com.atguigu.config;

import com.atguigu.bean.Color;
import com.atguigu.bean.Red;
import org.springframework.context.annotation.*;

@Configuration
@Import(value = {Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegister.class})
public class JavaConfig2 {

}

测试方法:

@Test
public void testImport() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig2.class);
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
        System.out.println(name);
        //javaConfig2
        //com.atguigu.bean.Color
        //com.atguigu.bean.Red
        //com.atguigu.bean.Blue
        //com.atguigu.bean.Yellow
        //rainbow
    }
}

1.10、使用FactoryBean注册组件

package com.atguigu.bean;

import org.springframework.beans.factory.FactoryBean;

//创建一个Spring定义的工厂bean
public class ColorFactoryBean implements FactoryBean<Color> {
    //返回一个Color对象,这个对象会添加到容器中
    @Override
    public Color getObject() throws Exception {
        System.out.println("调用了ColorFactoryBean的getObject()方法");
        return new Color();
    }

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

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

在配置类中添加注册FactoryBean对象

package com.atguigu.config;

import com.atguigu.bean.ColorFactoryBean;
import org.springframework.context.annotation.*;

@Configuration
public class JavaConfig2 {
    @Bean
    public ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }
}

测试方法:

@Test
public void test() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig2.class);
    //工厂bean调用的是getObject()方法创建的对象
    Object colorFactoryBean = context.getBean("colorFactoryBean");
    System.out.println(colorFactoryBean.getClass());
    //调用了ColorFactoryBean的getObject()方法
    //class com.atguigu.bean.Color

    //如果想要获取ColorFactoryBean的对象
    Object bean = context.getBean("&colorFactoryBean");
    System.out.println(bean.getClass());
    //class com.atguigu.bean.ColorFactoryBean
}

虽然注册的是ColorFactoryBean,但是默认获取到的是Color实例。

猜你喜欢

转载自www.cnblogs.com/shawnyue-08/p/12426302.html