Spring Boot2.0深度实践 - 第2章 走向自动装配

Spring Boot2.0深度实践之核心技术篇 – 第2章 走向自动装配

第2章 走向自动装配

    • Spring Framework 手动装配
      • 1. Spring 模式注解装配
        • 装配方式
      • 2. @Enable 模块装配 -- 自定义 @Enable 模块
      • 3. @Enable 模块装配 -- Spring 中的 @Enable 模块
    • Spring Framework 条件装配
    • SpringBoot 自动装配
      • 1. 底层装配技术
      • 2. 实现三部曲

Spring Framework 手动装配

1. Spring 模式注解装配

模式注解是一种用于声明在应用中扮演“组件”角色的注解。如 Spring Framework 中的 @Repository 标注在任何类上 ,用于扮演仓储角色的模式注解。

@Component 作为一种由 Spring 容器托管的通用模式组件,任何被 @Component 标准的组件均为组件扫描的候选对象。类 似地,凡是被 @Component 元标注(meta-annotated)的注解,如 @Service ,当任何组件标注它时,也被视作组件扫 描的候选对象

  • @Component
/**
 * @author Mark Fisher
 * @since 2.5
 * @see Repository
 * @see Service
 * @see Controller
 * @see org.springframework.context.annotation.ClassPathBeanDefinitionScanner
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
	String value() default "";
}
  • @Service
/**
 * @author Juergen Hoeller
 * @since 2.5
 * @see Component
 * @see Repository
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
	@AliasFor(annotation = Component.class)
	String value() default "";
}
  • @Configuration
 * @author Rod Johnson
 * @author Chris Beams
 * @since 3.0
 * @see Bean
 * @see Profile
 * @see Import
 * @see ImportResource
 * @see ComponentScan
 * @see Lazy
 * @see PropertySource
 * @see AnnotationConfigApplicationContext
 * @see ConfigurationClassPostProcessor
 * @see org.springframework.core.env.Environment
 * @see org.springframework.test.context.ContextConfiguration
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
	@AliasFor(annotation = Component.class)
	String value() default "";
}

Spring Boot2.0深度实践 - 第2章 走向自动装配_第1张图片


装配方式

  • 第一种方式 ,(since 2.5)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    
    <context:annotation-config/>
    
    
    <context:component-scan base-package="meng.spring.test8">context:component-scan>

beans>
  • 第二种方式 @ComponentScan ,(since 3.1)
// BeanAnnotation.java
package meng.spring.test8.annotation;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope(value="prototype")//默认为单例 singleton 可以改为prototype
@Component("beanAnnotation")//默认就是beanAnnotation
public class BeanAnnotation {
	public void say(String arg) {System.out.println("BeanAnnotation : " + arg);}
}

// AnnotationConfig.java
@ComponentScan(basePackages = "meng.spring.test8.annotation")
public class AnnotationConfig {
}

// Test2.java
public class Test2 {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AnnotationConfig.class);

        BeanAnnotation beanAnnotation = context.getBean("beanAnnotation",
                BeanAnnotation.class);
        beanAnnotation.say("Hello 注解bean");
        System.out.println(beanAnnotation.hashCode());

        BeanAnnotation beanAnnotation2 = context.getBean("beanAnnotation",
                BeanAnnotation.class);
        beanAnnotation2.say("Hello 注解bean");
        System.out.println(beanAnnotation2.hashCode());
    }
}

2. @Enable 模块装配 – 自定义 @Enable 模块

  • @EnableHelloWorld (基于注解驱动实现)
import com.example.applicationdemo.configuration.HelloWorldConfiguration;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}

@Configuration
public class HelloWorldConfiguration {
    @Bean
    public String helloWorld() {return "Hello World";}
}

// HelloWorldBootStrap.java
@EnableHelloWorld
public class HelloWorldBootStrap {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(HelloWorldBootStrap.class).web(WebApplicationType.NONE).run(args);
        String bean = applicationContext.getBean("helloWorld", String.class);
        System.out.println("----> " + bean);
    }
}
  • @EnableCaching (基于接口驱动实现)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldImportSelector.class)
public @interface EnableHelloWorld2 {
}

public class HelloWorldImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{HelloWorldConfiguration.class.getName()};
    }
}

// HelloWorldConfiguration.java 同上

// HelloWorldBootStrap.java
@EnableHelloWorld2
public class HelloWorldBootStrap {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(HelloWorldBootStrap.class).web(WebApplicationType.NONE).run(args);
        String bean = applicationContext.getBean("helloWorld", String.class);
        System.out.println("----> " + bean);
    }
}

3. @Enable 模块装配 – Spring 中的 @Enable 模块

Spring Framework 3.1 开始支持“@Enable 模块驱动”。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立 的单元。比如 Web MVC 模块、AspectJ 代理模块、Caching (缓存)模块、JMX(Java 管理扩展)模块、Async (异步处理)模块等。
Spring Boot2.0深度实践 - 第2章 走向自动装配_第2张图片

  1. 注解驱动方式
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
 @Configuration
public class DelegatingWebMvcConfiguration extends
WebMvcConfigurationSupport {
    // ... 
}
  1. 接口编程方式
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
    // ... 
}
 public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
        case PROXY:
            return new String[] { AutoProxyRegistrar.class.getName() , ProxyCachingConfiguration.class.getName() };
        case ASPECTJ:
            return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
        default:
            return null;
        }
    }
}

Spring Framework 条件装配

从 Spring Framework 3.1 开始,允许在 Bean 装配时增加前置条件判断

  1. 配置化条件装配 @Profile
    since 3.1
public interface AddressService {
    String getMainCity();
}

@Profile("Anhui")
@Service
public class AnhuiService implements AddressService {
    @Override
    public String getMainCity() {return "HeFei";}
}

@Profile("Zejiang")
@Service
public class ZejiangService implements AddressService {
    @Override
    public String getMainCity() {return "HangZhou";}
}

// AddressBootstrap.java
@SpringBootApplication(scanBasePackages = "com.example.applicationdemo.service")
public class AddressBootstrap {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(AddressBootstrap.class)
                .web(WebApplicationType.NONE)
                .profiles("Zejiang")
                .run(args);
        AddressService bean = applicationContext.getBean(AddressService.class);
        System.out.println("----> " + bean.getMainCity());
    }
}
  1. 编程条件装配 @Conditional
    since 4.0
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {
    String name();
    String value();
}


import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
import java.util.Objects;
public class OnSystemPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> map = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
        String name = String.valueOf(map.get("name"));
        String value = String.valueOf(map.get("value"));
        String systemValue = System.getProperty(name);
        return Objects.equals(value, systemValue);
    }
}


import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
public class OnSystemPropertyConditionBootStrap {

    @Bean
    @ConditionalOnSystemProperty(name = "user.name", value = "kevin")
    public String helloWorld() {
        return "Hello OnSystemPropertyConditionBootStrap";
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(OnSystemPropertyConditionBootStrap.class).web(WebApplicationType.NONE).run(args);
        String bean = applicationContext.getBean("helloWorld", String.class);
        System.out.println("----> " + bean);
    }
}


SpringBoot 自动装配

在 Spring Boot 场景下,基于约定大于配置的原则,实现 Spring 组件自动装配的目的

1. 底层装配技术

  • Spring 模式注解装配
  • Spring @Enable 模块装配
  • Spring 条件装配装配
  • Spring 工厂加载机制
    • 实现类: SpringFactoriesLoader
    • 配置资源: META-INF/spring.factories

SpringFactoriesLoader.java

package org.springframework.core.io.support;

/**
 * @author Arjen Poutsma
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 3.2
 */
public final class SpringFactoriesLoader {

	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();


	private SpringFactoriesLoader() {
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

	// ...

}

2. 实现三部曲

  • 激活自动装配 @EnableAutoConfiguration

import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;


@EnableAutoConfiguration
public class EnableAutoConfigurationBootStrap {

    public static void main(String[] args) {

        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(EnableAutoConfigurationBootStrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        String bean = applicationContext.getBean("helloWorld", String.class);
        System.out.println("----> " + bean);
        applicationContext.close();
    }

}
  • 实现自动装配 XxxAutoConfiguration
package com.example.applicationdemo.auto;

import com.example.applicationdemo.annotation.EnableHelloWorld;
import com.example.applicationdemo.condition.ConditionalOnSystemProperty;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableHelloWorld
@ConditionalOnSystemProperty(name = "user.name", value = "kevin")
public class HelloWorldAutoConfiguration {

}
  • 配置自动装配实现 META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.applicationdemo.auto.HelloWorldAutoConfiguration

https://coding.imooc.com/lesson/252.html#mid=16193

你可能感兴趣的:(SpringBoot)