├── pull_request_template.md ├── spring-easy-strategy-starter ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring.factories │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── cmt │ │ │ └── easystrategy │ │ │ └── starter │ │ │ ├── StrategyContainer.java │ │ │ ├── EasyStrategy.java │ │ │ ├── EasyStrategyConfiguration.java │ │ │ ├── NormalStrategyContainerFactoryBean.java │ │ │ └── StrategyAnnotationBeanPostProcessor.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── cmt │ │ └── easystrategy │ │ └── starter │ │ └── test │ │ ├── function │ │ ├── StrategyI.java │ │ ├── AStrategyI.java │ │ ├── BStrategyI.java │ │ └── TestService.java │ │ └── TestApplication.java └── pom.xml ├── spring-easy-strategy-core ├── src │ ├── test │ │ ├── java │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── cmt │ │ │ │ └── easystrategy │ │ │ │ └── test │ │ │ │ ├── demo │ │ │ │ ├── UserLevel.java │ │ │ │ ├── ProductTypeEnum.java │ │ │ │ ├── rewardpoints │ │ │ │ │ ├── PointsRewardStrategy.java │ │ │ │ │ ├── HighLevelPointsRewardStrategy.java │ │ │ │ │ ├── LowLevelPointsRewardStrategy.java │ │ │ │ │ └── MediumLevelPointsRewardStrategy.java │ │ │ │ ├── calculateprice │ │ │ │ │ ├── CalculatePriceStrategy.java │ │ │ │ │ ├── VegetableCaculatePriceStrategy.java │ │ │ │ │ └── FruitCalculatePriceStrategy.java │ │ │ │ ├── service │ │ │ │ │ ├── CreateOrderRequest.java │ │ │ │ │ └── OrderService.java │ │ │ │ ├── StrategyConfiguration.java │ │ │ │ └── StrategyTest.java │ │ │ │ └── function │ │ │ │ ├── common │ │ │ │ ├── GenderEnum.java │ │ │ │ ├── PlatformEnum.java │ │ │ │ ├── HelloStrategy.java │ │ │ │ ├── PlatformStrategy.java │ │ │ │ ├── BaiDuStrategy.java │ │ │ │ ├── TencentStrategy.java │ │ │ │ ├── AliStrategy.java │ │ │ │ ├── JapanGirlHelloStrategy.java │ │ │ │ ├── ChineseGirlHelloStrategy.java │ │ │ │ ├── DefaultHelloStrategy.java │ │ │ │ ├── Platform.java │ │ │ │ └── People.java │ │ │ │ ├── multi │ │ │ │ ├── Validation.java │ │ │ │ ├── BValidation.java │ │ │ │ ├── DValidation.java │ │ │ │ ├── AValidation.java │ │ │ │ └── CValidation.java │ │ │ │ ├── repeatable │ │ │ │ ├── RepeatableStrategy.java │ │ │ │ ├── RepeatableStrategy2.java │ │ │ │ ├── RepeatableStrategy1.java │ │ │ │ ├── One.java │ │ │ │ └── Many.java │ │ │ │ ├── advice │ │ │ │ └── TestAdvice.java │ │ │ │ ├── StrategyConfiguration2.java │ │ │ │ ├── SpringFeatureTest.java │ │ │ │ └── FunctionTest.java │ │ └── resources │ │ │ └── applicationContext.xml │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── cmt │ │ └── easystrategy │ │ ├── StrategyContainer.java │ │ ├── MultiStrategyContainer.java │ │ ├── DefaultStrategy.java │ │ ├── StrategyIdentifier.java │ │ ├── StrategyIdentifiers.java │ │ ├── exceptions │ │ └── StrategyException.java │ │ ├── MultiStrategyContainerFactoryBean.java │ │ └── StrategyContainerFactoryBean.java └── pom.xml ├── .gitignore ├── .travis.yml ├── pom.xml └── README.md /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## 变更的目的是什么 2 | 3 | XXXXX 4 | 5 | ## 简短的变更日志 6 | 7 | XXXXX 8 | 9 | ## 验证此变更 10 | 11 | XXXXX 12 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.github.cmt.easystrategy.starter.EasyStrategyConfiguration -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/UserLevel.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-07-30 6 | **/ 7 | public enum UserLevel { 8 | LOW,MEDIUM,HIGH 9 | } 10 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/ProductTypeEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-07-30 6 | **/ 7 | public enum ProductTypeEnum { 8 | FRUIT,VEGETABLE 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # idea 2 | *.iml 3 | *.ipr 4 | *.iws 5 | /.idea 6 | 7 | # maven 8 | target 9 | 10 | # node modules 11 | node_modules 12 | .project 13 | .settings 14 | .classpath 15 | APP_HOME_IS_UNDEFINED 16 | catalina.base_IS_UNDEFINED 17 | version.vm 18 | version.vm 19 | 20 | **.DS_Store 21 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/GenderEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-08-01 6 | **/ 7 | public enum GenderEnum { 8 | MALE,FEMALE 9 | } 10 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/PlatformEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author jiangzihao 5 | * @date 2020-03-30 6 | **/ 7 | public enum PlatformEnum { 8 | ALI, TENCENT, BAI_DU 9 | } 10 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/multi/Validation.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.multi; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-09-19 6 | **/ 7 | public interface Validation { 8 | 9 | void validate(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/HelloStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-08-01 6 | **/ 7 | public interface HelloStrategy { 8 | 9 | String hello(); 10 | } 11 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/PlatformStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-08-01 6 | **/ 7 | public interface PlatformStrategy { 8 | 9 | String hello(); 10 | } 11 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/test/java/io/github/cmt/easystrategy/starter/test/function/StrategyI.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter.test.function; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2020/12/6 6 | **/ 7 | public interface StrategyI { 8 | 9 | public String test(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/main/java/io/github/cmt/easystrategy/starter/StrategyContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2020/12/6 6 | **/ 7 | public interface StrategyContainer extends io.github.cmt.easystrategy.StrategyContainer { 8 | } 9 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/repeatable/RepeatableStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.repeatable; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-09-10 6 | **/ 7 | public interface RepeatableStrategy { 8 | 9 | String test(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/rewardpoints/PointsRewardStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.rewardpoints; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-07-30 6 | **/ 7 | public interface PointsRewardStrategy { 8 | 9 | Integer rewardPoints(Integer price); 10 | } 11 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/BaiDuStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author jiangzihao 5 | * @date 2020-03-30 6 | **/ 7 | @Platform(PlatformEnum.BAI_DU) 8 | public class BaiDuStrategy implements PlatformStrategy { 9 | 10 | @Override 11 | public String hello() { 12 | return "百度"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/repeatable/RepeatableStrategy2.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.repeatable; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-09-10 6 | **/ 7 | @One(test = "2") 8 | public class RepeatableStrategy2 implements RepeatableStrategy { 9 | @Override 10 | public String test() { 11 | return "2"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/TencentStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author jiangzihao 5 | * @date 2020-03-30 6 | **/ 7 | @Platform(PlatformEnum.TENCENT) 8 | public class TencentStrategy implements PlatformStrategy { 9 | 10 | @Override 11 | public String hello() { 12 | return "腾讯"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false # faster builds 3 | 4 | jdk: 5 | - openjdk8 6 | 7 | cache: 8 | directories: 9 | - $HOME/.m2 10 | install: true 11 | 12 | script: 13 | - mvn clean package install -DskipTests=true -Dmaven.javadoc.skip=true -Dgpg.skip -B -V 14 | 15 | after_success: 16 | - echo "build success!" 17 | - bash <(curl -s https://codecov.io/bash) 18 | 19 | after_failure: 20 | - echo "build failed!" 21 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/AliStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author jiangzihao 5 | * @date 2020-03-30 6 | **/ 7 | @Platform(PlatformEnum.ALI)//标记注解 完成策略实现类与标识符的绑定 8 | public class AliStrategy implements PlatformStrategy { 9 | 10 | @Override 11 | public String hello() { 12 | return "阿里巴巴"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/repeatable/RepeatableStrategy1.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.repeatable; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-09-10 6 | **/ 7 | @One(test = "1") 8 | @One(test = "3") 9 | public class RepeatableStrategy1 implements RepeatableStrategy { 10 | @Override 11 | public String test() { 12 | return "1"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/JapanGirlHelloStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-08-01 6 | **/ 7 | @People(district = "japan",gender = GenderEnum.FEMALE) 8 | public class JapanGirlHelloStrategy implements HelloStrategy{ 9 | @Override 10 | public String hello() { 11 | return "ohayo"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/ChineseGirlHelloStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-08-01 6 | **/ 7 | @People(district = "chinese",gender = GenderEnum.FEMALE) 8 | public class ChineseGirlHelloStrategy implements HelloStrategy { 9 | @Override 10 | public String hello() { 11 | return "你好"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/StrategyContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | /** 4 | * @param 策略标识符 Key 5 | * @param 策略接口 Value 6 | * @author shengchaojie 7 | * @date 2019-07-30 8 | * @see StrategyContainerFactoryBean 9 | * 类似Map,通过Key来获取Value 10 | **/ 11 | 12 | public interface StrategyContainer { 13 | 14 | T getStrategy(R identify); 15 | 16 | void register(R identify, T strategy); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/DefaultHelloStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | import io.github.cmt.easystrategy.DefaultStrategy; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2019-09-10 8 | **/ 9 | @DefaultStrategy 10 | public class DefaultHelloStrategy implements HelloStrategy{ 11 | @Override 12 | public String hello() { 13 | return "default"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/MultiStrategyContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * R 唯一标识 7 | * T 策略接口 8 | * @author shengchaojie 9 | * @date 2019-09-16 10 | **/ 11 | public interface MultiStrategyContainer { 12 | 13 | /** 14 | * 通过identify获取对应多个策略实现 15 | * @param identify 标识符 16 | * @return 策略实现List 17 | */ 18 | List getStrategies(R identify); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/main/java/io/github/cmt/easystrategy/starter/EasyStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author shengchaojie 10 | * @date 2020/11/20 11 | **/ 12 | @Target({ElementType.FIELD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface EasyStrategy { 15 | } 16 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/calculateprice/CalculatePriceStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.calculateprice; 2 | 3 | /** 4 | * 计算价格策略 5 | * @author shengchaojie 6 | * @date 2019-07-30 7 | **/ 8 | public interface CalculatePriceStrategy { 9 | 10 | /** 11 | * 根据sku以及购买数量计算价格 12 | * @param sku 13 | * @param num 14 | * @return 15 | */ 16 | Integer calculate(String sku, Integer unitPrice, Integer num); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/repeatable/One.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.repeatable; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * @author shengchaojie 9 | * @date 2019-09-10 10 | **/ 11 | @Target({ElementType.TYPE}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Repeatable(Many.class) 14 | @Component 15 | public @interface One { 16 | 17 | String test(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/test/java/io/github/cmt/easystrategy/starter/test/function/AStrategyI.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter.test.function; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2020/12/6 8 | **/ 9 | @StrategyIdentifier(identifyCode = "A") 10 | public class AStrategyI implements StrategyI{ 11 | @Override 12 | public String test() { 13 | System.out.println("A"); 14 | return "A"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/test/java/io/github/cmt/easystrategy/starter/test/function/BStrategyI.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter.test.function; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2020/12/6 8 | **/ 9 | @StrategyIdentifier(identifyCode = "B") 10 | public class BStrategyI implements StrategyI{ 11 | @Override 12 | public String test() { 13 | System.out.println("B"); 14 | return "B"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/test/java/io/github/cmt/easystrategy/starter/test/TestApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter.test; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author shengchaojie 8 | * @date 2020/12/6 9 | **/ 10 | @SpringBootApplication 11 | public class TestApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(TestApplication.class,args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/advice/TestAdvice.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.advice; 2 | 3 | import org.springframework.aop.MethodBeforeAdvice; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * @author shengchaojie 9 | * @date 2019-09-06 10 | **/ 11 | public class TestAdvice implements MethodBeforeAdvice { 12 | 13 | 14 | @Override 15 | public void before(Method method, Object[] args, Object target) throws Throwable { 16 | System.out.println("test"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/rewardpoints/HighLevelPointsRewardStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.rewardpoints; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2019-07-30 8 | **/ 9 | @StrategyIdentifier(identifyCode = "HIGH") 10 | public class HighLevelPointsRewardStrategy implements PointsRewardStrategy{ 11 | public Integer rewardPoints(Integer price) { 12 | System.out.println("返百分之15积分"); 13 | return price /10000 * 15; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/rewardpoints/LowLevelPointsRewardStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.rewardpoints; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2019-07-30 8 | **/ 9 | @StrategyIdentifier(identifyCode = "LOW") 10 | public class LowLevelPointsRewardStrategy implements PointsRewardStrategy{ 11 | 12 | public Integer rewardPoints(Integer price) { 13 | System.out.println("返百分之5积分"); 14 | return price /10000 * 5; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/rewardpoints/MediumLevelPointsRewardStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.rewardpoints; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2019-07-30 8 | **/ 9 | @StrategyIdentifier(identifyCode = "MEDIUM") 10 | public class MediumLevelPointsRewardStrategy implements PointsRewardStrategy{ 11 | public Integer rewardPoints(Integer price) { 12 | System.out.println("返百分之10积分"); 13 | return price /10000 * 10; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/main/java/io/github/cmt/easystrategy/starter/EasyStrategyConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | /** 7 | * @author shengchaojie 8 | * @date 2020/12/6 9 | **/ 10 | @Configuration 11 | public class EasyStrategyConfiguration { 12 | 13 | @Bean 14 | public StrategyAnnotationBeanPostProcessor strategyAnnotationBeanPostProcessor(){ 15 | return new StrategyAnnotationBeanPostProcessor(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/multi/BValidation.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.multi; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | import org.springframework.core.annotation.Order; 5 | 6 | /** 7 | * @author shengchaojie 8 | * @date 2019-09-19 9 | **/ 10 | @StrategyIdentifier(identifyCode = "1") 11 | @StrategyIdentifier(identifyCode = "3") 12 | @Order(3) 13 | public class BValidation implements Validation{ 14 | @Override 15 | public void validate() { 16 | System.out.println("BBBBBBB"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/multi/DValidation.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.multi; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | import org.springframework.core.annotation.Order; 5 | 6 | /** 7 | * @author shengchaojie 8 | * @date 2019-09-19 9 | **/ 10 | @StrategyIdentifier(identifyCode = "2") 11 | @StrategyIdentifier(identifyCode = "3") 12 | @Order(1) 13 | public class DValidation implements Validation{ 14 | @Override 15 | public void validate() { 16 | System.out.println("DDDDD"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/repeatable/Many.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.repeatable; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author shengchaojie 12 | * @date 2019-09-10 13 | **/ 14 | @Target({ElementType.TYPE}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Component 17 | public @interface Many { 18 | 19 | One[] value(); 20 | } 21 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/calculateprice/VegetableCaculatePriceStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.calculateprice; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2019-07-30 8 | **/ 9 | @StrategyIdentifier(identifyCode = "VEGETABLE") 10 | public class VegetableCaculatePriceStrategy implements CalculatePriceStrategy{ 11 | 12 | public Integer calculate(String sku, Integer unitPrice, Integer num) { 13 | System.out.println("没有优惠"); 14 | return unitPrice*num; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/DefaultStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 默认策略实现 12 | * 匹配不到策略时,默认走该注解的策略 13 | * 统一接口策略下,该注解只能有一个 14 | * @author shengchaojie 15 | * @date 2019-09-10 16 | **/ 17 | @Target({ElementType.TYPE}) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Component 20 | public @interface DefaultStrategy { 21 | } 22 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/StrategyIdentifier.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 策略类注解 9 | * 如果策略标识符只有一个直接使用该注解就行 10 | * @author shengchaojie 11 | * @date 2019-07-30 12 | **/ 13 | @Target({ElementType.TYPE}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Repeatable(StrategyIdentifiers.class) 16 | @Component 17 | public @interface StrategyIdentifier { 18 | 19 | /** 20 | * 获取策略唯一标识符 21 | * @return 策略标识符 22 | */ 23 | String identifyCode(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/Platform.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author jiangzihao 12 | * @date 2020-03-30 13 | **/ 14 | @Target({ElementType.TYPE}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Component 17 | public @interface Platform { 18 | 19 | PlatformEnum value(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/StrategyIdentifiers.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 用来解决有时候不同策略对应相同逻辑的情况 12 | * @author shengchaojie 13 | * @date 2019-09-10 14 | **/ 15 | @Target({ElementType.TYPE}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Component 18 | public @interface StrategyIdentifiers { 19 | 20 | StrategyIdentifier[] value(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/multi/AValidation.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.multi; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | import org.springframework.core.annotation.Order; 5 | 6 | /** 7 | * @author shengchaojie 8 | * @date 2019-09-19 9 | **/ 10 | @StrategyIdentifier(identifyCode = "1") 11 | @StrategyIdentifier(identifyCode = "1") 12 | @StrategyIdentifier(identifyCode = "3") 13 | @Order(4) 14 | public class AValidation implements Validation{ 15 | @Override 16 | public void validate() { 17 | System.out.println("AAAAAA"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/common/People.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.common; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author shengchaojie 12 | * @date 2019-08-01 13 | **/ 14 | @Target({ElementType.TYPE}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Component 17 | public @interface People { 18 | 19 | String district(); 20 | 21 | GenderEnum gender(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/service/CreateOrderRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.service; 2 | 3 | import io.github.cmt.easystrategy.test.demo.UserLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author shengchaojie 11 | * @date 2019-07-30 12 | **/ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Builder 17 | public class CreateOrderRequest { 18 | 19 | private String sku; 20 | 21 | private Integer num; 22 | 23 | private Long userId; 24 | 25 | private UserLevel userLevel; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/calculateprice/FruitCalculatePriceStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.calculateprice; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2019-07-30 8 | **/ 9 | @StrategyIdentifier(identifyCode = "FRUIT") 10 | public class FruitCalculatePriceStrategy implements CalculatePriceStrategy{ 11 | 12 | public Integer calculate(String sku, Integer unitPrice, Integer num) { 13 | System.out.println("满2件,打8折"); 14 | if(num <2){ 15 | return unitPrice*num; 16 | }else{ 17 | return unitPrice*num*8/10; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/multi/CValidation.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function.multi; 2 | 3 | import io.github.cmt.easystrategy.StrategyIdentifier; 4 | import org.springframework.core.Ordered; 5 | import org.springframework.core.annotation.Order; 6 | 7 | /** 8 | * @author shengchaojie 9 | * @date 2019-09-19 10 | **/ 11 | @StrategyIdentifier(identifyCode = "2") 12 | @StrategyIdentifier(identifyCode = "3") 13 | @Order(-1) 14 | public class CValidation implements Validation , Ordered { 15 | @Override 16 | public void validate() { 17 | System.out.println("CCCCC"); 18 | } 19 | 20 | @Override 21 | public int getOrder() { 22 | return 2; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/exceptions/StrategyException.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.exceptions; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2019-09-10 6 | **/ 7 | public class StrategyException extends RuntimeException{ 8 | 9 | public StrategyException() { 10 | } 11 | 12 | public StrategyException(String message) { 13 | super(message); 14 | } 15 | 16 | public StrategyException(String message, Throwable cause) { 17 | super(message, cause); 18 | } 19 | 20 | public StrategyException(Throwable cause) { 21 | super(cause); 22 | } 23 | 24 | public StrategyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 25 | super(message, cause, enableSuppression, writableStackTrace); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | .*hello 18 | .*validate 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/StrategyConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo; 2 | 3 | import io.github.cmt.easystrategy.StrategyContainerFactoryBean; 4 | import io.github.cmt.easystrategy.test.demo.calculateprice.CalculatePriceStrategy; 5 | import io.github.cmt.easystrategy.test.demo.rewardpoints.PointsRewardStrategy; 6 | import io.github.cmt.easystrategy.StrategyIdentifier; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * @author shengchaojie 12 | * @date 2019-07-30 13 | **/ 14 | @Configuration 15 | public class StrategyConfiguration { 16 | 17 | @Bean 18 | public StrategyContainerFactoryBean calculatePriceStrategyManager2(){ 19 | return StrategyContainerFactoryBean.build(CalculatePriceStrategy.class, StrategyIdentifier.class,(a)->a.identifyCode()); 20 | } 21 | 22 | @Bean 23 | public StrategyContainerFactoryBean pointsRewardStrategyManager3(){ 24 | StrategyContainerFactoryBean factoryBean = new StrategyContainerFactoryBean<>(); 25 | factoryBean.setStrategyClass(PointsRewardStrategy.class); 26 | factoryBean.setStrategyAnnotationClass(StrategyIdentifier.class); 27 | factoryBean.setIdentifierGetter((a)->a.identifyCode()); 28 | return factoryBean; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/main/java/io/github/cmt/easystrategy/starter/NormalStrategyContainerFactoryBean.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter; 2 | 3 | import io.github.cmt.easystrategy.StrategyContainerFactoryBean; 4 | import io.github.cmt.easystrategy.StrategyIdentifier; 5 | import org.springframework.util.Assert; 6 | 7 | /** 8 | * @author shengchaojie 9 | * @date 2020/11/21 10 | **/ 11 | public class NormalStrategyContainerFactoryBean extends StrategyContainerFactoryBean { 12 | 13 | public NormalStrategyContainerFactoryBean(Class strategyClass) { 14 | super(strategyClass,StrategyIdentifier.class, StrategyIdentifier::identifyCode); 15 | } 16 | 17 | 18 | @Override 19 | public StrategyContainer getObject() throws Exception { 20 | Assert.notNull(strategyClass, "strategyClass must not be null"); 21 | Assert.notNull(strategyAnnotationClass, "strategyAnnotationClass must not be null"); 22 | Assert.notNull(identifierGetter, "identifierGetter must not be null"); 23 | 24 | return new StrategyContainer() { 25 | @Override 26 | public T getStrategy(String identify) { 27 | return getStrategyFromContainer(identify); 28 | } 29 | 30 | @Override 31 | public void register(String identify, T strategy) { 32 | strategyTable.put(identify, strategy); 33 | } 34 | 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-easy-strategy-parent 7 | io.github.dsc-cmt 8 | 1.4.0 9 | 10 | 4.0.0 11 | 12 | spring-easy-strategy-starter 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | io.github.dsc-cmt 22 | spring-easy-strategy 23 | 1.3.0 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter 29 | 2.0.6.RELEASE 30 | test 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | 2.0.6.RELEASE 37 | test 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/test/java/io/github/cmt/easystrategy/starter/test/function/TestService.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter.test.function; 2 | 3 | import io.github.cmt.easystrategy.starter.EasyStrategy; 4 | import io.github.cmt.easystrategy.starter.StrategyContainer; 5 | import io.github.cmt.easystrategy.starter.test.TestApplication; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | 12 | /** 13 | * @author shengchaojie 14 | * @date 2020/12/6 15 | **/ 16 | @SpringBootTest(classes = TestApplication.class) 17 | @RunWith(SpringJUnit4ClassRunner.class) 18 | public class TestService { 19 | 20 | @EasyStrategy 21 | StrategyContainer strategyContainer; 22 | 23 | @EasyStrategy 24 | StrategyContainer strategyContainerSample; 25 | 26 | @EasyStrategy 27 | io.github.cmt.easystrategy.StrategyContainer strategyContainer2; 28 | 29 | @Test 30 | public void testCache(){ 31 | Assert.assertEquals(strategyContainer,strategyContainerSample); 32 | } 33 | 34 | @Test 35 | public void test111(){ 36 | Assert.assertEquals(strategyContainer.getStrategy("A").test(),"A"); 37 | Assert.assertEquals(strategyContainer.getStrategy("B").test(),"B"); 38 | Assert.assertEquals(strategyContainer2.getStrategy("B").test(),"B"); 39 | Assert.assertEquals(strategyContainer2.getStrategy("B").test(),"B"); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/StrategyTest.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo; 2 | 3 | import io.github.cmt.easystrategy.test.demo.service.CreateOrderRequest; 4 | import io.github.cmt.easystrategy.test.demo.service.OrderService; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 | 11 | /** 12 | * @author shengchaojie 13 | * @date 2019-07-30 14 | **/ 15 | @RunWith(SpringJUnit4ClassRunner.class) 16 | @ContextConfiguration("classpath*:applicationContext.xml") 17 | public class StrategyTest { 18 | 19 | @Autowired 20 | private OrderService orderService; 21 | 22 | @Test 23 | public void testAssembleStrategy(){ 24 | orderService.createOrder(CreateOrderRequest.builder() 25 | .sku("F1233") 26 | .num(3) 27 | .userId(1234L) 28 | .userLevel(UserLevel.MEDIUM) 29 | .build()); 30 | 31 | orderService.createOrder(CreateOrderRequest.builder() 32 | .sku("F1233") 33 | .num(3) 34 | .userId(1234L) 35 | .userLevel(UserLevel.HIGH) 36 | .build()); 37 | 38 | orderService.createOrder(CreateOrderRequest.builder() 39 | .sku("V1233") 40 | .num(3) 41 | .userId(1234L) 42 | .userLevel(UserLevel.HIGH) 43 | .build()); 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-easy-strategy-parent 7 | io.github.dsc-cmt 8 | 1.4.0 9 | 10 | 4.0.0 11 | 12 | spring-easy-strategy 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | org.springframework 23 | spring-context 24 | ${spring.version} 25 | 26 | 27 | 28 | org.springframework 29 | spring-core 30 | ${spring.version} 31 | 32 | 33 | 34 | org.springframework 35 | spring-beans 36 | ${spring.version} 37 | 38 | 39 | 40 | org.springframework 41 | spring-test 42 | ${spring.version} 43 | test 44 | 45 | 46 | 47 | com.google.guava 48 | guava 49 | 27.0.1-jre 50 | 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | ${lombok.version} 56 | provided 57 | 58 | 59 | 60 | junit 61 | junit 62 | ${junit.version} 63 | test 64 | 65 | 66 | 67 | org.mockito 68 | mockito-all 69 | 2.0.2-beta 70 | test 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/StrategyConfiguration2.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function; 2 | 3 | import com.google.common.base.Joiner; 4 | import io.github.cmt.easystrategy.MultiStrategyContainerFactoryBean; 5 | import io.github.cmt.easystrategy.StrategyContainerFactoryBean; 6 | import io.github.cmt.easystrategy.StrategyIdentifier; 7 | import io.github.cmt.easystrategy.test.function.common.*; 8 | import io.github.cmt.easystrategy.test.function.multi.Validation; 9 | import io.github.cmt.easystrategy.test.function.repeatable.One; 10 | import io.github.cmt.easystrategy.test.function.repeatable.RepeatableStrategy; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | /** 15 | * @author shengchaojie 16 | * @date 2019-08-01 17 | **/ 18 | @Configuration 19 | public class StrategyConfiguration2 { 20 | 21 | @Bean 22 | public StrategyContainerFactoryBean helloStrategyManager() { 23 | /*StrategyManagerFactoryBean factoryBean = new StrategyManagerFactoryBean<>(); 24 | factoryBean.setStrategyClass(HelloStrategy.class); 25 | factoryBean.setStrategyAnnotationClass(People.class); 26 | factoryBean.setIdentifyCodeGetter(a -> Joiner.on(",").join(a.district(),a.gender().name())); 27 | return factoryBean;*/ 28 | return StrategyContainerFactoryBean.build(HelloStrategy.class, People.class, a -> Joiner.on(",").join(a.district(), a.gender().name())); 29 | } 30 | 31 | @Bean 32 | public StrategyContainerFactoryBean platformStrategyManager() { 33 | /* StrategyContainerFactoryBean factoryBean = new StrategyContainerFactoryBean<>(); 34 | factoryBean.setStrategyClass(PlatformStrategy.class);//策略接口 35 | factoryBean.setStrategyAnnotationClass(Platform.class);//策略实现类标注注解 36 | factoryBean.setIdentifierGetter(Platform::value);// 策略实现类标记注解到标识符的转换逻辑 37 | return factoryBean;*/ 38 | return StrategyContainerFactoryBean.build(PlatformStrategy.class, Platform.class, Platform::value); 39 | } 40 | 41 | @Bean 42 | public StrategyContainerFactoryBean repeatableStrategyManager() { 43 | return StrategyContainerFactoryBean.build(RepeatableStrategy.class, One.class, a -> a.test()); 44 | } 45 | 46 | @Bean 47 | public MultiStrategyContainerFactoryBean validation() { 48 | return MultiStrategyContainerFactoryBean.build(Validation.class, StrategyIdentifier.class, a -> a.identifyCode()); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/demo/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.demo.service; 2 | 3 | import io.github.cmt.easystrategy.test.demo.calculateprice.CalculatePriceStrategy; 4 | import io.github.cmt.easystrategy.StrategyContainer; 5 | import io.github.cmt.easystrategy.test.demo.ProductTypeEnum; 6 | import io.github.cmt.easystrategy.test.demo.rewardpoints.PointsRewardStrategy; 7 | import org.springframework.stereotype.Service; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.concurrent.ThreadLocalRandom; 11 | 12 | /** 13 | * @author shengchaojie 14 | * @date 2019-07-31 15 | **/ 16 | @Service 17 | public class OrderService { 18 | 19 | /** 20 | * 需要注意,spring容器注入会把泛型丢掉,所以必须通过beanname注入 21 | */ 22 | @Resource(name = "calculatePriceStrategyManager2") 23 | private StrategyContainer calculatePriceStrategyContainer; 24 | 25 | 26 | @Resource(name = "pointsRewardStrategyManager3") 27 | private StrategyContainer pointsRewardStrategyContainer; 28 | 29 | /** 30 | * 创建订单 31 | * @return 32 | */ 33 | public void createOrder(CreateOrderRequest createOrderRequest){ 34 | //创建订单实体 35 | //计算价格 36 | CalculatePriceStrategy calculatePriceStrategy = calculatePriceStrategyContainer.getStrategy(getSkuProductType(createOrderRequest.getSku())); 37 | Integer originPrice = getSkuUnitPrice(createOrderRequest.getSku()); 38 | System.out.println("原价"+originPrice/100.0f+"购买件数"+createOrderRequest.getNum()); 39 | Integer price = calculatePriceStrategy.calculate(createOrderRequest.getSku(),originPrice,createOrderRequest.getNum()); 40 | System.out.println("下单价格为"+price/100.0f); 41 | //计算积分 42 | PointsRewardStrategy pointsRewardStrategy = pointsRewardStrategyContainer.getStrategy(createOrderRequest.getUserLevel().name()); 43 | Integer points = pointsRewardStrategy.rewardPoints(price); 44 | System.out.println("奖励积分"+points); 45 | 46 | //下单完成 47 | System.out.println("下单完成"); 48 | } 49 | 50 | /** 51 | * 根据sku决定产品类型 52 | * @param sku 53 | * @return 54 | */ 55 | private String getSkuProductType(String sku){ 56 | if(sku.startsWith("F")){ 57 | return ProductTypeEnum.FRUIT.name(); 58 | }else if(sku.startsWith("V")){ 59 | return ProductTypeEnum.VEGETABLE.name(); 60 | }else{ 61 | throw new RuntimeException("暂不支持该SKU"); 62 | } 63 | } 64 | 65 | /** 66 | * 单价在100-200内波动 67 | * @param sku 68 | * @return 69 | */ 70 | private Integer getSkuUnitPrice(String sku){ 71 | return ThreadLocalRandom.current().nextInt(1000,2000) *100; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/MultiStrategyContainerFactoryBean.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | import com.google.common.collect.ListMultimap; 4 | import com.google.common.collect.MultimapBuilder; 5 | import lombok.Setter; 6 | import org.springframework.aop.support.AopUtils; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.FactoryBean; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.ApplicationContextAware; 11 | import org.springframework.core.annotation.AnnotationAwareOrderComparator; 12 | import org.springframework.core.annotation.AnnotationUtils; 13 | import org.springframework.util.CollectionUtils; 14 | 15 | import java.lang.annotation.Annotation; 16 | import java.util.*; 17 | import java.util.function.Function; 18 | import java.util.stream.Collectors; 19 | 20 | /** 21 | * 支持identifycode对应多策略的场景 22 | * 支持继承org.springframework.core.Ordered接口 或 使用org.springframework.core.annotation.Order注解对策略进行排序 23 | * 排序优先级接口大于注解 24 | * @author shengchaojie 25 | * @date 2019-09-16 26 | **/ 27 | public class MultiStrategyContainerFactoryBean implements FactoryBean>, ApplicationContextAware { 28 | 29 | @Setter 30 | private Class strategyClass; 31 | 32 | @Setter 33 | private Class strategyAnnotationClass; 34 | 35 | @Setter 36 | private Function identifierGetter; 37 | 38 | private List defaultStrategies = new ArrayList<>(); 39 | 40 | private ListMultimap strategyMultiMap = MultimapBuilder.ListMultimapBuilder.hashKeys().arrayListValues().build(); 41 | 42 | public static MultiStrategyContainerFactoryBean build(Class strategyClass, Class strategyAnnotationClass , Function identifierGetter) { 43 | MultiStrategyContainerFactoryBean factoryBean = new MultiStrategyContainerFactoryBean<>(); 44 | factoryBean.setStrategyClass(strategyClass); 45 | factoryBean.setStrategyAnnotationClass(strategyAnnotationClass); 46 | factoryBean.setIdentifierGetter(identifierGetter); 47 | return factoryBean; 48 | } 49 | 50 | @Override 51 | public MultiStrategyContainer getObject() throws Exception { 52 | return new MultiStrategyContainer() { 53 | @Override 54 | public List getStrategies(R identifier) { 55 | List strategies = strategyMultiMap.get(identifier); 56 | if(!CollectionUtils.isEmpty(strategies)){ 57 | return Collections.unmodifiableList(strategies); 58 | } 59 | return Collections.unmodifiableList(defaultStrategies); 60 | } 61 | }; 62 | } 63 | 64 | @Override 65 | public Class getObjectType() { 66 | return MultiStrategyContainer.class; 67 | } 68 | 69 | @Override 70 | public boolean isSingleton() { 71 | return true; 72 | } 73 | 74 | @Override 75 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 76 | String[] names = applicationContext.getBeanNamesForType(strategyClass); 77 | Arrays.stream(names).forEach(name->{ 78 | T object = applicationContext.getBean(name,strategyClass); 79 | if(Objects.nonNull(AnnotationUtils.getAnnotation(AopUtils.getTargetClass(object),DefaultStrategy.class))){ 80 | defaultStrategies.add(object); 81 | } 82 | AnnotationUtils.getRepeatableAnnotations(AopUtils.getTargetClass(object), strategyAnnotationClass) 83 | .stream() 84 | .map(identifierGetter) 85 | .collect(Collectors.toSet()) 86 | .forEach(id->{ 87 | strategyMultiMap.put(id, object); 88 | }); 89 | }); 90 | //sort 91 | strategyMultiMap.keys().forEach(key->{ 92 | AnnotationAwareOrderComparator.sort(strategyMultiMap.get(key)); 93 | }); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/SpringFeatureTest.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function; 2 | 3 | import io.github.cmt.easystrategy.test.function.common.ChineseGirlHelloStrategy; 4 | import io.github.cmt.easystrategy.test.function.common.HelloStrategy; 5 | import io.github.cmt.easystrategy.test.function.common.JapanGirlHelloStrategy; 6 | import io.github.cmt.easystrategy.test.function.common.People; 7 | import io.github.cmt.easystrategy.test.function.repeatable.One; 8 | import io.github.cmt.easystrategy.test.function.repeatable.RepeatableStrategy1; 9 | import io.github.cmt.easystrategy.test.function.repeatable.RepeatableStrategy2; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.springframework.aop.MethodBeforeAdvice; 13 | import org.springframework.aop.framework.AdvisedSupport; 14 | import org.springframework.aop.framework.DefaultAdvisorChainFactory; 15 | import org.springframework.aop.framework.DefaultAopProxyFactory; 16 | import org.springframework.aop.support.AopUtils; 17 | import org.springframework.core.annotation.AnnotationUtils; 18 | 19 | import java.lang.reflect.Method; 20 | 21 | /** 22 | * @author shengchaojie 23 | * @date 2019-09-06 24 | **/ 25 | public class SpringFeatureTest { 26 | 27 | @Test 28 | public void testCglibAop() { 29 | DefaultAopProxyFactory defaultAopProxyFactory = new DefaultAopProxyFactory(); 30 | 31 | AdvisedSupport advisedSupport = new AdvisedSupport(); 32 | advisedSupport.setInterfaces(HelloStrategy.class); 33 | advisedSupport.setTarget(new ChineseGirlHelloStrategy()); 34 | advisedSupport.setAdvisorChainFactory(new DefaultAdvisorChainFactory()); 35 | advisedSupport.addAdvice(new MethodBeforeAdvice() { 36 | @Override 37 | public void before(Method method, Object[] args, Object target) throws Throwable { 38 | System.out.println("test"); 39 | } 40 | }); 41 | advisedSupport.setProxyTargetClass(true); 42 | 43 | HelloStrategy helloStrategy = (HelloStrategy) defaultAopProxyFactory.createAopProxy(advisedSupport).getProxy(); 44 | Assert.assertTrue(AopUtils.isAopProxy(helloStrategy)); 45 | Assert.assertEquals(AopUtils.getTargetClass(helloStrategy),ChineseGirlHelloStrategy.class); 46 | } 47 | 48 | @Test 49 | public void testJDKAop(){ 50 | DefaultAopProxyFactory defaultAopProxyFactory = new DefaultAopProxyFactory(); 51 | 52 | AdvisedSupport advisedSupport = new AdvisedSupport(); 53 | advisedSupport.setInterfaces(HelloStrategy.class); 54 | advisedSupport.setTarget(new ChineseGirlHelloStrategy()); 55 | advisedSupport.setAdvisorChainFactory(new DefaultAdvisorChainFactory()); 56 | advisedSupport.addAdvice(new MethodBeforeAdvice() { 57 | @Override 58 | public void before(Method method, Object[] args, Object target) throws Throwable { 59 | System.out.println("test"); 60 | } 61 | }); 62 | advisedSupport.setProxyTargetClass(false); 63 | 64 | HelloStrategy helloStrategy = (HelloStrategy) defaultAopProxyFactory.createAopProxy(advisedSupport).getProxy(); 65 | Assert.assertTrue(AopUtils.isAopProxy(helloStrategy)); 66 | Assert.assertEquals(AopUtils.getTargetClass(helloStrategy),ChineseGirlHelloStrategy.class); 67 | } 68 | 69 | /** 70 | * AnnotationUtils.getRepeatableAnnotations兼容了Repeatable模式和非Repeatable模式 71 | */ 72 | @Test 73 | public void testSpringAnnotationUtil(){ 74 | Assert.assertEquals(AnnotationUtils.getRepeatableAnnotations(RepeatableStrategy1.class, One.class).size(),2); 75 | Assert.assertTrue(AnnotationUtils.getAnnotation(RepeatableStrategy1.class, One.class)==null); 76 | Assert.assertEquals(AnnotationUtils.getRepeatableAnnotations(RepeatableStrategy2.class, One.class).size(),1); 77 | Assert.assertTrue(AnnotationUtils.getAnnotation(RepeatableStrategy2.class, One.class)!=null); 78 | 79 | Assert.assertEquals(AnnotationUtils.getRepeatableAnnotations(JapanGirlHelloStrategy.class, People.class).size(),1); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/main/java/io/github/cmt/easystrategy/StrategyContainerFactoryBean.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy; 2 | 3 | import io.github.cmt.easystrategy.exceptions.StrategyException; 4 | import com.google.common.collect.Lists; 5 | import lombok.Setter; 6 | import org.springframework.aop.support.AopUtils; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.FactoryBean; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.ApplicationContextAware; 11 | import org.springframework.core.annotation.AnnotationUtils; 12 | import org.springframework.util.Assert; 13 | import org.springframework.util.CollectionUtils; 14 | 15 | import java.lang.annotation.Annotation; 16 | import java.util.*; 17 | import java.util.function.Function; 18 | 19 | /** 20 | * @param 策略接口 21 | * @param 策略实现类的标注注解 22 | * @param 策略标识符,可以为数组,枚举 23 | * @author shengchaojie 24 | * @date 2019-07-30 25 | **/ 26 | public class StrategyContainerFactoryBean implements FactoryBean>, ApplicationContextAware { 27 | 28 | @Setter 29 | protected Class strategyClass; 30 | 31 | @Setter 32 | protected Class strategyAnnotationClass; 33 | 34 | @Setter 35 | protected Function identifierGetter; 36 | 37 | protected Map strategyTable = new HashMap<>(); 38 | 39 | private T defaultStrategy; 40 | 41 | public StrategyContainerFactoryBean() { 42 | } 43 | 44 | public StrategyContainerFactoryBean(Class strategyClass, Class strategyAnnotationClass, Function identifierGetter) { 45 | this.strategyClass = strategyClass; 46 | this.strategyAnnotationClass = strategyAnnotationClass; 47 | this.identifierGetter = identifierGetter; 48 | } 49 | 50 | public static StrategyContainerFactoryBean build(Class strategyClass, Class strategyAnnotationClass, Function identifyGetter) { 51 | StrategyContainerFactoryBean factoryBean = new StrategyContainerFactoryBean<>(); 52 | factoryBean.setStrategyClass(strategyClass); 53 | factoryBean.setStrategyAnnotationClass(strategyAnnotationClass); 54 | factoryBean.setIdentifierGetter(identifyGetter); 55 | return factoryBean; 56 | } 57 | 58 | @Override 59 | public StrategyContainer getObject() throws Exception { 60 | Assert.notNull(strategyClass, "strategyClass must not be null"); 61 | Assert.notNull(strategyAnnotationClass, "strategyAnnotationClass must not be null"); 62 | Assert.notNull(identifierGetter, "identifierGetter must not be null"); 63 | 64 | return new StrategyContainer() { 65 | @Override 66 | public T getStrategy(R identify) { 67 | return getStrategyFromContainer(identify); 68 | } 69 | 70 | @Override 71 | public void register(R identify, T strategy) { 72 | strategyTable.put(identify, strategy); 73 | } 74 | }; 75 | } 76 | 77 | /** 78 | * 抽象通过identify获取策略逻辑 79 | * 80 | * 如果有自定义获取逻辑 81 | * 可以进行重载 82 | * 比如类似cola extension的逻辑 83 | * @param identify 84 | * @return 85 | */ 86 | protected T getStrategyFromContainer(R identify){ 87 | return Optional.ofNullable(strategyTable.get(identify)).orElse(defaultStrategy); 88 | } 89 | 90 | @Override 91 | public Class getObjectType() { 92 | return StrategyContainer.class; 93 | } 94 | 95 | @Override 96 | public boolean isSingleton() { 97 | return true; 98 | } 99 | 100 | @Override 101 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 102 | String[] names = applicationContext.getBeanNamesForType(strategyClass); 103 | Arrays.stream(names).forEach(name -> { 104 | T object = applicationContext.getBean(name, strategyClass); 105 | if (Objects.nonNull(AnnotationUtils.getAnnotation(AopUtils.getTargetClass(object), DefaultStrategy.class))) { 106 | if (Objects.nonNull(defaultStrategy)) { 107 | throw new StrategyException("StrategyClass=" + strategyClass.getName() + "can only have one default strategy"); 108 | } else { 109 | defaultStrategy = object; 110 | } 111 | } 112 | List identifiers = Lists.newArrayList(); 113 | identifiers.addAll(AnnotationUtils.getRepeatableAnnotations(AopUtils.getTargetClass(object), strategyAnnotationClass)); 114 | if (!CollectionUtils.isEmpty(identifiers)) { 115 | identifiers.forEach(i -> { 116 | R identify = identifierGetter.apply(i); 117 | if (Objects.nonNull(strategyTable.putIfAbsent(identify, object))) { 118 | throw new StrategyException("StrategyClass=" + strategyClass.getName() + ",identify=" + identify + "exist multi config"); 119 | } 120 | }); 121 | } 122 | }); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /spring-easy-strategy-starter/src/main/java/io/github/cmt/easystrategy/starter/StrategyAnnotationBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.starter; 2 | 3 | import io.github.cmt.easystrategy.exceptions.StrategyException; 4 | import org.springframework.beans.BeansException; 5 | import org.springframework.beans.PropertyValues; 6 | import org.springframework.beans.factory.BeanCreationException; 7 | import org.springframework.beans.factory.annotation.InjectionMetadata; 8 | import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; 9 | import org.springframework.beans.factory.support.*; 10 | import org.springframework.context.ApplicationContext; 11 | import org.springframework.context.ApplicationContextAware; 12 | import org.springframework.core.PriorityOrdered; 13 | import org.springframework.core.ResolvableType; 14 | import org.springframework.util.ReflectionUtils; 15 | 16 | import java.beans.PropertyDescriptor; 17 | import java.lang.reflect.Field; 18 | import java.lang.reflect.Modifier; 19 | import java.util.*; 20 | 21 | import static org.springframework.core.annotation.AnnotationUtils.getAnnotation; 22 | 23 | /** 24 | * @author shengchaojie 25 | * @date 2020/11/20 26 | **/ 27 | public class StrategyAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter 28 | implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware { 29 | 30 | private ApplicationContext applicationContext; 31 | 32 | private static final Map CACHE = new HashMap<>(); 33 | 34 | @Override 35 | public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { 36 | 37 | } 38 | 39 | @Override 40 | public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { 41 | 42 | InjectionMetadata metadata = findStrategyMetadata(bean.getClass()); 43 | try { 44 | metadata.inject(bean, beanName, pvs); 45 | } catch (BeanCreationException ex) { 46 | throw ex; 47 | } catch (Throwable ex) { 48 | throw new BeanCreationException(beanName, "Injection of @EasyStrategy dependencies failed", ex); 49 | } 50 | return pvs; 51 | } 52 | 53 | @Override 54 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 55 | this.applicationContext = applicationContext; 56 | } 57 | 58 | @Override 59 | public int getOrder() { 60 | return LOWEST_PRECEDENCE; 61 | } 62 | 63 | private InjectionMetadata findStrategyMetadata(final Class beanClass) { 64 | List elements = new ArrayList<>(); 65 | 66 | ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() { 67 | @Override 68 | public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { 69 | 70 | EasyStrategy reference = getAnnotation(field, EasyStrategy.class); 71 | 72 | if (reference != null) { 73 | 74 | if (Modifier.isStatic(field.getModifiers())) { 75 | return; 76 | } 77 | 78 | elements.add(new StrategyFieldElement(field, reference, applicationContext)); 79 | } 80 | 81 | } 82 | }); 83 | 84 | return new InjectionMetadata(beanClass, elements); 85 | } 86 | 87 | private static class StrategyFieldElement extends InjectionMetadata.InjectedElement { 88 | 89 | private final Field field; 90 | 91 | private final EasyStrategy easyStrategy; 92 | 93 | private final ApplicationContext applicationContext; 94 | 95 | protected StrategyFieldElement(Field member, EasyStrategy easyStrategy, ApplicationContext applicationContext) { 96 | super(member, null); 97 | this.field = member; 98 | this.easyStrategy = easyStrategy; 99 | this.applicationContext = applicationContext; 100 | } 101 | 102 | @Override 103 | protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable { 104 | ResolvableType resolvableType = ResolvableType.forField(field); 105 | Class strategyClass = null; 106 | if (ResolvableType.forClass(StrategyContainer.class).isAssignableFrom(resolvableType)) { 107 | strategyClass = resolvableType.getGeneric(0).getRawClass(); 108 | } else if (ResolvableType.forClass(io.github.cmt.easystrategy.StrategyContainer.class).isAssignableFrom(resolvableType)) { 109 | if (!resolvableType.getGeneric(0).isAssignableFrom(String.class)) { 110 | throw new StrategyException("first generic type must be String!"); 111 | } 112 | strategyClass = resolvableType.getGeneric(1).getRawClass(); 113 | } 114 | ReflectionUtils.makeAccessible(field); 115 | StrategyCacheKey strategyCacheKey = new StrategyCacheKey(resolvableType.getRawClass(), strategyClass); 116 | Object container = CACHE.get(strategyCacheKey); 117 | if (Objects.isNull(container)) { 118 | NormalStrategyContainerFactoryBean normalStrategyContainerFactoryBean = new NormalStrategyContainerFactoryBean(strategyClass); 119 | normalStrategyContainerFactoryBean.setApplicationContext(applicationContext); 120 | container = normalStrategyContainerFactoryBean.getObject(); 121 | CACHE.put(strategyCacheKey, container); 122 | } 123 | 124 | field.set(target, container); 125 | } 126 | } 127 | 128 | private static class StrategyCacheKey { 129 | 130 | private Class containerClass; 131 | 132 | private Class strategyClass; 133 | 134 | public StrategyCacheKey(Class containerClass, Class strategyClass) { 135 | this.containerClass = containerClass; 136 | this.strategyClass = strategyClass; 137 | } 138 | 139 | @Override 140 | public boolean equals(Object o) { 141 | if (this == o) return true; 142 | if (o == null || getClass() != o.getClass()) return false; 143 | StrategyCacheKey that = (StrategyCacheKey) o; 144 | return Objects.equals(containerClass, that.containerClass) && Objects.equals(strategyClass, that.strategyClass); 145 | } 146 | 147 | @Override 148 | public int hashCode() { 149 | return Objects.hash(containerClass, strategyClass); 150 | } 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.dsc-cmt 8 | spring-easy-strategy-parent 9 | pom 10 | 1.4.0 11 | 12 | spring-easy-strategy-core 13 | spring-easy-strategy-starter 14 | 15 | 16 | spring-easy-strategy 17 | a useful framework for use strategy design pattern in spring 18 | https://github.com/dsc-cmt/spring-easy-strategy.git 19 | 20 | 21 | 22 | The Apache License, Version 2.0 23 | http://www.apache.org/licenses/LICENSE-2.0.txt 24 | 25 | 26 | 27 | 28 | 29 | shengchaojie 30 | shengchaojie@163.com 31 | 32 | 33 | jiangzihao 34 | zihaojiang@163.com 35 | 36 | 37 | 38 | 39 | https://github.com/dsc-cmt/spring-easy-strategy 40 | https://github.com/dsc-cmt/spring-easy-strategy.git 41 | 42 | 43 | 44 | 5.0.10.RELEASE 45 | 1.8 46 | 1.8 47 | 1.8 48 | 1.18.6 49 | 4.12 50 | ${java.home}/../bin/javadoc 51 | UTF-8 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.springframework 59 | spring-context 60 | ${spring.version} 61 | 62 | 63 | 64 | org.springframework 65 | spring-core 66 | ${spring.version} 67 | 68 | 69 | 70 | org.springframework 71 | spring-beans 72 | ${spring.version} 73 | 74 | 75 | 76 | org.springframework 77 | spring-test 78 | ${spring.version} 79 | test 80 | 81 | 82 | 83 | com.google.guava 84 | guava 85 | 27.0.1-jre 86 | 87 | 88 | 89 | org.projectlombok 90 | lombok 91 | ${lombok.version} 92 | provided 93 | 94 | 95 | 96 | junit 97 | junit 98 | ${junit.version} 99 | test 100 | 101 | 102 | 103 | org.mockito 104 | mockito-all 105 | 2.0.2-beta 106 | test 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | ossrh 116 | https://oss.sonatype.org/service/local/staging/deploy/maven2 117 | 118 | 119 | ossrh 120 | https://oss.sonatype.org/content/repositories/snapshots/ 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-source-plugin 130 | 2.2.1 131 | 132 | 133 | package 134 | 135 | jar-no-fork 136 | 137 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-javadoc-plugin 143 | 2.9.1 144 | 145 | UTF-8 146 | true 147 | UTF-8 148 | UTF-8 149 | 150 | 151 | date 152 | a 153 | date 154 | 155 | 156 | 157 | 158 | 159 | package 160 | 161 | jar 162 | 163 | 164 | 165 | 166 | 167 | org.apache.maven.plugins 168 | maven-gpg-plugin 169 | 1.6 170 | 171 | 172 | verify 173 | 174 | sign 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /spring-easy-strategy-core/src/test/java/io/github/cmt/easystrategy/test/function/FunctionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.cmt.easystrategy.test.function; 2 | 3 | import io.github.cmt.easystrategy.MultiStrategyContainer; 4 | import io.github.cmt.easystrategy.StrategyContainer; 5 | import io.github.cmt.easystrategy.StrategyContainerFactoryBean; 6 | import io.github.cmt.easystrategy.exceptions.StrategyException; 7 | import io.github.cmt.easystrategy.test.function.common.GenderEnum; 8 | import io.github.cmt.easystrategy.test.function.common.HelloStrategy; 9 | import io.github.cmt.easystrategy.test.function.common.PlatformEnum; 10 | import io.github.cmt.easystrategy.test.function.common.PlatformStrategy; 11 | import io.github.cmt.easystrategy.test.function.repeatable.One; 12 | import io.github.cmt.easystrategy.test.function.repeatable.RepeatableStrategy; 13 | import io.github.cmt.easystrategy.test.function.repeatable.RepeatableStrategy1; 14 | import com.google.common.base.Joiner; 15 | import io.github.cmt.easystrategy.test.function.multi.*; 16 | import org.junit.Assert; 17 | import org.junit.Test; 18 | import org.junit.runner.RunWith; 19 | import org.mockito.Mockito; 20 | import org.springframework.aop.support.AopUtils; 21 | import org.springframework.context.ApplicationContext; 22 | import org.springframework.test.context.ContextConfiguration; 23 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 24 | 25 | import javax.annotation.Resource; 26 | import java.util.List; 27 | 28 | /** 29 | * @author shengchaojie 30 | * @date 2019-08-01 31 | **/ 32 | @RunWith(SpringJUnit4ClassRunner.class) 33 | @ContextConfiguration("classpath*:applicationContext.xml") 34 | public class FunctionTest { 35 | 36 | /** 37 | * 需要注意,spring容器注入会把泛型丢掉,所以必须通过beanname注入 38 | */ 39 | @Resource(name = "helloStrategyManager") 40 | private StrategyContainer helloStrategyContainer; 41 | 42 | @Resource(name = "platformStrategyManager") 43 | private StrategyContainer platformStrategyContainer; 44 | 45 | @Resource(name = "repeatableStrategyManager") 46 | private StrategyContainer repeatableStrategyContainer; 47 | 48 | @Resource(name = "validation") 49 | private MultiStrategyContainer validationMultiStrategyContainer; 50 | 51 | @Test 52 | public void testBasicGetStrategy() { 53 | HelloStrategy helloStrategy = helloStrategyContainer.getStrategy(Joiner.on(",").join("chinese", GenderEnum.FEMALE.name())); 54 | Assert.assertEquals("你好", helloStrategy.hello()); 55 | 56 | helloStrategy = helloStrategyContainer.getStrategy(Joiner.on(",").join("japan", GenderEnum.FEMALE.name())); 57 | Assert.assertEquals("ohayo", helloStrategy.hello()); 58 | } 59 | 60 | @Test 61 | public void testEnumGetStrategy() { 62 | PlatformStrategy platformStrategy = platformStrategyContainer.getStrategy(PlatformEnum.ALI); 63 | Assert.assertEquals("阿里巴巴", platformStrategy.hello()); 64 | 65 | platformStrategy = platformStrategyContainer.getStrategy(PlatformEnum.TENCENT); 66 | Assert.assertEquals("腾讯", platformStrategy.hello()); 67 | 68 | platformStrategy = platformStrategyContainer.getStrategy(PlatformEnum.BAI_DU); 69 | Assert.assertEquals("百度", platformStrategy.hello()); 70 | } 71 | 72 | @Test 73 | public void testProgrammaticAddStrategy() { 74 | helloStrategyContainer.register(Joiner.on(",").join("american", GenderEnum.FEMALE.name()), () -> { 75 | return "custom"; 76 | }); 77 | 78 | HelloStrategy helloStrategy = helloStrategyContainer.getStrategy(Joiner.on(",").join("american", GenderEnum.FEMALE.name())); 79 | Assert.assertNotNull(helloStrategy); 80 | Assert.assertEquals("custom", helloStrategy.hello()); 81 | } 82 | 83 | @Test 84 | public void testDefaultStrategy() { 85 | HelloStrategy helloStrategy = helloStrategyContainer.getStrategy("2324234234324"); 86 | Assert.assertNotNull(helloStrategy); 87 | Assert.assertEquals("default", helloStrategy.hello()); 88 | } 89 | 90 | @Test 91 | public void testRepeatable() { 92 | Assert.assertEquals(repeatableStrategyContainer.getStrategy("1").test(), "1"); 93 | Assert.assertEquals(repeatableStrategyContainer.getStrategy("3").test(), "1"); 94 | Assert.assertEquals(repeatableStrategyContainer.getStrategy("2").test(), "2"); 95 | } 96 | 97 | @Test 98 | public void testMultiStrategyThrowError() throws Exception { 99 | ApplicationContext applicationContext = Mockito.mock(ApplicationContext.class); 100 | 101 | Mockito.when(applicationContext.getBeanNamesForType(RepeatableStrategy.class)).thenReturn(new String[]{"1", "2", "3"}); 102 | Mockito.when(applicationContext.getBean("1", RepeatableStrategy.class)).thenReturn(new RepeatableStrategy1()); 103 | Mockito.when(applicationContext.getBean("2", RepeatableStrategy.class)).thenReturn(new RepeatableStrategy1()); 104 | Mockito.when(applicationContext.getBean("3", RepeatableStrategy.class)).thenReturn(new RepeatableStrategy1()); 105 | 106 | try { 107 | StrategyContainerFactoryBean factoryBean = new StrategyContainerFactoryBean<>(); 108 | factoryBean.setStrategyClass(RepeatableStrategy.class); 109 | factoryBean.setStrategyAnnotationClass(One.class); 110 | factoryBean.setIdentifierGetter(a -> a.test()); 111 | factoryBean.setApplicationContext(applicationContext); 112 | } catch (Exception ex) { 113 | Assert.assertTrue(ex instanceof StrategyException); 114 | } 115 | 116 | } 117 | 118 | @Test 119 | public void testGetOrderedMultiStrategy() { 120 | List strategies = validationMultiStrategyContainer.getStrategies("1"); 121 | Assert.assertEquals(2, strategies.size()); 122 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(0)).isAssignableFrom(BValidation.class)); 123 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(1)).isAssignableFrom(AValidation.class)); 124 | strategies = validationMultiStrategyContainer.getStrategies("2"); 125 | Assert.assertEquals(2, strategies.size()); 126 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(0)).isAssignableFrom(DValidation.class)); 127 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(1)).isAssignableFrom(CValidation.class)); 128 | strategies = validationMultiStrategyContainer.getStrategies("3"); 129 | Assert.assertEquals(4, strategies.size()); 130 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(0)).isAssignableFrom(DValidation.class)); 131 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(1)).isAssignableFrom(CValidation.class)); 132 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(2)).isAssignableFrom(BValidation.class)); 133 | Assert.assertTrue(AopUtils.getTargetClass(strategies.get(3)).isAssignableFrom(AValidation.class)); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/dsc-cmt/spring-easy-strategy.svg?branch=master)](https://travis-ci.com/dsc-cmt/spring-easy-strategy) 2 | > 运行环境需要java8 3 | 4 | ## 介绍 5 | spring-easy-strategy 是一个在spring环境下方便进行策略模式开发的工具类。 6 | 在业务开发使用策略模式中,发现策略的注册以及获取逻辑是一个重复的功能,所以设计该工具类方便开发。 7 | 主要原理是利用spring的FactoryBean针对每个接口T策略生成一个StrategyManger\ Bean。 8 | 9 | ## 策略模式? 10 | 策略模式绝对是我们写业务代码中用到最多的设计模式之一。 11 | 举个简单的例子 12 | 比如现在我们针对不同的商品类型,有不同的计算价格方式 13 | 在不使用设计模式的情况下,肯定存在以下代码 14 | ``` 15 | if(type='水果'){ 16 | //计算价格 17 | }else if(type='蔬菜'){ 18 | //另一种计算价格模式 19 | }else{ 20 | ... 21 | } 22 | ``` 23 | 在使用策略模式后,我们的代码变成这样 24 | ``` 25 | strategy = strategyManger.get('水果'); 26 | price = strategy.calculate(...) 27 | ``` 28 | 有什么好处? 29 | 30 | 1. 减少if/else,让我们的逻辑更加内聚 31 | 2. 符合开闭原则,新增逻辑时,主流程代码不需要改动,只要增加新的策略即可 32 | 33 | ## 引入 34 | ``` 35 | 36 | 37 | io.github.dsc-cmt 38 | spring-easy-strategy 39 | 1.4.0 40 | 41 | ``` 42 | 43 | ## 如何使用 44 | ### 基础使用 45 | 在这个框架中,每个接口的策略实现都会存在一个identify对应,该identifyCode通过注解以及自定义逻辑生成 46 | 47 | #### 0. 你的策略接口 48 | ```java 49 | public interface PlatformStrategy { 50 | 51 | String hello(); 52 | } 53 | ``` 54 | 55 | #### 1. 实现自定义注解标记策略实现类 56 | 57 | > 如果只有一个属性,直接使用自带的`@StrategyIdentifier`注解即可 58 | 59 | 自定义注解是策略和标识符的桥梁 60 | ```java 61 | @Target({ElementType.TYPE}) 62 | @Retention(RetentionPolicy.RUNTIME) 63 | @Component 64 | public @interface Platform { 65 | 66 | PlatformEnum value(); 67 | } 68 | 69 | public enum PlatformEnum { 70 | ALI, TENCENT, BAI_DU 71 | } 72 | ``` 73 | 74 | > 注意@Component元注解 75 | 76 | 将该注解注解到策略实现类上去,完成策略实现类与标识符的绑定 77 | 78 | ```java 79 | @Platform(PlatformEnum.ALI) 80 | public class AliStrategy implements PlatformStrategy { 81 | @Override 82 | public String hello() { 83 | return "阿里巴巴"; 84 | } 85 | } 86 | ``` 87 | 88 | #### 2. 配置StrategyContainerFactoryBean 89 | 90 | StrategyContainerFactoryBean配置有三个 91 | 92 | |参数|作用| 93 | |---|---| 94 | |strategyClass\ | 策略类 | 95 | |strategyAnnotationClass\| 策略实现类标记注解类(作用是完成策略实现类与标识符的绑定) | 96 | |identifyGetter\ | 从注解提取identify的逻辑(\一般为枚举,也可以定义为String)
我们使用会通过\来获取对应策略 | 97 | 98 | ```java 99 | @Bean 100 | public StrategyContainerFactoryBean platformStrategyManager() { 101 | StrategyContainerFactoryBean factoryBean = new StrategyContainerFactoryBean<>(); 102 | factoryBean.setStrategyClass(PlatformStrategy.class);//策略接口 103 | factoryBean.setStrategyAnnotationClass(Platform.class);//策略实现类标记注解 104 | factoryBean.setIdentifyGetter(Platform::value);// 策略实现类标记注解到标识符的转换逻辑 105 | return factoryBean; 106 | } 107 | ``` 108 | 109 | > 目前来看不支持xml配置,因为有函数入参,都sb时代了,谁还xml 110 | 111 | 由下面的注入可以发现,Spring忽略了泛型,因此在StrategyContainerFactoryBean增加了一个build方法,简化配置 112 | ```java 113 | @Bean 114 | public StrategyContainerFactoryBean platformStrategyManager() { 115 | return StrategyContainerFactoryBean.build( 116 | PlatformStrategy.class, 117 | Platform.class, 118 | Platform::value); 119 | } 120 | ``` 121 | 122 | #### 3. 注入StrategyContainer 123 | 124 | key为identify\,value为strategyClass\ 策略类 125 | 126 | ```java 127 | /** 128 | * 需要注意,spring容器注入会把泛型丢掉,所以必须通过beanname注入 129 | * 或者@bean的方法名和下面的属性名platformStrategyManager保持一致 130 | */ 131 | @Resource(name = "platformStrategyManager") 132 | private StrategyContainer platformStrategyContainer; 133 | ``` 134 | 135 | #### 4. 使用 136 | ```java 137 | PlatformStrategy platformStrategy = platformStrategyContainer.getStrategy(PlatformEnum.ALI); 138 | platformStrategy.hello() 139 | ``` 140 | 141 | > 这边getStrategy的入参需要和自定义注解生成的identify相同 142 | > 如果对于一个策略接口,有相同的identify产生,在FactoryBean初始化的时候会报错 143 | 144 | ### 进阶功能 145 | 146 | #### 0. 减免配置(SpringBootStarter) 147 | 考虑到现在使用方式上的繁琐以及使用策略模式大部分的场景都为简单场景(一个策略标识符对应一个策略实现),因此针对SpringBoot开发了Starter 148 | 149 | 使用这个Starter存在一个约束,**策略注解必须使用内置的@StrategyIdentifier**,如果你的策略模式比较复杂,只能自己去配置 150 | 151 | 使用方式很简单,引入starter包 152 | 153 | ``` 154 | 155 | io.github.dsc-cmt 156 | spring-easy-strategy-starter 157 | 1.4.0 158 | 159 | ``` 160 | 161 | 配置策略接口以及对应实现后,注入Container即可 162 | 163 | ``` 164 | @EasyStrategy 165 | StrategyContainer strategyContainerSample; 166 | ``` 167 | 168 | 169 | #### 1. 多个属性匹配策略 170 | 171 | 使用String作为identify,使用时将多个属性通过一定规则拼接成String来匹配策略 172 | 173 | ```java 174 | //0.定义策略接口 175 | public interface HelloStrategy { 176 | String hello(); 177 | } 178 | 179 | //1.定义标记注解注解 180 | @Target({ElementType.TYPE}) 181 | @Retention(RetentionPolicy.RUNTIME) 182 | @Component 183 | public @interface People { 184 | String district(); 185 | GenderEnum gender(); 186 | } 187 | //2.将该注解注解到策略实现类上去,完成策略实现类与标识符的绑定 188 | @People(district = "chinese", gender = GenderEnum.FEMALE) 189 | public class ChineseGirlHelloStrategy implements HelloStrategy { 190 | @Override 191 | public String hello() { 192 | return "你好"; 193 | } 194 | } 195 | //3.注册配置StrategyContainerFactoryBean 196 | @Bean 197 | public StrategyContainerFactoryBean helloStrategyContainer(){ 198 | return StrategyContainerFactoryBean.build( 199 | HelloStrategy.class, 200 | People.class, 201 | a -> Joiner.on(",").join(a.district(),a.gender().name())); 202 | } 203 | //4.在调用类中注入StrategyContainer 204 | @Resource(name = "helloStrategyContainer") 205 | private StrategyContainer helloStrategyContainer; 206 | //5.使用 207 | HelloStrategy helloStrategy = helloStrategyContainer.getStrategy(Joiner.on(",").join("chinese", GenderEnum.FEMALE.name())); 208 | helloStrategy.hello() 209 | ``` 210 | 211 | #### 2. 手动绑定策略 212 | 213 | StrategyContainer接口提供一个register方法用于手动绑定identifyCode和策略 214 | ```java 215 | helloStrategyContainer.register(Joiner.on(",").join("american", GenderEnum.FEMALE.name()),()->{ 216 | return "hello"; 217 | }); 218 | ``` 219 | 220 | #### 3. 单策略支持多注解 221 | 在业务开发中,很多场景下,对于多个identifyCode我们的策略是相同。 222 | 在原有自定义注解的基础上进行改造,比如原有注解如下 223 | 224 | ```java 225 | @Target({ElementType.TYPE}) 226 | @Retention(RetentionPolicy.RUNTIME) 227 | @Component 228 | public @interface One { 229 | 230 | String test(); 231 | 232 | } 233 | ``` 234 | 我们新增一个容器注解,为了支持在同一类上标注多个相同注解 235 | ```java 236 | @Target({ElementType.TYPE}) 237 | @Retention(RetentionPolicy.RUNTIME) 238 | @Component 239 | public @interface Many { 240 | 241 | One[] value(); 242 | } 243 | ``` 244 | 修改原有注解为 245 | ```java 246 | @Target({ElementType.TYPE}) 247 | @Retention(RetentionPolicy.RUNTIME) 248 | @Repeatable(Many.class) 249 | @Component 250 | public @interface One { 251 | 252 | String test(); 253 | 254 | } 255 | ``` 256 | 这是java8提供的语法糖,在做如上配置后,你就能在一个策略类标注多个相同注解了 257 | 258 | 框架自带的StrategyIdentifier已支持多注解模式 259 | 260 | 261 | #### 4. 默认策略 262 | 新增了一个`@DefaultStrategy`注解,策略类标注该注解后,如果通过identifyCode找不到策略实现,会执行默认策略逻辑。 263 | 264 | > 注意: 一个接口默认策略只能有一个,不然会报错 265 | 266 | #### 5. 支持多策略模式 267 | 某些业务场景下,针对一个identifyCode可能需要多个策略执行,比如我们的校验逻辑,针对一个业务方,可能会触发多个。 268 | 因此实现了MultiStrategyContainerFactoryBean类,基本使用和StrategyContainerFactoryBean类似。 269 | 也正是因为通过identifyCode可以获取到多个策略了,针对这种模式,新增了排序功能,可以使用spring的Order注解来指定顺序。 270 | 271 | 使用方式如下 272 | 策略配置 273 | ```java 274 | @StrategyIdentifier(identifyCode = "1") 275 | @StrategyIdentifier(identifyCode = "1") 276 | @StrategyIdentifier(identifyCode = "3") 277 | @Order(4) 278 | public class AValidation implements Validation{ 279 | @Override 280 | public void validate() { 281 | System.out.println("AAAAAA"); 282 | } 283 | } 284 | 285 | @StrategyIdentifier(identifyCode = "1") 286 | @StrategyIdentifier(identifyCode = "3") 287 | @Order(3) 288 | public class BValidation implements Validation{ 289 | @Override 290 | public void validate() { 291 | System.out.println("BBBBBBB"); 292 | } 293 | } 294 | ``` 295 | 框架配置 296 | ```java 297 | @Bean 298 | public MultiStrategyContainerFactoryBean validation(){ 299 | return MultiStrategyContainerFactoryBean.build(Validation.class,StrategyIdentifier.class,a -> a.identifyCode()); 300 | } 301 | ``` 302 | 注入 303 | ```java 304 | @Resource(name = "validation") 305 | private MultiStrategyContainer validationMultiStrategyContainer; 306 | ``` 307 | 使用 308 | ```java 309 | List strategies = validationMultiStrategyContainer.getStrategies("1"); 310 | ``` 311 | 312 | ## 更新 313 | 314 | - 2021-01-11 315 | 增加starter 316 | 317 | - 2020-03-30 318 | 策略标识符identifyCode使用泛型替换 319 | 320 | - 2019-09-19 321 | 支持多策略模式 322 | 323 | - 2019-09-10 324 | 支持单策略多注解 325 | 支持默认注解 326 | 327 | - 2019-09-06 328 | 解决策略类生成Aop代理时获取不到策略的问题,增加Aop相关测试用例 329 | 330 | - 2019-08-12 331 | 修改类名StrategyManager为StrategyContainer 332 | 333 | 334 | > 完整使用案例示例见test用例 335 | --------------------------------------------------------------------------------