ff4j aop模式动态bean 切换

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

原文地址:https://www.cnblogs.com/rongfengliang/p/12739882.html