Spring IOC使用手札

2019-10-09  本文已影响0人  花神子

Spring IOC

一 背景

Spring是一个非侵入的、轻量级的构建企业级应用的解决方案。它采用的是模块化设计,能够简单的开箱即用,它的无侵入设计,使得开发人员能够专注于业务逻辑的开发,而这些都是建立在IOC容器基础上的。Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

二 IOC

IoC 作为 Spring 第一个最核心的概念:IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
了解上述的IoC 我们需要理解组件时如何注册到IoC容器中的?

2.1 组件注册

Spring以往版本中会有如下诸多中方式支持组件的注册

2.1.1 XML

早期使用Spring时我们使用XML去驱动Spring,通常我们会如下使用:
使用XML中的<bean>标签去注册组件,然后使用
ClassPathXmlApplicationContext启动Spring.

SpringContext.xml :

<bean class="org.mzw.spring.annotation.entity.Person" >
        <property name="age" value="15"></property>
        <property name="name" value="maozw"></property>
</bean>

Main

/**
 * 通过xml 配置加载SpringContext
 */
private static void starterSpringrForXml() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringContext.xml");
    Person bean = applicationContext.getBean(Person.class);
    System.out.println(bean);
}

2.1.2 注解方式注册

Spring提供很多注解的方式来注册组件。
比如:@Controller@Servixw@Repository@Component...
一般规则如下:

示例代码

@Controller public class AddressController {
}

@Repository public @Data class AddressDao {
    private String label = "1"; 
}

@Service public class AddressService {
}

如果使用xml方式驱动Spring, 如要在xml配置文件中指定扫描路径

<context:component-scan base-package="org.mzw.spring"/>

使用配置类的方式驱动
下文都是采用配置类的方式驱动Spring

@Configuration 
public class SpringConfig {
}

Main

/**
 * 通过Annotation配置加载SpringContext
 */
private static void starterSpringrForAnnotation() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    Person bean = applicationContext.getBean(Person.class);
    String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
    Arrays.asList(beanNamesForType).forEach(beanNames -> System.out.println(beanNames));
    System.out.println(bean);
}
  1. 使用@Bean 组件注册
@Bean("person")
public Person person2(){
    return new Person("maozw",28);
}
  1. @Import 快速导入注册组件
    参数是个数组:需要导入的组件,导入后默认组件名称为类的全类名。
    配置类:
@Configuration 
@Import({Person.class})//组件数组 
public class SpringConfig {
}
  1. 实现ImportSelector注册组件
    实现ImportSelector,重写selectImports方法,返回需要导入的组件数组。
public class MyImportSelector implements ImportSelector {
    /**
     *
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     *
     *
     *  返回值就是注册容器的所有组件的全类名数组
     *  AnnotationMetadata: 标注@Import注解类的所有注解信息
     * @param importingClassMetadata
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"org.mzw.spring.annotation.entity.Person","org.mzw.spring.annotation.entity.Person1"};
    }
}

配置类

@Configuration 
@Import({MyImportSelector.class})
public class SpringConfig {
}
  1. 实现ImportBeanDefinitionRegistrar
    ImportBeanDefinitionRegistrar,重写registerBeanDefinitions方法手动注册组件到IOC容器

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * 
     * BeanDefinitionRegistry : BeanDefinition注册类,通过BeanDefinitionRegistry可以把所有需要的bean注册到容器中
     * 调用BeanDefinitionRegistry#registerBeanDefinition进行手动注册
     *
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry               current bean definition registry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        boolean definition = registry.containsBeanDefinition("org.mzw.spring.annotation.entity.Person");
        if (definition) {
            // 第一个参数指定 beanName
            // 第二个参数 指定Bean的定义信息
            registry.registerBeanDefinition("person", new RootBeanDefinition(Person.class));
        }
    }
}

配置类

@Configuration 
@Import({MyImportBeanDefinitionRegistrar.class})
public class SpringConfig {
}
  1. 创建一个Spring定义的FactoryBean注册组件
    使用Spring定义的FactoryBean来注册组件, 然后使用上面的诸多方法把FactoryBean注册给Spring也可以完成我们的容器调用。
public class PersonFactoryBean implements FactoryBean<Person> {
    /**
     * Return an instance (possibly shared or independent) of the object
     * managed by this factory.
     */
    @Override
    public Color getObject() throws Exception {
        //返回一个Color对象 注册到IOC容器中
        System.out.println("PersonFactoryBean...");
        return new Person();
    }

    /**
     * Return the type of object that this FactoryBean creates,
     * or {@code null} if not known in advance.
     */
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

    /**
     * Is the object managed by this factory a singleton? That is,
     * will {@link #getObject()} always return the same object
     */
    @Override
    public boolean isSingleton() {
        //是否是单例
        return true;
    }
}

2.1.3 按条件注解方式注册组件

Spring提供一种机制,我们可以通过某种条件判断来选择某个组件进行注册给Spring。可通过@Conditional匹配注册组件。
比如自定义注册规则,根据不同的操作系统注册不同的bean

@Configuration
public class SpringConfig {
    @Bean("person")
    public Person person() {
        System.out.println("person Bean 注册IOC ...");
        return new Person("maozw", 28);
    }

    @Bean("macOs")
    @Conditional(value = {MacOsCondition.class})
    public Person person1() {

        System.out.println("macOs Bean 注册IOC ...");
        return new Person("macOs", 48);
    }

    @Bean("linux")
    @Conditional(value = {LinuxCondition.class})
    public Person person2() {
        System.out.println("linux Bean 注册IOC ...");
        return new Person("linux", 58);
    }
}

public class MacOsCondition implements Condition {

    private static final String macOs = "Mac OS X";

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("oss.name");
        if (macOs.equals(property)){
            return true;
        }
        return false;
    }
}

public class LinuxCondition implements Condition {

    private static final String linux = "linux-";    @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
  String property = environment.getProperty("oss.name");
 if (linux.equals(property)){
            return true;
  }
        return false;
  }
}

Main

private static void starterSpringrForAnnotationAndCondition() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfigCondition.class);

    ConfigurableEnvironment environment = (ConfigurableEnvironment) applicationContext.getEnvironment();

    System.out.println(environment.getProperty("oss.name"));

    String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Person.class);
    Arrays.asList(beanDefinitionNames).forEach(beanDefinitionName -> System.out.println(beanDefinitionName));

    Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
    System.out.println(beansOfType);
}

2.1.4 注册组件作用域

@Configuration
public class SpringConfig {
    /**
     *
     *   @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
     *   @see ConfigurableBeanFactory#SCOPE_SINGLETON
     *   @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
     *   @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
     * @return
     */
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean("person")
    public Person person2() {

        System.out.println("Person Bean 注册IOC ...");
        return new Person("maozw", 28);
    }
}

2.1.5 延迟注解方式注册组件

Spring 延迟注册,使用组件时才进行注册。
在需要的组件上加上@Lazy注解即可
配置示例

@Configuration
public class SpringConfig {
    @Lazy
    @Bean("person")
    public Person person2() {
        System.out.println("Person Bean 注册IOC ...");
        return new Person("maozw", 28);
    }
}

2.2 组件注册生命周期管理

bean的生命周期管理主要是初始化bean,以及移除bean,可以通过如下方式:

2.2.1 通过@Bean注解指定

通过@Bean注解属性指定initMethod = "init",destroyMethod = "destory" 方法


@Configuration 
public class SpringConfig {

    @Bean(initMethod = "init",destroyMethod = "destory")
    public Person person(){
        return new Person();
  }
}

public @Data class Person {

   private String name;
   private int age;

   public void init() {
      System.out.println("Person...init...");
      this.age = 10;
      this.name = "mzw";
   }

   public void destory() {
      System.out.println("Person...destroy...");
   }
}

2.2.2 实现:InitializingBean , DisposableBean接口

让Bean实现两个接口:InitializingBean , DisposableBean并重写下面两个方法

@NoArgsConstructor
@AllArgsConstructor
public @Data class Person implements InitializingBean , DisposableBean {

   private String name;
   private int age;

   @Override
   public void afterPropertiesSet() throws Exception {
      System.out.println("Person...InitializingBean...afterPropertiesSet");
      this.age = 10;
      this.name = "mzw";
   }

   @Override
   public void destroy() throws Exception {
      System.out.println("Person...DisposableBean..destroy...");
   }
}

2.2.3 使用JSR250规范定义注解 @PostConstruct @PreDestroy

@NoArgsConstructor
@AllArgsConstructor
public @Data class Person {

   private String name;
   private int age;

   @PostConstruct
   public void init() throws Exception {
      System.out.println("Person...PostConstruct...init");
      this.age = 10;
      this.name = "mzw";
   }

   @PreDestroy
   public void destroy() throws Exception {
      System.out.println("Person...PreDestroy...destroy...");
   }
}

2.2.4 使用BeanPostProcessor-后置处理器

BeanPostProcessor-后置处理器 在bean初始化前后(init,InitializingBean,PostConstruct)做一些准备工作 。
Spring底层对BeanPostProcessor的使用;bean赋值,注入其他组件:@Autowired,以及生命周期中的其他组件@PostContrustor,@PreDestroy @Async...

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization ... ");
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization ... ");
        return bean;
    }
}

源码分析:


2.3 组件自动装配

Spring利用依赖注入(DI),完成对IOC容器中的各个组件的依赖关系赋值

2.3.1 通过@AutoWired 自动注入

2.3.2 通过@Resource(JSR250) @Inject(JSR330)

spring支持这两种注入方式:

2.3.3 自定义组件使用Spring的组件

自定义组件使用Spring容器的一些组件(ApplicationContext,BeanFactory,xxx), 自定义组件需实现xxxAware接口(ApplicationContextAware); xxxAware:功能使用都是xxxProcessor实现的: 比如ApplicationContextAware:

上一篇 下一篇

猜你喜欢

热点阅读