spring注解驱动第二节之Bean的导入

写在前面

上一节对配置类和注入Bean的方式有一个最基本的认识,从这里开始讲解,想要往IOC容器中添加我们自定义的类的方式一共有哪些

2. Bean的导入

2.1 @ComponentScan包扫描

该注解需要配合@Configuration标识的配置类上使用才会生效,它的作用是指定spring的扫描包,则在扫描路径下的被特定注解标识的类就会被自动纳入到IOC中,如我们熟知的@Repository@Service@Controller等,而不需要每个都使用@Bean注解一个个注入。
*属性详解
value
指定要扫描的包
excludeFilters
指定扫描的时候按照什么规则排除那些组件
includeFilters
指定扫描的时候只需要包含哪些组件,如果需要生效需要加入useDefaultFilters = false属性
FilterType.ANNOTATION
按照注解
FilterType.ASSIGNABLE_TYPE
按照给定的类
FilterType.ASPECTJ
使用ASPECTJ表达式
FilterType.REGEX
使用正则指定
FilterType.CUSTOM 使用自定义规则

  • 添加一个UserController.java来测试排除注解类型为@Controller的类到容器中
package com.ddf.spring.annotation.controller;

import org.springframework.stereotype.Controller;

/**
 * @author DDf on 2018/7/19
 * 该类的作用是为了测试排除所有@Controller注解的类不需要注册到IOC容器中,
 * 详见{@link com.ddf.spring.annotation.configuration.AnnotationConfiguration}
 */
@Controller
public class UserController {
    public UserController() {
        System.out.println("UserController创建完成.................");
    }
}
  • 添加一个UserSerivce,使用@Service注解标注来测试扫描注入
package com.ddf.spring.annotation.service;

import org.springframework.stereotype.Service;

/**
 * @author DDf on 2018/7/19
 * 默认@Scope为singleton,IOC容器启动会创建该bean的实例,并且以后再次使用不会重新创建新的实例
 */
@Service
public class UserService {
    public UserService() {
        System.out.println("UserService创建完成...................");
    }
}
  • 添加一个实现了TypeFilter接口的类,来自定义过滤规则ExcludeTypeFilter.java
package com.ddf.spring.annotation.configuration;

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;

/**
 * @author DDf on 2018/7/19
 */
public class ExcludeTypeFilter implements TypeFilter {
    @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 true 还是return false是有讲究的,比如把这个规则当做excludeFilters,那么返回true,则匹配的会过滤掉,如果把这个规则
        // 应用到includeFilters,如果返回true,则是会加入到容器中
        if (className.contains("ExcludeFilter")) {
            return true;
        }
        return false;
    }
}
  • 添加一个符合ExcludeTypeFilter.java规则的@Service类,这个类本身应该会被纳入到容器中,在下面会演示如何根据自定义规则不纳入到容器中
package com.ddf.spring.annotation.service;

import org.springframework.stereotype.Service;

/**
 * @author DDf on 2018/7/19
 * 该类的作用请参考{@link com.ddf.spring.annotation.configuration.ExcludeTypeFilter}是为了测试自定义规则决定是否导入某些bean
 */
@Service
public class ExcludeFilterService {
    public ExcludeFilterService() {
        System.out.println("ExcludeFilterService创建完成...............");
    }
}

修改AnnotationConfiguration.java

package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.service.IncludeFilterService;
import com.ddf.spring.annotation.entity.User;
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;

/**
 * @author DDf on 2018/7/19
 * @Configuration 表明当前类是一个配置类
 * @ComponentScan 指定扫描的包路径,并且配置了excludeFilters来排除注解类型为@Controller的不纳入容器中,
 * 排除符合自定义ExcludeTypeFilter类中规则的类
 */
@Configuration
@ComponentScan(value = "com.ddf.spring.annotation", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
        @ComponentScan.Filter(type=FilterType.CUSTOM, classes = ExcludeTypeFilter.class)
})
public class AnnotationConfiguration {

    /**
     * 注入一个Type为User(方法返回值)的bean,bean的名称为user(方法名)
     * @return
     */
    @Bean
    public User user() {
        return new User();
    }
}

运行主启动类Application.java,可以看到多注入了名称为userServicebean,标注了@ControllerUserController被排除纳入,同样满足ExcludeTypeFilter.java定义规则的标注了@ServiceExcludeFilterService.java也被排除在外

-----------------------IOC容器初始化-------------------------
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.Application
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ExcludeTypeFilter
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.controller.UserController
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.entity.User
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.ExcludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.IncludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.UserService
七月 19, 2018 11:53:53 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
UserService创建完成...................
User创建完成...............
-----------------------IOC容器初始化完成-------------------------
bean name:org.springframework.context.annotation.internalConfigurationAnnotationProcessor, type: class org.springframework.context.annotation.ConfigurationClassPostProcessor
bean name:org.springframework.context.annotation.internalAutowiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalRequiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalCommonAnnotationProcessor, type: class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
bean name:org.springframework.context.event.internalEventListenerProcessor, type: class org.springframework.context.event.EventListenerMethodProcessor
bean name:org.springframework.context.event.internalEventListenerFactory, type: class org.springframework.context.event.DefaultEventListenerFactory
bean name:annotationConfiguration, type: class com.ddf.spring.annotation.configuration.AnnotationConfiguration$$EnhancerBySpringCGLIB$$19c06e9b
bean name:userService, type: class com.ddf.spring.annotation.service.UserService
bean name:user, type: class com.ddf.spring.annotation.entity.User
2.2 @Scope Bean的作用域

Spring初始化容器组件默认的scope都是singleton,即单实例。在容器初始化的时候就会把所有单实例的bean初始化创建出来,以后每次在使用的时候不会重新创建一个对象,可以通过@Scope来修改默认的作用域,作用域有以下属性
singleton 默认属性,单实例
prototype 启动创建,每次使用都是创建一个新的实例
request 基于HttpServletRequest,每次请求创建一个实例
session 基于HttpSession,每个session作用域下使用同一个实例

  • 新建一个Service使用@Scope("prototype")注解修饰, PrototypeScopeService.java
package com.ddf.spring.annotation.service;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

/**
 * @author DDf on 2018/7/21
 * 测试@Scope的作用域为prototype,每次使用该bean都会重新生成一个实例
 */
@Service
@Scope("prototype")
public class PrototypeScopeService {
    public PrototypeScopeService() {
        System.out.println("PrototypeScopeService创建完成。。。。。。。。");
    }
}
  • 使用上面建立的UserService.java来作为对比测试
package com.ddf.spring.annotation.service;

import org.springframework.stereotype.Service;

/**
 * @author DDf on 2018/7/19
 * 默认@Scope为singleton,IOC容器启动会创建该bean的实例,并且以后再次使用不会重新创建新的实例
 */
@Service
public class UserService {
    public UserService() {
        System.out.println("UserService创建完成...................");
    }
}
  • 修改主启动类Application.java,添加测试@Scope的方法testPrototypeScopeService
package com.ddf.spring.annotation;

import com.ddf.spring.annotation.service.PrototypeScopeService;
import com.ddf.spring.annotation.service.UserService;
import com.ddf.spring.annotation.configuration.AnnotationConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author DDf on 2018/7/19
 */
public class Application {
    public static void main(String[] args) {
        System.out.println("-----------------------IOC容器初始化-------------------------");
        // 创建一个基于配置类启动的IOC容器,如果主配置类扫描包的路径下包含其他配置类,则其他配置类可以被自动识别
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationConfiguration.class);
        System.out.println("-----------------------IOC容器初始化完成-------------------------");
        // 获取当前IOC中所有bean的名称
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        // 打印当前IOC中对应名称的bean和bean的类型
        for (String name : definitionNames) {
            Object bean = applicationContext.getBean(name);
            System.out.println("bean name:" + name + ", type: " + bean.getClass());
        }

        // 测试@Scope bean的作用域
        testPrototypeScopeService(applicationContext);

    }


    /**
     * 测试@Scope bean的作用域
     * @param applicationContext
     */
    public static void testPrototypeScopeService(ApplicationContext applicationContext) {
        System.out.println("-----------------------测试@Scope-------------------------");
        UserService userService = (UserService) applicationContext.getBean("userService");
        UserService userService1 = applicationContext.getBean(UserService.class);
        System.out.println("默认单实例bean UserService是否相等 " + (userService == userService1));

        PrototypeScopeService prototypeScopeService = applicationContext.getBean(PrototypeScopeService.class);
        PrototypeScopeService prototypeScopeService1 = applicationContext.getBean(PrototypeScopeService.class);
        System.out.println("PrototypeScopeService prototype scope作用域是否相等: " + (prototypeScopeService == prototypeScopeService1));
    }
}
  • 测试结果如控制台打印所示
-----------------------IOC容器初始化-------------------------
七月 21, 2018 6:52:36 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Sat Jul 21 18:52:36 CST 2018]; root of context hierarchy
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.Application
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ExcludeTypeFilter
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.controller.UserController
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.entity.User
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.ExcludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.IncludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.PrototypeScopeService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.UserService
七月 21, 2018 6:52:37 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
UserService创建完成...................
User创建完成...............
-----------------------IOC容器初始化完成-------------------------
bean name:org.springframework.context.annotation.internalConfigurationAnnotationProcessor, type: class org.springframework.context.annotation.ConfigurationClassPostProcessor
bean name:org.springframework.context.annotation.internalAutowiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalRequiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalCommonAnnotationProcessor, type: class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
bean name:org.springframework.context.event.internalEventListenerProcessor, type: class org.springframework.context.event.EventListenerMethodProcessor
bean name:org.springframework.context.event.internalEventListenerFactory, type: class org.springframework.context.event.DefaultEventListenerFactory
bean name:annotationConfiguration, type: class com.ddf.spring.annotation.configuration.AnnotationConfiguration$$EnhancerBySpringCGLIB$$5ee69aa7
PrototypeScopeService创建完成。。。。。。。。
bean name:prototypeScopeService, type: class com.ddf.spring.annotation.service.PrototypeScopeService
bean name:userService, type: class com.ddf.spring.annotation.service.UserService
bean name:user, type: class com.ddf.spring.annotation.entity.User

-----------------------测试@Scope开始-------------------------
默认单实例bean UserService是否相等 true
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService prototype scope作用域是否相等: false
-----------------------测试@Scope结束-------------------------
2.3 @Lazy 单实例Bean的懒加载

默认情况下所有的单实例bean都会在容器启动的时候创建出来,使用的时候不会直接获取不会重复创建,但是可以通过@Lazy注解来让容器启动的时候不创建某些类,而是等待第一次获取使用该类的时候才创建该Bean对象.
* 创建LazyBeanService.java,使用@Lazy@Service注解修饰来懒加载注入

package com.ddf.spring.annotation.service;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

/**
 * @author DDf on 2018/7/21
 * 测试单实例bean的@Lazy IOC容器启动的时候不创建该bean,只有到第一次获取的时候才创建
 * 但当第一次使用这个bean的时候再创建,以后使用不再创建
 */
@Service
@Lazy
public class LazyBeanService {
    public LazyBeanService() {
        System.out.println("LazyBeanService创建完成...............");
    }
}
  • 修改主启动类Application.java,增加测试懒加载的方法testLazyBeanService,需要注意的是主启动类在以前的代码中为了测试方便,在启动后获取了所有预定义的bean的名称然后获取打印了出来,为了更清楚的测试懒加载这一块的getBean()需要注释掉,因为这里会牵扯到获取创建的问题
package com.ddf.spring.annotation;

import com.ddf.spring.annotation.service.LazyBeanService;
import com.ddf.spring.annotation.service.PrototypeScopeService;
import com.ddf.spring.annotation.service.UserService;
import com.ddf.spring.annotation.configuration.AnnotationConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author DDf on 2018/7/19
 */
public class Application {
    public static void main(String[] args) {
        System.out.println("-----------------------IOC容器初始化-------------------------");
        // 创建一个基于配置类启动的IOC容器,如果主配置类扫描包的路径下包含其他配置类,则其他配置类可以被自动识别
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationConfiguration.class);
        System.out.println("-----------------------IOC容器初始化完成-------------------------");
        // 获取当前IOC中所有bean的名称,即使是懒加载类型的bean也会获取到
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        // 打印当前IOC中对应名称的bean和bean的类型
        /*for (String name : definitionNames) {
            // 这个会影响到测试懒加载的效果,如果需要测试懒加载,这行代码需要注释掉,因为getBean方法一旦调用则会初始化
            Object bean = applicationContext.getBean(name);
            System.out.println("bean name:" + name + ", type: " + bean.getClass());
        }*/

        // 测试@Scope bean的作用域
        testPrototypeScopeService(applicationContext);
        // 测试单实例bean的@Lazy懒加载
        testLazyBeanService(applicationContext);

    }


    /**
     * 测试@Scope bean的作用域
     * @param applicationContext
     */
    public static void testPrototypeScopeService(ApplicationContext applicationContext) {
        System.out.println("-----------------------测试@Scope-------------------------");
        UserService userService = (UserService) applicationContext.getBean("userService");
        UserService userService1 = applicationContext.getBean(UserService.class);
        System.out.println("默认单实例bean UserService是否相等 " + (userService == userService1));

        PrototypeScopeService prototypeScopeService = applicationContext.getBean(PrototypeScopeService.class);
        PrototypeScopeService prototypeScopeService1 = applicationContext.getBean(PrototypeScopeService.class);
        System.out.println("PrototypeScopeService prototype scope作用域是否相等: " + (prototypeScopeService == prototypeScopeService1));
    }

    /**
     * 测试单实例bean的懒加载,只有等使用的时候再创建实例。
     * IOC容器启动后不会创建该bean的实例,如果是在该方法中才创建这个bean的实例,并且获得的两个bean是同一个的话,则测试通过。
     */
    public static void testLazyBeanService(ApplicationContext applicationContext) {
        System.out.println("---------------测试单实例bean的@Lazy懒加载----------------------");
        LazyBeanService lazyBeanService = applicationContext.getBean(LazyBeanService.class);
        LazyBeanService lazyBeanService1 = applicationContext.getBean(LazyBeanService.class);
        System.out.println("lazyBeanService==lazyBeanService1?: " + (lazyBeanService == lazyBeanService1));
    }
}
  • 启动后查看控制台日志,LazyBeanService直到第一次获取的时候才被创建,并且是单实例
-----------------------IOC容器初始化-------------------------
七月 21, 2018 9:29:55 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Sat Jul 21 21:29:55 CST 2018]; root of context hierarchy
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.Application
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ExcludeTypeFilter
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.controller.UserController
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.entity.User
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.ExcludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.IncludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.LazyBeanService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.PrototypeScopeService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.UserService
七月 21, 2018 9:29:55 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
UserService创建完成...................
User创建完成...............
-----------------------IOC容器初始化完成-------------------------

-----------------------测试@Scope开始-------------------------
默认单实例bean UserService是否相等 true
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService prototype scope作用域是否相等: false
-----------------------测试@Scope结束-------------------------


---------------测试单实例bean的@Lazy懒加载开始----------------------
lazyBeanService==lazyBeanService1?: true
---------------测试单实例bean的@Lazy懒加载结束----------------------

Process finished with exit code 0
2.4 @Conditional 动态判断是否导入Bean

@Conditional注解可以和导入Bean的注解组合使用,导入Bean的注解的作用是为容器中添加一个Bean,而且只要使用了注解就无条件的导入,而@Conditional注解可以指定一个类,该类必须实现org.springframework.context.annotation.Condition接口,重写matches方法返回true,则可以正常导入组件,但是如果返回false,则不能导入组件。而且该Condition类中还可以额外注册一些组件,这对于某些组件是否导入是根据某些条件导入的,而且导入之后还需要附加某些组件来说是很有用处的。

现在来模拟一个场景,有两个需要动态导入的Bean,一个是DevelopmentBean,该Bean有一个附加BeanDevelopmentBeanLog,同时还有一个Bean名为ProductionBean,该Bean同样也有一个附加Bean名为ProductionBeanLog。现在的需求是根据不同的环境,如果是dev环境则注册的DevelopmentBean会生效,同时导入DevelopmentBeanLog。如果当前环境是prd,则注册的ProductionBean会生效,同时会导入ProductionBeanLog。环境的切换应该由外部配置文件来生效,但是为了方便测试,也为了不改代码就能够测试到两种不同的环境,所以使用当前时间的分钟,如果使偶数,则dev环境生效,如果使奇数,则prd环境生效;
* 创建DevelopmentBean.java

package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/7/21
 */
public class DevelopmentBean {
    public DevelopmentBean() {
        System.out.println("DevelopmentBean创建完成.............");
    }
}
  • 创建DevelopmentBeanLog.java
package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/7/21
 */
public class DevelopmentBeanLog {
    public DevelopmentBeanLog() {
        System.out.println("DevelopmentBeanLog创建完成...............");
    }
}
  • 创建ProductionBean.java
package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/7/21
 */
public class ProductionBean {
    public ProductionBean() {
        System.out.println("ProductionBean创建完成.............");
    }
}
  • 创建ProductionBeanLog.java
package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/7/21
 */
public class ProductionBeanLog {
    public ProductionBeanLog() {
        System.out.println("ProductionBeanLog创建完成...................");
    }
}
  • 创建dev环境生效的判断条件和导入组件类DevelopmentProfileCondition.java
package com.ddf.spring.annotation.configuration;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.time.LocalDateTime;

/**
 * @author DDf on 2018/7/21
 * 创建根据条件来判断是否导入某些组件,该类需要配合@Condition注解,@Condition注解需要用在要导入容器的地方,与导入组件注解组合使用,如果当前类
 * 返回true,则可以导入组件,反之,则不能。
 */
public class DevelopmentProfileCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取到bean定义的注册类(可以获取bean,注册bean,删除预定义的bean名称)
        BeanDefinitionRegistry registry = context.getRegistry();
        // 获取IOC容器使用的beanfactory(可以获取bean的定义信息,可以获取到bean的定义注册类)
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 可以获取到环境变量
        ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment();
        // 根据当前时间的分钟动态切换环境变量的值
        LocalDateTime localDateTime = LocalDateTime.now();
        int minute = localDateTime.getMinute();
        String profile;
        if (minute % 2 == 0) {
            profile = "dev";
        } else {
            profile = "prd";
        }
        System.out.println("DevelopmentProfileCondition profile: " + profile);
        // 如果是dev环境,并且当前IOC容器中未定义ProductionBean则返回true,同时注册一个DevelopmentBeanLog
        if ("dev".equals(profile)) {
            if (!registry.containsBeanDefinition("ProductionBean")) {
                RootBeanDefinition devServiceLogBean = new RootBeanDefinition(
                        "com.ddf.spring.annotation.bean.DevelopmentBeanLog");
                registry.registerBeanDefinition("DevelopmentBeanLog", devServiceLogBean);
                return true;
            }
        }
        return false;
    }
}
  • 创建prd环境生效的判断条件和导入组件类ProductionProfileCondition.java
package com.ddf.spring.annotation.configuration;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.time.LocalDateTime;

/**
 * @author DDf on 2018/7/21
 * 创建根据条件来判断是否导入某些组件,该类需要配合@Condition注解,@Condition注解需要用在要导入容器的地方,与导入组件注解组合使用,如果当前类
 * 返回true,则可以导入组件,反之,则不能。
 */
public class ProductionProfileCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取到bean定义的注册类(可以获取bean,注册bean,删除预定义的bean名称)
        BeanDefinitionRegistry registry = context.getRegistry();
        // 获取IOC容器使用的beanfactory(可以获取bean的定义信息,可以获取到bean的定义注册类)
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 可以获取到环境变量
        ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment();
        // 根据当前时间的分钟动态切换环境变量的值
        LocalDateTime localDateTime = LocalDateTime.now();
        int minute = localDateTime.getMinute();
        String profile;
        if (minute % 2 == 0) {
            profile = "dev";
        } else {
            profile = "prd";
        }
        System.out.println("ProductionProfileCondition profile: " + profile);
        // 如果是prd环境,并且当前IOC容器中未定义DevelopmentBean则返回true,同时注册一个ProductionBeanLog
        if ("prd".equals(profile)) {
            // 如果是prd环境,并且当前IOC容器中未定义DevelopmentBean则返回true,同时注册一个DevelopmentBeanLog
            if (!registry.containsBeanDefinition("DevelopmentBean")) {
                RootBeanDefinition prdServiceLogBean = new RootBeanDefinition(
                        "com.ddf.spring.annotation.bean.ProductionBeanLog");
                registry.registerBeanDefinition("prdServiceLog", prdServiceLogBean);
                return true;
            }
        }
        return false;
    }
}
  • 修改主配置类AnnotationConfiguration,使用@Bean注解和@Condition注解分别标注对应的类
package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.bean.DevelopmentBean;
import com.ddf.spring.annotation.bean.ProductionBean;
import com.ddf.spring.annotation.entity.User;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;

/**
 * @author DDf on 2018/7/19
 * @Configuration 表明当前类是一个配置类
 * @ComponentScan 指定扫描的包路径,并且配置了excludeFilters来排除注解类型为@Controller的不纳入容器中,
 * 排除符合自定义ExcludeTypeFilter类中规则的类
 */
@Configuration
@ComponentScan(value = "com.ddf.spring.annotation", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
        @ComponentScan.Filter(type=FilterType.CUSTOM, classes = ExcludeTypeFilter.class)
})
public class AnnotationConfiguration {

    /**
     * 注入一个Type为User(方法返回值)的bean,bean的名称为user(方法名)
     * @return
     */
    @Bean
    public User user() {
        return new User();
    }


    /**
     * 满足{@link DevelopmentProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
     * @return
     */
    @Bean
    @Conditional({DevelopmentProfileCondition.class})
    public DevelopmentBean DevelopmentBean() {
        System.out.println("-------------------------测试@Conditional------------------");
        return new DevelopmentBean();
    }


    /**
     * 满足{@link ProductionProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
     * @return
     */
    @Bean
    @Conditional({ProductionProfileCondition.class})
    public ProductionBean ProductionBean() {
        System.out.println("-------------------------测试@Conditional------------------");
        return new ProductionBean();
    }
}
  • 运行主启动类Application.java,控制台效果如下,通过控制台可以看到,虽然在配置类中定义了ProductionBean()DevelopmentBean()两个方法,但是根据当前生效的环境,最终只会有一个被成功注入,另外一个就被忽略了,而且也可以看到在Condition类中注册的类比方法体中注册的要早。
-----------------------IOC容器初始化-------------------------
七月 21, 2018 10:54:48 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Sat Jul 21 22:54:48 CST 2018]; root of context hierarchy
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.Application
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.DevelopmentProfileCondition
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ExcludeTypeFilter
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ProductionProfileCondition
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.controller.UserController
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.entity.User
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.DevelopmentBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.DevelopmentBeanLog
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.ExcludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.IncludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.LazyBeanService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ProductionBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ProductionBeanLog
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.PrototypeScopeService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.UserService
DevelopmentProfileCondition profile: dev
ProductionProfileCondition profile: dev
七月 21, 2018 10:54:48 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
UserService创建完成...................
User创建完成...............
DevelopmentBeanLog创建完成...............
-------------------------测试@Conditional------------------
DevelopmentBean创建完成.............
-----------------------IOC容器初始化完成-------------------------
bean name:org.springframework.context.annotation.internalConfigurationAnnotationProcessor, type: class org.springframework.context.annotation.ConfigurationClassPostProcessor
bean name:org.springframework.context.annotation.internalAutowiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalRequiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalCommonAnnotationProcessor, type: class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
bean name:org.springframework.context.event.internalEventListenerProcessor, type: class org.springframework.context.event.EventListenerMethodProcessor
bean name:org.springframework.context.event.internalEventListenerFactory, type: class org.springframework.context.event.DefaultEventListenerFactory
bean name:annotationConfiguration, type: class com.ddf.spring.annotation.configuration.AnnotationConfiguration$$EnhancerBySpringCGLIB$$75d3a279
LazyBeanService创建完成...............
bean name:lazyBeanService, type: class com.ddf.spring.annotation.service.LazyBeanService
PrototypeScopeService创建完成。。。。。。。。
bean name:prototypeScopeService, type: class com.ddf.spring.annotation.service.PrototypeScopeService
bean name:userService, type: class com.ddf.spring.annotation.service.UserService
bean name:user, type: class com.ddf.spring.annotation.entity.User
bean name:DevelopmentBeanLog, type: class com.ddf.spring.annotation.bean.DevelopmentBeanLog
bean name:DevelopmentBean, type: class com.ddf.spring.annotation.bean.DevelopmentBean

-----------------------测试@Scope开始-------------------------
默认单实例bean UserService是否相等 true
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService prototype scope作用域是否相等: false
-----------------------测试@Scope结束-------------------------


---------------测试单实例bean的@Lazy懒加载开始----------------------
lazyBeanService==lazyBeanService1?: true
---------------测试单实例bean的@Lazy懒加载结束----------------------

Process finished with exit code 0
2.5 @Import导入组件

该注解需要标注在带有@Configuration的类上,@Import的value属性可以直接指定一个普通的java类,即可直接导入组件,也可以实现ImportSelector接口来导入一组组件,也可以实现ImportBeanDefinitionRegistrar接口来获得当前注解信息,通过BeanDefinitionRegistry来注册组件,这种方式可以指定导入的组件ID

2.5.1 @Import
  • 新建一个普通的类ImportBean.java,用来实验直接通过@Import导入
package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/7/30
 */
public class ImportBean {
    public ImportBean() {
        System.out.println("ImportBean创建完成(测试@Import导入组件)..........");
    }
}
  • Configuration类参考2.5.4
2.5.2 @Import + ImportSelector
  • 新建一个CustomImportSelector.java实现ImportSelector接口
package com.ddf.spring.annotation.configuration;

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

import java.util.Set;

/**
 * @author DDf on 2018/7/30
 * 测试和@Import一起使用通过ImportSelector来导入组件
 */
public class CustomImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
        // 做个简单的判断,如果标注了@Import注解的类上还有指定的另外一个注解,则导入一些组件,不可以自定义bean的名称
        if (annotationTypes.contains("org.springframework.context.annotation.Import")) {
            return new String[]{"com.ddf.spring.annotation.bean.ImportSelectorBean"};
        }
        return new String[0];
    }
}
  • Configuration类参考2.5.4
2.5.3 @Import + ImportBeanDefinitionRegistrar
  • 新建一个CustomImportBeanDefinitionRegistrar.java实现ImportBeanDefinitionRegistrar接口
package com.ddf.spring.annotation.configuration;

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 DDf on 2018/7/31
 * 测试使用@Import注解结合ImportBeanDefinitionRegistrar
 */
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
        // 做个简单的判断,如果标注了@Import注解的类上还有指定的另外一个注解,则导入一些组件
        if (annotationTypes.contains("org.springframework.context.annotation.Import")) {
            // 通过BeanDefinitionRegistry导入一个组件
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(
                    "com.ddf.spring.annotation.bean.ImportBeanDefinitionRegistrarBean");
            // 通过BeanDefinitionRegistry导入的组件可以自定义bean的名称
            beanDefinitionRegistry.registerBeanDefinition("importBeanDefinitionRegistrarBean", rootBeanDefinition);
        }
    }
}
  • Configuration类参考2.5.4
2.5.4 修改AnnotationConfiguration.java配置类,在前面的基础上加上@Import的注解
package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.bean.DevelopmentBean;
import com.ddf.spring.annotation.bean.ImportBean;
import com.ddf.spring.annotation.bean.ProductionBean;
import com.ddf.spring.annotation.entity.User;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;

/**
 * @author DDf on 2018/7/19
 * @Configuration 表明当前类是一个配置类
 * @ComponentScan 指定扫描的包路径,并且配置了excludeFilters来排除注解类型为@Controller的不纳入容器中,
 * 排除符合自定义ExcludeTypeFilter类中规则的类
 * @Import 导入组件,可以直接导入普通类,或者通过ImportSelector接口或者ImportBeanDefinitionRegistrar接口来自定义导入
 */
@Configuration
@ComponentScan(value = "com.ddf.spring.annotation", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = ExcludeTypeFilter.class)
})
@Import(value = {ImportBean.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class AnnotationConfiguration {

    /**
     * 注入一个Type为User(方法返回值)的bean,bean的名称为user(方法名)
     *
     * @return
     */
    @Bean
    public User user() {
        return new User();
    }


    /**
     * 测试@Conditional 满足{@link DevelopmentProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
     *
     * @return
     */
    @Bean
    @Conditional({DevelopmentProfileCondition.class})
    public DevelopmentBean DevelopmentBean() {
        return new DevelopmentBean();
    }


    /**
     * 满足{@link ProductionProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
     *
     * @return
     */
    @Bean
    @Conditional({ProductionProfileCondition.class})
    public ProductionBean ProductionBean() {
        return new ProductionBean();
    }
}
2.5.5 演示结果
  • 启动主类Application.java,打印如下,可以看到ImportBean.java ImportSelectorBean ImportBeanDefinitionRegistrarBean在IOC容器初始化的时候创建成功。
-----------------------IOC容器初始化-------------------------
八月 01, 2018 5:43:40 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Wed Aug 01 17:43:40 CST 2018]; root of context hierarchy
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.Application
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.CustomImportBeanDefinitionRegistrar
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.CustomImportSelector
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.DevelopmentProfileCondition
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ExcludeTypeFilter
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.FactoryPrototypeBeanConfiguration
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.FactorySingletonBeanConfiguration
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ProductionProfileCondition
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.controller.UserController
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.entity.User
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.DevelopmentBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.DevelopmentBeanLog
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.ExcludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.FactoryPrototypeBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.FactorySingletonBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ImportBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ImportBeanDefinitionRegistrarBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ImportSelectorBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.IncludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.LazyBeanService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ProductionBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ProductionBeanLog
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.PrototypeScopeService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.UserService
DevelopmentProfileCondition profile: prd
ProductionProfileCondition profile: prd
八月 01, 2018 5:43:40 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
UserService创建完成...................
ImportBean创建完成(测试@Import导入组件)..........
ImportSelectorBean创建完成,测试@Import通过ImportSelector接口导入组件
User创建完成...............
ProductionBeanLog创建完成.......测试@Condition............
ProductionBean创建完成......测试@Condition.......
ImportBeanDefinitionRegistrarBean创建完成,测试@Import接口通过ImportBeanDefinitionRegistrar接口注入组件。。。。。
-----------------------IOC容器初始化完成-------------------------

bean name:org.springframework.context.annotation.internalConfigurationAnnotationProcessor, type: class org.springframework.context.annotation.ConfigurationClassPostProcessor
bean name:org.springframework.context.annotation.internalAutowiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalRequiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalCommonAnnotationProcessor, type: class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
bean name:org.springframework.context.event.internalEventListenerProcessor, type: class org.springframework.context.event.EventListenerMethodProcessor
bean name:org.springframework.context.event.internalEventListenerFactory, type: class org.springframework.context.event.DefaultEventListenerFactory
bean name:annotationConfiguration, type: class com.ddf.spring.annotation.configuration.AnnotationConfiguration$$EnhancerBySpringCGLIB$$d7abd5ba
LazyBeanService创建完成...............
bean name:lazyBeanService, type: class com.ddf.spring.annotation.service.LazyBeanService
PrototypeScopeService创建完成。。。。。。。。
bean name:prototypeScopeService, type: class com.ddf.spring.annotation.service.PrototypeScopeService
bean name:userService, type: class com.ddf.spring.annotation.service.UserService
bean name:com.ddf.spring.annotation.bean.ImportBean, type: class com.ddf.spring.annotation.bean.ImportBean
bean name:com.ddf.spring.annotation.bean.ImportSelectorBean, type: class com.ddf.spring.annotation.bean.ImportSelectorBean
bean name:user, type: class com.ddf.spring.annotation.entity.User
bean name:prdServiceLog, type: class com.ddf.spring.annotation.bean.ProductionBeanLog
bean name:ProductionBean, type: class com.ddf.spring.annotation.bean.ProductionBean
bean name:importBeanDefinitionRegistrarBean, type: class com.ddf.spring.annotation.bean.ImportBeanDefinitionRegistrarBean

-----------------------测试@Scope开始-------------------------
默认单实例bean UserService是否相等 true
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService prototype scope作用域是否相等: false
-----------------------测试@Scope结束-------------------------


---------------测试单实例bean的@Lazy懒加载开始----------------------
lazyBeanService==lazyBeanService1?: true
---------------测试单实例bean的@Lazy懒加载结束----------------------

Process finished with exit code 0
2.6 通过FactoryBean接口注入组件

通过FactoryBean工厂注册组件,该类本身需要注册到IOC容器中,如在类上标注@Component等或者使用@Bean注解,实际在IOC中注册的组件为FactoryBean中接口的方法来决定而非FactoryBean类本身,通过这种方式创建的组件可以isSingleton方法来决定组件是否为单实例,如果为单实例则同时该Bean是以懒加载的方式注册的

  • 创建FactorySingletonBean.java,用来演示单实例bean
package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/7/31
 * 该类用于测试使用FactoryBean来注册单实例组件
 */
public class FactorySingletonBean {
    public FactorySingletonBean() {
        System.out.println("FactorySingletonBean创建完成。。。。,测试通过FactoryBean来注册单实例组件。。。。");
    }
}
  • 创建FactorySingletonBeanFactoryBean接口,FactorySingletonBeanConfiguration.java
package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.bean.FactorySingletonBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

/**
 * @author DDf on 2018/8/1
 * 通过FactoryBean工厂注册组件,该类本身需要注册到IOC容器中
 * 实际在IOC中注册的组件为FactoryBean中接口的方法来决定
 */
// @Component
public class FactorySingletonBeanConfiguration implements FactoryBean<FactorySingletonBean> {
    /**
     * 要注册的组件
     * @return
     */
    @Override
    public FactorySingletonBean getObject() {
        return new FactorySingletonBean();
    }

    /**
     * 要注册的组件类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return FactorySingletonBean.class;
    }

    /**
     * 要注册的组件是否是单实例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}
  • 创建FactoryPrototypeBean.java,用来演示非单实例bean
package com.ddf.spring.annotation.service;

/**
 * @author DDf on 2018/8/1
 * 该类用于测试使用FactoryBean来注册每个请求重新创建组件
 */
public class FactoryPrototypeBean {
    public FactoryPrototypeBean() {
        System.out.println("FactoryPrototypeBean创建完成....,测试通过FactoryBean来注册Prototype组件。。。。");
    }
}
  • 创建FactoryPrototypeBeanFactoryBean接口FactoryPrototypeBeanConfiguration.java
package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.bean.FactoryPrototypeBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

/**
 * @author DDf on 2018/8/1
 * 通过FactoryBean工厂注册组件,该类本身需要注册到IOC容器中
 * 实际在IOC中注册的组件为FactoryBean中接口的方法来决定
 */
// @Component
public class FactoryPrototypeBeanConfiguration implements FactoryBean<FactoryPrototypeBean> {

    /**
     * 要注册的组件
     * @return
     */
    @Override
    public FactoryPrototypeBean getObject() {
        return new FactoryPrototypeBean();
    }

    /**
     * 要注册的组件类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return FactoryPrototypeBean.class;
    }

    /**
     * 要注册的组件是否是单实例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}
  • 修改AnnotationConfiguration.java,使用配置类的方式将FactoryPrototypeBeanConfigurationFactorySingletonBeanConfiguration两个FactoryBean接口注册到容器中
package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.bean.DevelopmentBean;
import com.ddf.spring.annotation.bean.ImportBean;
import com.ddf.spring.annotation.bean.ProductionBean;
import com.ddf.spring.annotation.entity.User;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;

/**
 * @author DDf on 2018/7/19
 * @Configuration 表明当前类是一个配置类
 * @ComponentScan 指定扫描的包路径,并且配置了excludeFilters来排除注解类型为@Controller的不纳入容器中,
 * 排除符合自定义ExcludeTypeFilter类中规则的类
 * @Import 导入组件,可以直接导入普通类,或者通过ImportSelector接口或者ImportBeanDefinitionRegistrar接口来自定义导入
 */
@Configuration
@ComponentScan(value = "com.ddf.spring.annotation", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = ExcludeTypeFilter.class)
})
@Import(value = {ImportBean.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class AnnotationConfiguration {

    /**
     * 注入一个Type为User(方法返回值)的bean,bean的名称为user(方法名)
     *
     * @return
     */
    @Bean
    public User user() {
        return new User();
    }


    /**
     * 测试@Conditional 满足{@link DevelopmentProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
     *
     * @return
     */
    @Bean
    @Conditional({DevelopmentProfileCondition.class})
    public DevelopmentBean DevelopmentBean() {
        return new DevelopmentBean();
    }


    /**
     * 满足{@link ProductionProfileCondition} 这个类的条件返回true则当前Bean能够成功注入,反之不能
     *
     * @return
     */
    @Bean
    @Conditional({ProductionProfileCondition.class})
    public ProductionBean ProductionBean() {
        return new ProductionBean();
    }


    /**
     * 使用FactoryBean工厂来注册组件
     * @return
     */
    @Bean
    public FactoryPrototypeBeanConfiguration factoryPrototypeBeanConfiguration() {
        return new FactoryPrototypeBeanConfiguration();
    }

    /**
     * 使用FactoryBean工厂来注册组件
     * @return
     */
    @Bean
    public FactorySingletonBeanConfiguration factorySingletonBeanConfiguration() {
        return new FactorySingletonBeanConfiguration();
    }
}
  • 修改主启动类Application.java,用来验证FactoryBean创建bean的作用域
package com.ddf.spring.annotation;

import com.ddf.spring.annotation.service.*;
import com.ddf.spring.annotation.configuration.AnnotationConfiguration;
import com.ddf.spring.annotation.configuration.FactoryPrototypeBeanConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author DDf on 2018/7/19
 */
public class Application {
    public static void main(String[] args) {
        System.out.println("-----------------------IOC容器初始化-------------------------");
        // 创建一个基于配置类启动的IOC容器,如果主配置类扫描包的路径下包含其他配置类,则其他配置类可以被自动识别
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationConfiguration.class);
        System.out.println("-----------------------IOC容器初始化完成-------------------------\n");
        // 获取当前IOC中所有bean的名称,即使是懒加载类型的bean也会获取到
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        // 打印当前IOC中对应名称的bean和bean的类型
        for (String name : definitionNames) {
            // 这个会影响到测试懒加载的效果,如果需要测试懒加载,这行代码需要注释掉,因为getBean方法一旦调用则会初始化
            Object bean = applicationContext.getBean(name);
            System.out.println("bean name:" + name + ", type: " + bean.getClass());
        }

        // 测试@Scope bean的作用域
        testPrototypeScopeService(applicationContext);
        // 测试单实例bean的@Lazy懒加载
        testLazyBeanService(applicationContext);
        // 测试FactoryBean接口导入单实例与Prototype作用域的组件
        testFactoryBeanPrototypeBean(applicationContext);

    }


    /**
     * 测试@Scope bean的作用域
     *
     * @param applicationContext
     */
    public static void testPrototypeScopeService(ApplicationContext applicationContext) {
        System.out.println("\n-----------------------测试@Scope开始-------------------------");
        UserService userService = (UserService) applicationContext.getBean("userService");
        UserService userService1 = applicationContext.getBean(UserService.class);
        System.out.println("默认单实例bean UserService是否相等 " + (userService == userService1));

        PrototypeScopeService prototypeScopeService = applicationContext.getBean(PrototypeScopeService.class);
        PrototypeScopeService prototypeScopeService1 = applicationContext.getBean(PrototypeScopeService.class);
        System.out.println("PrototypeScopeService prototype scope作用域是否相等: " + (prototypeScopeService == prototypeScopeService1));
        System.out.println("-----------------------测试@Scope结束-------------------------\n");
    }

    /**
     * 测试单实例bean的懒加载,只有等使用的时候再创建实例。
     * IOC容器启动后不会创建该bean的实例,如果是在该方法中才创建这个bean的实例,并且获得的两个bean是同一个的话,则测试通过。
     */
    public static void testLazyBeanService(ApplicationContext applicationContext) {
        System.out.println("\n---------------测试单实例bean的@Lazy懒加载开始----------------------");
        LazyBeanService lazyBeanService = applicationContext.getBean(LazyBeanService.class);
        LazyBeanService lazyBeanService1 = applicationContext.getBean(LazyBeanService.class);
        System.out.println("lazyBeanService==lazyBeanService1?: " + (lazyBeanService == lazyBeanService1));
        System.out.println("---------------测试单实例bean的@Lazy懒加载结束----------------------\n");
    }


    /**
     * 测试通过FactoryBean接口导入单实例与Prototype作用域的组件,根据打印可以看出FactoryBean创建的单实例Bean都是懒加载的
     * @param applicationContext
     */
    public static void testFactoryBeanPrototypeBean(ApplicationContext applicationContext) {
        System.out.println("\n----------测试通过FactoryBean注册单实例和Prototype作用域的组件开始----------");
        FactorySingletonBean factorySingletonBean = applicationContext.getBean(FactorySingletonBean.class);
        FactorySingletonBean factorySingletonBean1 = applicationContext.getBean(FactorySingletonBean.class);

        FactoryPrototypeBean factoryPrototypeBean = applicationContext.getBean(FactoryPrototypeBean.class);
        FactoryPrototypeBean factoryPrototypeBean1 = applicationContext.getBean(FactoryPrototypeBean.class);

        System.out.println("单实例factorySingletonBean==factorySingletonBean1?" + (factorySingletonBean==factorySingletonBean1));

        System.out.println("Prototype作用域factoryPrototypeBean==factoryPrototypeBean1?" + (factoryPrototypeBean==factoryPrototypeBean1));
        System.out.println("----------测试通过FactoryBean注册单实例和Prototype作用域的组件结束----------\n");
    }
}
  • 启动测试,根据打印可以看出FactoryBean创建的单实例Bean都是懒加载的,因为在IOC容器创建的时候并没有创建,但是在获得所有定义的bean打印的时候才创建
-----------------------IOC容器初始化-------------------------
八月 01, 2018 6:06:46 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Wed Aug 01 18:06:46 CST 2018]; root of context hierarchy
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.Application
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.CustomImportBeanDefinitionRegistrar
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.CustomImportSelector
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.DevelopmentProfileCondition
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ExcludeTypeFilter
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.FactoryPrototypeBeanConfiguration
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.FactorySingletonBeanConfiguration
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.configuration.ProductionProfileCondition
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.controller.UserController
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.entity.User
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.DevelopmentBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.DevelopmentBeanLog
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.ExcludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.FactoryPrototypeBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.FactorySingletonBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ImportBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ImportBeanDefinitionRegistrarBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ImportSelectorBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.IncludeFilterService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.LazyBeanService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ProductionBean
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.bean.ProductionBeanLog
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.PrototypeScopeService
自定义扫描类规则当前扫描类为: com.ddf.spring.annotation.service.UserService
DevelopmentProfileCondition profile: dev
ProductionProfileCondition profile: dev
八月 01, 2018 6:06:46 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition
信息: Overriding bean definition for bean 'factoryPrototypeBeanConfiguration' with a different definition: replacing [Generic bean: class [com.ddf.spring.annotation.configuration.FactoryPrototypeBeanConfiguration]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\dev-tools\idea_root\spring-annotation\target\classes\com\ddf\spring\annotation\configuration\FactoryPrototypeBeanConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=annotationConfiguration; factoryMethodName=factoryPrototypeBeanConfiguration; initMethodName=null; destroyMethodName=(inferred); defined in com.ddf.spring.annotation.configuration.AnnotationConfiguration]
八月 01, 2018 6:06:46 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition
信息: Overriding bean definition for bean 'factorySingletonBeanConfiguration' with a different definition: replacing [Generic bean: class [com.ddf.spring.annotation.configuration.FactorySingletonBeanConfiguration]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\dev-tools\idea_root\spring-annotation\target\classes\com\ddf\spring\annotation\configuration\FactorySingletonBeanConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=annotationConfiguration; factoryMethodName=factorySingletonBeanConfiguration; initMethodName=null; destroyMethodName=(inferred); defined in com.ddf.spring.annotation.configuration.AnnotationConfiguration]
八月 01, 2018 6:06:47 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
UserService创建完成...................
ImportBean创建完成(测试@Import导入组件)..........
ImportSelectorBean创建完成,测试@Import通过ImportSelector接口导入组件
User创建完成...............
DevelopmentBeanLog创建完成.......测试@Condition........
DevelopmentBean创建完成.....测试@Condition........
ImportBeanDefinitionRegistrarBean创建完成,测试@Import接口通过ImportBeanDefinitionRegistrar接口注入组件。。。。。
-----------------------IOC容器初始化完成-------------------------

bean name:org.springframework.context.annotation.internalConfigurationAnnotationProcessor, type: class org.springframework.context.annotation.ConfigurationClassPostProcessor
bean name:org.springframework.context.annotation.internalAutowiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalRequiredAnnotationProcessor, type: class org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
bean name:org.springframework.context.annotation.internalCommonAnnotationProcessor, type: class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
bean name:org.springframework.context.event.internalEventListenerProcessor, type: class org.springframework.context.event.EventListenerMethodProcessor
bean name:org.springframework.context.event.internalEventListenerFactory, type: class org.springframework.context.event.DefaultEventListenerFactory
bean name:annotationConfiguration, type: class com.ddf.spring.annotation.configuration.AnnotationConfiguration$$EnhancerBySpringCGLIB$$7e17d64c
FactoryPrototypeBean创建完成....,测试通过FactoryBean来注册单实例组件。。。。
bean name:factoryPrototypeBeanConfiguration, type: class com.ddf.spring.annotation.bean.FactoryPrototypeBean
FactorySingletonBean创建完成。。。。,测试通过FactoryBean来注册单实例组件。。。。
bean name:factorySingletonBeanConfiguration, type: class com.ddf.spring.annotation.bean.FactorySingletonBean
LazyBeanService创建完成...............
bean name:lazyBeanService, type: class com.ddf.spring.annotation.service.LazyBeanService
PrototypeScopeService创建完成。。。。。。。。
bean name:prototypeScopeService, type: class com.ddf.spring.annotation.service.PrototypeScopeService
bean name:userService, type: class com.ddf.spring.annotation.service.UserService
bean name:com.ddf.spring.annotation.bean.ImportBean, type: class com.ddf.spring.annotation.bean.ImportBean
bean name:com.ddf.spring.annotation.bean.ImportSelectorBean, type: class com.ddf.spring.annotation.bean.ImportSelectorBean
bean name:user, type: class com.ddf.spring.annotation.entity.User
bean name:DevelopmentBeanLog, type: class com.ddf.spring.annotation.bean.DevelopmentBeanLog
bean name:DevelopmentBean, type: class com.ddf.spring.annotation.bean.DevelopmentBean
bean name:importBeanDefinitionRegistrarBean, type: class com.ddf.spring.annotation.bean.ImportBeanDefinitionRegistrarBean

-----------------------测试@Scope开始-------------------------
默认单实例bean UserService是否相等 true
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService创建完成。。。。。。。。
PrototypeScopeService prototype scope作用域是否相等: false
-----------------------测试@Scope结束-------------------------


---------------测试单实例bean的@Lazy懒加载开始----------------------
lazyBeanService==lazyBeanService1?: true
---------------测试单实例bean的@Lazy懒加载结束----------------------


----------测试通过FactoryBean注册单实例和Prototype作用域的组件开始----------
FactoryPrototypeBean创建完成....,测试通过FactoryBean来注册单实例组件。。。。
FactoryPrototypeBean创建完成....,测试通过FactoryBean来注册单实例组件。。。。
单实例factorySingletonBean==factorySingletonBean1?true
Prototype作用域factoryPrototypeBean==factoryPrototypeBean1?false
----------测试通过FactoryBean注册单实例和Prototype作用域的组件结束----------


Process finished with exit code 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值