Spring Framework 3.1 开始支持”@Enable 模块驱动“。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立的单元。比如 Web MVC 模块、AspectJ代理模块、Caching(缓存)模块、JMX(Java 管 理扩展)模块、Async(异步处理)模块等。
手动装配
定义具备相同领域的功能组件集合,组合所形成的一个独立的单元
框架实现
@Enable 注解模块
激活模块
Spring Framework
@EnableWebMvc
Web MVC 模块
@EnableTransactionManagement
事务管理模块
@EnableCaching
Caching模块
@EnableMBeanExport
JMX 模块
@EnableAsync
异步处理模块
@EnableWebFlux
Web Flux模块
@EnableAspectJAutoProxy
AspectJ 代理模块
Spring Boot
@EnableAutoConfiguration
自动装配模块
@EnableManagementContext
Actuator 管理模块
@EnableConfigurationProperties
配置属性绑定模块
@EnableOAuth2Sso
OAuth2 单点登录模块
SpringCloud
@EnableEurekaServer
Eureka服务器模块
@EnableConfigServer
配置服务器模块
@EnableFeignClients
Feign客户端模块
@EnableZuulProxy
服务网关 Zuul 模块
@EnableCircuitBreaker
服务熔断模块
手动装配实现原理,以@EnableWebMvc为例
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { } 复制代码
该注解中的关键是使用了@Import注解,@Import是这样的。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { Class<?>[] value(); }
java doc 给出这样一段注释: Indicates one or more {@link Configuration @Configuration} classes to import. 大概意思就是:指示要导入的一个或多个{@link Configuration@Configuration}类。 可以大概的理解为向SpringIOC容器中注入一个或者多个配置类,就像我们下面这样
@Configuration public class XConfigure { @Bean public xxx(){ return new Xxx(); } }
自定义@EnableXXX模块
基于注解驱动实现
创建一个需要被装载的类public class HelloWorld { public helloWorld(){ System.out.println("hello world"); } } 创建一个配置类,装载HelloWorld.
public class HelloWorldConfig { @Bean public HelloWorld helloWorld(){ return new HelloWorld(); } } 自定义模块驱动注解
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({HelloWorldConfig.class}) public @interface EnableHelloWorld { } 测试,在启动类/配置类上写@EnableHelloWorld装载该组件,启动SpringBoo项目,控制台打印helloworld,说明自定义注解生效,HelloWorld类被装载至SpringIOC容器中。
基于接口编程方式
新建配置类,并实现ImportSelector接口public class HelloWorldSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{HelloWorld.class.getName()}; } } 修改@EnableHelloWorld注解
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({HelloWorldSelector.class}) public @interface EnableHelloWorld { } 启动容器,控制台打印HelloWorld,说明接口生效,基于注解驱动实现HelloWorldImportSelector--->HelloWorldConfiguration--->HelloWorld
这样通过HelloWorldImportSelector间接的进行装配更加的灵活
条件装配
从Spring Framework3.1开始,允许Bean装配时增加前置条件判断
配置方式-@Profile
编程方式-@Condifional
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass { /** * The classes that must be present. Since this Annotation is parsed by loading class * bytecode, it is safe to specify classes here that may ultimately not be on the * classpath, only if this annotation is directly on the affected component and * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to * use this annotation as a meta-annotation, only use the {@link #name} attribute. * @return the classes that must be present */ Class<?>[] value() default {}; /** * The classes names that must be present. * @return the class names that must be present. */ String[] name() default {}; }
自定义条件装配
eg:实现一个方法,java 7使用for循环的形式实现累加,java 8使用Lambda的形式实现累加。
基于配置方式实现-@Profile
接口:public interface CalculateService { /** * 从多个整数sum求和 * @param values 多个整数 * @return sum累加值 */ Integer sum(Integer ...values); } java7实现
@Profile("Java7") @Service public class Java7CalculateServiceImpl implements CalculateService { @Override public Integer sum(Integer... values) { System.out.println("Java7 for 循环实现{@link CalculateService}"); int sum=0; for (Integer value : values) { sum += value; } return sum; } } java8实现
@Profile("Java8") @Service public class Java8CalculateServiceImpl implements CalculateService { @Override public Integer sum(Integer... values) { System.out.println("Java8 Lambda 循环实现{@link CalculateService}"); return Stream.of(values).reduce(0,Integer::sum); } } 启动类 只扫描service包
@SpringBootApplication(scanBasePackages = "org.ywb.service") public class CalculateServiceBootStrap { public static void main(String[] args) { ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootStrap.class) .web(WebApplicationType.NONE) .profiles("Java8") .run(args); 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(); } }
基于编程方式实现
场景:只有name的值符合要求才返回相应的Bean
自定义注解@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) @Documented @Conditional(OnSystemPropertyCondition.class) public @interface ConditionOnSystemProperty { /** * Java 系统属性名称 * @return */ String name(); /** * Java 系统属性名称 * @return */ String value(); } 创建Condition类
public class OnSystemPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnSystemProperty.class.getName()); String propertyName = String.valueOf(attributes.get("name")); String propertyValue = String.valueOf(attributes.get("value")); return "happy".equals(propertyName); } } 启动类
public class ConditionOnSystemPropertyBootstrap { @Bean @ConditionOnSystemProperty(name="happy",value = "balabala") // 只有name的值等于happy的时候,该“hello,world:-)”才会被装载 public String helloWorld(){ return "hello,world :-)"; } public static void main(String[] args) { ConfigurableApplicationContext context = new SpringApplicationBuilder(ConditionOnSystemPropertyBootstrap.class) .web(WebApplicationType.NONE) .run(args); String helloWorld = context.getBean("helloWorld", String.class); System.out.println(helloWorld); //关闭上下文 context.close(); } }
自动装配
SpringBoot自动装配依赖的技术
Spring模式注解装配 Spring @Enable模块装配 传送门 Spring 条件装配 Spring 工厂加载机制 实现类: SpringFactoriesLoader 配置资源: META-INF/spring.factories
SpringFactoriesLoader部分源码/** * Load and instantiate the factory implementations of the given type from * {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader. * <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}. * <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames} * to obtain all registered factory names. * @param factoryClass the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default) * @throws IllegalArgumentException if any factory implementation class cannot * be loaded or if an error occurs while instantiating any factory * @see #loadFactoryNames */ public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) { Assert.notNull(factoryClass, "'factoryClass' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } List<T> result = new ArrayList<>(factoryNames.size()); for (String factoryName : factoryNames) { result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; } META-INF/spring.facotries
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration, org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration, org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration, org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration, org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration ...
可以看出,该方法通过实例化配置文件中的工厂对象,通过工厂对象创建Bean。完成自动化装配。
具体实现方式
激活自动配置-@EnableAutoConfiguration实现自动装配-@XXXAutoConfiguration配置自动装配实现-META-INF/spring.facotries自定义实现自动装配
创建一个要被装载的Beanpublic class HelloWorld { public HelloWorld(){ System.out.println("hello world :-)"); } } 创建配置类
public class HelloWorldConfig { @Bean public HelloWorld helloWorld() { return new HelloWorld(); } } 创建自定义模块装配注解
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({HelloWorldConfig.class}) public @interface EnableHelloWorld { } 创建条件装配注解,并定义条件装配规则
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Conditional(OnSystemPropertyCondition.class) public @interface ConditionOnSystemProperty { String name(); String value(); } public class OnSystemPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnSystemProperty.class.getName()); String propertyName = String.valueOf(attributes.get("name")); String propertyValue = String.valueOf(attributes.get("value")); return "happy".equals(propertyName); } } 复制代码创建自动装配类
@Configuration //Spring 模式装配 @EnableHelloWorld //Spring @Enable模块装配 @ConditionOnSystemProperty(name = "happy", value = "balabala") //条件装配 public class HelloWorldAutoConfiguration { } 在resources目录下创建META-INF文件夹并创建spring.factories文件
文件中添加自动装配类
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.xxx.HelloWorldAutoConfiguration 启动SpringBoot,控制台打印HelloWorld构造方法内容
还没有评论,来说两句吧...