Spring 模式注解
模式注解是一种用于声明在应用中扮演“组件”角色的注解。如 Spring Framework 中的 @Repository 标注在任何类上 ,用于扮演仓储角色的模式注解。
@Component 作为一种由 Spring 容器托管的通用模式组件,任何被 @Component 标注的组件均为组件扫描的候选对象。凡是被 @Component 元标注的注解标注时,如 @Service ,也被视作组件扫描的候选对象。
装配方式
@ComponentScan 方式
@ComponentScan(basePackages = "com.dive.in.spring.boot")
public class SpringConfiguration {
...
}
Spring @Enable 模块装配
Spring Framework 3.1 开始支持”@Enable 模块驱动“。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立的单元。比如 Web MVC 模块(@EnableWebMvc)、AspectJ代理模块(@EnableAspectJAutoProxy) 等。
自定义 @Enable 模块
HelloWorldConfiguration:
public class HelloWorldConfiguration {
@Bean
public String helloWorld() { // 方法名即 Bean 名称
return "Hello,World 2020";
}
}
HelloWorldImportSelector:
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import com.example.demo.configuration.HelloWorldConfiguration;
public class HelloWorldImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata arg0) {
// TODO Auto-generated method stub
return new String[]{HelloWorldConfiguration.class.getName()};
}
}
EnableHelloWorld:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloWorldImportSelector.class)
public @interface EnableHelloWorld {
}
EnableHelloWorldBootstrap:
@EnableHelloWorld
public class EnableHelloWorldBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloWorldBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
// helloWorld Bean 是否存在
String helloWorld =
context.getBean("helloWorld", String.class);
System.out.println("helloWorld Bean : " + helloWorld);
// 关闭上下文
context.close();
}
}
运行结果:
helloWorld Bean : Hello,World 2020
Spring 条件装配
1、基于配置方式实现 - @Profile
定义一个服务接口CalculateService :
package com.example.demo.service;
public interface CalculateService {
/**
* 从多个整数 sum 求和
* @param values 多个整数
* @return sum 累加值
*/
Integer sum(Integer... values);
}
Java7CalculateService:
package com.example.demo.service;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Profile("Java7")
@Service
public class Java7CalculateService implements CalculateService {
@Override
public Integer sum(Integer... values) {
System.out.println("Java 7 for 循环实现 ");
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
}
Java8CalculateService:
package com.example.demo.service;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import java.util.stream.Stream;
@Profile("Java8")
@Service
public class Java8CalculateService implements CalculateService {
@Override
public Integer sum(Integer... values) {
System.out.println("Java 8 Lambda 实现");
int sum = Stream.of(values).reduce(0, Integer::sum);
return sum;
}
}
启动类
@SpringBootApplication(scanBasePackages = "com.example.demo.service")
public class CalculateServiceBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootstrap.class)
.web(WebApplicationType.NONE)
.profiles("Java8")
.run(args);
// CalculateService Bean 是否存在
CalculateService calculateService = context.getBean(CalculateService.class);
System.out.println("calculateService.sum(1...10) : " +
calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// 关闭上下文
context.close();
}
}
可以根据在context中设置不同的profile装配不同的Bean。
2、基于接口编程方式实现 - @Conditional
系统属性条件判断OnSystemPropertyCondition:
/**
* 系统属性条件判断
*/
public class OnSystemPropertyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
String propertyName = String.valueOf(attributes.get("name"));
String propertyValue = String.valueOf(attributes.get("value"));
String javaPropertyValue = System.getProperty(propertyName);
return propertyValue.equals(javaPropertyValue);
}
}
条件注解ConditionalOnSystemProperty:
/**
* Java 系统属性 条件判断
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {
/**
* Java 系统属性名称
* @return
*/
String name();
/**
* Java 系统属性值
* @return
*/
String value();
}
系统属性条件引导类ConditionalOnSystemPropertyBootstrap:
/**
* 系统属性条件引导类
*/
public class ConditionalOnSystemPropertyBootstrap {
@Bean
@ConditionalOnSystemProperty(name = "user.name", value = "rose")
public String helloWorld() {
return "Hello,World 2020";
}
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(ConditionalOnSystemPropertyBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
// 通过名称和类型获取 helloWorld Bean
String helloWorld = context.getBean("helloWorld", String.class);
System.out.println("helloWorld Bean : " + helloWorld);
// 关闭上下文
context.close();
}
}
运行之后propertyValue不等于javaPropertyValue,所以会拿不到helloWorld这个Bean.
Spring Boot 自动装配
springboot的自动装配基于约定大于配置的原则,其中使用了底层装配技术:
1、Spring 模式注解装配
2、Spring @Enable 模块装配
3、Spring 条件装配装配
4、Spring 工厂加载机制
- 实现类: SpringFactoriesLoader
- 配置资源: META-INF/spring.factories
实现方法:
- 激活自动装配 - @EnableAutoConfiguration
- 实现自动装配 - XXXAutoConfiguration
- 配置自动装配实现 - META-INF/spring.factories
自定义自动装配:
在resources目录下新建 META-INF/spring.factories,并指定spring工厂机制要自动装配的类:
HelloWorldAutoConfiguration:
import com.example.demo.annotation.EnableHelloWorld;
import com.imooc.diveinspringboot.condition.ConditionalOnSystemProperty;
import org.springframework.context.annotation.Configuration;
/**
* HelloWorld 自动装配
*/
@Configuration // Spring 模式注解装配
@EnableHelloWorld // Spring @Enable 模块装配
@ConditionalOnSystemProperty(name = "user.name", value = "Administrator") // 条件装配
public class HelloWorldAutoConfiguration {
}
EnableAutoConfigurationBootstrap启动类:
/**
* {@link EnableAutoConfiguration} 引导类
*/
@EnableAutoConfiguration
public class EnableAutoConfigurationBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
// helloWorld Bean 是否存在
String helloWorld =
context.getBean("helloWorld", String.class);
System.out.println("helloWorld Bean : " + helloWorld);
// 关闭上下文
context.close();
}
}
整体流程:
1、HelloWorldAutoConfiguration
2、条件判断: user.name == “Administrator”
3、模式注解: @Configuration
4、@Enable 模块: @EnableHelloWorld -> HelloWorldImportSelector -> HelloWorldConfiguration - > helloWorld
最后通过了@Enable 模块装配了helloWorld这个Bean。
关于spring工厂机制参见SpringApplication初始化源码分析。