博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【斗医】【4】Web应用开发20天
阅读量:6543 次
发布时间:2019-06-24

本文共 16403 字,大约阅读时间需要 54 分钟。

在上面把日志文件打印到了D:\log下,考虑到Liunx服务器环境下,让最终用户修改可能不可接受,接下来完成三件事情:(1)应用程序指定输出路径(2)完善异常类的处理(3)完成页面跳转的封装处理

一、指定输出路径

由于Logback的<FILE>指定相对路径与Log4J存在差异,所以在修改日志输出路径之前,得让Eclipse能调试代码。

1、在下载Tomcat插件,下载时确认自已的JDK、Eclipse、Tomcat与该插件是否配套。我曾见同事使用的插件不配套,导致Eclipse不能启动Tomcat

2、假如Eclipse的路径为%Eclipse%,在%Eclipse%下新建links\tomcatplugin\features和links\tomcatplugin\plugins文件夹,把下载的插件解压到links\tomcatplugin\plugins下,其目录结构如图:

3、在%eclipse%\links\下创建tomcat.ini文件,填写如下内容:

path=D:\\eclipse\\links\\tomcatplugin

其中D:\\eclipse即为%eclipse%,读者视自己环境更改。

4、若Eclipse打开则请关闭,然后删除%eclipse%\configuration\org.eclipse.update目录,重启Eclipse后应该在工具栏上看到小猫咪的图标,表明加载成功:

5、在Eclipse配置Tomcat。依次选择“Window > Preferences > Tomcat”,其中“Tomcat Version”选择“Version 7.x”,“Tomcat home”选择Tomcat放置路径。在前面说过我把Tomcat放置到了D盘下,如图:

6、确认后点击小猫咪图标,应该能看到Tomcat在Eclisps的启动信息了

接下来我们要修改logconfig.xml中的日志输出路径了。由于要修改XML,所以我们创建一个FrameXmlUtil.java类,该类用于解析XML内容、读取结点的属性集合、保存XML内容等。

1、创建FrameXmlUtil,由于涉及代码较多这里只粘贴出代码构造,具体代码见附件。读者也可以自已写此工具类:

public class FrameXmlUtil{    /**     * XML文档     */    private Document xmlDocument = null;    /**     * XML的根元素     */    private Element rootElement = null;                                                                                                                                      /**     * 读取XML文件     */    public void readXmlFile(String filePath) throws FrameException    {        // 这里省略,具体见附件    }                                                                                                                                                                /**     * 获取结点属性集合     */    public Map
 getAttrs(Node node)    {         // 这里省略,具体见附件    }                                                                                                                                                               /**     * 保存XML     */    public void saveXML() throws FrameException    {         // 这里省略,具体见附件    }                                                                                                                                                                 /**     * 获取XML根元素     */    public Element getRootElement()    {        return rootElement;    }}


【备注】:关注解析XML在JAVA开发中有多种不同的解析方法,感兴趣的话可以在谷歌上搜索一下,有很多人写过这方面的贴子


2、 对FrameConfigUtil增加静态工厂方法modifyLogOutPath(),对D:\medical\war\etc\logconfig.xml中的<property name="LOG_HOME" value="D:\logs" />进行修改:

/** * 修改D:/medical/war/etc/logconfig.xml文件中的日志输出路径 */public static void modifyLogOutPath(ServletContext context) throws FrameException{    String webPath = context.getRealPath("/");    StringBuilder filePath = new StringBuilder(webPath);    filePath.append(File.separator).append("etc");    filePath.append(File.separator).append("logconfig.xml");                                                                                 // 载入D:\medical\war\etc\logconfig.xml文件    FrameXmlUtil xmlUtil = new FrameXmlUtil();    xmlUtil.readXmlFile(filePath.toString());                                                                                 // 初步判断文件的合法性    Element rootElement = xmlUtil.getRootElement();    if (rootElement == null)    {        return;    }                                                                                                                                                                       // 由logconfig.xml知日志输出路径宏定义就在根结点下,所以此处遍历根结点的孩子    NodeList childNodeList = rootElement.getChildNodes();    for (int index = 0; index < childNodeList.getLength(); index++)    {        Node childNode = childNodeList.item(index);        if (Node.ELEMENT_NODE != childNode.getNodeType())        {            continue;        }                                                                                            // 判断孩子结点是否为
        Element childElement = (Element) childNode;        String elementName = childElement.getNodeName();        String logHome = childElement.getAttribute("name");        if (FrameConstant.LOG_PROPERTY.equals(elementName) && FrameConstant.LOG_HOME.equals(logHome))        {            StringBuilder path = new StringBuilder(webPath);                      path.append(File.separator).append("var").append(File.separator).append("logs");                          childElement.setAttribute(FrameConstant.LOG_VALUE, path.toString());            break;         }    }                                                                                                                                                                 // 保存修改后的XML文件    xmlUtil.saveXML();}

3、若使用logback则在加载logconfig.xml之后调用FrameConfigUtil.modifyLogOutPath()方法,所以修改FrameLauncher的init()方法,修改后如下:

@Overridepublic void init() throws ServletException{    ServletContext context = getServletContext();    try    {        FrameConfigUtil.modifyLogOutPath(context);        FrameConfigUtil.initLogConfig(context);    }    catch (FrameException e)    {        throw new ServletException("[FrameLauncher] init error.", e);    }}

好了,在Eclipse中点击小猫咪图标启动Tomcat服务,在浏览器中输入http://localhost:8080/medical,理论上应该在D:\medical\war\var\logs下生成日志文件,但理想与现实之间往往存在差距。这是什么原因呢?细心观察在Tomcat目录下多出一个medicalwarvarlogs文件夹,里面的日志文件正是我们想要的!!

这说明什么?

说明我们修改logconfig.xml之后的<property name="LOG_HOME" value="D:\medical\war\var\logs"/>存在问题。

那是什么问题呢?想想什么是转义字符?\n是什么?\b是什么?明白了吧:value="D:\medical\war\var\logs"是错误的,应该对\再进行转义。修改FrameConfigUtil.modifyLogOutPath()方法中输出路径代码处增加如下处理:

String realPath = path.toString().replaceAll("\\\\", "/");              childElement.setAttribute(FrameConstant.LOG_VALUE, realPath);

再重启Tomcat服务,在浏览器中输入http://localhost:8080/medical,点击下图所示的超链接,此时会发现日志文件能按您的预期输出了。

二、完善异常类

在上面日志文件输出搞定之后,让我们回到第二天的异常类封装问题上,当时说异常的描述应该从中英文资源中读取,随着我们的想法自然地前行吧。

1、在D:\medical\war\etc\下新建local文件夹,然后在local下分别创建en、zh文件夹,再在en、zh下分别创建resource.properties文件,其目录结构如下:

2、向resource.properties中填写信息,由于我们这个斗医系统是给中国人看的,外国人不懂中医是什么,所以只填写zh下的resource.properties内容即可。这里暂时把前面所涉及的错误码填写上:

#UTF-8

1=创建XML生成器时异常

5=SAX解析XML文件时异常

10=解析XML时出现IO异常

15=解析XML时参数设置异常

20=生成XML翻译器失败

25=翻译XML失败

100=HTTP请求动作跳转异常

3、考虑到异常信息只要从一个地方读取,这里新建一个FrameCache.java文件,专门用于缓存全局数据

public class FrameCache{    private static FrameCache instance = new FrameCache();                                                                                                        /**     * 全局资源文件缓存     */    private Properties resourceProp = new Properties();                        private FrameCache()    {    }                                                               public static FrameCache getInstance()    {        return instance;    }                                                                                                                                                         public void setResourceProp(Properties resourceProp)    {        this.resourceProp = resourceProp;    }                                                                                                                                                        public String getResourceValue(String resourceKey)    {        return resourceProp.getProperty(resourceKey, resourceKey);    }}

4、在Servlet加载时需要把这些资源信息读入内存,这样才能根据错误码读取到异常描述信息,所以需要

(1)在FrameConfigUtil中定义loadResource(),用于加载Properties文件

public static void loadResource(ServletContext context) throws FrameException{    // 获取中文资源文件路径    StringBuilder resourcePath = new StringBuilder(context.getRealPath("/"));        resourcePath.append(File.separator).append("etc").append(File.separator).append("local");        resourcePath.append(File.separator).append("zh").append(File.separator).append("resource.properties");                                                                                                               // 具体加载动作    Properties resourceProp = new Properties();    try    {        InputStream in = new BufferedInputStream(new FileInputStream(resourcePath.toString()));        resourceProp.load(in);     }    catch (IOException e)    {        throw new FrameException(FrameErrorCode.Prop_ERROR_LOAD, e);    }                                                                                                    // 设置到全局缓存中    FrameCache.getInstance().setResourceProp(resourceProp);}

(2)在FrameLauncher的init()方法中调用loadResource()方法

ServletContext context = getServletContext();try{    // 加载logback配置    FrameConfigUtil.modifyLogOutPath(context);    FrameConfigUtil.initLogConfig(context);                                                                       // 加载中文资源配置    FrameConfigUtil.loadResource(context);}catch (FrameException e){    throw new ServletException("[FrameLauncher] init error.", e);}

5、为了测试,我们在FrameLauncher.doGet()方法中打印一句话

System.out.println(FrameCache.getInstance().getResourceValue("1"));

6、在Eclipse中启动Tomcat服务,在浏览器中输入http://localhost:8080/medical,点击“Test Logback”超链接,可以看到有类似如下输出:


【备注】:这里的乱码是由于字符的编码问题,在此处我们只要能加载即可,具体的可读性遗留到后面界面展示时处理。


7、完善FrameException读取异常描述的遗留,把原来标注“// errorDesc应该从中英文资源文件中根据errorCode读取”的地方,使用errorDesc = FrameCache.getInstance().getResourceValue(String.valueOf(errorCode));代替。

三、页面跳转封装

接下来我们做一件系统较为重要的事情:页面跳转封装。比如在系统导航菜单上点击了“话题”菜单项,那么希望系统能进入“话题”页面,站在纯HTML角度来看只需要<a>标签即可,但有时候还需要处理一些逻辑,所以此封装还是较有意义的。封装之后的XML类似如下:

    
        
        
    

从这个XML上很容易看出,这个业务是进入topic页面,在进入之前用户可以不登录,同时进入页面之前的逻辑部分由FrameTopic处理,无论处理结果如何都最终进入topic.html。下面封装FrameBusiness类来对应这个业务实体:

1、创建com.medical.frame.config.FrameForward类,它包括successPath(成功时跳转路径)、failurePath(失败时跳转路径),然后再对外提供get和set方法

public class FrameForward{    /**     * 成功跳转路径     */    private String successPath = null;                                                                        /**     * 失败跳转路径     */    private String failurePath = null;                                                                                             // 省略相关的get&set方法 }

2、创建com.medical.frame.config.FrameBusiness类,里面有name、mustLogin、businessClass和FrameForward对象,然后再对外提供get和set方法

public class FrameBusiness{    /**     * 业务名称     */    private String name = null;                                                                   /**     * 是否必须登录     */    private boolean mustLogin = false;                                                          /**     * 业务逻辑处理类     */    private String businessClass = null;                                                          /**     * 业务跳转路径     */    private FrameForward forward = null;                                                                                             // 省略相关的get&set方法}

3、假如系统有多个业务,我们不希望所有业务的配置都放到一个XML,同时不同业务的XML又希望按业务文件夹放置,如下:

(1)在运行环境D:\medical\war\WEB-INF下创建config文件夹

(2)在config下创建sm和test两个文件夹

(3)在sm和test下分别创建system-action.xml和test-action.xml

(4)在system-action.xml中填充如下内容:

    
        
            
                 
        
        
        
            
                    
        
    


【备注】:后面若无特殊说明,文件均以UTF-8编码


(5)的test-action.xml中填充如下内容:

    
        
            
                  
        
    

4、前面说过action这个Servlet,现在我们希望该Servlet在启动时把上面的业务配置文件(xxx-action.xml)里的业务配置加载到内存

(1)在FrameLauncher.init()方法中调用FrameConfigUtil.loadBusiness(),加载业务配置

public void init() throws ServletException{    ServletContext context = getServletContext();    try    {        // 加载logback配置        FrameConfigUtil.modifyLogOutPath(context);        FrameConfigUtil.initLogConfig(context);                                                   // 加载中文资源配置        FrameConfigUtil.loadResource(context);                                                      // 加载业务配置文件        FrameConfigUtil.loadBusiness(context);    }    catch (FrameException e)    {        throw new ServletException("[FrameLauncher] init error.", e);    }}

(2)在FrameConfigUtil中定义一个公共静态方法loadBusiness(),用于加载D:\medical\war\WEB-INF\config下的所有业务配置

public static void loadBusiness(ServletContext context) throws FrameException{    findActFile(context, "/WEB-INF/config");    parseBusiness(context);}

(3)上面的loadBusiness()方法中的findActFile()作用是查找出"/WEB-INF/config"下的所有以-action.xml结尾的文件,并把文件名保存到全局缓存中

I、在FrameCache中定义文件集合,并提供get()/add()方法

/** * 系统业务配置文件路径集合 */private Set
 busActFileSet = new HashSet
();public Set
 getBusinessActFile(){    return busActFileSet;}                                                                                                                              public void addBusinessActFile(String businessActFile){    this.busActFileSet.add(businessActFile);}

II、在FrameConfigUtil.findActFile()中查找-action.xml文件,并保存在全局缓存中

private static void findActFile(ServletContext context, String path){    Set
 businessSet = context.getResourcePaths(path);    if (businessSet == null)    {        return;    }    for (String item : businessSet)    {        if (item == null)        {            continue;        }        if (item.endsWith(FrameConstant.BUS_CONF_POSTFIX))        {            FrameCache.getInstance().addBusinessActFile(item);        }        else        {            findActFile(context, item);        }    }}

(4)FrameConfigUtil.parseBusiness()是真正的解析过程,也是本文的重点,所以请读者重点关注。下面把其逻辑分别列举出:

I、从全局缓存中读取XXX-action.xml文件集合,判断是否为空,若为空则返回

Set
 actionFileSet = FrameCache.getInstance().getBusinessActFile();if (actionFileSet == null || actionFileSet.isEmpty()){    return;}

II、若集合不为空,则把XXX-action.xml文件用FrameXMlUtil工具解析

// 遍历XXX-action.xml文件for (String fileName : actionFileSet){    InputSource in = null;    try    {        URL url = context.getResource(fileName);        in = new InputSource(url.toExternalForm());    }    catch (MalformedURLException e)    {        throw new FrameException(FrameErrorCode.XML_ERROR_GET_PATH);    }    try    {        FrameXmlUtil xmlUtil = new FrameXmlUtil();        xmlUtil.readXmlFile(in);        //.......    }    catch (FrameException e)    {        throw e;    }}

III、真正解析结点形成FrameBusiness对象,并缓存到全局变量中

FrameCache.java部分代码:/** * 系统配置业务 */private Map
 businessMap = new HashMap
();public FrameBusiness getBusiness(String businessName){    return businessMap.get(businessName);}                                                                                        public void addBusiness(FrameBusiness business){    businessMap.put(business.getName(), business);}

FrameConfigUtil.parseBusiness(ServletContext)部分代码:FrameXmlUtil xmlUtil = new FrameXmlUtil();xmlUtil.readXmlFile(in);Element rootElement = xmlUtil.getRootElement();NodeList busNodeList = rootElement.getElementsByTagName(FrameConstant.BUS_KEY);if (busNodeList == null || busNodeList.getLength() == 0){    continue;}// 开始解析for (int i = 0; i < busNodeList.getLength(); i++){    Node busNode = busNodeList.item(i);    Map
 busNodeAttributs = xmlUtil.getAttrs(busNode);    FrameBusiness business = new FrameBusiness();    business.setName(busNodeAttributs.get(FrameConstant.BUS_NAME));    business.setBusinessClass(busNodeAttributs.get(FrameConstant.BUS_CLASS));    String mustLogin = busNodeAttributs.get(FrameConstant.BUS_MUST_LOGIN);    mustLogin = mustLogin == null ? "false" : mustLogin;    business.setMustLogin("TRUE".equalsIgnoreCase(mustLogin));    // 解析
属性    NodeList forwardNodeList = ((Element) busNode).getElementsByTagName(FrameConstant.BUS_FORWARD);    if (forwardNodeList == null || forwardNodeList.getLength() != 1)    {        continue;    }    Element forwardElement = (Element) forwardNodeList.item(0);    NodeList pathNodeList = forwardElement.getElementsByTagName(FrameConstant.BUS_PATH);    if (pathNodeList == null || pathNodeList.getLength() == 0)    {        continue;    }    FrameForward forward = new FrameForward();    for (int j = 0; j < pathNodeList.getLength(); j++)    {        Node fowardNode = pathNodeList.item(j);        Map
 forwardAttrMap = xmlUtil.getAttrs(fowardNode);        String forwardName = forwardAttrMap.get(FrameConstant.BUS_FORWARD_NAME);        if (FrameConstant.BUS_FORWARD_SUCCESS.equals(forwardName))        {            forward.setSuccessPath(forwardAttrMap.get(FrameConstant.BUS_PATH));        }        else if (FrameConstant.BUS_FORWARD_FAILURE.equals(forwardName))        {            forward.setFailurePath(forwardAttrMap.get(FrameConstant.BUS_PATH));        }    }    business.setForward(forward);    // 添加到全局缓存中    FrameCache.getInstance().addBusiness(business);}


【备注】:上面的代码是该封装的关键代码,限于篇幅问题,这里只把较为关键的代码粘贴出,其中涉及FrameXmlUtil.readXmlFile(InputSource in)、FrameConstant接口常量值定义等都没有列出,具体可参见附件。

若读者有兴趣运行的话,强烈建议读者亲身写一下。


转载地址:http://qqado.baihongyu.com/

你可能感兴趣的文章
实战CGLib系列之proxy篇(一):方法拦截MethodInterceptor
查看>>
php 字符串截取
查看>>
ttcn-3
查看>>
00.java虚拟机的基本结构概念
查看>>
深入浅出 ES6:ES6 与 Babel - Broccoli 的联用
查看>>
ThreadLocal使用出现的问题
查看>>
关于十六进制和八进制负数的问题
查看>>
连接池并发的实现原理
查看>>
创建Pch预编译文件
查看>>
阿里云Centos配置iptables防火墙
查看>>
UML类图几种关系的总结
查看>>
PHP面试题汇总
查看>>
LeetCode (11): Container With Most Water
查看>>
【技巧】easyUI的datagrid,如何在翻页以后仍能记录被选中的行
查看>>
经过强制类型转换以后,变量a, b的值分别为( )short a = 128; byte b = (byte) a;
查看>>
ubuntu下msmtp+mutt的安装和配置
查看>>
spring中注解说明
查看>>
QLabel显示图片,图片可以自适应label的大小
查看>>
阅读下面程序,请回答如下问题:
查看>>
BZOJ3994:[SDOI2015]约数个数和——题解
查看>>