- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
上一篇博文中,我们分析了Brave是如何在普通Web项目中使用的,这一篇博文我们继续分析Brave和SpringMVC项目的整合方法及原理。
我们分两个部分来介绍和SpringMVC的整合,及XML配置方式和Annotation注解方式
pom.xml添加相关依赖spring-web和spring-webmvc
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-web</artifactId>
<version>${brave.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
<version>${brave.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
在Servlet2.5规范中,必须配置web.xml,我们只需要配置DispatcherServlet,SpringMVC的核心控制器就可以了
相关代码在Chapter5/springmvc-servlet25中
web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>SpringMVC Servlet2.5 Application</display-name>
<servlet>
<servlet-name>spring-webmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-webmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
然后在WEB-INF下配置spring-webmvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<context:property-placeholder/>
<bean id="sender" class="zipkin2.reporter.okhttp3.OkHttpSender" factory-method="create">
<constructor-arg type="String" value="http://localhost:9411/api/v2/spans"/>
</bean>
<bean id="tracing" class="brave.spring.beans.TracingFactoryBean">
<property name="localServiceName" value="${zipkin.service:springmvc-servlet25-example}"/>
<property name="spanReporter">
<bean class="brave.spring.beans.AsyncReporterFactoryBean">
<property name="encoder" value="JSON_V2"/>
<property name="sender" ref="sender"/>
<!-- wait up to half a second for any in-flight spans on close -->
<property name="closeTimeout" value="500"/>
</bean>
</property>
<property name="propagationFactory">
<bean id="propagationFactory" class="brave.propagation.ExtraFieldPropagation" factory-method="newFactory">
<constructor-arg index="0">
<util:constant static-field="brave.propagation.B3Propagation.FACTORY"/>
</constructor-arg>
<constructor-arg index="1">
<list>
<value>user-name</value>
</list>
</constructor-arg>
</bean>
</property>
<property name="currentTraceContext">
<bean class="brave.context.log4j2.ThreadContextCurrentTraceContext" factory-method="create"/>
</property>
</bean>
<bean id="httpTracing" class="brave.spring.beans.HttpTracingFactoryBean">
<property name="tracing" ref="tracing"/>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="interceptors">
<list>
<bean class="brave.spring.web.TracingClientHttpRequestInterceptor" factory-method="create">
<constructor-arg type="brave.http.HttpTracing" ref="httpTracing"/>
</bean>
</list>
</property>
</bean>
<mvc:interceptors>
<bean class="brave.spring.webmvc.TracingHandlerInterceptor" factory-method="create">
<constructor-arg type="brave.http.HttpTracing" ref="httpTracing"/>
</bean>
</mvc:interceptors>
<!-- Loads the controller -->
<context:component-scan base-package="org.mozhu.zipkin.springmvc"/>
<mvc:annotation-driven/>
</beans>
使用brave.spring.beans.TracingFactoryBean创建tracing
使用brave.spring.beans.HttpTracingFactoryBean创建httpTracing
配置springmvc的拦截器brave.spring.webmvc.TracingHandlerInterceptor
并配置org.springframework.web.client.RestTemplate作为客户端发送http请求
再来看看两个Controller:Frontend和Backend,和前面FrontendServlet,BackendServlet功能一样
@RestController
public class Frontend {
private final static Logger LOGGER = LoggerFactory.getLogger(Frontend.class);
@Autowired
RestTemplate restTemplate;
@RequestMapping("/")
public String callBackend() {
LOGGER.info("frontend receive request");
return restTemplate.getForObject("http://localhost:9000/api", String.class);
}
}
Frontend中使用Spring提供的restTemplate向Backend发送请求
@RestController
public class Backend {
private final static Logger LOGGER = LoggerFactory.getLogger(Backend.class);
@RequestMapping("/api")
public String printDate(@RequestHeader(name = "user-name", required = false) String username) {
LOGGER.info("backend receive request");
if (username != null) {
return new Date().toString() + " " + username;
}
return new Date().toString();
}
}
Backend中收到来自Frontend的请求,并给出响应,打出当前的时间戳,如果headers中存在user-name,也会添加到响应字符串尾部
跟前面博文一样,启动Zipkin,然后分别运行
mvn jetty:run -Pbackend
mvn jetty:run -Pfrontend
浏览器访问 http://localhost:8081/ 会显示当前时间
在Zipkin的Web界面中,也能查询到这次跟踪信息
现在来分析下两个Spring相关的类
brave.spring.webmvc.TracingHandlerInterceptor - 服务端请求的拦截器,在这个类里会处理服务端的trace信息
brave.spring.web.TracingClientHttpRequestInterceptor - 客户端请求的拦截器,在这个类里会处理客户端的trace信息
TracingHandlerInterceptor
public final class TracingHandlerInterceptor extends HandlerInterceptorAdapter {
public static AsyncHandlerInterceptor create(Tracing tracing) {
return new TracingHandlerInterceptor(HttpTracing.create(tracing));
}
public static AsyncHandlerInterceptor create(HttpTracing httpTracing) {
return new TracingHandlerInterceptor(httpTracing);
}
final Tracer tracer;
final HttpServerHandler<HttpServletRequest, HttpServletResponse> handler;
final TraceContext.Extractor<HttpServletRequest> extractor;
@Autowired TracingHandlerInterceptor(HttpTracing httpTracing) { // internal
tracer = httpTracing.tracing().tracer();
handler = HttpServerHandler.create(httpTracing, new HttpServletAdapter());
extractor = httpTracing.tracing().propagation().extractor(HttpServletRequest::getHeader);
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) {
if (request.getAttribute(SpanInScope.class.getName()) != null) {
return true; // already handled (possibly due to async request)
}
Span span = handler.handleReceive(extractor, request);
request.setAttribute(SpanInScope.class.getName(), tracer.withSpanInScope(span));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object o, Exception ex) {
Span span = tracer.currentSpan();
if (span == null) return;
((SpanInScope) request.getAttribute(SpanInScope.class.getName())).close();
handler.handleSend(response, ex, span);
}
}
TracingHandlerInterceptor继承了HandlerInterceptorAdapter,覆盖了其中preHandle和afterCompletion方法,分别在请求执行前,和请求完成后执行。
这里没办法向前面几篇博文的一样,使用try-with-resources来自动关闭SpanInScope,所以只能在preHandle中将SpanInScope放在request的attribute中,然后在afterCompletion中将其取出来手动close,其他代码逻辑和前面TracingFilter里一样
TracingClientHttpRequestInterceptor
public final class TracingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
static final Propagation.Setter<HttpHeaders, String> SETTER = HttpHeaders::set;
public static ClientHttpRequestInterceptor create(Tracing tracing) {
return create(HttpTracing.create(tracing));
}
public static ClientHttpRequestInterceptor create(HttpTracing httpTracing) {
return new TracingClientHttpRequestInterceptor(httpTracing);
}
final Tracer tracer;
final HttpClientHandler<HttpRequest, ClientHttpResponse> handler;
final TraceContext.Injector<HttpHeaders> injector;
@Autowired TracingClientHttpRequestInterceptor(HttpTracing httpTracing) {
tracer = httpTracing.tracing().tracer();
handler = HttpClientHandler.create(httpTracing, new HttpAdapter());
injector = httpTracing.tracing().propagation().injector(SETTER);
}
@Override public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
Span span = handler.handleSend(injector, request.getHeaders(), request);
ClientHttpResponse response = null;
Throwable error = null;
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
return response = execution.execute(request, body);
} catch (IOException | RuntimeException | Error e) {
error = e;
throw e;
} finally {
handler.handleReceive(response, error, span);
}
}
static final class HttpAdapter
extends brave.http.HttpClientAdapter<HttpRequest, ClientHttpResponse> {
@Override public String method(HttpRequest request) {
return request.getMethod().name();
}
@Override public String url(HttpRequest request) {
return request.getURI().toString();
}
@Override public String requestHeader(HttpRequest request, String name) {
Object result = request.getHeaders().getFirst(name);
return result != null ? result.toString() : null;
}
@Override public Integer statusCode(ClientHttpResponse response) {
try {
return response.getRawStatusCode();
} catch (IOException e) {
return null;
}
}
}
}
TracingClientHttpRequestInterceptor里的逻辑和前面博文分析的brave.okhttp3.TracingInterceptor类似,此处不再展开分析
下面再来介绍用Annotation注解方式来配置SpringMVC和Brave
相关代码在Chapter5/springmvc-servlet3中
在Servlet3以后,web.xml不是必须的了,org.mozhu.zipkin.springmvc.Initializer是我们整个应用的启动器
public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected String[] getServletMappings() {
return new String[] {
"/"};
}
@Override protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override protected Class<?>[] getServletConfigClasses() {
return new Class[] {TracingConfiguration.class};
}
}
org.mozhu.zipkin.springmvc.Initializer,继承自AbstractDispatcherServletInitializer,实现了WebApplicationInitializer
WebApplicationInitializer
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
关于Servlet3的容器是如何启动的,我们再来看一个类SpringServletContainerInitializer,该类实现了javax.servlet.ServletContainerInitializer接口,并且该类上有一个javax.servlet.annotation.HandlesTypes注解
Servlet3规范规定实现Servlet3的容器,必须加载classpath里所有实现了ServletContainerInitializer接口的类,并调用其onStartup方法,传入的第一个参数是类上HandlesTypes中所指定的类,这里是WebApplicationInitializer的集合
在SpringServletContainerInitializer的onStartup方法中,会将传入的WebApplicationInitializer类,全部实例化,并且排序,然后依次调用它们的initializer.onStartup(servletContext)。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
另外Servlet3在ServletContext中提供了addServlet方法,允许以编码方式向容器中添加Servlet
public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet);
而在AbstractDispatcherServletInitializer中registerDispatcherServlet方法会将SpringMVC的核心控制器DispatcherServlet添加到Web容器中。
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return empty or null");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
return new DispatcherServlet(servletAppContext);
}
以前用xml配置bean的方式,全改为在TracingConfiguration类里用@Bean注解来配置,并且使用@ComponentScan注解指定controller的package,让Spring容器可以扫描到这些Controller
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "org.mozhu.zipkin.springmvc")
@Import({TracingClientHttpRequestInterceptor.class, TracingHandlerInterceptor.class})
public class TracingConfiguration extends WebMvcConfigurerAdapter {
@Bean Sender sender() {
return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
}
@Bean AsyncReporter<Span> spanReporter() {
return AsyncReporter.create(sender());
}
@Bean Tracing tracing(@Value("${zipkin.service:springmvc-servlet3-example}") String serviceName) {
return Tracing.newBuilder()
.localServiceName(serviceName)
.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
.currentTraceContext(ThreadContextCurrentTraceContext.create()) // puts trace IDs into logs
.spanReporter(spanReporter()).build();
}
@Bean HttpTracing httpTracing(Tracing tracing) {
return HttpTracing.create(tracing);
}
@Autowired
private TracingHandlerInterceptor serverInterceptor;
@Autowired
private TracingClientHttpRequestInterceptor clientInterceptor;
@Bean RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors =
new ArrayList<>(restTemplate.getInterceptors());
interceptors.add(clientInterceptor);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(serverInterceptor);
}
}
然后在getServletConfigClasses方法中指定TracingConfiguration,让Spring容器可以加载所有的配置
@Override protected Class<?>[] getServletConfigClasses() {
return new Class[] {TracingConfiguration.class};
}
Annotation和XML配置的方式相比,简化了不少,而其中使用的Tracing相关的类都一样,这里就不用再分析了
前言 这个东西有啥用,好玩? 确实, 好玩归好玩,其实很有使用场景。 可以自己选则一些业务节点触发这个机器人助手的消息推送; 简单举例: 有人给你的系统留下反馈意见了,推送到运营群去; 2.项目部署成
1. JWT 简介 JSON Web Token(JWT) 是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信
我的页面上有多个 ajax 调用,我想将它们合并为一个函数。 目前我在几个地方都有这种类型的功能: function AjaxCallOne () { //do something $.ajax(
我的 Facebook 集成基本上可以在我的应用程序中运行:出现 Facebook 对话框,用户可以选择“允许”或“不允许”。但是,我不明白 API 是如何工作的!我有一个使用此代码的 Activit
我必须将文件夹结构从我的应用程序共享到 OneDrive。 我已经检查了一个驱动器的 sdk,但在那个 sdk 中只能共享文件而不是文件夹,并且没有在该 sdk 中创建文件夹的选项 https://g
我是支付网关集成方面的新手。我必须在我的项目 (CORE PHP) 中集成 CCAvenue 支付网关集成。但是我不知道如何为开发人员测试创建商户帐户,如何获取商户 key 等。我已经进行了研发,但是
我正在尝试将“社交选项”集成到我的应用程序中。 我有 iOS6,但我的想法是有一个适用于 iOS5 的应用程序。使用 Twitter 框架非常简单,并且可以在 r.0 版本和 6.0 版本的设备上运行
我正在尝试将 flurryAds 集成到我的 iPhone 应用程序中,但我无法做到这一点。我导入名为 的 .h 文件 #import "Flurry.h" #import "FlurryAds.h"
我正在尝试在我的网站中实现类似 facebook 的按钮和评论,但我在 IE7 中遇到了评论框问题。 COMMENT USING 下拉框不知何故没有显示其他可用选项。这是我用来实现它的代码片段:
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve th
我正在使用 SOAP API 进行 PayPal 集成(Express Checkout)。在 DoExpressCheckout 调用后,我调用 GetExpressCheckoutDetails。
我正在尝试将 paypal 作为支付网关之一集成到我的应用程序中,但在我点击支付按钮后它会返回以下错误。 错误 java.lang.RuntimeException:无法使用 Intent { cmp
我目前正在尝试将 paypal 结账与我们的在线商店集成。我们正在针对 Sandbox 进行测试。除了 IPN(即时付款通知)之外的所有内容都有效。 我们阅读了很多有关 Paypal 更改其安全模型的
我正在开发一个 android 应用程序,我想在其中集成 facebook 之类的。我正在浏览链接 http://developers.facebook.com/docs/guides/mobile/
所以我正在尝试构建一个集成了 FitBit 的 iOS 应用程序 (Swift 2)。 一旦用户打开“步行”页面,用户应该能够看到他每天的步数。 理想情况下,我们不希望每个用户都注册到 FitBit。
我是集成投递箱的新手,但我不太确定如何生成调用以获取请求 token secret 。 https://www.dropbox.com/developers/reference/api#request
我已经成功集成了 PayPal。一切正常。但我希望我的表格在成功付款后重定向到我的网站。另一个问题:如何从 PayPal 得到回应?这是我的 Paypal 表格。谢谢。 `
我在我的 Android 应用程序中集成了 Paypal 。我有一个主要 Activity - 和关于 Activity ,我在其中显示 Paypal 按钮。关于从主 Activity 访问的 Act
前言: 小编引入的图片和文字描述都是来自于尚硅谷的视频讲解,在此感谢尚硅谷的老师,同时也结合 seata文档官方文档进行整合 项目地址(gitee): https://gitee.com/qine
目录 1. demo project 1.1 接口准备 1.2 配置准备 2. docker 开启远程连接
我是一名优秀的程序员,十分优秀!