实验目的
使用idea+wildfly+ejb搭建web项目:
假设某天你毕业了,需要构建一个校友的信息收集系统。 你定义了管理员格Admin表,需要收集的校友的信息Alumni表。Alumni字段定义如下:
{姓名、性别、生日、入学年份、毕业年份、工作城市/地区、工作单位、职务、手机、邮箱、微信} 。
Stateless
实验内容
建立无状态的Java Bean,实现以下功能:
验证操作用户(录入人员)的登陆的信息是否正确;
把校友的数据插入到数据库的中;应包含校友的所有信息;
对校友目录进行的检索、修改、删除、统计等功能;
随机生成20个录入员,生成1000个校友用户。进行各种增删改的操作。
实验过程
- 搭建EJb环境
- 实现modelBean
- 定义sessionBean接口
- 实现sessionBean
- 部署项目
- 客户端测试
搭建EJb环境
参考博文https://blog.csdn.net/c_j33/article/details/78990742
不使用jndi连接数据库,直接导入jdbc驱动:
1.导入jdbc驱动的jar包并add as library
2.File->Project Structure->Artifacts->server:war exploded->在WEB-INF目录下新建lib目录,把jdbc的library加进去(这样部署的时候才会把jdbc的驱动依赖也部署上去)
最后项目结构如下
实现modelBean
SchoolFellowModel
public class SchoolFellowModel implements Serializable {
Connection connection;
public static String URL = "jdbc:mysql://localhost:3306/schoowfellow?useUnicode=true&characterEncoding=UTF8&useSSL=false";
public static String USER = xxx;
public static String PASSWORD = xxx;
public SchoolFellowModel() throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(URL, USER, PASSWORD);
}
public boolean update(T schoolFellow) throws InvocationTargetException, IllegalAccessException, SQLException {
String values = "";
int start = 0;
Field[] fields = SchoolFellow.class.getDeclaredFields();
//遍历这个类所有的属性
for (Field field : SchoolFellow.class.getDeclaredFields()) {
//遍历这个类所有的方法,找到对应属性的get方法,并在传入的实例调用,如果不为空的话则将它作为插入信息加到sql语句里
for (Method method : SchoolFellow.class.getMethods()) {
String methodName = method.getName();
if (methodName.substring(0, 3).equals("get")) {
String methodAttribute = methodName.substring(3, methodName.length());
String transfer = methodAttribute.substring(0, 1).toLowerCase()
+ methodAttribute.substring(1, methodAttribute.length());
if (!transfer.equals(field.getName())) {
continue;
}
if (method.invoke(schoolFellow) == null || method.getName().equals("getId")) {
break;
}
if (start > 0) {
values += ",";
}
values += field.getName() + "='" + method.invoke(schoolFellow) + "'";
start++;
break;
}
}
}
String sql = "update schoolfellow set " + values;
Statement statement = connection.createStatement();
statement.execute(sql);
statement.close();
return true;
}
}
这里使用java的反射机制实现了简单的增删改的框架,这里只贴出了insert的代码,其他的实现大同小异(我猜其他orm框架实现原理也差不多)
这里测试的时候发现通过java反射获取到的method列表的排列顺序并不源文件里定义的顺序,跟属性定义的顺序对不上,所以只能像上面的方法那样做两次遍历。
定义sessionBean接口
@Remote
public interface SchoolFellowService {
public Object login(String username, String password) throws Exception;
public boolean insert(SchoolFellow schoolFellow) throws InvocationTargetException, IllegalAccessException, SQLException;
public boolean delete(int id) throws SQLException;
public boolean update(SchoolFellow schoolFellow) throws InvocationTargetException, IllegalAccessException, SQLException;
public SchoolFellow get(int id) throws InvocationTargetException, IllegalAccessException, SQLException;
public int getCount() throws SQLException;
}
实现sessionBean
@Stateless
@Interceptors(CheckIsLogin.class)
public class SchoolFellowServiceBean implements SchoolFellowService {
private SchoolFellowModel schoolFellowBean;
private AdminModel adminModel;
public SchoolFellowServiceBean() throws SQLException, ClassNotFoundException {
schoolFellowBean = new SchoolFellowModel();
adminModel = new AdminModel();
}
@Override
public Object login(String username, String password) throws Exception {
Admin admin = new Admin();
admin.setUsername(username);
admin.setPassword(password);
if (adminModel.check(admin)) {
return true;
}
return false;
}
@Override
public boolean insert(SchoolFellow schoolFellow) throws InvocationTargetException, IllegalAccessException, SQLException {
return schoolFellowBean.insert(schoolFellow);
}
@Override
public boolean delete(int id) throws SQLException {
return schoolFellowBean.delete(id);
}
@Override
public boolean update(SchoolFellow schoolFellow) throws InvocationTargetException, IllegalAccessException, SQLException {
return schoolFellowBean.update(schoolFellow);
}
@Override
public SchoolFellow get(int id) throws InvocationTargetException, IllegalAccessException, SQLException {
return schoolFellowBean.get(id);
}
@Override
public int getCount() throws SQLException {
return schoolFellowBean.getCount();
}
}
使用AOP实现登录验证,这里为每个bean实现一个动态代理类,在调用login方法时验证登录信息,并把它标记为已登录。调用其他方法时验证是否登录,如果没有登录则报错。(stateful的bean这样做没有问题,但是stateless的bean这样做会出现问题,测试的时候再解释)
public class CheckIsLogin {
private boolean isLogin;
public CheckIsLogin() throws Exception {
isLogin = false;
}
@AroundInvoke
public Object check(InvocationContext ic) throws Exception {
if (ic.getMethod().getName().equals("login")) {
isLogin = (boolean) ic.proceed();
return isLogin;
}
if (isLogin) {
return ic.proceed();
}
throw new RuntimeException("not login!");
}
}
部署项目
选择edit configrations配置运行,部署到wildfly。在deployment选项卡中,添加生成的war包。
客户端测试
测试crud操作
if((boolean)schoolFellowService.login("bao","123456")){
System.out.println("login success!");
}
else{
System.out.println("username or password error");
}
schoolFellowService.insert(schoolFellow);
System.out.println("insert success!");
SchoolFellow schoolFellow1=schoolFellowService.get(38);
System.out.println("get success!");
System.out.println(schoolFellow1);
schoolFellow1.setWorkLocation("beijin");
schoolFellow1.setJob("boss");
schoolFellowService.update(schoolFellow1);
System.out.println("update success!");
System.out.println(schoolFellowService.get(38));
schoolFellowService.delete(38);
System.out.println("delete success!");
测试登录验证
client1
try {
if((boolean)schoolFellowService.login("bao","123456")){
System.out.println("login success!");
}
else{
System.out.println("username or password error");
}
schoolFellowService.insert(schoolFellow);
System.out.println("insert success!");
} catch (Exception e) {
System.out.println(e.getMessage());
}
client2
try {
schoolFellowService.insert(schoolFellow);
System.out.println("insert success!");
} catch (Exception e) {
System.out.println(e.getMessage());
}
这里client1和client2区别仅在于client1在执行sessionBean操作前先进行了登录验证,而client没有.
先运行client1再运行client2.
client1:
client2:
根据前面aop处理登录的定义,client2应该会输出错误信息"not login!",然而并没有。说明两个客户端的sessionBean的aop代理类是同一个,所以调用的sessionBean也是同一个。client1产生的sessionBean并没有销毁,而是保存在了对象池中,而client2调用了同一个sessionBean。
测试生成用户记录
Stateful
实验内容
建立有状态的Java Bean,实现以下功能:
操作用户(录入人员)登陆后,显示本次登陆的次数和上一次登陆的时间;
操作用户登录后,可进行校友的检索、修改、删除、统计等功能;
5分钟如果没有操作,则自动登出系统;
操作用户退出时,显示用户连接的时间长度,并把此次登陆记录到数据库。
在2台机器上模拟2个录入员,生成1000个校友用户,并进行各种增删改的操作。
实验过程
搭建EJb环境
实现modelBean
adminModel
public class AdminModel implements Serializable {
Connection connection;
public static String URL = "jdbc:mysql://localhost:3306/schoowfellow?useUnicode=true&characterEncoding=UTF8&useSSL=false";
public static String USER = "xxx";
public static String PASSWORD = "xxx";
public AdminModel() throws SQLException {
connection = DriverManager.getConnection(URL, USER, PASSWORD);
}
public boolean check(Admin admin) throws SQLException {
String sql = "select * from admin where username='" + admin.getUsername() + "' and password=" + "'" + admin.getPassword() + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
statement.close();
return true;
} else {
statement.close();
return false;
}
}
public void insert(Admin admin) throws SQLException {
String sql = "insert into admin(username,password) values('" + admin.getUsername() + "','" + admin.getPassword() + "')";
Statement statement = connection.createStatement();
statement.execute(sql);
statement.close();
}
public void insertConnectInfo(String username, String startTime, String connectTime) throws SQLException {
String sql = "insert into connect_info values('" + username + "','" + startTime + "','" + connectTime + "')";
Statement statement = connection.createStatement();
statement.execute(sql);
statement.close();
}
}
定义sessionBean接口
@Remote
public interface SchoolFellowStatefulService extends SchoolFellowService {
public String logout();
}
实现sessionBean
@Stateful
//@Interceptors(CheckIsLogin.class)
public class SchoolFellowStatefulServiceBean extends SchoolFellowServiceBean implements SchoolFellowStatefulService {
public SchoolFellowStatefulServiceBean() throws SQLException, ClassNotFoundException {
super();
}
@Override
public String logout() {
return null;
}
}
AOP代理类保存登录信息,同时处理登录登出请求:
当执行login方法时返回此次登录的次数loginCount,和上次登录的时间 lastLoginTime,并更新loginCount和lastLoginTime以及lastOperateTime(上一次操作的时间)。
当执行其他方法时,先判断当前的时间和lastOperateTime的差值是否大于5分钟,如果是则跑错提醒用户需重新登录。
然后判断方法是否为logout,是的话则返回此次连接的时间。
最后调用原方法。
因为是StatefulBean是存在于整个会话的,一个StatefulBean对应一个用户,且一个StatefulBean有一个AOP代理类,所以这样做是可行的。
public class CheckIsLogin {
private String username;
private boolean isLogin;
private boolean isStateful;
private int loginCount;
private String lastLoginTime;
private long lastOperateTime;
private AdminModel adminModel;
public CheckIsLogin() throws Exception {
username = null;
loginCount = 0;
lastOperateTime = 0;
lastLoginTime = "first login";
isLogin = false;
isStateful = false;
adminModel = new AdminModel();
}
@AroundInvoke
public Object check(InvocationContext ic) throws Exception {
if (ic.getMethod().getName().equals("login")) {
isLogin = (boolean) ic.proceed();
if (ic.getTarget().getClass().getAnnotation(javax.ejb.Stateful.class) != null && isLogin) {
username = (String) ic.getParameters()[0];
isStateful = true;
loginCount++;
lastOperateTime = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
lastLoginTime = df.format(new Date());
return "login count:" + loginCount + " lastLoginTime:" + lastLoginTime;
}
return isLogin;
}
if (isLogin) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (ic.getMethod().getName().equals("logout")) {
long connectTime = System.currentTimeMillis() - df.parse(lastLoginTime).getTime();
String connectTimeString = df.format(new Date(new Long(connectTime)));
adminModel.insertConnectInfo(username,lastLoginTime,connectTimeString);
return "connect time:" + connectTime + "ms";
}
if (ic.getTarget().getClass().getAnnotation(javax.ejb.Stateful.class) != null) {
isLogin = false;
if (System.currentTimeMillis() - lastOperateTime >= 300000) {
String connectTime = df.format(new Date(new Long(300000)));
adminModel.insertConnectInfo(username,lastLoginTime,connectTime);
throw new RuntimeException("too long to operate you should login again!");
}
lastOperateTime = System.currentTimeMillis();
}
return ic.proceed();
}
throw new RuntimeException("not login!");
}
}
部署项目
客户端测试
登录和自动登出
执行crud操作然后登出
public class testStateful {
public static void main(String[] args) throws Exception {
SchoolFellowStatefulService schoolFellowStatefulService = (SchoolFellowStatefulService) EJBFactory.getEJB(
"ejb:/server_war_exploded/SchoolFellowStatefulServiceBean!stateful.SchoolFellowStatefulService?stateful");
SchoolFellow schoolFellow = new SchoolFellow();
schoolFellow.setName("happy");
schoolFellow.setSex("boy");
schoolFellow.setPhone("123456");
schoolFellow.setJob("student");
schoolFellow.setEmail("[email protected]");
schoolFellow.setAttendTime("2012-8-25 00:00:00");
schoolFellow.setBirth("1998-10-17 00:00:00");
schoolFellow.setGraduateTime("2016-7-12 00:00:00");
schoolFellow.setWechat("bao");
schoolFellow.setWorkCity("changsha");
schoolFellow.setWorkLocation("hangzhou");
try {
System.out.println(schoolFellowStatefulService.login("bao","123456"));
schoolFellowStatefulService.insert(schoolFellow);
System.out.println("insert success!");
SchoolFellow schoolFellow1=schoolFellowStatefulService.get(504);
System.out.println(schoolFellow1);
System.out.println("get success!");
schoolFellow1.setSex("girl");
System.out.println("update success!");
schoolFellowStatefulService.delete(504);
System.out.println("delete success!");
System.out.println(schoolFellowStatefulService.logout());
System.out.println("logout success!");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}