《Spring Cloud微服务架构进阶》——3.3.7 自制一个Starter
3.3.7 自制一个Starter
下面通过自定义一个Starter来理解Starter的运行机制。自定义Starter的名字叫做filter-log-starter,它的作用非常简单:为应用添加一个过滤器,在控制台中打印每次访问的URI。
1.创建Starter项目和导入依赖
首先创建一个新的Spring Boot项目,导入以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
因为过滤器属于Serlvet包中的类,所以需要引入Spring WebMvc相关依赖。
2.定义过滤器
定义LogFilter地址日志过滤器,具体代码如下:
public class LogFilter implements Filter{
private Logger logger = LoggerFactory.getLogger(LogFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("logFilter init...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 从request中获取到访问的地址,并在控制台中打印出来
HttpServletRequest request = (HttpServletRequest) servletRequest;
logger.info("uri {} is working.", request.getRequestURI());
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
logger.info("logFilter destroy...");
}
}
定义LogFilterRegistrationBean用于将LogFilter过滤器封装成Spring Bean,具体代码如下:
public class LogFilterRegistrationBean extends FilterRegistrationBean<LogFilter>{
public LogFilterRegistrationBean(){
super();
this.setFilter(new LogFilter()); //添加LogFilter过滤器
this.addUrlPatterns("/*"); // 匹配所有路径
this.setName("LogFilter"); // 定义过滤器名
this.setOrder(1); // 设置优先级
}
}
3.定义自动配置类
接着,定义一个自动配置类将LogFilterRegistrationBean注入到Spring的上下文中,具体代码如下:
@Configuration
@ConditionalOnClass({LogFilterRegistrationBean.class, LogFilter.class})
public class LogFilterAutoConfiguration {
@Bean
@ConditionalOnMissingBean(LogFilterRegistrationBean.class)
public LogFilterRegistrationBean logFilterRegistrationBean(){
return new LogFilterRegistrationBean();
}
}
@Configuration通常与@Bean相配合,使用这两个注解可以创建一个简单的Spring配置类,代替相应的xml配置文件。@ConditionalOnClass声明只有当某个或某些class位于类路径上,才会实例化一个Bean。如上述代码中只有当LogFilterRegistrationBean和LogFilter的class在类路径上,LogFilterAutoConfiguration配置类才会生效。
添加@Bean注解的方法将返回一个对象,该对象会被注册为Spring上下文中的Bean。@ConditionalOnMissingBean声明仅仅在当前Spring上下文中不存在某个对象时,才会实例化一个Bean。上述代码中,当LogFilterRegistrationBean不存在于Spring上下文时,才会创建LogFilterRegistrationBean的Bean并注入到Spring上下文中。
4.定义使自动配置类生效的注解
然后我们需要一个注解使LogFilterAutoConfiguration配置类生效。因为Starter是通过jar包的方式引入项目中,对应的classes并不在项目的Spring扫描范围内,所以无法自动引入项目的Spring管理中。对此需要用额外的方式将LogFilterAutoConfiguration引入到项目的Spring管理中,如通过注解的方式将配置类引入项目的Spring扫描范围内。
定义EnableLogFilter引入LogFilterAutoConfiguration配置类到项目的Spring扫描范围内,具体代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(LogFilterAutoConfiguration.class) //引入LogFilterAutoConfiguration配置类
public @interface EnableLogFilter {
}
除了在注解中直接通过@Import引入对应的配置类外,还可以通过ImportSelector的方式从resources/META-INF/spring.factories中读入需要加载的自动配置类。
首先需要在META-INF中定义spring.factories,代码如下所示:
com.xuan.chapter3.starter.annotation.EnableLogFilter=\
com.xuan.chapter3.starter.config.LogFilterAutoConfiguration
声明EnableLogFilter注解将使LogFilterAutoConfiguration自动配置类生效。
定义EnableLogFilterImportSelector用于从spring.factories获取需要加载的自动配置类。具体代码如下:
public class EnableLogFilterImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(EnableLogFilterImportSelector.class);
private Class annotationClass = EnableLogFilter.class;
...
@Override
public String[] selectImports(AnnotationMetadata metadata) {
// 是否生效,默认为true
if (!isEnabled()) {
return new String[0];
}
// 获取注解中的属性
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
Assert.notNull(attributes, "can not be null…");
// 从spring.factories中获取所有通过EnableLogFilter注解引入的自动配置类,并
进行去重操作
List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
if (factories.isEmpty() && !hasDefaultFactory()) {
throw new IllegalStateException("…");
}
if (factories.size() > 1) {
logger.warn("More than one implementation ");
}
return factories.toArray(new String[factories.size()]);
}
}
...
}
通过覆盖selectImports方法,使用SpringFactoriesLoader从spring.factories中获取通过EnableLogFilter注解引入的自动配置类,获取的自动配置类是在spring.factories中配置的LogFilterAutoConfiguration。EnableLogFilterImportSelector可以扩展成一个通用的配置加载类,从而实现在spring.factories中通过不同的注解引入不同的自动配置类的功能。
修改EnableLogFilter注解,代码如下所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(EnableLogFilterImportSelector.class) // 引入自动配置加载类
public @interface EnableLogFilter {
}
5.使用Starter
最后我们使用一下Starter,在boot-demo项目中添加Starter的依赖,如下所示:
<dependency>
<groupId>com.xuan</groupId>
<artifactId>chapter3-filter-log-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在启动类中添加@EnableLogFilter注解,修改后代码如下所示:
@SpringBootApplication
@RestController
@EnableLogFilter
public class Chapter3BootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(Chapter3BootDemoApplication.class, args);
}
@GetMapping("/test")
public String test() {
return "this is a demo boot.";
}
}
启动项目,访问http://localhost:8000/test接口,将会在控制台中看到如下访问日志:
2018-04-09 21:30:36.045 INFO 32080 --- [nio-8008-exec-1] c.x.chapter1.starter.filter.LogFilter : uri /test is working.
- 点赞
- 收藏
- 关注作者
评论(0)