最近在做数据库大表拆分接ES的优化需求,针对某个大表数据逻辑接口IPoolProductService,增加策略实现类PoolProductProxyService,用于控制访问走老逻辑PoolProductService还是新逻辑PoolProductInfoService。
数据逻辑接口IPoolProductService:
public interface IPoolProductService {
ProductDTO query(Long id);
}
老逻辑实现类PoolProductService:
@Service
public class PoolProductService implements IPoolProductService {
@Override
public ProductDTO query(Long id) {
System.out.println("I AM oldService");
return null;
}
}
策略类PoolProductProxyService:
@Service
@Primary
public class PoolProductProxyService implements IPoolProductService {
@Resource
private PoolProductService oldService;
@Resource
private PoolProductInfoService newService;
@Override
public ProductDTO query(Long id) {
System.out.println("I AM proxyService");
if (oldRead) {
return oldService.query(id);
}
if (newRead) {
return newService.query(id);
}
return null;
}
}
新逻辑实现类PoolProductInfoService:
@Service
public class PoolProductInfoService implements IPoolProductService {
@Override
public ProductDTO query(Long id) {
System.out.println("I AM newService");
return null;
}
}
调用类PoolProductBiz:
@Service
public class PoolProductBiz {
@Resource
private IPoolProductService poolProductService;
private ProductDTO query(Long id) {
return poolProductService.query(id);
}
}
为减小对业务逻辑调用的代码改动,在策略类PoolProductProxyService上加注解@Primary,希望业务代码调用优先调PoolProductProxyService,经过策略分发走新老逻辑。
但在测试时,发现加了@Primary注解的类PoolProductProxyService未被优先调用,仍走老逻辑,注解没有生效。
Spring启动时,发现注入相同类型的bean有多个,会报异常NoUniqueBeanDefinitionException,可以通过加注解@Primary 赋予某个bean更高优先级或@Priority 决定优先注入的bean等。
此处采用加@Primary 注解,但@Primary 注解不能解决调用指定实现类的问题。
在注入类时使用@Resource,其默认按照名称进行装配,找不到bean,会通过byType寻找bean。而@Primary是指在根据type寻找时,优先取加了该注解的实现类。
在上述示例中,调用类PoolProductBiz通过@Resource注入接口IPoolProductService,name为poolProductService,所以会优先根据poolProductService寻找对应的实现类,即老逻辑实现类,走不到策略类,@Primary注解未生效。
针对一个接口多个实现类,想调用指定实现类可以有下述几种改进方案:
@Service
public class PoolProductBiz {
@Resource
private IPoolProductService poolProductServiceImpl;
public ProductDTO query(Long id) {
return poolProductServiceImpl.query(id);
}
}
@Service
public class PoolProductBiz {
@Autowired
private IPoolProductService poolProductService;
public ProductDTO query(Long id) {
return poolProductService.query(id);
}
}
@Service
public class PoolProductBiz {
@Autowired
@Qualifier("PoolProductProxyService")
private IPoolProductService poolProductService;
}
@Service
@Repository(value = "PoolProductProxyService")
public class PoolProductProxyService implements IPoolProductService {
@Resource
private PoolProductService oldService;
@Resource
private PoolProductInfoService newService;
}
当然还有其他解决方案,在本case中,采用方案1。
因有大量业务代码通过以下方式注入(如下代码块),考虑代码重构成本和业务影响大小,将老逻辑实现类PoolProductService改名为PoolProductServiceImpl,在策略类PoolProductProxyService上加@Primary 注解,解决一个接口多个实现类调用指定类失效的问题。
@Resource
private IPoolProductService poolProductService;
另外,也可以简单粗暴直接在业务代码中注入策略类PoolProductProxyService,但这种业务改动兼容成本太高,后面切换新老逻辑读写,下掉老逻辑和策略分发类也不方便。
其次业务代码中,若没有注入接口,直接注入老逻辑实现类,改动回归成本也会非常高。