精华内容
下载资源
问答
  • java 递归实现权限菜单树

    千次阅读 多人点赞 2019-12-18 17:56:12
    java 递归实现权限菜单树)省市县多级结构

    完整示例代码地址如下:
    https://github.com/Dr-Water/springboot-action/tree/master/springboot-shiro

    一、 权限树的问题由来

    1. 在开发中难免遇到一个有多级菜单结构树,或者多级部门的结构树,亦或是省市区县的多级结构,数据结构类似如下的json数据:
    [
        {
            "id": "1",
            "name": "主菜单1",
            "pid": "0",
            "menuChildren": [
                {
                    "id": "4",
                    "name": "子菜单1.1",
                    "pid": "1",
                    "menuChildren": [
                        {
                            "id": "6",
                            "name": "子菜单1.1.1",
                            "pid": "4",
                            "menuChildren": []
                        },
                        {
                            "id": "9",
                            "name": "子菜单1.1.2",
                            "pid": "4",
                            "menuChildren": []
                        }
                    ]
                },
                {
                    "id": "5",
                    "name": "子菜单1.2",
                    "pid": "1",
                    "menuChildren": []
                }
            ]
        },
        {
            "id": "2",
            "name": "主菜单2",
            "pid": "0",
            "menuChildren": [
                {
                    "id": "7",
                    "name": "子菜单2.1",
                    "pid": "2",
                    "menuChildren": []
                },
                {
                    "id": "8",
                    "name": "子菜单2.2",
                    "pid": "2",
                    "menuChildren": []
                }
            ]
        },
        {
            "id": "3",
            "name": "主菜单3",
            "pid": "0",
            "menuChildren": []
        }
    ]
    

    二、 解决方案

    目前的解决方案主要有以下两种方案:

    • 方案一:后端把所有需要的数据以一个大list返回前端,前端进行操作,把数据搞成树状结构
    • 方案二: 后端在后端返回数据之前把数据搞成已经有层次结构的数据,方案二也分为两种解决方法
      • 方法一:次性将数据查询出来,在java程序中进行树状结构的构建
      • 方法二: 第一次将最高层次的数据查询出来,然后多次循环查询数据库将子数据查询出来

    由于博主的前端水平有限,目前只能用后端的实现方式,再加上每次查询数据库的开销比较大,所以本文使用方案二的方法一进行验证

    实现步骤

    以菜单的结构树为例

    1. 准备mysql数据库的基础数据
      在这里插入图片描述
    2. java的实体类:
    @Data
    @NoArgsConstructor
    public class Menu implements Serializable {
    
        private String  id;
        private String name;
        private String pid;
        private List<Menu> menuChildren;
    }
    
    1. java的dao层
    @Mapper
    public interface MenuDao {
    
        /**
         * 根据父类id查询子类菜单
         * @param pid
         * @return
         */
         List<Menu> selectByPid(Integer pid);
    
        /**
         * 查询所有的菜单
         * @return
         */
         List<Menu> selectAll();
    
        /**
         * 查询除了一级菜单以外的菜单
         * @return
         */
         List<Menu> selectAllNotBase();
    }
    
    1. mapper文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.ratel.shiro.dao.MenuDao">
    
    
        <select id="selectByPid" resultType="com.ratel.shiro.entity.Menu">
            SELECT * FROM menu WHERE  pid=#{pid}
        </select>
    
        <select id="selectAll" resultType="com.ratel.shiro.entity.Menu">
            SELECT * FROM menu
        </select>
    
        <select id="selectAllNotBase" resultType="com.ratel.shiro.entity.Menu">
            SELECT * FROM menu where pid!= 0
        </select>
    </mapper>
    
    1. Controller层(由于是查询操作,并且没有复杂的操作,偷个懒就不写service层)
    @RestController
    @RequestMapping("mymenu")
    public class MenuController {
    
        @Autowired
        private MenuDao menuDao;
    
    
        @RequestMapping("/getMenuTree")
        public List<Menu> getMenuTree(){
            List<Menu> menusBase = menuDao.selectByPid(0);
            List<Menu> menuLNotBase = menuDao.selectAllNotBase();
            for (Menu menu : menusBase) {
                List<Menu> menus = iterateMenus(menuLNotBase, menu.getId());
                menu.setMenuChildren(menus);
            }
            return  menusBase;
        }
    
    
        /**
         *多级菜单查询方法
         * @param menuVoList 不包含最高层次菜单的菜单集合
         * @param pid 父类id
         * @return
         */
        public List<Menu> iterateMenus(List<Menu> menuVoList,String pid){
            List<Menu> result = new ArrayList<Menu>();
            for (Menu menu : menuVoList) {
                //获取菜单的id
                String menuid = menu.getId();
                //获取菜单的父id
                String parentid = menu.getPid();
                if(StringUtils.isNotBlank(parentid)){
                    if(parentid.equals(pid)){
                        //递归查询当前子菜单的子菜单
                        List<Menu> iterateMenu = iterateMenus(menuVoList,menuid);
                        menu.setMenuChildren(iterateMenu);
                        result.add(menu);
                    }
                }
            }
            return result;
        }
    }
    
    1. 启动程序用postman进行测试:
      在这里插入图片描述
      返回的json数据如下:
    [
        {
            "id": "1",
            "name": "主菜单1",
            "pid": "0",
            "menuChildren": [
                {
                    "id": "4",
                    "name": "子菜单1.1",
                    "pid": "1",
                    "menuChildren": [
                        {
                            "id": "6",
                            "name": "子菜单1.1.1",
                            "pid": "4",
                            "menuChildren": []
                        },
                        {
                            "id": "9",
                            "name": "子菜单1.1.2",
                            "pid": "4",
                            "menuChildren": []
                        }
                    ]
                },
                {
                    "id": "5",
                    "name": "子菜单1.2",
                    "pid": "1",
                    "menuChildren": []
                }
            ]
        },
        {
            "id": "2",
            "name": "主菜单2",
            "pid": "0",
            "menuChildren": [
                {
                    "id": "7",
                    "name": "子菜单2.1",
                    "pid": "2",
                    "menuChildren": []
                },
                {
                    "id": "8",
                    "name": "子菜单2.2",
                    "pid": "2",
                    "menuChildren": []
                }
            ]
        },
        {
            "id": "3",
            "name": "主菜单3",
            "pid": "0",
            "menuChildren": []
        }
    ]
    

    参考链接:
    java递归 处理权限管理菜单树或分类
    一次性搞定权限树遍历--------权限树后台遍历的通用解决方案
    (java后台)用户权限的多级菜单遍历方法
    java 用递归实现球上下级(牵涉到对上级的去重)
    java递归获取某个父节点下面的所有子节点
    java递归算法总结

    展开全文
  • java实现菜单转换为的格式

    千次阅读 2018-11-21 12:33:54
    这个时候想转化成菜单树的格式就需要自己编写代码实现了。 1.获取所有的菜单元素 2.遍历所有的菜单元素,找到一级菜单元素( pid 为null的就是一级菜单 ) 3.递归查找子菜单,用一级菜单的 id 去匹配所有的菜单...

    菜单一般有很多级,然后数据库一般都是存的  自己的id,父id,名字

    这个时候想转化成菜单树的格式就需要自己编写代码实现了。

    1.获取所有的菜单元素

    2.遍历所有的菜单元素,找到一级菜单元素( pid 为null的就是一级菜单 )

    3.递归查找子菜单,用一级菜单的 id 去匹配所有的菜单元素,如果 其他的菜单元素的 pid 为 一级菜单的 id,则证明该菜单是一级菜单的子元素,然后重复此步骤,知道 找不到子元素,则该这一个支结束

    ==>代码实现:

    Element 菜单元素里面添加 List<Element> childs; 子菜单,用来装子菜单

    import java.util.List;
    
    public class Element {
        private String id;
        private String pid;
        private String name;
    
        private List<Element> childs;
    
        ....省略 getset
    
    }

    GetTree类

    思路我已经说了,看代码的时候注意看注释

    import java.util.ArrayList;
    import java.util.List;
    
    public class GetTree {
    
        // 菜单节点s
        private  static List<Element> list = new ArrayList<>();
    
        static {
            list.add( new Element("1",null,"执法考评") );
            list.add( new Element("2","1","日常考评") );
            list.add( new Element("5","1","行政考评") );
            list.add( new Element("4","2","警情考评") );
            list.add( new Element("6","4","刑事考评") );
            list.add( new Element("3","1","抽案考评") );
            list.add( new Element("7",null,"一级菜单") );
        }
    
        public List<Element> getTree() {
    
            List<Element> baseLists = new ArrayList<>();
            // 总菜单,出一级菜单,一级菜单没有父id
            for (Element e: list) {
                if( e.getPid()==null ){
                    baseLists.add( e );
                }
            }
            // 遍历一级菜单
            for (Element e: baseLists) {
                // 将子元素 set进一级菜单里面
                e.setChilds( getChild(e.getId(),list) );
            }
    
            return baseLists;
        }
    
        /**
         * 获取子节点
         * @param pid
         * @param elements
         * @return
         */
        private List<Element> getChild(String pid , List<Element> elements){
            List<Element> childs = new ArrayList<>();
            for (Element e: elements) {
                if(e.getPid()!=null){
                    if(e.getPid().equals( pid )){
                        // 子菜单的下级菜单
                        childs.add( e );
                    }
                }
            }
            // 把子菜单的子菜单再循环一遍
            for (Element e: childs) {
                // 继续添加子元素
                e.setChilds( getChild( e.getId() , elements ) );
            }
    
            //停下来的条件,如果 没有子元素了,则停下来
            if( childs.size()==0 ){
                return null;
            }
            return childs;
        }
    
    }

    实现效果:

    展开全文
  • //====================简单JSON数据的单选菜单===================================== String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerNa

    //系统结构图


    //====================简单JSON数据的单选树形菜单=====================================


    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>My JSP 'simgleSimple.jsp' starting page</title>
        
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    <link rel="stylesheet" href="jsplus/ztree/css/zTreeStyle/zTreeStyle.css" type="text/css">
    	<script type="text/javascript" src="jsplus/ztree/js/jquery-1.4.4.min.js"></script>
    	<script type="text/javascript" src="jsplus/ztree/js/jquery.ztree.core-3.5.js"></script>
    	<script type="text/javascript" src="jsplus/ztree/js/jquery.ztree.excheck-3.5.js"></script>
      </head>
      	<SCRIPT type="text/javascript">
    		var setting = {
    			view: {
    				showLine: true,
    				selectedMulti: false
    			},
    			data: {  
    				simpleData: {  
    					enable: true  
    				}  
    			},
    			callback: {
    				onClick: zTreeOnClick
    			}
    		};
    		 function zTreeOnClick(event, treeId, treeNode) {
    	            var id=treeNode.id;
    	            var name=treeNode.name;
    	            var pId=treeNode.pId;
    	          alert("当前节点为信息为"+id+name+pId);  
    	    }
    		var zNodes =[
    			{ id:1, pId:0, name:"山东省",isParent:true,open:true},
    				{ id:1001, pId:1, name:"青岛市"},
    				{ id:1002, pId:1, name:"济南市"},
    				
    			{ id:2, pId:0, name:"北京"},
    				{ id:2001, pId:2, name:"海淀区"}
    		];
    
    		$(document).ready(function(){
    			$.fn.zTree.init($("#treeDemo"), setting, zNodes);
    		});
    	</SCRIPT>
      <body>
      	<div class="zTreeDemoBackground left">
    		<ul id="treeDemo" class="ztree"></ul>
    	</div>
      </body>
    </html>
    

    //=======================嵌套递归型JSON单选菜单



    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>My JSP 'SingleStandard.jsp' starting page</title>
        
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    <link rel="stylesheet" href="jsplus/ztree/css/zTreeStyle/zTreeStyle.css" type="text/css">
    	<script type="text/javascript" src="jsplus/ztree/js/jquery-1.4.4.min.js"></script>
    	<script type="text/javascript" src="jsplus/ztree/js/jquery.ztree.core-3.5.js"></script>	
    
    	
    	<SCRIPT type="text/javascript">
    		var setting = {	};
    
    		var zNodes =[
    			{ 
    				id:1,
    				pId:1,
    				name:"山东省", 
    				open:true,
    				isParent:true,
    				children: [
    							{ 
    								id:1001,
    								pId:1,
    								name:"济南市",
    								isParent:true,
    								open:true,
    								children: [
    											{ 
    												id:1001001,
    												pId:1001,
    												name:"历下区"
    											},
    								]
    						    },
    							{ 
    						    	id:1002,
    								pId:1,
    						    	name:"青岛市",
    						    }
    					     ]
    		    },
    			{ 
    		    	id:2,
    		    	name:"河北省", 
    		    	isParent:false
    			}
    
    		];
    
    		$(document).ready(function(){
    			$.fn.zTree.init($("#treeDemo"), setting, zNodes);
    		});
    	</SCRIPT>
    
      </head>
      
      <body>
      	<div class="zTreeDemoBackground left">
    		<ul id="treeDemo" class="zTree"></ul>
    	</div>
      </body>
    </html>
    
    后台json递归封装

    package com.ztree.single;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Administrator
     * 菜单实体类
     */
    public class MenuEntity{
    	//数据库字段
    	private String id    ;//   varchar2(32)                            
    	private String name  ;//   varchar2(30)  y                         
    	private String pId;// varchar2(32)  y                         
    	private String icon   ;//  varchar2(50)  y       
    	
    	//辅助字段
    	private List<MenuEntity> children = new ArrayList<MenuEntity>();
    	
    	
    	//封装set,get
    	
    	public String getId() {
    		return id;
    	}
    	public List<MenuEntity> getChildren() {
    		return children;
    	}
    	public void setChildren(List<MenuEntity> children) {
    		this.children = children;
    	}
    	public void setId(String id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getpId() {
    		return pId;
    	}
    	public void setpId(String pId) {
    		this.pId = pId;
    	}
    	public String getIcon() {
    		return icon;
    	}
    	public void setIcon(String icon) {
    		this.icon = icon;
    	}
    	
    }
    
    package com.ztree.single;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import com.alibaba.fastjson.JSON;
    
    public class MenuTreeUtil {
    	private static String ROOT_ID = "1";
    
    	public static List<MenuEntity>  getMenuTree(List<MenuEntity> menuList) {
    		List<MenuEntity> list = groupMenuList(ROOT_ID, menuList);
    		return list;
    	}
    //根据id和pId进行地柜
    	private static List<MenuEntity> groupMenuList(String pId, List<MenuEntity> list) {
    		List<MenuEntity> IteratorMenuList = new ArrayList<MenuEntity>();
    		for (MenuEntity m : list) {
    			if (m.getpId().equals(pId)) {
    				List<MenuEntity> childMenuList = groupMenuList(m.getId(), list);
    				m.setChildren(childMenuList);
    				IteratorMenuList.add(m);
    			}
    		}
    		return IteratorMenuList;
    	}
    
    	public static void main(String[] args) {
    		//构造三栋济南有1个子节点嵌套JSON
    		MenuEntity msd=new MenuEntity();
    			msd.setId("1001");
    			msd.setName("山东");	
    			msd.setpId("1");
    			MenuEntity jnd=new MenuEntity();
    				jnd.setId("1001001");
    				jnd.setName("济南");
    				jnd.setpId("1001");
    		MenuEntity qdd=new MenuEntity();
    			qdd.setId("1002");
    			qdd.setName("青岛");	
    			qdd.setpId("1");
    
    		List<MenuEntity> mlist = new ArrayList<MenuEntity>();
    			mlist.add(msd);
    			mlist.add(jnd);
    		List<MenuEntity> treeList=getMenuTree(mlist);
    		System.out.println(JSON.toJSONString(treeList));
    	}
    }
    
    数据效果


    //=====================================多选树形菜单



    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>My JSP 'checkbox.jsp' starting page</title>
        
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    <link rel="stylesheet" href="jsplus/ztree/css/zTreeStyle/zTreeStyle.css" type="text/css">
    	<script type="text/javascript" src="jsplus/ztree/js/jquery-1.4.4.min.js"></script>
    	<script type="text/javascript" src="jsplus/ztree/js/jquery.ztree.core-3.5.js"></script>
    	<script type="text/javascript" src="jsplus/ztree/js/jquery.ztree.excheck-3.5.js"></script>	
    	<!--
    	<link rel="stylesheet" type="text/css" href="styles.css">
    	-->
    	<SCRIPT type="text/javascript">
    		var setting = {
    			check: {
    				enable: true
    			},
    			data: {
    				simpleData: {
    					enable: true
    				}
    			}
    		};
    
    		var zNodes =[
    			{ id:1, pId:0, name:"山东省", open:true,checked:true},
    				{ id:1001, pId:1, name:"济南市", open:true,checked:true},
    					{ id:1001001, pId:1001, name:"历下区", open:true,checked:true},
    				{ id:1002, pId:1, name:"青岛市"},
    			{ id:2, pId:0, name:"河北省"},
    				{ id:2001, pId:2, name:"石家庄", open:false},
    			{ id:3, pId:0, name:"上海市"},
    		];
    		$(document).ready(function(){
    			$.fn.zTree.init($("#treeDemo"), setting, zNodes);
    			var zTree = $.fn.zTree.getZTreeObj("treeDemo");
    			zTree.setting.check.chkboxType = { "Y":"ps", "N":"ps"};
    			$("#py").bind("change", setCheck);
    			$("#sy").bind("change", setCheck);
    			$("#pn").bind("change", setCheck);
    			$("#sn").bind("change", setCheck);
    		});
    	</SCRIPT>
      </head>
      
      <body>
      	<div class="zTreeDemoBackground left">
    		<ul id="treeDemo" class="ztree"></ul>
    	</div>
    	
      </body>
    </html>
    

    //===================================ztree的异步数据加载 事件触发才加载



    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>My JSP 'synload.jsp' starting page</title>
        
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    <link rel="stylesheet" href="jsplus/ztree/css/zTreeStyle/zTreeStyle.css" type="text/css">
    	<script type="text/javascript" src="jsplus/ztree/js/jquery-1.4.4.min.js"></script>
    	<script type="text/javascript" src="jsplus/ztree/js/jquery.ztree.core-3.5.js"></script>	
    	<!--
    	<link rel="stylesheet" type="text/css" href="styles.css">
    	-->
    	<SCRIPT type="text/javascript">
    		var setting = {
    				view: {
    					selectedMulti: false
    				},
    				data: {  
    					simpleData: {  
    						enable: true  
    					}  
    				},
    				async: {
    					enable: true,
    					url:"${ctx}/SynServlet",
    					autoParam:["id", "name", "pId"],
    					otherParam:{"otherParam":"zTreeAsyncTest"}
    				},
    				callback: {
    					onClick: zTreeOnClick
    				}
    		};  
            function zTreeOnClick(event, treeId, treeNode) {
                var id=treeNode.id;
                var name=treeNode.name;
                var root=treeNode.root;
            }
            var zNodes =[  
                { id:1, pId:0, name:"山东省",isParent:true,open:true},  
               		{ id:1001, pId:1, name:"济南市",isParent:false,open:true},  
               		{ id:1002, pId:1, name:"青岛",isParent:false,open:true},  
               		{ id:1003, pId:1, name:"烟台",isParent:false,open:true},  
                
               	{ id:2, pId:0, name:"河北省",isParent:true},  
                	{ id:2001, pId:2, name:"石家庄市",isParent:false},  
            ];  
      
            $(document).ready(function(){  
                $.fn.zTree.init($("#treeDemo"), setting, zNodes);  
            });  
        </SCRIPT>	
      </head>
      
      <body>
    	  <div class="zTreeDemoBackground left">
    			<ul id="treeDemo" class="zTree"></ul>
    		</div>
      </body>
    </html>
    
    package com.ztree.syn;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    
    /**
     * @author Administrator
     * 异步树
     */
    @WebServlet("/SynServlet")
    public class SynServlet extends HttpServlet{
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		doPost(req, resp);
    	}
    	@Override
    	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		req.setCharacterEncoding("UTF-8");
    		resp.setCharacterEncoding("UTF-8");
    		//获取请求参数
    		String id = req.getParameter("id");
    		String name = req.getParameter("name");
    		if (id == null& "".equals(id)) {
    			id = "0";
    			String sql="select * from table_tree where pId=0";
    		}else{
    			String sql="select * from table_tree where pId='"+id+"'";
    		}
    	}
    	
    }
    





    展开全文
  • JAVA记录操作日志步骤

    万次阅读 多人点赞 2019-08-06 14:01:55
    import java.util.Arrays; /** * Web层日志切面 */ @Aspect //这里使用@Aspect注解方式设置AOP @Order(5) //值越小,越先加载 @Component public class WebLogAspect { private Logger logger = Logger.getLogger...

    项目地址:https://gitee.com/Selegant/logs-demo.git

    说明

    系统日志不论是在日常的管理还是维护中都会起到很大的作用,但是在日志的记录中通常会存在很多的问题

    1. 日志记录的不规范性
    2. 日志记录的重复性
    3. 日志记录的难分类

    目前日志主要记录的有三方面

    1. 请求的入参,出参
    2. 关于业务上的操作
    3. 异常日常日志的打印

    解决方案

    1.记录请求的出参入参

    记录出参入参这是日志记录最好操作的一部分,而这里会存在一定的重复性,因为每个请求都需要记录,这是重复操作,完全可以使用Spring AOP进行入参和出参的记录

    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Arrays;
    
    /**
     * Web层日志切面
     */
    @Aspect    //这里使用@Aspect注解方式设置AOP
    @Order(5)  //值越小,越先加载
    @Component
    public class WebLogAspect {
    
        private Logger logger = Logger.getLogger(getClass());
    
        ThreadLocal<Long> startTime = new ThreadLocal<>();
    	
    	//这里@Pointcut设置切点可以设置为Controller层的地址
        @Pointcut("execution(public * com.training..*.*Controller(..))")
        public void webLog(){}
    
    	//@Before指在切点方法之前执行,也就是在Controller层方法执行之前执行,这里可以通过JoinPoint获取一些有关方法的信息,在这里也可以修改参数的值
        
    	//@Before()括号里设置的是切点方法的名称
    	@Before("webLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            startTime.set(System.currentTimeMillis());
    
            // 接收到请求,记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
    
            // 记录下请求内容
            logger.info("URL : " + request.getRequestURL().toString());
            logger.info("HTTP_METHOD : " + request.getMethod());
            logger.info("IP : " + request.getRemoteAddr());
            logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    
        }
    
        @AfterReturning(returning = "ret", pointcut = "webLog()")
        public void doAfterReturning(Object ret) throws Throwable {
            // 处理完请求,返回内容
            logger.info("RESPONSE : " + ret);
            logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
        }
    
    }
    

    2.记录操作日志

    在系统中可能会有很多的增删改查或是会涉及到一些业务操作,这时候我们需要记录一下操作的入口,在此还可以记录下操作的类型,或是业务的名称.不过在就操作日志前需要构建日志的基础部件

    日志对象

    import java.util.Date;
    
    /**
     * 操作日志
     */
    public class OperationLog extends Base {
    
        private static final long serialVersionUID = 1L;
    
        /**
         * 日志类型
         */
        private String  logtype;
        /**
         * 日志名称
         */
        private String  logname;
        /**
         * 用户id
         */
        private Integer userid;
        /**
         * 类名称
         */
        private String  classname;
        /**
         * 方法名称
         */
        private String  method;
        /**
         * 创建时间
         */
        private Date    createtime;
        /**
         * 是否成功
         */
        private String  succeed;
        /**
         * 备注
         */
        private String  message;
    
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getLogtype() {
            return logtype;
        }
    
        public void setLogtype(String logtype) {
            this.logtype = logtype;
        }
    
        public String getLogname() {
            return logname;
        }
    
        public void setLogname(String logname) {
            this.logname = logname;
        }
    
        public Integer getUserid() {
            return userid;
        }
    
        public void setUserid(Integer userid) {
            this.userid = userid;
        }
    
        public String getClassname() {
            return classname;
        }
    
        public void setClassname(String classname) {
            this.classname = classname;
        }
    
        public String getMethod() {
            return method;
        }
    
        public void setMethod(String method) {
            this.method = method;
        }
    
        public Date getCreatetime() {
            return createtime;
        }
    
        public void setCreatetime(Date createtime) {
            this.createtime = createtime;
        }
    
        public String getSucceed() {
            return succeed;
        }
    
        public void setSucceed(String succeed) {
            this.succeed = succeed;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    

    日志对象创建工厂

    import com.stylefeng.guns.common.constant.state.LogSucceed;
    import com.stylefeng.guns.common.constant.state.LogType;
    import com.stylefeng.guns.common.persistence.model.LoginLog;
    import com.stylefeng.guns.common.persistence.model.OperationLog;
    
    import java.util.Date;
    
    /**
     * 日志对象创建工厂
     */
    public class LogFactory {
    
        /**
         * 创建操作日志
         */
        public static OperationLog createOperationLog(LogType logType, Integer userId, String bussinessName, String clazzName, String methodName, String msg, LogSucceed succeed) {
            OperationLog operationLog = new OperationLog();
            operationLog.setLogtype(logType.getMessage());
            operationLog.setLogname(bussinessName);
            operationLog.setUserid(userId);
            operationLog.setClassname(clazzName);
            operationLog.setMethod(methodName);
            operationLog.setCreatetime(new Date());
            operationLog.setSucceed(succeed.getMessage());
            operationLog.setMessage(msg);
            return operationLog;
        }
    
        /**
         * 创建登录日志
         */
        public static LoginLog createLoginLog(LogType logType, Integer userId, String msg,String ip) {
            LoginLog loginLog = new LoginLog();
            loginLog.setLogname(logType.getMessage());
            loginLog.setUserid(userId);
            loginLog.setCreatetime(new Date());
            loginLog.setSucceed(LogSucceed.SUCCESS.getMessage());
            loginLog.setIp(ip);
            loginLog.setMessage(msg);
            return loginLog;
        }
    }
    

    日志任务创建工厂

    日志任务创建工厂的作用是将日志记录存储到数据库中

    import com.stylefeng.guns.common.constant.state.LogSucceed;
    import com.stylefeng.guns.common.constant.state.LogType;
    import com.stylefeng.guns.common.persistence.dao.LoginLogMapper;
    import com.stylefeng.guns.common.persistence.dao.OperationLogMapper;
    import com.stylefeng.guns.common.persistence.model.LoginLog;
    import com.stylefeng.guns.common.persistence.model.OperationLog;
    import com.stylefeng.guns.core.log.LogManager;
    import com.stylefeng.guns.core.util.SpringContextHolder;
    import com.stylefeng.guns.core.util.ToolUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.TimerTask;
    
    /**
     * 日志操作任务创建工厂
     */
    public class LogTaskFactory {
    
        private static Logger logger  = LoggerFactory.getLogger(LogManager.class);
    	//LoginLogMapper记录登录登出日志
        private static LoginLogMapper loginLogMapper = SpringContextHolder.getBean(LoginLogMapper.class);
    	//OperationLogMapper记录操作日志	    
    	private static OperationLogMapper operationLogMapper = SpringContextHolder.getBean(OperationLogMapper.class);
    
        public static TimerTask loginLog(final Integer userId, final String ip) {
            return new TimerTask() {
                @Override
                public void run() {
                    try {
                        LoginLog loginLog = LogFactory.createLoginLog(LogType.LOGIN, userId, null, ip);
                        loginLogMapper.insert(loginLog);
                    } catch (Exception e) {
                        logger.error("创建登录日志异常!", e);
                    }
                }
            };
        }
    
        public static TimerTask loginLog(final String username, final String msg, final String ip) {
            return new TimerTask() {
                @Override
                public void run() {
                    LoginLog loginLog = LogFactory.createLoginLog(
                            LogType.LOGIN_FAIL, null, "账号:" + username + "," + msg, ip);
                    try {
                        loginLogMapper.insert(loginLog);
                    } catch (Exception e) {
                        logger.error("创建登录失败异常!", e);
                    }
                }
            };
        }
    
        public static TimerTask exitLog(final Integer userId, final String ip) {
            return new TimerTask() {
                @Override
                public void run() {
                    LoginLog loginLog = LogFactory.createLoginLog(LogType.EXIT, userId, null,ip);
                    try {
                        loginLogMapper.insert(loginLog);
                    } catch (Exception e) {
                        logger.error("创建退出日志异常!", e);
                    }
                }
            };
        }
    
        public static TimerTask bussinessLog(final Integer userId, final String bussinessName, final String clazzName, final String methodName, final String msg) {
            return new TimerTask() {
                @Override
                public void run() {
                    OperationLog operationLog = LogFactory.createOperationLog(
                            LogType.BUSSINESS, userId, bussinessName, clazzName, methodName, msg, LogSucceed.SUCCESS);
                    try {
                        operationLogMapper.insert(operationLog);
                    } catch (Exception e) {
                        logger.error("创建业务日志异常!", e);
                    }
                }
            };
        }
    
        public static TimerTask exceptionLog(final Integer userId, final Exception exception) {
            return new TimerTask() {
                @Override
                public void run() {
                    String msg = ToolUtil.getExceptionMsg(exception);
                    OperationLog operationLog = LogFactory.createOperationLog(
                            LogType.EXCEPTION, userId, "", null, null, msg, LogSucceed.FAIL);
                    try {
                        operationLogMapper.insert(operationLog);
                    } catch (Exception e) {
                        logger.error("创建异常日志异常!", e);
                    }
                }
            };
        }
    }
    

    记录操作日志

    这一步是最关键的一环

    原理:通过自定义的注解@BussinessLog(可以任意命名),里面定义了业务的名称,被修改的实体的唯一标识,字典(用于查找key的中文名称和字段的中文名称),然后通过AOP,拦截所有添加了@BussinessLog注解的方法,解析其注解里面的属性,然后记录到对应的操作日志表中,完成操作日志的记录

    @BussinessLog

    import java.lang.annotation.*;
    
    /**
     * 标记需要做业务日志的方法
     */
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface BussinessLog {
    
        /**
         * 业务的名称,例如:"修改菜单"
         */
        String value() default "";
    
        /**
         * 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id"
         */
        String key() default "id";
    
        /**
         * 字典(用于查找key的中文名称和字段的中文名称)
         */
        String dict() default "SystemDict";
    }
    

    @BussinessLog注解拦截AOP

    import com.stylefeng.guns.common.annotion.log.BussinessLog;
    import com.stylefeng.guns.common.constant.dictmap.base.AbstractDictMap;
    import com.stylefeng.guns.common.constant.dictmap.factory.DictMapFactory;
    import com.stylefeng.guns.core.log.LogManager;
    import com.stylefeng.guns.core.log.LogObjectHolder;
    import com.stylefeng.guns.core.log.factory.LogTaskFactory;
    import com.stylefeng.guns.core.shiro.ShiroKit;
    import com.stylefeng.guns.core.shiro.ShiroUser;
    import com.stylefeng.guns.core.support.HttpKit;
    import com.stylefeng.guns.core.util.Contrast;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.Map;
    
    /**
     * 日志记录
     */
    @Aspect
    @Component
    public class LogAop {
    
        private Logger log = LoggerFactory.getLogger(this.getClass());
    
        @Pointcut(value = "@annotation(com.stylefeng.guns.common.annotion.log.BussinessLog)")
        public void cutService() {
        }
    
        @Around("cutService()")
        public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
    
            //先执行业务
            Object result = point.proceed();
    
            try {
                handle(point);
            } catch (Exception e) {
                log.error("日志记录出错!", e);
            }
    
            return result;
        }
    
        private void handle(ProceedingJoinPoint point) throws Exception {
    
            //获取拦截的方法名
            Signature sig = point.getSignature();
            MethodSignature msig = null;
            if (!(sig instanceof MethodSignature)) {
                throw new IllegalArgumentException("该注解只能用于方法");
            }
            msig = (MethodSignature) sig;
            Object target = point.getTarget();
            Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
            String methodName = currentMethod.getName();
    
            //如果当前用户未登录,不做日志
            ShiroUser user = ShiroKit.getUser();
            if (null == user) {
                return;
            }
    
            //获取拦截方法的参数
            String className = point.getTarget().getClass().getName();
            Object[] params = point.getArgs();
    
            //获取操作名称
            BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
            String bussinessName = annotation.value();
            String key = annotation.key();
            String dictClass = annotation.dict();
    
            StringBuilder sb = new StringBuilder();
            for (Object param : params) {
                sb.append(param);
                sb.append(" & ");
            }
    
            //如果涉及到修改,比对变化
            String msg;
            if (bussinessName.indexOf("修改") != -1 || bussinessName.indexOf("编辑") != -1) {
                Object obj1 = LogObjectHolder.me().get();
                Map<String, String> obj2 = HttpKit.getRequestParameters();
                msg = Contrast.contrastObj(dictClass, key, obj1, obj2);
            } else {
                Map<String, String> parameters = HttpKit.getRequestParameters();
                AbstractDictMap dictMap = DictMapFactory.createDictMap(dictClass);
                msg = Contrast.parseMutiKey(dictMap,key,parameters);
            }
    
            LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg));
        }
    }
    

    @BussinessLog使用实例

    /**
     * 新增字典
     @param dictValues 格式例如   "1:启用;2:禁用;3:冻结"
     */
    @BussinessLog(value = "添加字典记录", key = "dictName,dictValues", dict = com.stylefeng.guns.common.constant.Dict.DictMap)
    @RequestMapping(value = "/add")
    @Permission(Const.ADMIN_NAME)
    @ResponseBody
    public Object add(String dictName, String dictValues) {
        if (ToolUtil.isOneEmpty(dictName, dictValues)) {
            throw new BussinessException(BizExceptionEnum.REQUEST_NULL);
        }
        dictService.addDict(dictName, dictValues);
        return SUCCESS_TIP;
    }
    

    3.记录异常日志

    记录异常日志其实也是一个重复式的过程,这也可以通过统一的处理来记录异常抛出的日志

    import com.stylefeng.guns.common.constant.tips.ErrorTip;
    import com.stylefeng.guns.common.exception.BizExceptionEnum;
    import com.stylefeng.guns.common.exception.BussinessException;
    import com.stylefeng.guns.common.exception.InvalidKaptchaException;
    import com.stylefeng.guns.core.log.LogManager;
    import com.stylefeng.guns.core.log.factory.LogTaskFactory;
    import com.stylefeng.guns.core.shiro.ShiroKit;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.CredentialsException;
    import org.apache.shiro.authc.DisabledAccountException;
    import org.apache.shiro.session.InvalidSessionException;
    import org.apache.shiro.session.UnknownSessionException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.HttpStatus;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.UndeclaredThrowableException;
    
    import static com.stylefeng.guns.core.support.HttpKit.getIp;
    import static com.stylefeng.guns.core.support.HttpKit.getRequest;
    
    /**
     * 全局的的异常拦截器(拦截所有的控制器)(带有@RequestMapping注解的方法上都会拦截)
     */
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        private Logger log = LoggerFactory.getLogger(this.getClass());
    
        /**
         * 拦截业务异常
         */
        @ExceptionHandler(BussinessException.class)
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ResponseBody
        public ErrorTip notFount(BussinessException e) {
            LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e));
            getRequest().setAttribute("tip", e.getMessage());
            log.error("业务异常:", e);
            return new ErrorTip(e.getCode(), e.getMessage());
        }
    
        /**
         * 用户未登录
         */
        @ExceptionHandler(AuthenticationException.class)
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public String unAuth(AuthenticationException e) {
            log.error("用户未登陆:", e);
            return "/login.html";
        }
    
        /**
         * 账号被冻结
         */
        @ExceptionHandler(DisabledAccountException.class)
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public String accountLocked(DisabledAccountException e, Model model) {
            String username = getRequest().getParameter("username");
            LogManager.me().executeLog(LogTaskFactory.loginLog(username, "账号被冻结", getIp()));
            model.addAttribute("tips", "账号被冻结");
            return "/login.html";
        }
    
        /**
         * 账号密码错误
         */
        @ExceptionHandler(CredentialsException.class)
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public String credentials(CredentialsException e, Model model) {
            String username = getRequest().getParameter("username");
            LogManager.me().executeLog(LogTaskFactory.loginLog(username, "账号密码错误", getIp()));
            model.addAttribute("tips", "账号密码错误");
            return "/login.html";
        }
    
        /**
         * 验证码错误
         */
        @ExceptionHandler(InvalidKaptchaException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public String credentials(InvalidKaptchaException e, Model model) {
            String username = getRequest().getParameter("username");
            LogManager.me().executeLog(LogTaskFactory.loginLog(username, "验证码错误", getIp()));
            model.addAttribute("tips", "验证码错误");
            return "/login.html";
        }
    
        /**
         * 无权访问该资源
         */
        @ExceptionHandler(UndeclaredThrowableException.class)
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        @ResponseBody
        public ErrorTip credentials(UndeclaredThrowableException e) {
            getRequest().setAttribute("tip", "权限异常");
            log.error("权限异常!", e);
            return new ErrorTip(BizExceptionEnum.NO_PERMITION);
        }
    
        /**
         * 拦截未知的运行时异常
         */
        @ExceptionHandler(RuntimeException.class)
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ResponseBody
        public ErrorTip notFount(RuntimeException e) {
            LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e));
            getRequest().setAttribute("tip", "服务器未知运行时异常");
            log.error("运行时异常:", e);
            return new ErrorTip(BizExceptionEnum.SERVER_ERROR);
        }
    
        /**
         * session失效的异常拦截
         */
        @ExceptionHandler(InvalidSessionException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public String sessionTimeout(InvalidSessionException e, Model model, HttpServletRequest request, HttpServletResponse response) {
            model.addAttribute("tips", "session超时");
            assertAjax(request, response);
            return "/login.html";
        }
    
        /**
         * session异常
         */
        @ExceptionHandler(UnknownSessionException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public String sessionTimeout(UnknownSessionException e, Model model, HttpServletRequest request, HttpServletResponse response) {
            model.addAttribute("tips", "session超时");
            assertAjax(request, response);
            return "/login.html";
        }
    
        private void assertAjax(HttpServletRequest request, HttpServletResponse response) {
            if (request.getHeader("x-requested-with") != null
                    && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) {
                //如果是ajax请求响应头会有,x-requested-with
                response.setHeader("sessionstatus", "timeout");//在响应头设置session状态
            }
        }
    
    }
    

    项目地址:https://gitee.com/Selegant/logs-demo.git

    展开全文
  • Java右键弹出菜单源码

    2021-03-16 07:06:38
    摘要:Java源码,窗体界面,右键菜单,弹出菜单 Java右键弹出菜单,用Java制作一个窗体菜单,挺简洁的那种菜单,实现步骤:实例化弹出菜单、初始化数组、增加菜单项到菜单上、菜单项事件处理、窗口的鼠标事件处理、调用...
  • java使用SVN详细步骤

    千次阅读 2018-06-15 19:54:13
     将SVA服务器安装完毕a) 启动SVN服务器--开始菜单—找到SVN—单击b) 创建一个数据仓库—Create New Repository(注意访问权限读和写)c) 创建用户—在创建好的数据仓库上面单击右键—所有任务—Manage Securi...
  • 主要介绍了Java实现微信公众号自定义菜单的创建方法,结合实例形式分析了java创建微信公众号自定义菜单的具体步骤、实现方法及相关操作注意事项,需要的朋友可以参考下
  • 1. 菜单栏、菜单菜单项之间的关系:  1) 菜单栏(MenuBar)就是窗口中常见的顶层菜单栏,包含文件、编辑、格式等等子菜单菜单条,即包含菜单的容器;  2) 菜单(Menu)是必定包含菜单项或者菜单(嵌套包含菜单...
  • 用eclipse创建JAVA程序的步骤

    千次阅读 2020-02-28 23:27:01
    一般分为如下4个步骤: 一、创建Java项目 二、创建程序包 三、编写Java源程序 四、运行Java程序 1、创建Java 项目 1.1打开eclipse 1.2 点击顶部菜单栏 File -> New - > Java Project ,输入项目名 2、创建...
  • 菜单File->New Project可进入如图界面,首先选择左边栏Maven,再配置JDK(一般如果之前添加了JDK的话会自动填充,如未添加的话点击旁边的New将JDK目录导入即可)。勾选"Create from archetype",然后选中maven-...
  • 本人活跃在开源中国 博客地址https://my.oschina.net/xshuai/blog/726459 2019年12月2日 最新的JS-API接入示例代码(SpringBoot+... 微信JSSDKJava版接入--步骤及问题处理和解决 可以关注测试微信号,查看效果...
  • 菜单项的窗口: 具体使用方法见代码及注释...Test_Menu.java public class Test_Menu { public static void main(String args[]) { Menu menu = new Menu("菜单"); menu.setBounds(10, 10, 300, 200); } } Menu.
  • Java递归实现多级菜单实现

    千次阅读 多人点赞 2020-02-26 21:59:52
    像这种需求,一般很简单的来说就是用递归实现了,可以从第一级一直往下查,一直查询到为空为止。 二、数据库表结构 这里小编之列出来几个基本的字段,但是够用了 CREATE TABLE `mall_categ...
  • (1)创建Java项目:“FileàNewàProjectàJava Project” (2)创建Java源程序:“FileàNewàOtheràClass” (3)编译Java源程序:“ProjectàBuild Project” (4)运行Java程序:使用“Run
  • java学习实践:eclipse安装步骤

    千次阅读 2013-03-13 15:15:12
    安装环境: OS:Oracle linux 5.6 Eclipse版本:eclipse-java-helios-SR2-linux-gtk-...Eclipse安装步骤: 1.下载eclipse-java-helios-SR2-linux-gtk-x86_64.tar.gz 下载地址: http://mirror.bjtu.edu.
  • Java抽象语法AST浅析与使用

    万次阅读 2019-10-30 10:51:02
    Java抽象语法AST浅析与使用概述作用Java项目模型对象AST模型对象AST试图具体使用 概述 抽象语法(Abstract Syntax Tree, AST)是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的结构,的每个节点...
  • IDEA编译java类的步骤

    万次阅读 2018-07-24 14:57:58
    在做javaEE项目时,因业务需要,需要把一个或者多个类编译后的.calss文件上传到服务器上运行,这个时候就需要对写好的类进行编译。...4.然后在菜单栏中的build中选择build Artifacts 5.然后为jar包建立bui...
  • java AWT PopupMenu(右键菜单)

    千次阅读 2017-10-08 18:08:10
    右键菜单使用PopupMenu对象表示,创建步骤如下: (1)创建PopupMenu的实例。 (2)创建多个MenuItem的多个实例,依次将这些实例加入到PopupMenu中。 (3)将PopupMenu加入到目标组件中。 (4)为需要出现上下文...
  • 《使用 Idea 远程断点调试 Weblogic 服务器的操作步骤》详细介绍了如何使用 Idea 通过本地源代码对远程 Weblogic 服务器进行 debug 调试的操作步骤。本文将对远程服务器/客户端任意 java -cp/-jar 命令启动进程进行 ...
  • 鉴于部分同学部署ofcms在idea存在问题,故编写此文档简述部署步骤。如有出入,望请指正。 项目地址:https://gitee.com/oufu/ofcms  如有问题加QQ群: ①185948055 从码云导入项目到idea File》New》Project ...
  • 文章目录zTree介绍zTree形组件入门使用1. 将下载好的zTree文件的css、js文件拷贝到项目的web模块中去。... 主要参数介绍**setting、zTree **4. 一个显示形图的案例jsp5....专门适合项目开发,尤其是树状菜单、树状数据
  • 使用IntelliJ IDEA,gradle开发Java web应用步骤 最近 正在学习gradle构建工具的使用,看了一堆的文档,有点一知半解,索性动作实践一把,在以后的自己的项目中尝试使用看看。目前手头用的是IntelliJ IDEA 14,搭建了...
  • 使用Java添加系统右键菜单

    千次阅读 2010-03-28 22:12:00
    注:ICE_JNIRegistry.dll 和registry.jar这两个文件可在我的资源中下载 如果你在系统中需要经常使用DOS进入某个文件夹,...比如说右击一个文件夹,将出现一个菜单项为“命令提示符”,单击此菜单项则会直接进入DOS,
  • java项目部署到阿里云服务器步骤

    万次阅读 多人点赞 2018-07-25 11:51:31
    阿里云服务器详细步骤 一:什么是云服务器ECS  是阿里云产品体系中,最基础的计算服务,通常用作应用程序的运行环境,最重要的特点是弹性。 二:基础运行环境  用户的应用程序运行在实例的操作系统上 三:特点 ...
  • Java面试题大全(2020版)

    万次阅读 多人点赞 2019-11-26 11:59:06
    发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~ 本套Java面试题大全,全的不能再全,哈哈~ 一、Java 基础 1. JDK 和 JRE 有什么区别? JDK:Java ...
  • Java调用MATLAB程序--环境配置与步骤

    千次阅读 2015-03-20 20:34:38
    环境配置与步骤 By 林伟东 ustc&xidian Java做界面,核心算法是用matlab,所以必须调用matlab代码。因此要进行这个Java-matlab混合编程的环境配置。
  • 这篇文章对 MouseEvent 想说的主要是关于鼠标右键弹出菜单的一些体会 关于MouseEvent的一些信息 事件名称:MouseEvent 事件监听接口:MouseListener 需要注意的是,这个接口有五个方法,分别响应 1.按下 2....
  • 根据网上零星的相关教程、步骤,自己摸索了一番,发现用MyEclipse10画UML用例图并生成JAVA代码也不是很复杂,所以在此记录并分享给大家! 实例是设计模式中的:简单工厂模式之简单电视机工厂
  • 牛逼!Java 从入门到精通,超全汇总版

    万次阅读 多人点赞 2021-05-06 19:40:33
    文章目录Java 基础Head First JavaJava 核心技术卷一Java 编程思想设计模式Head First 设计模式图解设计模式设计模式重学 Java 设计模式Java 进阶Java 并发编程实战Java 并发编程艺术Java 并发编程之美图解Java多...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,465
精华内容 30,986
关键字:

java菜单树查询步骤

java 订阅