Serving Web Content with Spring MVC

Spring MVC 框架是MVC设计模式的一种实现,因其简单易用、与Spring无缝融合等特性,被广泛应用在网站开发领域。

搭建第一个Spring MVC应用

实验目标

1.构建一个Web应用,当向浏览器地址 http://localhost:9001/greeting 发送GET请求时,返回

1
"Hello, World!"

2.给GET请求添加name参数,如 http://localhost:9001/greeting?name=User,返回对具体人名的问候语:

1
"Hello, User!"

Controller 层:注解方式

One of the successful implementation is Java Spring MVC. In Spring, if you want a class to be a controller you can use the annotation @RestController for this class. And in this class, use annotation @RequestMapping to show this is an RESTful API function.

HTTP报文如何解析到Controller方法?

Spring MVC框架的底层是Servlet。Servlet是一个标准,主要规定了处理请求与响应的service接口,在service接口中只需要实现业务逻辑即可。而Servlet由Servlet容器负责管理与运行,常见的Servlet容器有Tomcat,Jetty等。以Tomcat中的Servlet接口定义为例,如下所示:

servlet.png

由于Servlet只规定了接口,所以通常情况下不会直接继承Servlet进行实现,而是通过继承HttpServlet抽象类来有选择地覆盖相应的方法。而MVC更加方便,甚至不会去手动实现Servlet,只需要通过注解标注出Controller、Service、Model类即可,更加提高了开发的效率。

View 层实现:JSP

JSP是动态执行文件,由Tomcat生成java servlet执行文件。jsp中的内容被放到 _jspService() 方法体的 try...catch 代码块里执行。一个JSP文件的例子如下所示:

status.jsp

1
2
3
4
5
6
7
8
<li class="one"><a href="jsp/status.jsp">Home</a></li>

<div class="sectionTitle">Instance Info</div>
<table id='instanceInfo' class="stripeable">
<tr><th>Name</th><th>Value</th></tr>
<tr><td>ipAddr</td><td>192.168.1.104</td></tr><tr><td>status</td><td>UP</td></tr>
</table>
</div>

那么,当前 Eureka Server 的 InstanceInfo 是如何获取的呢?
status.jsp

1
146 StatusInfo statusInfo = (new StatusUtil(serverContext)).getStatusInfo();

StatusUtil.java

1
29 this.instanceInfo = server.getApplicationInfoManager().getInfo();

ApplicationInfoManager.java

1
2
3
4
5
6
7
58 private static ApplicationInfoManager instance = new ApplicationInfoManager(null, null, null);

@Inject
public ApplicationInfoManager(EurekaInstanceConfig config, InstanceInfo instanceInfo, OptionalArgs optionalArgs) {
this.instanceInfo = instanceInfo;
......
}

调试后验证了,这里的instanceInfo一开始初始化是null,之后在BootStrap.java中才正式通过Builder初始化

@Deprecated
DiscoveryManager.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Initializes the <tt>Discovery Client</tt> with the given configuration.
*
* @param config
* the instance info configuration that will be used for
* registration with Eureka.
* @param eurekaConfig the eureka client configuration of the instance.
*/
public void initComponent(EurekaInstanceConfig config,
EurekaClientConfig eurekaConfig, AbstractDiscoveryClientOptionalArgs args) {
this.eurekaInstanceConfig = config;
this.eurekaClientConfig = eurekaConfig;
if (ApplicationInfoManager.getInstance().getInfo() == null) {
// Initialize application info
ApplicationInfoManager.getInstance().initComponent(config);
}
InstanceInfo info = ApplicationInfoManager.getInstance().getInfo();
......
}

ApplicationInfoManager.java

1
2
3
4
5
6
7
8
public void initComponent(EurekaInstanceConfig config) {
try {
this.config = config;
this.instanceInfo = new EurekaConfigBasedInstanceInfoProvider(config).get();
} catch (Throwable e) {
throw new RuntimeException("Failed to initialize ApplicationInfoManager", e);
}
}

EurekaBootStrap.java

1
2
3
4
5
161 EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
? new CloudInstanceConfig()
: new MyDataCenterInstanceConfig(); // 调试发现用的是 MyDataCenterInstanceConfig()
165 applicationInfoManager = new ApplicationInfoManager(
instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());

EurekaConfigBasedInstanceInfoProvider.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
builder.setNamespace(config.getNamespace())
.setInstanceId(instanceId)
.setAppName(config.getAppname())
.setAppGroupName(config.getAppGroupName())
.setDataCenterInfo(config.getDataCenterInfo())
.setIPAddr(config.getIpAddress())
.setHostName(defaultAddress)
.setPort(config.getNonSecurePort())
.enablePort(PortType.UNSECURE, config.isNonSecurePortEnabled())
.setSecurePort(config.getSecurePort())
.enablePort(PortType.SECURE, config.getSecurePortEnabled())
.setVIPAddress(config.getVirtualHostName())
.setSecureVIPAddress(config.getSecureVirtualHostName())
.setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
.setStatusPageUrl(config.getStatusPageUrlPath(), config.getStatusPageUrl())
.setASGName(config.getASGName())
.setHealthCheckUrls(config.getHealthCheckUrlPath(),
config.getHealthCheckUrl(), config.getSecureHealthCheckUrl());

于是我们在builder. 后面加入 .setAccountAddress(accountAddress)

在 InstanceInfo.java 中的 Builder 类中添加

1
2
3
4
public Builder setAccountAddress(String accountAddress) {
result.accountAddress = accountAddress;
return this;
}

继续调试发现 EurekaBootStrap.java 这段代码速度很慢

1
2
214 // Copy registry from neighboring eureka node
215 int registryCount = registry.syncUp();
DIQRNG 论文翻译 Raspberry Pi parallel computing and cluster computing experiment

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×