Spring服务定制及相关问题解决

栏目: Java · 发布时间: 5年前

内容简介:​我们都知道如果使用Spring来进行bean管理的时候。如果同一个接口的实现类存在两个,直接使用​ 针对前面两套环境的情况,我们可以使用​ 最终输出的结果为:

问题总述

​我们都知道如果使用Spring来进行bean管理的时候。如果同一个接口的实现类存在两个,直接使用 @Autowired 注解来实现bean注入,会在启动的时候报异常。我们通常的做法是使用 @Resource 注解来执行bean的名称。不过通过 @Resource 注解类似于硬编码的方式,如果我们想修改接口的具体实现,必须修改代码。假设我们环境中针对所有接口,都有两套实现,一套在测试环境中使用,一个在生产环境中使用。那么当切换环境的时候,所有接口使用 @Resource 注入的地方都需要修改bean名称。

使用@Profile注解

​ 针对前面两套环境的情况,我们可以使用 @Profile 注解来轻松解决。具体代码示例如下:

public interface HelloService {
    
    void saySomething(String msg);
}

@Profile("kind1")
@Service
public class HelloServiceImpl1 implements HelloService {
    public void saySomething(String msg) {
        System.out.println("HelloServiceImpl1 say:" + msg);
    }
}

@Profile("kind2")
@Service
public class HelloServiceImpl2 implements HelloService {
    public void saySomething(String msg) {
        System.out.println("HelloServiceImpl2 say:" + msg);
    }
}

@EnableAspectJAutoProxy
@Configurable
@ComponentScan(basePackages="com.rampage.spring")
@EnableScheduling
public class ApplicationConfig {
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ApplicationConfig.class)
@ActiveProfiles(profiles={"kind1"})     // 启用kind1注入的bean
public class HelloServiceTest {
    
    @Autowired
    private HelloService helloService;
    
    @Test
    public void testServiceSelector() {
        helloService.saySomething("I Love U!");
    }
}

​ 最终输出的结果为:

HelloServiceImpl1 say:I Love U!

多服务共存定制

​ 考虑这样一种情况,假设 HelloService 是针对全国通用的服务,对于不同的省市使用不同的方言来 saySomething :smile: .假设系统都是使用一套,那么在使用Spring进行bean管理的时候要么针对不同的省市只打包对应的目录下的 HelloService 实现,要么同前面一样使用 @Profile 注解来区分不同的实现类型,最后针对不同的省市修改 @ActiveProfiles 的具体值。这两种方法都需要针对不同的地区来进行相应的代码修改,然后再重新打包。考虑到全国几百个市,如果一次统一全部升级,估计光打包可能都要打包一天。。。

​ 更进一步的情况,东北三省大部分城市都是说普通话,那么实际上只要使用一个默认的实现类就行了。换句话将,现在想实现这样一种定制: 每个接口有一个默认实现,不同的城市有一个定制实现的类型码。如果根据定制类型码能够找到对应的接口实现,则使用该实现类。如果未找到,则使用默认的实现类。

​ 很显然,上面要实现的是在代码运行过程中动态判断最后接口的具体实现类。其中定制的类型码可以通过数据库或者配置文件的方式指定,在代码运行的过程中根据定制码去获取对应的服务实现。

​ 该方案的一种实现如下:

public interface ServiceSelector {
    /**
     * 得到定制码
     * @return
     */
    String getCustCode();
}

public interface HelloService extends ServiceSelector {
    
    void saySomething(String msg);
}

public abstract class ServiceProvider <T, S extends T> implements BeanFactoryAware  {

    private ConfigurableListableBeanFactory beanFactory;
    
    private Map<String, T> serviceMap;
    
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof DefaultListableBeanFactory) {  
            this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;  
        }  
    }
    
    @SuppressWarnings({"unchecked", "restriction"})
    @PostConstruct
    public void init(){
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type[] types = pt.getActualTypeArguments();
        
        Class<T> interfaceClazz = (Class<T>)types[0];
        // Class<S> defaultImplClazz = (Class<S>)types[1];  // defaultImplClazz为默认实现
        
        Map<String, T> serviceBeanMap = beanFactory.getBeansOfType(interfaceClazz);
        serviceMap = new HashMap<String , T>(serviceBeanMap.size());
        for (T processor : serviceBeanMap.values()) {
            if (!(processor instanceof ServiceSelector)) {
                // 如果实现类没有实现OptionalServiceSelector接口,直接报错
                throw new RuntimeException("可选服务必须实现ServiceSelector接口!");
            }
            
            // 如果已经存在相同定制码的服务也直接抛异常
            ServiceSelector selector = (ServiceSelector)processor;
            if (null != serviceMap.get(selector.getCustCode())) {
                throw new RuntimeException("已经存在定制码为【" + selector.getCustCode() + "】的服务");
            }
            
            // 加入Map中
            serviceMap.put(selector.getCustCode(), processor);
        }
    }
    
    public T getService() {
        // 从配置文件或者数据库获取当前省市的定制码
        String custCode = "kind11";
        if (null != serviceMap.get(custCode)) {
            return serviceMap.get(custCode);
        }
        
        // 如果未找到则使用默认实现
        return serviceMap.get("DEFAULT");
    }
}

@Service
public class DefaultHelloService implements HelloService {

    public String getCustCode() {
        return "DEFAULT";
    }

    public void saySomething(String msg) {
        System.out.println("DefaultHelloService say:" + msg);
    }

}

@Service
public class HelloServiceImpl1 implements HelloService {
    public void saySomething(String msg) {
        System.out.println("HelloServiceImpl1 say:" + msg);
    }

    public String getCustCode() {
        return "kind1";
    }
}

@Service
public class HelloServiceImpl2 implements HelloService {
    public void saySomething(String msg) {
        System.out.println("HelloServiceImpl2 say:" + msg);
    }

    public String getCustCode() {
        return "kind2";
    }
}

@Service
public class HelloServiceProvider extends ServiceProvider<HelloService, DefaultHelloService> {
    
}

@EnableAspectJAutoProxy
@Configurable
@ComponentScan(basePackages="com.rampage.spring")
@EnableScheduling
public class ApplicationConfig {
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ApplicationConfig.class)
public class HelloServiceTest {
    
    // 注入接口服务提供器,而不是接口
    @Autowired
    private HelloServiceProvider helloServiceProvider;
    
    @Test
    public void testServiceSelector() {
        helloServiceProvider.getService().saySomething("I Love U!");
    }
}

​ 上例的最终输出为:

DefaultHelloService say:I Love U!

使用BFP来优雅定制服务实现

​ 上面的服务定制通过各种绕路实现了服务定制,但是不能看出上面的实现非常不优雅,存在很多问题:

ServiceSelector

​ 那么针对这种情况,有没有一个优雅的实现。既能满足前面所说的业务场景需求,又能够不初始化多余的类?当然是有的,其中的一套实现方案如下:

// 定制服务的注解声明,支持多个定制码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Customized {
    String[] custCodes() default {"DEFAULT"};
}


public interface HelloService {
    void saySomething(String msg);
}

@Component
public class CustomizedServiceBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {

    private static String custCode;

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomizedServiceBeanFactoryPostProcessor.class);

    static {
        Properties properties = new Properties();
        /*try {
            // 读取配置文件定制码
            properties.load(CustomizedServiceBeanFactoryPostProcessor.class.getClassLoader()
                    .getResource("app-config.properties").openStream());
        } catch (Exception e) {
            throw new RuntimeException("读取配置文件失败!", e);
        }*/
        // 这里假设取默认定制码
        custCode = properties.getProperty("custCode", "DEFAULT");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String[] beanDefNames = beanFactory.getBeanDefinitionNames();
        if (ArrayUtils.isEmpty(beanDefNames)) {
            return;
        }
        Class<?> beanClass = null;
        BeanDefinition beanDef = null;
        Customized customized = null;
        Set<Class<?>> foundCustomizedServices = new HashSet<Class<?>>();
        Map<String, Class<?>> waitDestroiedBeans = new HashMap<String, Class<?>>();
        String[] defaultCustCodes = {"DEFAULT"};
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        for (String name : beanDefNames) {
            beanDef = beanFactory.getBeanDefinition(name);
            if (beanDef == null || StringUtils.isEmpty(beanDef.getBeanClassName())) {
                continue;
            }
            try {
                // 加载类得到其上的注解的定义
                beanClass = classLoader.loadClass(beanDef.getBeanClassName());
            } catch (ClassNotFoundException e) {
                // 发生了异常,这里直接跳过
            }
            if (beanClass == null) {
                continue;
            }
            customized = this.getCustomizedAnnotations(beanClass);

            // 非定制类直接跳过
            if (customized == null) {
                continue;
            }
            if (ArrayUtils.contains(customized.custCodes(), custCode)) {
                foundCustomizedServices.addAll(this.getCustomizedServices(beanClass));
                LOGGER.info("定制码【{}】下装载到定制服务实现类【{}】......", custCode, beanClass);
            } else {
                if (!ArrayUtils.isEquals(customized.custCodes(), defaultCustCodes)) {
                    ((DefaultListableBeanFactory) beanFactory).removeBeanDefinition(name);
                    LOGGER.info("定制码【{}】下卸载定制服务实现类【{}��......", custCode, beanClass);
                } else {
                    // 默认实现类暂时不知道是否需要销毁,先暂存
                    waitDestroiedBeans.put(name, beanClass);
                }
            }
        }

        // 没有需要检测的是否需要销毁的默认实现类则直接返回
        if (MapUtils.isEmpty(waitDestroiedBeans)) {
            return;
        }

        // 看定制服务的默认实现类是否已经找到特定的实现类,如果找到了则需要销毁默认实现类
        for (Entry<String, Class<?>> entry : waitDestroiedBeans.entrySet()) {
            // 直接继承定制服务类实现
            if (foundCustomizedServices.contains(entry.getValue())) {
                ((DefaultListableBeanFactory) beanFactory).removeBeanDefinition(entry.getKey());
                LOGGER.info("定制码【{}】下卸载默认定制服务实现类【{}】......", custCode, entry.getValue());
            } else {
                // 通过定制服务接口实现定制
                Set<Class<?>> defaultCustServices = getCustomizedServices(entry.getValue());
                for (Class<?> clazz : defaultCustServices) {
                    if (foundCustomizedServices.contains(clazz)) {
                        ((DefaultListableBeanFactory) beanFactory).removeBeanDefinition(entry.getKey());
                        LOGGER.info("定制码【{}】下卸载默认定制服务实现类【{}】......", custCode, entry.getValue());
                        break;
                    }
                }
            }
        }
    }

    /**
     * 得到定制类的定制注解(一旦找到立即返回)
     * 
     * @param clazz
     *            传入的定制类
     * @return 得到定制类的定制注解
     */
    private Customized getCustomizedAnnotations(Class<? extends Object> clazz) {
        // 递归遍历寻找定制注解
        Annotation[] annotations = null;
        Class<?>[] interfaces = null;
        while (clazz != Object.class) {
            // 先直接找该类上的注解
            annotations = clazz.getAnnotations();
            if (annotations != null && annotations.length > 0) {
                for (Annotation one : annotations) {
                    if (one.annotationType() == Customized.class) {
                        return (Customized) one;
                    }
                }
            }

            // 再找该类实现的接口上的注解
            interfaces = clazz.getInterfaces();
            if (interfaces != null && interfaces.length > 0) {
                for (Class<?> intf : interfaces) {
                    annotations = intf.getAnnotations();
                    if (annotations != null && annotations.length > 0) {
                        for (Annotation one : annotations) {
                            if (one.annotationType() == Customized.class) {
                                return (Customized) one;
                            }
                        }
                    }
                }
            }

            // 未找到继续找父类上的注解
            clazz = clazz.getSuperclass();
        }

        return null;
    }

    /**
     * 得到定制的服务类列表(即有Customized注解的类列表)
     * 
     * @param orginalClass
     *            传入的原始类
     * @return 定制服务类的列表
     */
    private Set<Class<?>> getCustomizedServices(Class<?> orginalClass) {
        Class<?> class1 = orginalClass;
        Set<Class<?>> customizedInterfaces = new HashSet<Class<?>>();
        Class<?>[] interfaces = null;
        while (class1 != Object.class) {
            // 类也进行判断,这样能实现直接不通过接口的,而通过service继承实现定制服务
            if (class1.getAnnotation(Customized.class) != null) {
                customizedInterfaces.add(class1);
            }

            // 遍历接口,看接口是是定制服务接口
            interfaces = class1.getInterfaces();
            if (interfaces == null || interfaces.length == 0) {
                class1 = class1.getSuperclass();
                continue;
            }

            // 接口的实现只能有一个,所以一旦找到带有注解的实现类的接口,都作为定制服务接口
            for (Class<?> clazz : interfaces) {
                customizedInterfaces.add(clazz);
            }

            // 寻找父类定制服务
            class1 = class1.getSuperclass();
        }

        return customizedInterfaces;
    }

    public int getOrder() {
        return Integer.MIN_VALUE;
    }
}

@Customized     // 默认服务实现类
@Service
public class DefaultHelloService implements HelloService {

    public void saySomething(String msg) {
        System.out.println("DefaultHelloService say:" + msg);
    }

}

@Customized(custCodes="kind1")
@Service
public class HelloServiceImpl1 implements HelloService {
    public void saySomething(String msg) {
        System.out.println("HelloServiceImpl1 say:" + msg);
    }
}

@Customized(custCodes="kind2")
@Service
public class HelloServiceImpl2 implements HelloService {
    public void saySomething(String msg) {
        System.out.println("HelloServiceImpl2 say:" + msg);
    }

    public String getCustCode() {
        return "kind2";
    }
}

@EnableAspectJAutoProxy
@Configurable
@ComponentScan(basePackages="com.rampage.spring")
@EnableScheduling
public class ApplicationConfig {
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ApplicationConfig.class)
public class HelloServiceTest {
    
    @Autowired
    private HelloService helloService;
    
    @Test
    public void testServiceSelector() {
        helloService.saySomething("I Love U!");
    }
    
}

​ 最终输出的结果为:

DefaultHelloService say:I Love U!

总结

​关于服务定制,其实前面所讲的方法都可以。只不过在特定的情况下各有各的优势,需要根据具体情况来选择合适的定制方案。而定制方案的选择,依赖于深入地理解Spring的类管理和加载过程,会用BPP、BFP等来定制类的加载过程。

Linux公社的RSS地址https://www.linuxidc.com/rssFeed.aspx

本文永久更新链接地址: https://www.linuxidc.com/Linux/2019-03/157251.htm


以上所述就是小编给大家介绍的《Spring服务定制及相关问题解决》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Reality Is Broken

Reality Is Broken

Jane McGonigal / Penguin Press HC, The / 2011-1-20 / USD 26.95

Visionary game designer Jane McGonigal reveals how we can harness the power of games to solve real-world problems and boost global happiness. More than 174 million Americans are gamers, and......一起来看看 《Reality Is Broken》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具