SpringBoot构建微服务实战 之 @Enable*
本节我们将学一下SpringBoot 的@Enable* 注解。
SpringBoot 的@Enable* 的目的在于起因Spring的某一个特性,这一个特性可以是Spring自有的,也可以是我们自定义的。本节我们将详细学习一下启用 spring 自有的 @Enbale* 特性。
分类
Spring自生提供的 @Eable* 注解:
常用的@Enbale 注解说明:
@EnableAspectJAutoProxy:表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成;设置@EnableAspectJAutoProxy(exposeProxy=true)表示通过aop框架暴露该代理对象,aopContext能够访问。
@EnableAsync:@EnableAsync注解开启异步方法的支持。参考文章
@EnableScheduling:@EnableScheduling注解开启计划任务的支持。参考文章
@EnableWebMVC:@EnableWebMVC注解用来开启Web MVC的配置支持。
@EnableConfigurationProperties:@EnableConfigurationProperties注解是用来开启对@ConfigurationProperties注解配置Bean的支持。
@EnableJpaRepositories:@EnableJpaRepositories注解开启对Spring Data JPA Repostory的支持。Spring Data JPA 框架,主要针对的就是 Spring 唯一没有简化到的业务逻辑代码,至此,开发者连仅剩的实现持久层业务逻辑的工作都省了,唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
@EnableCaching:@EnableCaching注解开启注解式的缓存支持
- …
通过这些简单的@Enable*可以开启一项功能的支持,从而避免自己配置大量的代码,很大程度上降低了使用难度。
实例
下面我们着重讲解三个 @Enable* 注解功能。
@EnableConfigurationProperties
在默认配置文件(application.properties)中配置:
condition.onPropertyCondition=true tomcat.host=192.168.100.19 tomcat.port=8080
EnableConfigurationProperty.java
package com.springBoot.entrance.enableAnnotation; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.stereotype.Component; /** *使用@EnableConfigurationProperties 可以将SpringBoot读取的配置文件中的属性注入到某个实体中 *该实体的所有字段应和配置文件中的保持一致 */ @Component @ConfigurationProperties(prefix ="tomcat") @EnableConfigurationProperties public class EnableConfigurationProperty { private String host; private Integer port; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Override public String toString() { return "EnableConfigurationProperty [host=" + host + ", port=" + port + "]"; } }
App.java 核心代码:
System.out.println(context.getBean(EnableConfigurationProperty.class));
结果:
@EnableAsync
AsyncExample.java
package com.springBoot.entrance.enableAnnotation; import java.util.concurrent.TimeUnit; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Component; @Component @EnableAsync public class AsyncExample { @Async public void running() { try { for(int i=1;i<10;i++){ System.out.println("===================="+i); TimeUnit.SECONDS.sleep(1); } } catch (Exception e) { e.printStackTrace(); } } }
App.java
context.getBean(AsyncExample.class).running(); System.out.println("==================After Async sysout======================");
代码解读:一般@EnableAsync 需要和 @Async一起使用,@EnableAsync 用于类声明,@Async 用于方法声明。
结果:
有结果可以看出我们执行的两个方法是异步输出的,这就是@EnableAsync 的特性。
@EnableTransactionManagement
EnableTransaction.java
package com.springBoot.entrance.enableAnnotation; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Component @EnableTransactionManagement public class EnableTransaction { /** * 启用@EnableTransactionManagement 并陪着 @Transactional 实现Spring 声明式事务 */ @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,timeout=1,readOnly=true,rollbackFor=Throwable.class) public void enableTransaction(){ System.out.println("This Method has setted up EnableTransaction "); } }
代码解读:一般 @EnableTransactionManagement 需要和 @Transactional 配合使用,@EnableTransactionManagement 用于类声明,@Transactional 用户方法声明。
自定义@Enable * 特性
在SpringBoot 中我们可以通过自定义一些特定 @Enable* 注解,下面我们将通过一个实例来说明一下。
新建目录
Market.java(无任何属性或实现)
package com.springBoot.entrance.importBean.indirectImportSelector; public class Market { }
Sales.java(无任何属性或实现)
package com.springBoot.entrance.importBean.indirectImportSelector; public class Sales { }
EnableLogInfo.java
package com.springBoot.entrance.importBean.indirectImportSelector; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportSelector.class) /** *自定义Enable* *1. */ public @interface EnableLogInfo { String[] name(); }
MyImportSelector.java
package com.springBoot.entrance.importBean.indirectImportSelector; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.stereotype.Component; @Component @Import(value = { MyImportSelector.class }) @EnableLogInfo(name="onlySale") /** * Bean的批量注入 使用自定义 ImportSelector 须实现 ImportSelector * 接口,返回值必须是class全称测集合,该集合内的所有类都将被Spring IOC容器统一管理 */ public class MyImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata importingClassMetadata) { System.out.println(importingClassMetadata.getAnnotationAttributes(EnableLogInfo.class.getName()).toString().contains("onlySale")); //获取自定的@EnableLogInfo 信息 如果包含 onlySale 则只注入Sales class 否则 注入 Sales 和 Market 两个类 if (importingClassMetadata.getAnnotationAttributes(EnableLogInfo.class.getName()) != null && importingClassMetadata.getAnnotationAttributes(EnableLogInfo.class.getName()).toString().contains("onlySale")) { return new String[] {Sales.class.getName() }; } // 将指定的类 return new String[] { Market.class.getName(), Sales.class.getName() }; } }
代码解读:此处使用了@Import注解和实现了 ImportSelector 来注入Bean
代码逻辑:通过 实现 ImportSelector 获得 importingClassMetadata 对象,然后再由 importingClassMetadata 获得 我们自定的@EnableLogInfo 中的 值,如果 该值包含 onlySale 则 我们只装配Sale 类实例,否则 装配 Sale 和Market 两个类的实例。
特别说明:要理解ImportSelecotor 是如何抽象Bean 实例注入的请参考:SpringBoot构建微服务实战 之 @Import
App.java
//实现 ImportSelector 实现Bean 的批量注入 System.out.println(context.getBean(MyImportSelector.class)); System.out.println(context.getBean(Market.class)); System.out.println(context.getBean(Sales.class));
结果
本例子
由结果可知在我们指定的 @EnableLogInfo注解中我们传入了 onlySale 字符串,因而 Spring IOC Container 中只注入了 Sale.java 的实例。
其他结果
此次演示我们在自定义的 @EnableLogInfo 中传入 AllClasses 字符串。
由结果我们看出 Sale.java 和Market.java 的实例都已注入到了 Spring IOC 容器中。
小结
SpringBoot 为我们提供一些列的 @Enable* 特性,我们只需要根据逻辑的需要开启特定的特性即可,而不再需要做配置了。
SpringBoot 中除了提供了Spring 自有的@Enable* 特性我们还可以高度自定义 @Enable* 特性,来满足我特定的需求和应用场景。
一般SpringBoot @Enbale*特性需要结合@Import 注解来一起使用,关于@Import的使用我们将在后面的章节中学习,因而在学习是请一定结合@Enable * 和 @Import 一起学习。
在创建自定义@Enable* 时 类型必须为@interface 且 必须使用一下注解,具体可参考代码:
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.TYPE)
- @Documented