JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
JSON 可以将 JavaScript 对象中表示的一组数据转换为字符串,由于JSON是固定的字符串,因此它可以在任意平台上运行,本身就具备着跨平台的特性。
{}括起来的内容表示对象,数据结构为:{ key1 :value1, key2:value2}
,类似于Map的键值对
{
"student":{
"userName":"marco","sex":"男","age":18}}
解析对象方式:
JSONObject jsonObject = new JSONObject(jsonStr);
jsonObject.getJSONObject("student");
Student stu = new Student ();
stu.setName(jsonObj .getString("name"));
stu.setSex(jsonObj.getString("sex"));
stu.setAge(jsonObj.getInt("age"));
[ ] 括起来的内容表示数组,数据结构为:["java","javascript","android"]
,可以通过下标来取值(一般可以搭配对象来使用)
{
"students":[{
"userName":"marco","sex":"男","age":18},{
"userName":"sunnie","sex":"女","age":17}]}
解析数组方式:
JSONObject jsonObject = new JSONObject(jsonStr);
jsonObject.getJSONObject("students");
List<Person> list = new ArrayList<Person>();
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObj = (JSONObject) jsonArray.get(i);
// 封装javaBean对象
Student stu = new Student ();
stu.setName(jsonObj .getString("name"));
stu.setSex(jsonObj.getString("sex"));
stu.setAge(jsonObj.getInt("age"));
list.add(stu );
}
生成JSON字符串方式:
由于JSONObject重写了toString方法,因此直接打印该JSONObject对象即可
System.out.println(jsonObject.toString());
Fastjson是由阿里巴巴开发的一种高性能的JSON解析工具
特点:
①快速FAST (比其它任何基于Java的解析器和生成器更快,包括jackson)
②强大(支持普通JDK类包括任意Java Bean Class、Collection、Map、Date或enum)
③零依赖(没有依赖其它任何类库除了JDK)
上述的栗子可以通过以下方式分别获取
Student stu = JSON.parseObject(jsonStr, Student.class);//获取对象
List<Student> list = JSON.parseArray(jsonStr, Student.class);//获取数组
非常简洁对吧!以下是我封装好的一个类实现JSON字符串到JavaBean的转变,不过需要注意的是
1.定义的JavaBean的属性名,要和需要转化的JSON格式的字符串中属性名一一对应
2.建议统一生成各属性的get/set方法
public class JsonToBean {
public static void main(String[] args) {
InputStream resource = JsonToBean.class.getClassLoader().getResourceAsStream("json.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(resource));
String jsonStr;
try {
jsonStr = br.readLine();
@SuppressWarnings("unchecked")
Result<Data> result = JsonToBean.parseObject(jsonStr, Result.class);
System.out.println(result.getAppCode());
System.out.println(result.getDataType());
System.out.println(result.getPageToken());
System.out.println(result.getRetcode());
System.out.println(result.getData());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class JsonToBeanInstance {
private static final JsonToBean jsonToBean = new JsonToBean();
}
private JsonToBean() {
}
public static JsonToBean getParser() {
return JsonToBeanInstance.jsonToBean;
}
@SuppressWarnings("unchecked")
public static <T> T parseObject(String jsonStr, Class<T> clazz) {
Field[] fileds = clazz.getDeclaredFields();
JSONObject jsonObject;
T obj = null;
try {
jsonObject = new JSONObject(jsonStr);
obj = clazz.newInstance();
for (Field field : fileds) {
String type = field.getType().getSimpleName();
String fieldName = field.getName();
field.setAccessible(true);
if(type.equals("List")) {
org.json.JSONArray jsonArray = jsonObject.getJSONArray(fieldName);
List<T> list = new ArrayList<T>();
for (int i = 0; i < jsonArray.length(); i++) {
list.add((T)jsonArray.get(i));
}
field.set(obj,list);
} else if(type.equals("String")) {
field.set(obj,jsonObject.getString(fieldName));
} else if(type.equals("boolean")) {
field.set(obj,jsonObject.getBoolean(fieldName));
} else if(type.equals("double")) {
field.set(obj,jsonObject.getDouble(fieldName));
} else if(type.equals("int")) {
field.set(obj,jsonObject.getInt(fieldName));
} else if(type.equals("long")) {
field.set(obj,jsonObject.getLong(fieldName));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return obj;
}
}
AJAX : Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX技术的目的是为了实现在不重新加载新的页面的情况下能够更新部分网页。它通过与后台的服务器进行少量的数据交换,从而使得网页实现异步更新。
什么是同步和异步:
同步:提交请求–>等待服务器处理–>处理完毕返回给客户端
在此期间客户端浏览器不能干任何事,只能等待,就好比我们去移动营业点充值,我们交钱,专员帮你充值,你需要等待充值完成,再离去
异步: 请求通过JS事件触发–>服务器处理(这时浏览器仍然可以作其他事情)–>处理完毕
就好比我们在支付宝上进行话费充值,我们付款后,支付宝的后台会暂扣费用并发送请求到移动的后台,在此期间,客户可以退出支付宝,做其他的事情,如果充值失败,支付宝会返回支付失败信息,并且退还充值的费用
那么这样做的好处是什么呢?
1)通过异步交互模式, 优化了浏览器和服务器之间的传输,减少不必要的数据往返
2)减少了带宽占用
3)页面不会因为数据改变而整个进行刷新,只会局部进行刷新,提升了客户的体验度
XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
$.ajax({
url:'user.action', //要请求的服务器资源地址
type:'POST', //提交方式,GET或POST
async:true, //或false,是否异步,默认是true
//向服务器发送的参数,可写成json格式{key:value},也可写成"key=
// value&key2=valu2..."
data: {
"name":"zhangsan","age":20 },
timeout:5000, //超时时间
dataType:’text', //返回的数据格式:json/xml/html/script/jsonp/text
//服务器处理成功时调用的函数,data是服务器响应回来的数据
success:function(data){
console.log(data);
},
//服务器处理失败时调用的函数
error:function(xhr,textStatus){
console.log('错误');
console.log(xhr);
console.log(textStatus);
},
//服务器处理完成时调用的函数,不管成功或失败都会调用
complete : function(xhr, text) {
console.log("处理完成");
console.log(xhr);
console.log(text);
}
});
其实Ajax在我们日常生活中都存在着,比如说我们在作账号密码登录的时间,就会碰到一个问题。
当我们输入账号密码后,如果输入有误,当前的页面并不会跳转,而是在页面的某个位置弹出提醒文字输入有误,请重新输入。
试想一下,如果没有Ajax,我们输入账号密码错误之后,后台首先会校验,如果不正确,会使用重定向,或者location.href的方式跳转并刷新页面,这种方式的客户体验度并不好。因此使用Ajax可以很好的解决这个问题。
接下来我们通过案例模拟登录
项目要求
1.使用Ajax实现登录功能
2.验证码校验
3.使用Cookie技术记住账号密码
首先我们需要新建一个User类,里面包含账户和密码,这里大家可以自行发挥,添加一些其他的字段
当然数据库中也需要一个u_user表,字段值和User中的属性相对应。写之前,我们先做一些准备工作。
我自己手写了一个简易版的Connection Pool连接池,放在util包中
public class ConnPool {
private static String driver = null;
private static String url = null;
private static String userName = null;
private static String password = null;
private int minSize = 5;//最小连接数量为5
private int maxSize = 10;//最大连接数量为10
private static Properties pro = new Properties();
//创建一个线程池,用于存储本地的Connection
private LinkedList<Connection> connPool = new LinkedList<Connection>();
//记录当前的连接数量
private int currentSize = 0;
//获取操作对象
public static void main(String[] args) {
ConnPool cp = new ConnPool();
System.out.println(cp.getConnection());
System.out.println(cp.getConnection());
System.out.println(cp.getConnection());
}
//加载驱动
static {
try {
pro.load(ConnPool.class.getClassLoader().getResourceAsStream("config.properties"));
driver = pro.getProperty("driver");
url = pro.getProperty("url");
userName = pro.getProperty("username");
password = pro.getProperty("password");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public ConnPool() {
for (int i = 0; i < minSize; i++) {
Connection conn = this.createConn();
connPool.add(conn);
currentSize ++;
}
}
//获取连接对象(内部使用)
private Connection createConn() {
Connection conn = null;
//如果当前线程中没有绑定相应的Connection
try {
conn = DriverManager.getConnection(url, userName, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public Connection getConnection() {
if(connPool.size() > 0) {
Connection conn = connPool.getFirst();
connPool.removeFirst();
return conn;
} else if(connPool.size()==0 && currentSize< 8) {
currentSize++;
connPool.addLast(this.getConnection());
Connection conn = connPool.getFirst();
connPool.removeFirst();
return conn;
}
throw new RuntimeException("连接数量已经到到达上限,请稍等");
}
public void realse(Connection conn) {
//ThreadLocal取得当前线程的connection
connPool.add(conn);
}
//让当前线程放行
public static void realse(PreparedStatement ps) {
if(ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void realse(ResultSet rs, PreparedStatement ps) {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
realse(ps);
}
}
另外新建一个Result类,用于存放Servlet放回的数据(主要是为了适应layui前端开发)
public class Result {
/**
* 业务编码
*/
private String code;
/**
* 业务消息
*/
private String message;
/**
* 返回的数据
*/
private Object data;
public Result(CodeMsg codeMsg) {
super();
this.code = codeMsg.getCode();
this.message = codeMsg.getMessage();
}
public Result(CodeMsg codeMsg, Object data) {
super();
this.code = codeMsg.getCode();
this.message = codeMsg.getMessage();
this.data = data;
}
public Result(CodeMsg codeMsg, String errorMsg) {
super();
this.code = codeMsg.getCode();
this.message = errorMsg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
/**
* 重写toString方法,将对象以JSON字符串的形式输出并传递
*/
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
/**
* 根据返回的code判断是否登录成功
* @return
*/
public boolean isSuccess() {
return "200".equals(this.code);
}
}
然后新建一个枚举存放CodeMessage信息
public enum CodeMsg {
SUCCESS("200","SUCCESS"),
LOGIN_ERROR("10001","USERNAME OR PASSWORD INCORRECT"),
private final String code;
private final String message;
private CodeMsg(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
准备工作完成后,我们新建一个UserDao层接口
public interface UserDao {
//用户信息查询
public User query(String userName,String password);
}
新建UserDaoImpl(UserDao层接口的实现类)
public class UserDaoImpl extends BaseDao implements UserDao{
private ConnPool connPool = new ConnPool();//创建一个连接池
/**
* 根据用户名和密码查询user表中客户的信息,如果查询到,则返回user对象,否则返回null
*/
public User query(String userName, String password) {
Connection conn = connPool.getConnection();
String sql = "select id as id, user_name as userName, password as password, real_name as realName where user_name = ? and password = ?";
List<User> users = this.queryData(conn, User.class, sql, userName, password);
connPool.realse(conn);
return users.size() > 0?users.get(0):null;
}
接下来新建UserService(Service层接口)
public interface UserService {
Result login(String userName, String password);
}
新建UserServiceImpl(UserService层接口的实现类)
public class UserServiceImpl implements UserService{
@SuppressWarnings("unused")
private UserDao userDao;
public UserServiceImpl() {
userDao = new UserDaoImpl();
}
@Override
public Result login(String userName, String password) {
User user = userDao.query(userName, password);
Result result = new Result(CodeMsg.LOGIN_ERROR);
if(user != null) {
result = new Result(CodeMsg.SUCCESS, user);
}
return result;
}
}
由于我们这个登录的功能中添加了验证码,以及记住密码的操作,因此,我单独的创建了一个Servlet来生成验证码,话不多说上代码
@WebServlet(name = "IdentifyCode", urlPatterns = "/identifyCode.do")
public class IdentifyCode extends HttpServlet{
private static final long serialVersionUID = 6452075665545332363L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedImage img = new BufferedImage(100, 46, BufferedImage.TYPE_INT_RGB);
Graphics graphics = img.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0,0, 200, 100);
this.setInterferingLine(Color.LIGHT_GRAY, graphics);
//设置画笔的颜色
graphics.setColor(Color.RED);
graphics.setFont(new Font("宋体", Font.ITALIC | Font.BOLD, 22));
//drawString(str , x,y): str : 绘制的字符串 x 距离左边的距离 y 距离上面的距离
String code = this.getRandomCode(4);
graphics.drawString(code,25,30);
req.getSession().setAttribute("identifyCode", code);
//控制空客户端不要缓存图片
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Expires", "0");
ImageIO.write(img, "jpg", resp.getOutputStream());
}
public void setInterferingLine(Color color,Graphics graphics) {
graphics.setColor(color);
Random random = new Random();
for (int i = 0; i < 5; i++) {
graphics.drawLine(random.nextInt(100), random.nextInt(46), random.nextInt(100), random.nextInt(46));
}
}
public String getRandomCode(int m) {
String[] str = new String[]{
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i",
"j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D",
"E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
"Z" };
String code = "";
Random random = new Random();
for (int i = 0; i < m; i++) {
int index = random.nextInt(62);
code = code + str[index];
}
return code;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
最后我们建立UserServlet类,我在页面上还添加了记住密码功能,这个功能就可以通过我们之前提到的Cookie技术来实现
@WebServlet(urlPatterns = "/userServlet.do")
public class UserServlet extends HttpServlet {
private static final long serialVersionUID = 522964361071245256L;
private UserService userSvc = new UserServiceImpl();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getParameter("method");
System.out.println("method: " + method);
if("login".equals(method)) {
this.login(request, response);
}
}
/**
* 获取页面的用户名和密码进行登陆验证操作
* @param request
* @param response
* @throws IOException
*/
private void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
Map<String, String[]> parames= request.getParameterMap();
String resultStr = CheckParamUtil.checkEmpty(parames, "userName","password");
if(resultStr != null) {
Result error = new Result(CodeMsg.PARAM_ERROR, resultStr);
response.getWriter().print(error.toString());
}
String userName = request.getParameter("userName");
String password = request.getParameter("password");
String identifyCode = request.getParameter("identifyCode");
String code = (String) request.getSession().getAttribute("identifyCode");
String rememberPwd = request.getParameter("rememberPwd");
//判断验证码是否正确
if(!code.equals(identifyCode)) {
Result error = new Result(CodeMsg.CHECK_CODE_ERROR);
ServletUtil.print(error, response);
return;
}
Result result = userSvc.login(userName, password);
if(result.isSuccess()) {
//设置cookie实现账户密码持久化
Cookie uname = new Cookie("userName", userName);
Cookie pwd = new Cookie("password", password);
Cookie remember = new Cookie("rememberPwd", rememberPwd);
if("keep".equals(rememberPwd)) {
uname.setMaxAge(60*60*24*3);
pwd.setMaxAge(60*60*24*3);
remember.setMaxAge(60*60*24*3);
} else {
uname.setMaxAge(0);
pwd.setMaxAge(0);
remember.setMaxAge(0);
}
response.addCookie(uname);
response.addCookie(pwd);
response.addCookie(remember);
//监听用户登录
AlreadyEntryListener.isEntered(request.getSession(), userName);
//获取返回的查询到的对象
User user = (User) result.getData();
Role role = userSvc.searchRoleById(user.getId());//获取登录用户的role并置入session中
request.getSession().setAttribute("user", user);
request.getSession().setAttribute("role", role);
result = new Result(CodeMsg.SUCCESS);
ServletUtil.print(result, response);
return;
}
result = new Result(CodeMsg.LOGIN_ERROR);
response.getWriter().print(result.toString());
return;
}
核心来啦!!由于前端代码比较多,且花样也比较多,这个大家可以自行发挥自己的想象,创建一个属于自己的页面,我这边就将核心的部分展示出来供参考
<script type="text/javascript">
/*通过JQuery获取button对象*/
$("#login-btn").click(function() {
var userName = $("#userName").val();
var password = $("#password").val();
console.log(userName + " " + password);
var identifyCode = $("#identifyCode").val();
var rememberPwd = $("#remember").val();
var hint = $("#hint");//账号密码输错后的提示语句
if(userName == null || userName == "") {
hint.text("USER NAME CAN'T BE NONE");
$("#userName").css({
"border":"1px solid red"});
return;
}
if(password == null || password == "") {
hint.text("PASSWORD CAN'T BE NONE");
$("#password").css({
"border":"1px solid red"});
return;
}
if(identifyCode == null || identifyCode == "") {
hint.text("IDENTIFYCODE CAN'T BE NONE");
$("#identifyCode").css({
"border":"1px solid red"});
return;
}
var data = {
"userName":userName,"password":password,"identifyCode":identifyCode,"rememberPwd":rememberPwd};
$.post("userServlet.do?method=login",data,function(data) {
//通过Ajax中的post形式将数据发送给后台
/*特别注意后台返回的数据是转化为JSON字符串的对象,因此返回的数据一定要使用JSON转换*/
var result = $.parseJSON(data);
console.log(result.code);
if(result.code == '200') {
location.href = "main.jsp";//当后台返回结果的code为200,那么我们就跳到主页
} else {
hint.text(result.message);//否则提示账号或者密码输入错误
}
})
})
/*点击验证码图像,刷新图像*/
$("#identifyCodeImg").click(function() {
/*这里可以使用时间戳,也可以使用UUID,主要是避免每一次请求的数据一样,后台会认为之前请求的数据已经发给你了,不会再发送第二遍*/
$(this).attr("src","identifyCode.do?" + new Date());
})
</script>