sessionbean
(一)无状态SessionBean(Statless)
(1) 无状态Session Bean单个方法调用就能完成一个完整的业务流程
(2) 无状态Session是可以被重用,不能被客户端并发共享,只可以串行共享,并不保留客户端方法调用后的的状态,而是直接返回。
(3) 无状态的SessionBean是可以池化的(pooling),以优化性能,用以被多个客户共享。
无状态SessionBean的生命周期
如果实例不存在,就会调用构造方法,然后调用资源注入方法,接着会调用有@PostConstruct标注的方法,在销毁时会调用有@PerDestroy标注的方法,
然后销毁对象,如果实例存在就会从Bean实例池中取出实例并调用方法(而不是重新生成实例)。回调方法是基于事件机制的。
接口:
package senssic.ejb.bean;
public interface StatlessSession {
public void doSomething();
}
实现:
package senssic.ejb.bean.impl;
import javax.annotation.PostConstruct;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.PostRemove;
import senssic.ejb.bean.StatlessSession;
@Stateless
@Remote(StatlessSession.class)
public class SLSession implements StatlessSession {
private int num = 0;
@Override
public void doSomething() {
System.out.println("说说说说,说我爱你,说了:" + num + "次");
num++;
}
@PostConstruct
// 创建实例自动被调用的方法
public void beforStart() {
System.out.println("今天中秋哦,我被实例化了呀");
}
@PostRemove
// 销毁对象时回调此函数
public void afterDestory() {
System.out.println("连月饼都没有,我被销毁了");
}
}
打包jar部署到jboss的相应目录下
客户端:
package senssic.test;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import senssic.ejb.bean.StatlessSession;
public class Client {
public static void main(String[] args) {
Properties p = new Properties();
p.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
p.setProperty("java.naming.provider.url", "localhost:1099");
try {
InitialContext cxt = new InitialContext(p);
StatlessSession slSession = (StatlessSession) cxt
.lookup("SLSession/remote");
slSession.doSomething();
// 获取第二份无状态bean通过打印的num可以判定只实例化一次被容器放入池中,多个客户端共享一个实例
InitialContext cxt2 = new InitialContext(p);
StatlessSession slSession2 = (StatlessSession) cxt2
.lookup("SLSession/remote");
slSession2.doSomething();
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
打印结果:
11:45:26,453 INFO [STDOUT] 今天中秋哦,我被实例化了呀
11:45:26,484 INFO [STDOUT] 说说说说,说我爱你,说了:0次
11:45:26,531 INFO [STDOUT] 说说说说,说我爱你,说了:1次
(二)有状态的SessionBean(Statful)
有状态的SessionBean只为一个客户端服务,不能共享,并且会保留方法调用后的状态。
(1) 多个方法调用才能完成一个业务处理流程;
(2) 需要保留客户端的状态
(3) 不被多个客户共享。
当有状态的SessionBean暂时不被使用时,就会被存储到缓存当中,也就是被存到虚拟内存或者是将信息同步到Session数据��库是应用服务器所提共的小型数据库,用来保存Session的信息,多应用服务器共享Session数据库,同步Bean的信息,达到集群处理)。
实现类的@Stateless改为@Stateful就行了运行结果:
12:13:36,203 INFO [STDOUT] 今天中秋哦,我被实例化了呀
12:13:36,546 INFO [STDOUT] 说说说说,说我爱你,说了:0次
12:13:36,578 INFO [STDOUT] 今天中秋哦,我被实例化了呀
12:13:36,609 INFO [STDOUT] 说说说说,说我爱你,说了:0次
由此可见有状态会话bean每次调用时候容器都会实例化一个新的,就是每个客户端对应一个会话bean
注意:对有状态的SessionBean中有transient属性时,就需要在Bean中提供激活的方法
代码:
package senssic.ejb.bean.impl;
import javax.annotation.PostConstruct;
import javax.ejb.PostActivate;
import javax.ejb.Remote;
import javax.ejb.Stateful;
import javax.persistence.PostRemove;
import senssic.ejb.bean.StatlessSession;
@Stateful
@Remote(StatlessSession.class)
public class SLSession implements StatlessSession {
private int num = 0;
transient private String str;
@Override
public void doSomething() {
System.out.println("说说说说,说我爱你,说了:" + num + "次!" + "transient属性:" + str);
num++;
}
@PostConstruct
// 创建实例自动被调用的方法
public void beforStart() {
System.out.println("今天中秋哦,我被实例化了呀");
}
@PostRemove
// 销毁对象时回调此函数
public void afterDestory() {
System.out.println("连月饼都没有,我被销毁了");
}
@PostActivate
// 使用transient属性时侯使用此注解调用此方法激活
public void activate() {
str = "我是transient属性因为不能被序列化,所以每次自动调用此方法实例化我";
}
}
实体bean
实体beanuser:
package senssic.ejb.bean;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* 用户实体
*
* @author Senssic@163.com
*
*/
@Entity
@Table(name = "US")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(length = 35, name = "US_num")
private int num;
@Column(length = 35, nullable = false, unique = true, name = "US_name")
private String username;
@Column(length = 35, nullable = false, name = "US_password")
private String password;
@Column(length = 45, nullable = false, name = "US_email")
private String email;
@Column(length = 20, name = "US_relname")
private String relname;
@Column(length = 35, name = "US_department")
private String department;
@Column(length = 60, name = "US_address")
private String address;
@Column(length = 10, name = "US_postcode")
private int postcode;
@Column(length = 35, name = "US_phone")
private String phone;
@ManyToOne(cascade = CascadeType.REFRESH)
@JoinColumn(name = "AU_num", nullable = false)
private Privilege au_num;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getRelname() {
return relname;
}
public void setRelname(String relname) {
this.relname = relname;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getPostcode() {
return postcode;
}
public void setPostcode(int postcode) {
this.postcode = postcode;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Privilege getAu_num() {
return au_num;
}
public void setAu_num(Privilege au_num) {
this.au_num = au_num;
}
}
实体beanprivilege:
package webwork.senssic.bean.base;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "AU")
public class Privilege {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(length = 35, name = "AU_num")
private int num;
@Column(length = 35, nullable = false, name = "AU_name")
private String name;
@Column(length = 50, nullable = false, name = "AU_info")
private String info;
@OneToMany(mappedBy = "au_num", cascade = CascadeType.ALL)
private Set<User> users;
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
会话bean
package senssic.ejb.bean;
import java.util.List;
public interface Manager {
public void save(User user);
public void update(User user);
public void delete(Integer userid);
public User getUser(Integer userid);
public List<User> getAllUser();
public Privilege getPri(Integer pid);
}
实现:
package senssic.ejb.bean.impl;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import senssic.ejb.bean.Manager;
import senssic.ejb.bean.Privilege;
import senssic.ejb.bean.User;
@Stateless
@Remote(Manager.class)
public class ManagrImpl implements Manager {
// 指定持久化单元的名字,如果只有一个可以不做修改,容器已实例化好,注入即可
@PersistenceContext(unitName = "senssic")
EntityManager em;
@Override
public void save(User user) {
em.persist(user);
}
@Override
public void update(User user) {
em.merge(user);
}
@Override
public void delete(Integer userid) {
em.remove(em.getReference(User.class, userid));
}
@Override
public User getUser(Integer userid) {
return em.find(User.class, userid);
}
@SuppressWarnings("unchecked")
@Override
public List<User> getAllUser() {
return em.createQuery("select o from Person o").getResultList();
}
@Override
public Privilege getPri(Integer pid) {
return em.find(Privilege.class, pid);
}
}
persistence.xml
<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="senssic" transaction-type="JTA">
<jta-data-source>java:senssicDS</jta-data-source>
</persistence-unit>
</persistence>
mysql-ds.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- See http://www.jboss.org/community/wiki/Multiple1PC for information about local-tx-datasource -->
<!-- $Id: mysql-ds.xml 97536 2009-12-08 14:05:07Z jesper.pedersen $ -->
<!-- Datasource config for MySQL using 3.0.9 available from:
http://www.mysql.com/downloads/api-jdbc-stable.html
-->
<datasources>
<local-tx-datasource>
<jndi-name>senssicDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/senssic</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>qiyu0126</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<!-- should only be used on drivers after 3.22.1 with "ping" support
<valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name>
-->
<!-- sql to call when connection is created
<new-connection-sql>some arbitrary sql</new-connection-sql>
-->
<!-- sql to call on an existing pooled connection when it is obtained from pool - MySQLValidConnectionChecker is preferred for newer drivers
<check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql>
-->
<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
测试类:
package senssic.ejb.bean;
import static org.junit.Assert.fail;
import java.util.Properties;
import java.util.Random;
import javax.naming.InitialContext;
import org.junit.BeforeClass;
import org.junit.Test;
public class ManagerTest {
private static Manager manager;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
Properties p = new Properties();
p.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
p.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext cxt = new InitialContext(p);
manager = (Manager) cxt.lookup("ManagrImpl/remote");
}
@Test
public void testSave() {
User user = new User();
Privilege privilege = manager.getPri(1);
user.setUsername("煜");
user.setEmail("43349267@qq.com");
user.setPassword("qiyu");
user.setAddress("安徽池州---池州学院");
user.setDepartment("池州学院数学系部门");
user.setPhone("1325946667");
user.setPostcode(1 + new Random().nextInt(10) + 396);
user.setRelname("qiyuyu" + new Random().nextInt(100));
user.setAu_num(privilege);
manager.save(user);
}
@Test
public void testUpdate() {
fail("Not yet implemented");
}
@Test
public void testDelete() {
fail("Not yet implemented");
}
@Test
public void testGetUser() {
fail("Not yet implemented");
}
@Test
public void testGetAllUser() {
fail("Not yet implemented");
}
}
jms消息bean
Point-to-Point(PTP)点对点模型
① 消息服务器上用Queue队列来存放消息
② 允许多个消息的生产者发送消息到Queue,但是消息只允许一个消息消费者消费。一旦消息被消费,MOM会把消息从Queue中删除。
修改server\default\deploy\hornetq\hornetq-jms.xml文件添加如下代码:注:因为使用的是6.0的和5.0的配置不一样
<queue name="senssic">
<entry name="/queue/senssic"/>
</queue>
发送端:
package senssic.mq;
import java.util.Properties;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
public class PtoPTo {
public static void main(String[] args) {
Properties p = new Properties();
p.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
p.setProperty("java.naming.provider.url", "localhost:1099");
try {
InitialContext cxt = new InitialContext(p);
QueueConnectionFactory qFactory = (QueueConnectionFactory) cxt
.lookup("ConnectionFactory");//此处的QueueConnectionFactory为全局的jndi名称,不能随意更改由jms的各个实现厂商默认提供
QueueConnection connection = qFactory.createQueueConnection();
QueueSession qSession = connection.createQueueSession(false,
QueueSession.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) cxt.lookup("queue/senssic");
TextMessage msg = qSession.createTextMessage("今年的中秋节没有月饼吃,艹");
QueueSender sender = qSession.createSender(queue);
sender.send(msg);
qSession.close();
connection.close();
System.out.println("月饼已发送。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
接受监听结果:
package senssic.mq;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/senssic"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
public class PtoPFrom implements MessageListener {
@Override
public void onMessage(Message arg0) {
TextMessage textMessage = (TextMessage) arg0;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
部署运行后结果:
SLSession/remote - EJB3.x Default Remote Business Interface
SLSession/remote-senssic.ejb.bean.StatlessSession - EJB3.x Remote Business Interface
20:56:01,968 INFO [STDOUT] 今年的中秋节没有月饼吃,艹
Publish/Subscribe(pub/sub) 发布/订阅模型
① 用Topic存放消息
② 允许有多个生产者和消费者,同一个消息可被多个消费者消费,且在Topic中不会因消费而删除。
在同样的配置文件下配置如下:
<topic name="qiyu">
<entry name="/topic/qiyu"/>
</topic>
发送端:
package senssic.mq;
import java.util.Properties;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.naming.InitialContext;
public class TopicTo {
public static void main(String[] args) {
Properties p = new Properties();
p.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
p.setProperty("java.naming.provider.url", "localhost:1099");
try {
InitialContext cxt = new InitialContext(p);
TopicConnectionFactory tFactory = (TopicConnectionFactory) cxt
.lookup("ConnectionFactory");
TopicConnection connection = tFactory.createTopicConnection();
TopicSession tSession = connection.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
Topic topic = (Topic) cxt.lookup("topic/qiyu");
TextMessage msg = tSession
.createTextMessage("今年的中秋节没有月饼吃,那我就群发月饼………………");
TopicPublisher tPublisher = tSession.createPublisher(topic);
tPublisher.send(msg);
tSession.close();
connection.close();
System.out.println("月饼已分发。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
接受端监听结果(可以有多个监听接受):
package senssic.mq;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/qiyu"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
public class TopicFrom implements MessageListener {
@Override
public void onMessage(Message arg0) {
TextMessage tMessage = (TextMessage) arg0;
try {
System.out.println(tMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ejb定时服务
定时器接口:
package senssic.ejb.time;
public interface MyTimer {
public void setScheduleTimer(long milliseconds);
}
定时器实现:
package senssic.ejb.time;
import java.util.Date;
import javax.annotation.Resource;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
@Stateless
@Remote(MyTimer.class)
public class MyTimerBean implements MyTimer {
private static int count = 0;
@Resource
TimerService tService;
@Override
public void setScheduleTimer(long milliseconds) {
if (count == 0) {
count = 1;
tService.createTimer(new Date(new Date().getTime() + milliseconds),
milliseconds, "定时分发月饼器。");
}
}
// 每隔指定的时间回调函数
@Timeout
public void timerout(Timer timer) {
System.out.println("-----发了第" + count + "个月饼了------");
System.out.println("定时发月饼事件发生传递的月饼为:" + timer.getInfo());
if (count > 100) {
System.out.println("月饼分发完毕了,亲记得明年早点来哦!");
timer.cancel();
count = 0;
} else {
System.out.println("恭喜还有月饼,月饼剩余:" + (100-count) + "个");
count++;
}
}
}
测试类:
package senssic.ejb.time;
import java.util.Properties;
import javax.naming.InitialContext;
public class MyTimeTest {
/**
* @param args
*/
public static void main(String[] args) {
Properties p = new Properties();
p.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
p.setProperty("java.naming.provider.url", "localhost:1099");
try {
InitialContext cxt = new InitialContext(p);
MyTimer mTimer = (MyTimer) cxt.lookup("MyTimerBean/remote");
mTimer.setScheduleTimer(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印结果:
21:49:56,968 WARN [org.jboss.ejb3.TimerServiceContainer] EJBTHREE-2193: using deprecated TimerServiceFactory for restoring timers
21:50:16,781 INFO [STDOUT] -----发了第1个月饼了------
21:50:16,781 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:16,812 INFO [STDOUT] 恭喜还有月饼,月饼剩余:99个
21:50:17,765 INFO [STDOUT] -----发了第2个月饼了------
21:50:17,765 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:17,765 INFO [STDOUT] 恭喜还有月饼,月饼剩余:98个
21:50:18,750 INFO [STDOUT] -----发了第3个月饼了------
21:50:18,750 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:18,750 INFO [STDOUT] 恭喜还有月饼,月饼剩余:97个
21:50:19,765 INFO [STDOUT] -----发了第4个月饼了------
21:50:19,765 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:19,765 INFO [STDOUT] 恭喜还有月饼,月饼剩余:96个
21:50:20,765 INFO [STDOUT] -----发了第5个月饼了------
21:50:20,765 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:20,765 INFO [STDOUT] 恭喜还有月饼,月饼剩余:95个
21:50:21,750 INFO [STDOUT] -----发了第6个月饼了------
21:50:21,750 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:21,750 INFO [STDOUT] 恭喜还有月饼,月饼剩余:94个
21:50:22,765 INFO [STDOUT] -----发了第7个月饼了------
21:50:22,765 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:22,765 INFO [STDOUT] 恭喜还有月饼,月饼剩余:93个
21:50:23,781 INFO [STDOUT] -----发了第8个月饼了------
21:50:23,781 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:23,781 INFO [STDOUT] 恭喜还有月饼,月饼剩余:92个
21:50:24,781 INFO [STDOUT] -----发了第9个月饼了------
21:50:24,781 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:24,781 INFO [STDOUT] 恭喜还有月饼,月饼剩余:91个
21:50:25,781 INFO [STDOUT] -----发了第10个月饼了------
21:50:25,781 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:25,781 INFO [STDOUT] 恭喜还有月饼,月饼剩余:90个
21:50:26,781 INFO [STDOUT] -----发了第11个月饼了------
21:50:26,781 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:26,781 INFO [STDOUT] 恭喜还有月饼,月饼剩余:89个
21:50:27,781 INFO [STDOUT] -----发了第12个月饼了------
21:50:27,781 INFO [STDOUT] 定时发月饼事件发生传递的月饼为:定时分发月饼器。
21:50:27,781 INFO [STDOUT] 恭喜还有月饼,月饼剩余:88个