【问题标题】:Eliminate hardcoded file paths and urls in web development消除 Web 开发中的硬编码文件路径和 url
【发布时间】:2013-01-14 12:27:58
【问题描述】:

我目前正在使用 servlet 和 spring 框架开发一个网站。像往常一样,它包含很多文件(jsp、js、css、图像、各种资源等)。 我试图避免在任何文件中写入任何硬编码路径或域...

例如,您可能知道在处理请求时会将其“转发”到 jsp 页面(其路径可能会被硬编码)。其他示例是在 jsp 文件中导入 images/css/js 等...

是否有任何通用方法(或工具)来避免硬编码路径/url,因此任何重构都不会造成麻烦?

编辑
我使用 netbeans 7.1.2 ... 不幸的是 netbeans 只对纯 Java 代码有帮助。当使用 jsp 文件时,事情是有限的,如果你添加自定义标签文件和 Jsp 2.0 EL 就像在控制台模式下编程:p

【问题讨论】:

  • 你可以在 Eclipse 中使用Source -> Externalize strings
  • @madhead 我更新了我的帖子...我是 netbeans 的粉丝 :)

标签: java file jsp path hardcoded


【解决方案1】:

在 JSP 文件本身中,您可以使用 JSTL 避免几乎所有硬编码的域/网址

例如,当创建指向另一个页面的链接时,您会这样做:

<a href="<c:url value="/referrals/send.html"/>" target="_blank">Refer an Entrepreneur!</a>

这意味着,无论您的 web 应用程序在哪里,链接都将始终具有正确的 url。例如,在我的开发框中,此链接将是:

http://localhost:8080/accounts/referrals/send.html

但在我的生产服务器上,它正确解析为:

http://wwww.mydomain.com/referrals/send.html

您可以看到,在我的开发服务器中,webapp 上下文位于 /accounts 下,但在生产机器上,它位于 / 下,因为 webapp 位于根上下文下。

可以看小教程here

【讨论】:

  • 几周前我已经检查过 JSTL ......当然有一个收获,因为你的路径是相对的,但“/referrals/send.html”仍然是硬编码的......如果由于某种原因你重命名/重构 send.html 你将在你的应用程序中运行你的 jsp 页面......我使用它,但它并没有解决主要问题......
【解决方案2】:

属性文件始终是一个不错的选择,因此您只需在某一点进行更改(如果有的话)。

【讨论】:

    【解决方案3】:

    如果您要引用任何静态内容(js、图像、css 等),则不必对整个文件路径进行硬编码。相反,您可以这样做:-

    <img src="${pageContext.request.contextPath}/resources/images/test.jpg"/>
    

    其余的文件路径(Hibernate 域映射、Spring 控制器中的转发页面等)应该与您的项目结构相关,并且大多数 IDE 都足够聪明,可以毫无问题地重构它们……或者至少在我的情况下,IntelliJ 似乎为我处理了所有这些。

    在某些时候,您需要问自己,多少硬编码是可接受的,多少是不可接受的?此外,我不会试图偏离 Spring/Hibernate 推荐的解决方案太远。如果你把所有的东西都弄得太抽象,你就会有不同的问题要处理,这对其他可能在未来继承你的项目的同行来说会适得其反。

    【讨论】:

    • “如果你把所有的东西都搞得太抽象,你就会有不同的问题要处理”……什么样的问题?你能说得更具体点吗?
    • 您需要问自己的问题是,如果您将来将项目交给其他人,他们是否能够维护您的聪明的本土解决方案?您要物理更改文件名和位置多少次以证明这种本土解决方案的合理性? 5%的时间? 90%的时间?你的同行能理解你在这里实现了什么吗? 3个月后你能看懂自己的实现代码吗?
    • 文件名、url 和位置经常更改,因为项目正在开发中......我总是添加、删除、重构周围的东西......我猜随着项目的发展,事情会变得更加“稳定” ...我的实现基本上是将我的所有路径作为一个 .java 文件中的 java 变量,而不是 xmls 和属性,因为 java 变量很容易在 IDE 中处理/维护/重构。我不认为有人很难猜出我在做什么......
    【解决方案4】:

    其实我只是想出了一个主意。由于 netbeans 会分析并显示对 java 代码的依赖关系,因此将所有路径和域作为 java 变量处理可能会更好。

    我在我的项目上创建了一个名为 FileResolver 的包,在里面我为我的项目中的每种文件类型创建了一个类(例如,一个类用于 Jsp 文件,一个类用于 Css 文件等)。在这些文件中,我将在公共静态最终字符串变量中记录和硬编码所有文件的所有路径。示例:

    public class Jsps {
    
        public class layouts{
            public static final String main =       "layouts/main.jsp";
        }
    
        public class pages{
            public static final String error =      "pages/error.jsp";
            public static final String login =      "pages/login.jsp";
            public static final String register =   "pages/register.jsp";
        }
        ...
    }
    

    在我的整个项目中,我应该使用变量而不是路径。然后,每当我重构一个文件时,我只有一个文件要更改,那就是这些变量中的映射值...... 如果某些时候我需要更改变量,netbeans 将立即重构项目中的所有变量...... 我认为这会很好地工作,因为我让我的项目与文件路径无关,我唯一需要担心的是该文件中的变量映射到适当的文件路径。

    编辑
    我将编写一个简单的解析器来创建这些 java 文件,而不是为所有文件手动编写...完成后我会更新


    更新
    这是我的 FileResolverGenerator

    public class FileResolverGenerator {
    
        private static final String newFilePath = "C:/Users/Foo/Desktop/Jsps.java";
        private static final String scanRootFolder = "C:/Users/Foo/Desktop/myProject/web/WEB-INF/jsp";
    
        private static final String varValueReplaceSource = "C:/Users/Foo/Desktop/myProject/web/WEB-INF/jsp/";
        private static final String varValueReplaceTarget = "";
    
        private static final boolean valueAlign = true;
        private static final int varNameSpaces = 15;
    
    
        public static void main(String[] args){
            try {
                // Create file and a writer
                File f = new File(newFilePath);
                f.createNewFile();
                bw = new BufferedWriter( new FileWriter(f) );
                // Execute
                filesParser( new File(scanRootFolder) );
                // 'Burn' file
                bw.close();
            } catch (FileNotFoundException ex) {
                Logger.getLogger(ResolverGenerator.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(ResolverGenerator.class.getName()).log(Level.SEVERE, null, ex);
            }
    
        }
    
        // ================================================================================================ //
        // ============================================= WORK ============================================= //
        // ================================================================================================ //
    
        private static void filesParser(File rootFolder) throws FileNotFoundException, IOException{
    
            folderIn(rootFolder);
            // Files first
            if(!rootFolder.exists()) throw new FileNotFoundException();
            for(File f : rootFolder.listFiles()){
                if(f==null){ return; }
                if(f.isDirectory()){ continue; }
                else if(f.isFile()){ writeFileVariable(f); }
            }
            // Folders next
            for(File f : rootFolder.listFiles()){
                if(f==null){ return; }
                if(f.isDirectory()){ filesParser(f); }
                else if(f.isFile()){ continue; }
            }
            folderOut(rootFolder);
        }
    
    
        // ================================================================================================ //
        // ============================================ PRINTS ============================================ //
        // ================================================================================================ //
        private static BufferedWriter bw;
        private static int tabCount = 0;
    
    
        private static void folderIn(File f) throws IOException{
            bw.append("\n\n");
            for(int i=0; i<tabCount; i++)
                bw.append("\t");
            bw.append("public class "+f.getName()+"{\n");
            tabCount++;
        }
    
        private static void folderOut(File f) throws IOException{
            tabCount--;
            for(int i=0; i<tabCount; i++)
                bw.append("\t");
            bw.append("}\n");
        }
    
        private static void writeFileVariable(File f) throws IOException{
            String varName = f.getName().split("\\.")[0].replaceAll("-", "");
            String varValue = f.getPath().replaceAll("\\\\","/")
               .replace(varValueReplaceSource.replaceAll("\\\\","/"),varValueReplaceTarget.replaceAll("\\\\","/"));
    
            for(int i=0; i<tabCount; i++)
                bw.append("\t");
            bw.append("public static final String "+varName+" = ");
            if(valueAlign){
                for(int i=0; i<varNameSpaces-varName.length(); i++) bw.append(" ");
                bw.append("\t"); }
            bw.append("\""+varValue+"\";\n");
        }
    
    
    }
    

    具体一点...这会扫描“/WEB-INF/jsp/”下的所有文件并创建一个 java 文件,其中所有 jsp 文件“注册”到每个路径的公共静态最终字符串变量...这个想法是使用生成的 java 文件作为项目中所有 jsps 的参考...始终使用这些变量而不是硬编码路径.. 这与项目或任何项目无关。它只是一个拯救你的工具 时间,而不是手动为项目中的每个文件执行此操作。

    我还创建了另一个类 ResolverConsistencyChecker,它获取所有变量并检查文件路径是否正确(文件存在)......因为我们没有对文件名和文件路径进行任何更改,所以所有测试都通过了。
    在测试项目的“错误”时应该运行此方法

    public class ResolverConsistencyChecker {
    
        private static Class checkClass = Jsps.class;
        private static String fullPathPrefix = "C:/Users/Foo/Desktop/myProject/web/WEB-INF/jsp/";
    
    
        public static void main(String[] args){
            try {
                filesChecker( checkClass );
                System.out.println( "Tests passed. All files locations are valid" );
            } catch (FileNotFoundException ex) {
                Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
            }
    
        }
    
        // ================================================================================================ //
        // ============================================= WORK ============================================= //
        // ================================================================================================ //
    
        private static void filesChecker(Class rootClass) throws FileNotFoundException, IOException{
    
            // Check file paths in current class depth
            for(Field f : rootClass.getFields()){
                try {
                    String fullFilePath = fullPathPrefix+f.get(f.getName()).toString();
                    File file = new File( fullFilePath );
                    if( !file.exists() )
                        throw new FileNotFoundException("Variable: '"+f.getName()+"'\nFile "+fullFilePath);
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger(ResolverConsistencyChecker.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            // Check for embedded classes
            for(Class c : rootClass.getClasses()){
                filesChecker(c);
            }
        }
    
    }
    

    【讨论】:

    • 看起来我试图接近 android SDK 在 eclipse 上处理资源的方式。 “res”文件夹下的每个文件都会自动在 R.java 文件中生成一个变量 :)
    猜你喜欢
    • 1970-01-01
    • 2014-12-13
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 2012-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多