我正在使用Spring 4(Spring Boot)实现Rest WS。
基本思想是我想使用指定标识符(例如社会安全号或其他东西)的JSON有效负载,并在该标识符上运行多个子服务。 以下是有效负载示例:
{
"ssNumber" : "1111111111111111",
"subServicesDetails" :
[
{ "subServiceName" : "Foo" , "requestParameters" : {} },
{ "subServiceName" : "Dummy", "requestParameters" : {} }
]
}
在我的代码中,我有多个"子服务" (FooService
,DummyService
)实施SubService
界面:
package com.johnarnold.myws.service;
import com.johnarnold.myws.model.SubServiceDetails;
public interface SubService {
public boolean service(String ssNumber, SubServiceDetails ssd);
}
以下是FooService
代码。
包com.johnarnold.myws.service
;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.johnarnold.myws.dao.FooDao;
import com.johnarnold.myws.model.Foo;
import com.johnarnold.myws.model.SubServiceDetails;
@Component
public class FooService implements SubService{
private static Logger log = Logger.getLogger(FooService.class);
@Autowired
private FooDao dao;
public FooService()
{
log.debug("FooService ctor");
}
public boolean service(String ssNumber, SubServiceDetails ssd)
{
log.debug("FooService service");
Map <String, String> responseParameters = new HashMap<String, String>();
try
{
Foo foo = dao.getFoo(ssNumber);
if(foo.isCompromised())
{
responseParameters.put("listed", "true");
}
else
{
responseParameters.put("listed", "false");
}
ssd.setResponseParameters(responseParameters);
return true;
}
catch(Throwable ex)
{
log.error("Exception in service ", ex);
}
return false;
}
}
现在我编写了自己的工厂来创建子服务,但是当我这样做的时候因为我明确地在下面创建我的bean(例如FooService) - 我的容器不会自动注入任何@Autowired成员 - 例如FooDao: 包com.johnarnold.myws.service;
public class SubServiceFactory {
/*
* Instantiates a SubService for the supplied subServiceName or throws an exception if
* no valid SubService exists
*/
public static SubService createSubService(String subServiceNameStr)
{
SubService subService = null;
System.out.println("subServiceName [" + subServiceNameStr + "]");
if(subServiceNameStr.equals("Foo"))
{
subService = new FooService();
}
if(subServiceNameStr.equals("Dummy"))
{
subService = new DummyService();
}
else
{
System.out.println("subServiceName [" + subServiceNameStr + "] is not defined");
}
return subService;
}
}
为了完整性,这里是控制器:
package com.johnarnold.myws.controller;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.apache.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.johnarnold.myws.model.RawsPayload;
import com.johnarnold.myws.model.SubServiceDetails;
import com.johnarnold.myws.service.SubService;
import com.johnarnold.myws.service.SubServiceFactory;
import com.johnarnold.myws.web.ValidMessage;
@RestController
@RequestMapping("/raws/")
public class RawsController {
private static final Logger logger = Logger.getLogger(RawsController.class);
//@Autowired
//SubService [] subSvcs;
@RequestMapping(value="/{version}/status", method=RequestMethod.GET)
public ResponseEntity<ValidMessage> getServiceStatus()
{
return new ResponseEntity<>(new ValidMessage() , HttpStatus.OK);
}
/*
* Main entry point - orchestrates all of the WS Sub Services
*/
@RequestMapping(value="/{version}/raws", method=RequestMethod.PUT)
public ResponseEntity<String> raws(@Valid @RequestBody RawsPayload rawsPayload,
HttpServletRequest request)
{
logger.info("Request received");
System.out.println("payl " + rawsPayload);
System.out.println("ssNumber=" + rawsPayload.getSsNumber());
System.out.println("sub svcs details=" + rawsPayload.getSubServicesDetails().length);
SubServiceDetails[] subServiceDetails = rawsPayload.getSubServicesDetails();
for(SubServiceDetails ssd : subServiceDetails)
{
String subServiceNameStr = ssd.getSubServiceName();
System.out.println("svcname=" + subServiceNameStr);
System.out.println("svc req params=" + ssd.getRequestParameters());
System.out.println("svc resp params=" + ssd.getResponseParameters());
SubService subService = SubServiceFactory.createSubService(subServiceNameStr);
// Probably wrap the below with some timings
subService.service(rawsPayload.getSsNumber(), ssd);
}
//System.out.println("svcs are " + subSvcs + "size=" + subSvcs.length);
return new ResponseEntity<>("foo" , HttpStatus.OK);
}
}
这是主要的有效载荷类:
package com.johnarnold.myws.model;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.log4j.Logger;
import org.hibernate.validator.constraints.Length;
public class RawsPayload {
static Logger log = Logger.getLogger(RawsPayload.class);
@NotNull
@Length(min=16, max=19)
private String ssNumber;
@Valid
@NotNull
@Size(min=1, max=3)
private SubServiceDetails [] subServicesDetails;
public String getSsNumber() {
return ssNumber;
}
public void setSsNumber(String ssNumber) {
log.info("setSsNumber()");
this.ssNumber = ssNumber;
}
public SubServiceDetails[] getSubServicesDetails() {
return subServicesDetails;
}
public void setSubServicesDetails(SubServiceDetails[] subServicesDetails) {
this.subServicesDetails = subServicesDetails;
}
}
我已经在StackOverflow上阅读了关于Spring 4 Conditional Beans的一些答案 - 但是这个功能似乎是针对Context / Configuration类型信息而不是Request消息内容(如本例所示)。 任何人都可以指出我正确的方向。如有必要,我可以提供进一步的背景 KRgds 约翰
答案 0 :(得分:1)
解决此问题的两种可能方法:
如果你想坚持使用基本的Spring解决方案,下面有一个更简单的选择:
将子服务bean作为列表注入主服务,然后从中进行选择。它看起来像这样:
@Bean
public List<SubService> subServices(){
List<SubService> list = new SubService<>();
list.add(new AService());
list.add(new BService());
return list;
}
THEN
public SubService selectServiceByName() {
//iterate through the list, pick the service with the right name and return - this solution will require you to bind by beannames
}
答案 1 :(得分:1)
@ john-arnold首先,列出所有类似的服务,或使用@Service / @ Component注释它们,如下所示:名称以subServiceName
param的值开头并包含一个公共后缀, &#34;服务&#34;在这里,这很重要。
@Bean("FooService")
public SubService fooService() {
return new FooService();
}
@Bean("DummyService")
public SubService dummyService() {
return new DummyService();
}
然后改变你的工厂:
@Component
public class SubServiceFactory implements BeanFactoryAware{
private BeanFactory beanFactory;
private static final String MY_SERVICE_SUFFIX = "Service";
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public <T> T getServiceImplementation(String name, Class<T> requiredType) {
return beanFactory.getBean(name + MY_SERVICE_SUFFIX, requiredType);
}
}
现在我们在这里有一个BeanFactoryAware类,你可以注入到你的Rest Endpoint而不是if语句,试试这个:
subServiceFactory.getServiceImplementation(subServiceNameStr, SubService.class);
这将返回你的bean或异常,如果它没有找到。如果您不想要异常,则可以捕获它并返回null,或者您可以创建一个Service imp。只为这些并返回该实例。你的选择。
编辑:
作为快捷方式,您可以定义您的imp。豆,然后将其添加到您的休息终点
@Autowired
private Map<String, SubService> mySubServices;
Spring会自动注入你的所有imp。 REF。所以你可以只使用地图的get()方法。但我更喜欢第一个......
答案 2 :(得分:0)
这里你不需要任何花哨的东西。只需实施所有实现服务界面的服务,使用@Component
或@Service
对其进行注释,然后照常扫描。
然后,无论您何时必须选择具体的服务实现,请像这样自动装配您服务的所有实现:
@Autowired
Map<String, SubService> subServices;
映射的关键字是每个子服务实现的@Component
注释中指定的服务名称,值将是实例。
因此,当您收到JSON时,只需获取子服务的名称(即Foo
),然后获取地图的特定服务:
SubService fooSubService = subServices.get(subServiceName + "Service");
其中subServiceName
是您在JSON中收到的子服务的非大写字母名称(例如,如果您收到Foo
,则会foo
)。
约定是使用实现接口的类的非大写的名称作为bean名称,即对于FooService
类,bean名称将是fooService
,这是你拥有的密钥在地图中寻找。