ActiveMQ 和java应用的结合
章节导读
- 在java应用中嵌入ActiveMQ
- ActiveMq结合Spring
- 用Spring编写JMS客户端
1.1 将ActiveMq嵌入java
首先我们使用JMS API 中的BrokerService类来配置代理,接着我们使用xml文件配置的方式来配置代理.我们通过BrokerFactory类来达到这个目的,
1.1.1 使用org.apache.activemq.broker.BrokerService
这个类用来配置代理以及管理它的生命周期,让我们用之前的认证插件的例子来距离.之前那个例子的xml配置如下:
<broker xmlns="http://activemq.apache.org/schema/core"
brokerName="myBroker"
dataDirectory="${activemq.base}/data">
<transportConnectors>
<transportConnector name="openwire"
uri="tcp://localhost:61616" />
</transportConnectors>
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="admin"
password="password"
groups="admins,publishers,consumers"/>
<authenticationUser username="publisher"
password="password"
groups="publishers,consumers"/>
<authenticationUser username="consumer"
password="password"
groups="consumers"/>
<authenticationUser username="guest"
password="password"
groups="guests"/>
</users>
</simpleAuthenticationPlugin>
</plugins>
</broker>
对应的java代码如下
import java.util.ArrayList;
import java.util.List;
import org.apache.activemq.broker.BrokerPlugin;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.security.AuthenticationUser;
import org.apache.activemq.security.SimpleAuthenticationPlugin;
public class ActiveMqEmbedJava {
public static void main(String[] args) throws Exception {
BrokerService broker = new BrokerService();
broker.setBrokerName("myBroker");
broker.setDataDirectory("data/");
SimpleAuthenticationPlugin authentication = new SimpleAuthenticationPlugin();
List<AuthenticationUser> users = new ArrayList<AuthenticationUser>();
users.add(new AuthenticationUser("admin", "password", "admins,publishers,consumers"));
users.add(new AuthenticationUser("publisher", "password", "publishers,consumers"));
users.add(new AuthenticationUser("consumer", "password", "consumers"));
users.add(new AuthenticationUser("guest", "password", "guests"));
authentication.setUsers(users);
broker.setPlugins(new BrokerPlugin[] { authentication });
broker.addConnector("tcp://localhost:61616");
broker.start();
System.out.println();
System.out.println("Press any key to stop the broker");
System.out.println();
System.in.read();
}
}
注意:必须在增加Connector之前配置插件,否则插件不会被初始化.代理启动之后再去增加Connector将不会起到预期的作用.
1.1.2 使用org.apache.activemq.broker.BrokerFactory
在某些情况下,你想用相同的配置初始化代理,就可以使用BrokerFactory.它可以使用ActiveMQ URL来创建一个代理,基于代理 URl协议,找到合适的工厂类并创建对应的BrokerService.用的最多的就是XBeanBrokerFactory,通过XBean风格的URl配置,例如:xbean:/path/to/activemq.xml
这个URl告诉BrokerFactory使用XBeanBrokerFactory以及创建代理实例的路径.BrokerFactory可以使用ActivemQ的xml配置文件来初始化BrokerService.例子如下:
public class Factory {
public static void main(String[] args) throws Exception {
System.setProperty("activemq.base", System.getProperty("user.dir"));
String configUri =
"xbean:target/classes/org/apache/activemq/book/ch6/activemq-simple.xml"
URI brokerUri = new URI(configUri);
BrokerService broker = BrokerFactory.createBroker(brokerUri);
broker.start();
System.out.println();
System.out.println("Press any key to stop the broker");
System.out.println();
System.in.read();
}
}
1.2 将ActiveMq嵌入Spring
1.纯Spring XMl的形式(将BrokerService 作为一个bean)
spring.xml配置文件,放置在src/main/resources目录下(maven)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<bean id="admins" class="org.apache.activemq.security.AuthenticationUser">
<constructor-arg index="0" value="admin" />
<constructor-arg index="1" value="password" />
<constructor-arg index="2" value="admins,publisher,consumers" />
</bean>
<bean id="publishers"
class="org.apache.activemq.security.AuthenticationUser">
<constructor-arg index="0" value="publisher" />
<constructor-arg index="1" value="password" />
<constructor-arg index="2" value="publisher,consumers" />
</bean>
<bean id="consumers"
class="org.apache.activemq.security.AuthenticationUser">
<constructor-arg index="0" value="consumer" />
<constructor-arg index="1" value="password" />
<constructor-arg index="2" value="consumers" />
</bean>
<bean id="guests" class="org.apache.activemq.security.AuthenticationUser">
<constructor-arg index="0" value="guest" />
<constructor-arg index="1" value="password" />
<constructor-arg index="2" value="guests" />
</bean>
<bean id="simpleAuthPlugin"
class="org.apache.activemq.security.SimpleAuthenticationPlugin">
<property name="users">
<list>
<ref bean="admins" />
<ref bean="publishers" />
<ref bean="consumers" />
<ref bean="guests" />
</list>
</property>
</bean>
<bean id="broker" class="org.apache.activemq.broker.BrokerService"
init-method="start" destroy-method="stop">
<property name="brokerName" value="myBroker" />
<property name="persistent" value="false" />
<property name="transportConnectorURIs">
<list>
<value>tcp://localhost:61616</value>
</list>
</property>
<property name="plugins">
<list>
<ref bean="simpleAuthPlugin"/>
</list>
</property>
</bean>
</beans>
用以下代码加载spring配置文件启动ActiveMq即可
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class PurlSpringXml {
public static void main(String[] args) {
ClassPathXmlApplicationContext cpac = new ClassPathXmlApplicationContext("classpath:/*.xml");
}
}
2.使用BrokerFactoryBean
<bean id="broker"
class="org.apache.activemq.xbean.BrokerFactoryBean">
<property name="config" value="classpath:activemq.xml"/>
<property name="start" value="true" /><!--是否启动一个代理-->
</bean>
3.使用spring的namespace
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd">
<amq:broker brokerName="localhost" dataDirectory="${activemq.base}/data">
<amq:transportConnectors>
<amq:transportConnector name="openwire"
uri="tcp://localhost:61616" />
</amq:transportConnectors>
<amq:plugins>
<amq:simpleAuthenticationPlugin>
<amq:users>
<amq:authenticationUser username="admin"
password="password" groups="admins,publishers,consumers" />
<amq:authenticationUser username="publisher"
password="password" groups="publishers,consumers" />
<amq:authenticationUser username="consumer"
password="password" groups="consumers" />
<amq:authenticationUser username="guest"
password="password" groups="guests" />
</amq:users>
</amq:simpleAuthenticationPlugin>
</amq:plugins>
</amq:broker>
</beans>
1.3 实现请求/应答模式
请求/应答方案包含了应用发送一个消息并按预期的收到那个消息的响应.这样的设计通常都是用server/client模式来实现的,服务器和客户端通过TCP,UDP协议来同步的交流,这种形式的架构伸缩性比较差,并且很难拆分项目.下图就是一个请求应答模式的概述.
客户端和工作者(worker)都是由一个生产者和一个消费者组成的.
首先,生产者创建一个JMS消息格式的请求,并且设置一组重要的属性--关联ID(通过JMSCorrelationID属性),答复的目的地(JMSReplyTo属性).关联id是非常重要的,它允许请求关联到答复上(在有多种请求的情况下).答复的目的地就是回复预期被发送的地方(通常使用临时目的地,资源更友好).客户端配置一个消费者来监听答复目的地.
其次,工作者接受一个请求,处理它,并且发送应答消息,使用在JMSReplyTo属性中设置的目的地,响应消息必须使用原始请求中的JMSCorrelationID来设置应答消息中的JMSCorrelationID属性.当客户端收到应答消息后正确的关联上原始请求.
为了证明它是一种高可可伸缩的方案.假设一个单例的worker不足以承载那么多的请求.只需要增加worker分摊负载即可,这些worker可以分布在不同的主机上.
1.3.1 实现请求响应模式中的代理服务器和worker
/**
* 请求应答模式,服务器
* @author cfzhou
*
*/
public class ActiveMqBrokerAndWorker implements MessageListener {
private BrokerService broker;
private static String brokerURL = "tcp://localhost:61616";//地址
private Session session;
private MessageProducer producer;//生产者
private MessageConsumer consumer;//消费者
public void start() throws Exception {
createBroker();
setupConsumer();
}
//创建服务
private void createBroker() throws Exception {
System.out.println("启动服务");
broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(false);
broker.addConnector(brokerURL);
broker.start();
}
//启动消费者
private void setupConsumer() throws JMSException {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURL);
Connection connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination adminQueue = session.createQueue("queue");
//创建生产者
//没有默认的目的地,它会发送到每个消息JMSReplyTo属性指定的目的地去
producer = session.createProducer(null);
//非持久化
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//创建消费者
consumer = session.createConsumer(adminQueue);
//监听器
consumer.setMessageListener(this);
}
public void stop() throws Exception {
producer.close();
consumer.close();
session.close();
broker.stop();
}
@Override
public void onMessage(Message message) {
try {
//创建回复信息
TextMessage response = this.session.createTextMessage();
//处理收到的信息
if (message instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) message;
String messageText = txtMsg.getText();
response.setText(handleRequest(messageText));
}
//设置响应消息对应的关联Id
System.out.println("收到请求消息Id:" + message.getJMSCorrelationID());
System.out.println(message.getJMSReplyTo().toString());
//设置关联id
response.setJMSCorrelationID(message.getJMSCorrelationID());
producer.send(message.getJMSReplyTo(), response);
} catch (JMSException e) {
e.printStackTrace();
}
}
public String handleRequest(String messageText) {
return "Response to '" + messageText + "'";
}
public static void main(String[] args) throws Exception {
//开启服务
ActiveMqBrokerAndWorker server = new ActiveMqBrokerAndWorker();
server.start();
System.out.println();
System.out.println("Press any key to stop the server");
System.out.println();
System.in.read();
server.stop();
}
}
1.3.2 实现客户端
/**
* 请求应答模式,客户端
* @author cfzhou
*
*/
public class ActiveMqRequestReplyClient implements MessageListener {
private static String brokerURL = "tcp://localhost:61616";//地址
private Session session;
private MessageProducer producer;//生产者
private MessageConsumer consumer;//消费者
private Destination destination;
private Destination tempDestination;
private Connection connection;
private AtomicInteger count = new AtomicInteger(0);
public void start() throws Exception {
setUpClient();
}
//发送消息
private void sendMessage() throws JMSException {
for(int i = 0 ;i< 10 ;i++){
Message message = session.createTextMessage("消息" + count.incrementAndGet());
//设置woker回复的目的地
message.setJMSReplyTo(tempDestination);
//用UUID生成关联Id
String correlationId = UUID.randomUUID().toString();
message.setJMSCorrelationID(correlationId);
producer.send(destination, message);
}
}
//启动消费者
private void setUpClient() throws JMSException {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURL);
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("queue");
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//创建临时队列
tempDestination = session.createTemporaryQueue();
//创建消费者
consumer = session.createConsumer(tempDestination);
//监听器
consumer.setMessageListener(this);
}
public void stop() throws Exception {
producer.close();
consumer.close();
session.close();
connection.close();
}
@Override
public void onMessage(Message message) {
try {
//处理收到的信息
TextMessage txtMsg = (TextMessage) message;
String messageText = txtMsg.getText();
System.out.println("收到答复消息:" + messageText + ",messageid:" + message.getJMSMessageID());
} catch (JMSException e) {
e.printStackTrace();
}
}
public String handleRequest(String messageText) {
return " to '" + messageText + "'";
}
public static void main(String[] args) throws Exception {
ActiveMqRequestReplyClient client = new ActiveMqRequestReplyClient();
client.start();
client.sendMessage();
//Thread.sleep(3000);
//client.stop();
}
}
客户端的消费者发送消息到请求队列,消费者监听临时队列.
1.4 用spring写JMS客户端
1.配置JMS连接
<bean id="jmsConnectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="userName" value="admin" />
<property name="password" value="password" />
</bean>
通常为了性能还是需要配置连接池,activeMq提供了PooledConnectionFactory类,它提供了JMS连接池和session.
2.配置连接池
<bean id="pooledJmsConnectionFactory"
class="org.apache.activemq.pool.PooledConnectionFactory"
destroy-method="stop">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
连接池maven
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-pool -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.14.3</version>
</dependency>
3.配置Destinations(队列和主题)
<bean id="cscoDest" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="STOCKS.CSCO" />
</bean>
<bean id="orclDest" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="STOCKS.ORCL" />
</bean>
4. 配置消费者
<!-- 消息监听器-->
<bean id="portfolioListener" class="com.zcf.activemq.chapter7.spring.JmsMessageListener">
</bean>
<!-- Spring 队列消费者 -->
<bean id="cscoConsumer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="destination" ref="cscoDest" />
<property name="messageListener" ref="portfolioListener" />
</bean>
<!-- Spring 主题消费者 -->
<bean id="orclConsumer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="destination" ref="orclDest" />
<property name="messageListener" ref="portfolioListener" />
</bean>
消息监听器
public class JmsMessageListener implements MessageListener{
@Override
public void onMessage(Message message) {
MapMessage map = (MapMessage) message;
}
}
5.配置生产者
spring也提供了发送消息的模板,JmsTemplate. 使用spring发送消息最普遍的方式就是靠实现Spring MessageCreator接口.这个接口只定义了一个方法createMessage(),返回一个jms消息,
下面的例子中,实现了创建一个随机价格逻辑的消息,并且创建了一个MapMessage来保存多个数据.为了发送消息,需要利用JmsTemplate’s send()方法.
public class StockMessageCreator implements MessageCreator {
private int MAX_DELTA_PERCENT = 1;
private Map<Destination, Double> LAST_PRICES = new Hashtable<Destination, Double>();
Destination stock;
public StockMessageCreator(Destination stock) {
this.stock = stock;
}
public Message createMessage(Session session) throws JMSException {
Double value = LAST_PRICES.get(stock);
if (value == null) {
value = new Double(Math.random() * 100);
}
// lets mutate the value by some percentage
double oldPrice = value.doubleValue();
value = new Double(mutatePrice(oldPrice));
LAST_PRICES.put(stock, value);
double price = value.doubleValue();
double offer = price * 1.001;
boolean up = (price > oldPrice);
MapMessage message = session.createMapMessage();
message.setString("stock", stock.toString());
message.setDouble("price", price);
message.setDouble("offer", offer);
message.setBoolean("up", up);
System.out.println("Sending: " + ((ActiveMQMapMessage) message).getContentMap() + " on destination: " + stock);
return (Message) message;
}
protected double mutatePrice(double price) {
double percentChange = (2 * Math.random() * MAX_DELTA_PERCENT)
- MAX_DELTA_PERCENT;
return price * (100 + percentChange) / 100;
}
}
使用spring Jms模板发送消息的类
public class SpringPublisher {
//JMS模板
private JmsTemplate template;
private int count = 10;
private int total;
private Destination[] destinations;
private HashMap<Destination, StockMessageCreator> creators = new HashMap<Destination, StockMessageCreator>();
public void start() {
while (total < 1000) {
for (int i = 0; i < count; i++) {
sendMessage();
}
total += count;
System.out.println("Published '" + count + "' of '" + total + "' price messages");
try {
Thread.sleep(1000);
} catch (InterruptedException x) {
}
}
}
protected void sendMessage() {
int idx = 0;
while (true) {
idx = (int) Math.round(destinations.length * Math.random());
if (idx < destinations.length) {
break;
}
}
Destination destination = destinations[idx];
template.send(destination, getStockMessageCreator(destination));
}
private StockMessageCreator getStockMessageCreator(Destination dest) {
if (creators.containsKey(dest)) {
return creators.get(dest);
} else {
StockMessageCreator creator = new StockMessageCreator(dest);
creators.put(dest, creator);
return creator;
}
}
// getters
}
配置jms模板和发送消息类到spring
<!-- Spring JMS Template -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="pooledJmsConnectionFactory" />
</bean>
<bean id="stockPublisher" class="com.zcf.activemq.chapter7.spring.SpringPublisher">
<property name="template" ref="jmsTemplate" />
<property name="destinations">
<list>
<ref local="cscoDest" />
<ref local="orclDest" />
</list>
</property>
</bean>
测试类
public class SpringClient {
public static void main(String[] args) throws Exception {
BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:61616");
broker.setPersistent(false);
broker.start();
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring-jmstemplate.xml");
SpringPublisher publisher = (SpringPublisher) context.getBean("stockPublisher");
publisher.start();
}
}
- 大小: 28.7 KB
- 大小: 127.8 KB
分享到:
相关推荐
其Remote Activation(RA)模块,即activemq-ra,是ActiveMQ提供的一种资源适配器,使得应用程序能够与Java EE应用程序服务器集成,实现JMS(Java Message Service)功能。在本文中,我们将深入探讨activemq-ra-3.1-...
9. **测试技术**:熟悉junit,结合SpringTest、Dbunit和EasyMock进行单元测试和集成测试,保证代码质量。 10. **Linux操作系统**:了解基本Linux操作系统和常用命令,能在Linux环境下安装和管理服务。 11. **安全...
ActiveMQ 是一个开源的消息中间件,它遵循开放消息模型(JMS)标准,提供可靠的消息传递,用于在分布式系统中解耦组件,实现...理解并熟练掌握ActiveMQ的启动和测试方法,对于开发高效、可靠的分布式应用程序至关重要。
在这里,"ch9_3_4"可能代表第9章第3小节的第4个部分,具体内容可能涵盖了SpringBoot集成ActiveMQ的实践步骤、配置、代码示例,以及可能的错误处理和最佳实践。 集成SpringBoot与ActiveMQ的过程通常涉及以下步骤: ...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
Java 3DMenu 界面源码 5个目标文件 内容索引:Java源码,窗体界面,3DMenu Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JavaEE是企业级应用开发的重要框架,它基于Java平台,为构建分布式、多层的Web应用程序提供了丰富的组件和服务。在学习JavaEE的过程中,课后习题是检验和巩固理论知识的关键环节。"javaee程序设计与应用开发第二版...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
JGraphEd 是一个 Java 的图形编辑应用和绘图框架。 Java 穿越NAT方案 JSTUN.tar JSTUN 是另外一个用来解决穿越 NAT 进行通讯的 Java 解决方案,是 STUN 协议的 Java实现。 在浏览器上运行Java程序 Doppio DoppioVM...
该框架提供了一系列通用的微服务模块,包括但不限于权限管理(单点登录)、内容管理、支付中心、用户管理(支持第三方登录)、微信平台、存储系统、配置中心、日志分析、任务和通知等。 #### 二、zheng项目的技术栈...