實驗目的
使用idea+wildfly+ejb搭建web項目:
假設某天你畢業(yè)了哗讥,需要構建一個校友的信息收集系統(tǒng)米愿。 你定義了管理員格Admin表讼载,需要收集的校友的信息Alumni表惋啃。Alumni字段定義如下:
{姓名霎槐、性別、生日青伤、入學年份督怜、畢業(yè)年份、工作城市/地區(qū)狠角、工作單位号杠、職務、手機丰歌、郵箱究流、微信} 。
Stateless
實驗內容
建立無狀態(tài)的Java Bean动遭,實現(xiàn)以下功能:
驗證操作用戶(錄入人員)的登陸的信息是否正確芬探;
把校友的數(shù)據(jù)插入到數(shù)據(jù)庫的中;應包含校友的所有信息厘惦;
對校友目錄進行的檢索偷仿、修改、刪除宵蕉、統(tǒng)計等功能酝静;
隨機生成20個錄入員,生成1000個校友用戶羡玛。進行各種增刪改的操作别智。
實驗過程
- 搭建EJb環(huán)境
- 實現(xiàn)modelBean
- 定義sessionBean接口
- 實現(xiàn)sessionBean
- 部署項目
- 客戶端測試
搭建EJb環(huán)境
參考博文https://blog.csdn.net/c_j33/article/details/78990742
不使用jndi連接數(shù)據(jù)庫,直接導入jdbc驅動:
1.導入jdbc驅動的jar包并add as library
2.File->Project Structure->Artifacts->server:war exploded->在WEB-INF目錄下新建lib目錄稼稿,把jdbc的library加進去(這樣部署的時候才會把jdbc的驅動依賴也部署上去)
最后項目結構如下
實現(xiàn)modelBean
SchoolFellowModel
public class SchoolFellowModel<T> 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的反射機制實現(xiàn)了簡單的增刪改的框架敞恋,這里只貼出了insert的代碼,其他的實現(xiàn)大同小異(我猜其他orm框架實現(xiàn)原理也差不多)
這里測試的時候發(fā)現(xiàn)通過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;
}
實現(xiàn)sessionBean
@Stateless
@Interceptors(CheckIsLogin.class)
public class SchoolFellowServiceBean implements SchoolFellowService {
private SchoolFellowModel schoolFellowBean;
private AdminModel adminModel;
public SchoolFellowServiceBean() throws SQLException, ClassNotFoundException {
schoolFellowBean = new SchoolFellowModel<SchoolFellow>();
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實現(xiàn)登錄驗證改执,這里為每個bean實現(xiàn)一個動態(tài)代理類啸蜜,在調用login方法時驗證登錄信息,并把它標記為已登錄辈挂。調用其他方法時驗證是否登錄衬横,如果沒有登錄則報錯。(stateful的bean這樣做沒有問題呢岗,但是stateless的bean這樣做會出現(xiàn)問題冕香,測試的時候再解釋)
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區(qū)別僅在于client1在執(zhí)行sessionBean操作前先進行了登錄驗證挫酿,而client沒有.
先運行client1再運行client2.
client1:
client2:
根據(jù)前面aop處理登錄的定義构眯,client2應該會輸出錯誤信息"not login!",然而并沒有早龟。說明兩個客戶端的sessionBean的aop代理類是同一個惫霸,所以調用的sessionBean也是同一個。client1產生的sessionBean并沒有銷毀葱弟,而是保存在了對象池中壹店,而client2調用了同一個sessionBean。
測試生成用戶記錄
Stateful
實驗內容
建立有狀態(tài)的Java Bean芝加,實現(xiàn)以下功能:
操作用戶(錄入人員)登陸后硅卢,顯示本次登陸的次數(shù)和上一次登陸的時間;
操作用戶登錄后藏杖,可進行校友的檢索将塑、修改、刪除蝌麸、統(tǒng)計等功能点寥;
5分鐘如果沒有操作,則自動登出系統(tǒng)来吩;
操作用戶退出時敢辩,顯示用戶連接的時間長度,并把此次登陸記錄到數(shù)據(jù)庫弟疆。
在2臺機器上模擬2個錄入員责鳍,生成1000個校友用戶,并進行各種增刪改的操作兽间。
實驗過程
搭建EJb環(huán)境
實現(xiàn)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();
}
實現(xiàn)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代理類保存登錄信息历葛,同時處理登錄登出請求:
當執(zhí)行l(wèi)ogin方法時返回此次登錄的次數(shù)loginCount,和上次登錄的時間 lastLoginTime嘀略,并更新loginCount和lastLoginTime以及l(fā)astOperateTime(上一次操作的時間)恤溶。
當執(zhí)行其他方法時,先判斷當前的時間和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!");
}
}
部署項目
客戶端測試
登錄和自動登出
執(zhí)行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("123@qq.com");
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());
}
}
}