ff4j 提供了基于aop模式的开发,我们只需要使用简单的注解,以及一个远端存储(中心化控制)就可以方便的动态调整
线上bean的选择,以下是一个简单的demo(使用mysql中心化存储)
环境准备
- docker-compose 文件
version: "3"
services:
mysql:
image: mysql:5.7.16
ports:
- 3306:3306
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: dalongrong
MYSQL_DATABASE: ff4j_v1
TZ: Asia/Shanghai
项目准备
- 项目结构
├── docker-compose.yaml
├── pom.xml
└── src
└── main
├── java
│ └── org
│ └── ff4j
│ └── sample
│ ├── Application.java
│ ├── config
│ │ └── FF4JConfiguration.java
│ ├── resources
│ │ └── SampleResource.java
│ └── service
│ ├── GreetingService.java
│ └── impl
│ ├── GreetingServiceEnglishImpl.java
│ └── GreetingServiceFrenchImpl.java
└── resources
└── application.properties
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.ff4j</groupId>
<artifactId>ff4j-sample-springboot-jdbc</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<ff4j.version>1.8.4</ff4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.ff4j</groupId>
<artifactId>ff4j-spring-boot-starter</artifactId>
<version>${ff4j.version}</version>
</dependency>
<dependency>
<groupId>org.ff4j</groupId>
<artifactId>ff4j-store-springjdbc</artifactId>
<version>${ff4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 代码说明
ff4j 提供的aop注解,可以方便的进行bean切换管理
参考格式:
public interface GreetingService {
@Flip(name="language-french", alterClazz = GreetingServiceFrenchImpl.class)
String sayHello(String name);
}
说明:
当language-french 为true 的时候使用GreetingServiceFrenchImpl这个实现
接口的几个实现
GreetingServiceEnglishImpl.java
注意添加了Primary,为了使用上便捷
package org.ff4j.sample.service.impl;
import org.ff4j.sample.service.GreetingService;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component()
@Primary
public class GreetingServiceEnglishImpl implements GreetingService {
public String sayHello(String name) {
return "Hello " + name;
}
}
GreetingServiceFrenchImpl.java
package org.ff4j.sample.service.impl;
import org.ff4j.sample.service.GreetingService;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component()
public class GreetingServiceFrenchImpl implements GreetingService {
public String sayHello(String name) {
return "Bonjour " + name;
}
}
引用bean
SampleResource.java
package org.ff4j.sample.resources;
import org.ff4j.FF4j;
import org.ff4j.sample.service.GreetingService;
import org.ff4j.spring.autowire.FF4JFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SampleResource {
@Autowired
private FF4j getFF4j;
@Autowired
private GreetingService greeting;
@RequestMapping(value = "/", method = RequestMethod.GET, produces = "text/html")
public String sayHello() {
StringBuilder response = new StringBuilder("<html><body><ul>");
response.append("<p>Is <span style="color:red">Awesome</span> feature activated ? from ff4j.check("feature_X") <span style="color:blue">");
response.append(getFF4j.check("feature_X"));
response.append("</span></body></html>");
return response.toString();
}
// 引用bean,基于配置的注解
@RequestMapping(value = "/msg", method = RequestMethod.GET, produces = "text/html")
public String sayHelloMessage() {
String message = greeting.sayHello("CLU");
return message;
}
}
FF4JConfiguration.java
主要是配置ff4j bean
package org.ff4j.sample.config;
import javax.sql.DataSource;
import org.ff4j.FF4j;
import org.ff4j.springjdbc.store.EventRepositorySpringJdbc;
import org.ff4j.springjdbc.store.FeatureStoreSpringJdbc;
import org.ff4j.springjdbc.store.PropertyStoreSpringJdbc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration()
public class FF4JConfiguration {
@Autowired
private DataSource dataSource;
@Bean
public FF4j getFF4j() {
FF4j ff4j = new FF4j();
ff4j.setFeatureStore(new FeatureStoreSpringJdbc(dataSource));
ff4j.setPropertiesStore(new PropertyStoreSpringJdbc(dataSource));
ff4j.setEventRepository(new EventRepositorySpringJdbc(dataSource));
ff4j.audit(true);
ff4j.createSchema();
ff4j.autoCreate(true);
return ff4j;
}
}
spring boot 应用配置
spring.datasource.name=ff4j_db
spring.datasource.url=jdbc:mysql://localhost:3306/ff4j_v1
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.username=root
debug=true
spring.datasource.password=dalongrong
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
运行&&效果
- 启动mysql
docker-compose up -d
- 启动服务
mvn spring-boot:run
- 效果
从配置上默认应该是GreetingServiceEnglishImpl的,当我们启用language-french 会使用GreetingServiceFrenchImpl的实现
启用language-french特性(修改数据库数据)
访问效果
参考资料
https://github.com/ff4j/ff4j/wiki/Advanced-Concepts#aspect-oriented-programming