У меня есть поток, который, казалось, работал нормально до вчерашнего дня, когда внезапно я начал получать следующее исключение на моей странице HTML, которая отображается в первом состоянии в моем потоке:Spring WebFlow (по-видимому) случайно перестает работать в Spring Boot app
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'flowScope' cannot be found on null
нарушитель строка кода была:
<h3 th:text="${flowRequestContext.flowScope}"/>
Дальнейшие исследования показали, что ни одна из переменных потока не доступны больше. Более того, если я поставлю операторы печати в службу, к которой поток обращается к различным вызовам, я вижу, что ни один из этих методов больше не вызывается - это похоже на то, что поток просто не работает.
Этот был, работая нормально ранее. Я даже вернул все мои локальные изменения в ранее стабильную версию кода, и эта же проблема также происходила там. Единственная вещь, которая, казалось, временно обошла эту проблему, заключалась в том, чтобы перезагрузить компьютер - проблема исчезла ненадолго, а затем вернулась.
Чтобы быть честным, я полностью не в курсе, что могло начаться, вызвав такую прерывистую проблему. Я думал о том, что старый процесс Java работает в фоновом режиме, мешая будущим запускам приложения, но проверил и уничтожил любой оставшийся процесс между развертываниями безрезультатно.
Я включил, что я надеюсь, соответствующий файл ниже. Любая помощь в решении этой проблемы будет очень оценена.
checkout.xml
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<on-start>
<set name="flowScope.paymentMethods" value="checkoutWidgetService.getPaymentMethods()"/>
<set name="flowScope.deliveryAddress" value="checkoutWidgetService.getDeliveryAddress()"/>
<set name="flowScope.sessionId" value="externalContext.nativeRequest.session.id"/>
</on-start>
<view-state id="payment-methods" view="payment-methods">
<transition on="selectPaymentMethod" to="new-details">
<evaluate expression="checkoutWidgetService.getCardDetails(requestParameters.type)" result="flowScope.cardDetails"/>
</transition>
</view-state>
<view-state id="new-details" view="new-details">
<transition on="submitDetails" to="summary">
<evaluate expression="checkoutWidgetService.buildCardDetails(requestParameters)" result="flowScope.cardDetails"/>
</transition>
</view-state>
<view-state id="summary" view="summary">
<transition on="completeCheckout" to="redirect">
<evaluate expression="checkoutWidgetService.completeCheckout(externalContext.nativeRequest.session, flowRequestContext, flowScope.cardDetails)"/>
</transition>
<transition on="cancelCheckout" to="redirect">
<evaluate expression="checkoutWidgetService.cancelCheckout(externalContext.nativeRequest.session, flowRequestContext)"/>
</transition>
</view-state>
<end-state id="redirect" view="externalRedirect:contextRelative:/payments/checkout-widgets/end"/>
</flow>
WebflowConfig.java
@Configuration
@AutoConfigureAfter(MvcConfig.class)
public class WebflowConfig extends AbstractFlowConfiguration {
@Autowired
private SpringTemplateEngine templateEngine;
@Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry())
.addFlowExecutionListener(new SecurityFlowExecutionListener())
.build();
}
@Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder(flowBuilderServices())
.addFlowLocation("classpath:/templates/checkout.xml", "payments/checkout-widget/start")
.build();
}
@Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setDevelopmentMode(true)
.build();
}
@Bean
public FlowController flowController() {
FlowController flowController = new FlowController();
flowController.setFlowExecutor(flowExecutor());
return flowController;
}
@Bean
public FlowHandlerMapping flowHandlerMapping() {
FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
flowHandlerMapping.setFlowRegistry(flowRegistry());
flowHandlerMapping.setOrder(-1);
return flowHandlerMapping;
}
@Bean
public FlowHandlerAdapter flowHandlerAdapter() {
FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor());
flowHandlerAdapter.setSaveOutputToFlashScopeOnRedirect(true);
return flowHandlerAdapter;
}
@Bean
public AjaxThymeleafViewResolver thymeleafViewResolver() {
AjaxThymeleafViewResolver viewResolver = new AjaxThymeleafViewResolver();
viewResolver.setViewClass(FlowAjaxThymeleafView.class);
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
@Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
List<ViewResolver> viewResolvers = new ArrayList<>();
viewResolvers.add(thymeleafViewResolver());
MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();
mvcViewFactoryCreator.setViewResolvers(viewResolvers);
mvcViewFactoryCreator.setUseSpringBeanBinding(true);
return mvcViewFactoryCreator;
}
}
CheckoutWidgetSessionMvcController.java
@Controller
@RequestMapping("/payments/checkout-widgets")
public class CheckoutWidgetSessionMvcController {
@Inject
private CheckoutWidgetService service;
@RequestMapping(value = {"/start"}, method = RequestMethod.GET)
public ModelAndView paymentMethods() {
return new ModelAndView("payment-methods", null);
}
@RequestMapping(value = "/end", method = RequestMethod.GET)
public String invalidateSession(HttpSession session) {
service.invalidateSession(session);
return "dummy-redirect-post";
}
}
CheckoutWidgetService.java
public interface CheckoutWidgetService {
List<PaymentMethod> getPaymentMethods();
CardDetails getCardDetails(String name);
CardDetails buildCardDetails(LocalParameterMap params);
String getDeliveryAddress();
void completeCheckout(HttpSession session, RequestContext context, CardDetails cardDetails);
void cancelCheckout(HttpSession session, RequestContext context);
void invalidateSession(HttpSession session);
}
CheckoutWidgetServiceImpl.java
@Service("checkoutWidgetService")
public class CheckoutWidgetServiceImpl implements CheckoutWidgetService {
@Inject
private CheckoutWidgetSessionService sessionService;
private final List<PaymentMethod> paymentMethods = new ArrayList<>();
private final String deliveryAddress;
public CheckoutWidgetServiceImpl() {
paymentMethods.add(new PaymentMethod("PayPal", "/images/paypal-logo.png"));
paymentMethods.add(new PaymentMethod("Mastercard", "/images/mc-logo.png"));
paymentMethods.add(new PaymentMethod("Visa", "/images/visa-logo.png"));
paymentMethods.add(new PaymentMethod("Amex", "/images/amex-logo.png"));
paymentMethods.add(new PaymentMethod("Google Checkout", "/images/google-logo.png"));
deliveryAddress = "xxxxx";
}
@Override
public List<PaymentMethod> getPaymentMethods() {
System.out.println("Returning paymentMethods: " + paymentMethods);
return paymentMethods;
}
@Override
public CardDetails getCardDetails(String name) {
CardDetails cardDetails = new CardDetails();
cardDetails.setCardType(name);
return cardDetails;
}
@Override
public CardDetails buildCardDetails(LocalParameterMap params) {
CardDetails cardDetails = new CardDetails();
cardDetails.setCardNumber(params.get("cardNumber"));
cardDetails.setExpiryMonth(params.get("expiryMonth"));
cardDetails.setExpiryYear(params.get("expiryYear"));
cardDetails.setNameOnCard(params.get("nameOnCard"));
cardDetails.setCvv2(params.get("cvv2"));
return cardDetails;
}
@Override
public String getDeliveryAddress() {
return deliveryAddress;
}
@Override
public void invalidateSession(HttpSession session) {
session.invalidate();
}
private RedirectUrls getRedirectUrls(String sessionId) {
CheckoutWidgetSession widgetSession = sessionService.getCheckoutWidgetSession(sessionId).get();
return widgetSession.getRedirectUrls();
}
@Override
public void completeCheckout(HttpSession session, RequestContext context, CardDetails cardDetails) {
RedirectUrls redirects = getRedirectUrls(session.getId());
context.getFlowScope().remove("paymentMethods");
UriBuilder uriBuilder = UriBuilder.fromUri(URI.create(redirects.getSuccessUrl()));
String forwardUrl = uriBuilder.queryParam("transactionId", "12345").toString();
context.getFlowScope().put("forwardUrl", forwardUrl);
context.getFlowScope().put("target", "_top");
}
@Override
public void cancelCheckout(HttpSession session, RequestContext context) {
RedirectUrls redirects = getRedirectUrls(session.getId());
context.getFlowScope().remove("paymentMethods");
String forwardUrl = redirects.getCancelUrl();
context.getFlowScope().put("forwardUrl", forwardUrl);
context.getFlowScope().put("target", "_top");
}
}
Применение.Java
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.pay.widgets.checkout"})
@Import(JerseyAutoConfiguration.class)
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}