Использование базового загрузчика классов из сети и файлов в Java 1.1( Джек Харич ) Проблемы с загрузчиком классов? Вот вариант, написанный на Java 1.1, который работает через Интернет или с локальным файлом. Tips 'N Tricks
Один из наиболее частых вопросов по Java, с которыми я сталкиваюсь, это: «У меня проблемы с загрузкой классов через Интернет». По моему собственному опыту, когда я был должен наконец развернуть приложение в интранете, мои загрузчики классов не работали. Мои попытки обойти проблему, применяя встроенный RMIClassLoader , удались с апплетами на Java 1.0, но провалились с программами на Java 1.1. В своей колонке «Java in depth» в октябрьском номере JavaWorld за 1996 г. Чак Мак-Манис обсуждал основы загрузчиков классов Java в статье «The basics of Java class loaders» . Основанный на этой статье, данный совет по Java демонстрирует, как успешно использовать загрузчик классов Java 1.1, и идет намного дальше. Требования к загрузчику класса JavaДавайте сначала обсудим наши требования к загрузчику классов. Мы хотим, чтобы он:
Образец высокого уровняНаиболее гибкий подход к вышеупомянутым требованиям эти три класса: public abstract class MultiClassLoader extends ClassLoader public class URLClassLoader extends MultiClassLoader public class FileClassLoader extends MultiClassLoader Это позволяет определять тип загрузчика классов во время выполнения, и остальная часть приложения не затрагивается. Испытание модуляВыполнить перечисленные требования не кажется слишком сложным. Давайте начнём с примера использования, а именно примера кода того, как мы будем использовать MultiClassLoader . Это лучше сделать, создав испытательный класс TestLoader . Теперь напишем класс TestLoader для испытания модуля. Он показывает, как установить тип загрузчика классов во время выполнения, и затем использует его разными способами, чтобы показать, как эффективно использовать загрузчик классов. Вот исходный текст для TestLoader.java . Рассмотрим основную часть тестового кода: // Инициализация предпочтительного загрузчика классов MultiClassLoader loader = null; if (args.length == 0) { String root = "http://www.mindspring.com/~happyjac/"; loader = new URLClassLoader(root); } else { loader = new FileClassLoader("store\\"); } loader.setClassNameReplacementChar('_'); // ряд тестов: Class testClass = null; try { testClass = loader.loadClass("Hello"); } catch(Exception ex) { print("Load failed"); ex.printStackTrace(); return; } print("Loaded class " + testClass.getName() ); try { Runnable hello = (Runnable)testClass.newInstance(); hello.run(); } catch(Exception ex) { print("Failed to instantiate"); ex.printStackTrace(); } Хорошие тесты понятны и коротки. Этот тест сначала создает экземпляр загрузчика классов под именем «loader». Затем он использует вспомогательную функцию, чтобы перевести точки в «_». Затем мы решаем, производить ли загрузку из Интернета или из локального файла, в зависимости от того, был ли введен какой-либо параметр командной строки. Обратите внимание, что Вам нужно указать корень или filePrefix к URL источника, который вы используете как базу. Мы создали классы-потомки от MultiClassLoader , позволяющие получить различные специфичные источники классов. Затем мы пытаемся загрузить класс Hello и прерываем программу, если загрузка не удалась. Если выброшено исключение, это будет отображено в командной строке. Это сделано с помощью одной-единственной строчки кода, демонстрирующей преимущества архитектуры Java: testClass = loader.loadClass("Hello"); Пока что всё замечательно. Мы загрузили testClass . Теперь нам нужен экземпляр testClass , легко получаемый методом newInstance() . Но мы также должны преобразовать Object , возвращённый методом newInstance() , в класс, который известен загрузчику, загрузившему TestLoader . Это обычно первоначальный загрузчик классов. Один способ состоит в том, чтобы предоставить желательный интерфейс локально, например, EntryPoint.class . Но это противоречит стратегии тонкого клиента, значит, вместо этого мы преобразуем Object в Runnable , что обеспечивает Java core API. Все это сделано одной из наиболее мощных строк кода Java, которую Вы увидите: Runnable hello = (Runnable)testClass.newInstance(); Интерфейс Runnable имеет только один метод, run() , который мы затем выполняем hello.run(); Готово! Вот они: три важных строчки кода, а остальное делает Java и наш MultiClassLoader . Эта методика из трёх строк известна как «инициализация при загрузке на стороне клиента». Расслабьтесь и пока отдохните, три строчки кода тяжёлая работа. Распространённая ошибка состоит в том, чтобы преобразовать объект во что-нибудь типа EntryPoint , и когда это работает, считать, что всё в порядке. Вероятнее всего, это сработало, потому что базовый или иной загрузчик классов нашел вашу местную копию EntryPoint.class , используя CLASSPATH, когда был вызван метод findSystemClass() ; а Вы, вероятно, подумали, что он был загружен с другого места. Когда класс Hello начинает выполняться, он выполняет дополнительный тест, чтобы показать, что MultiClassLoader используется, чтобы загрузить «чужие» классы, на которые ссылается Hello , и что преобразование к чужим интерфейсам работает. Тестовые классы это Hello.java , Worker.java , и Person.java. Исследуем MultiClassLoaderДавайте исследуем конечный результат загрузки Hello.class с помощью нашего загрузчика классов. Мы теперь имеем выполняющийся Hello . Любые классы, на которые ссылается Hello , будут загружены загрузчиком классов, который загрузил Hello , а именно MultiClassLoader . Исходный текстMultiClassLoader.java . Как мы можем видеть из нашего предыдущего теста, ключевой метод это: public Class loadClass(String className) throws ClassNotFoundException { return (loadClass(className, true)); } Метод loadClass(String className) удобный метод, который вызывает другой метод, делающий всю работу. Этот другой метод, loadClass(className, true) , является абстрактным методом в java.lang.ClassLoader , который должны имплементировать все классы-потомки. Вот исходный текст: public synchronized Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException { Class result; byte[] classBytes; monitor(">> MultiClassLoader. loadClass(" + className + ", " + resolveIt + ")"); // ----- Проверка локального кэша классов result = (Class)classes.get(className); if (result != null) { monitor(">> returning cached result."); return result; } // ----- Проверка с базовым загрузчиком классов try { result = super.findSystemClass(className); monitor(">> returning system class (in CLASSPATH)."); return result; } catch (ClassNotFoundException e) { monitor(">> Not a system class."); } // ----- Попытка загрузить его из предпочтительного источника // Обратите внимание: loadClassBytes() — абстрактный метод classBytes = loadClassBytes(className); if (classBytes == null) { throw new ClassNotFoundException(); } // ----- Определяем его (анализируем файл класса) result = defineClass(classBytes, 0, classBytes.length); if (result == null) { throw new ClassFormatError(); } // ----- Разрешаем, если необходимо if (resolveIt) resolveClass(result); // Сделано classes.put(className, result); monitor(">> Returning newly loaded class."); return result; } Метод не очень отличается от описанного в статье Чака. Создание классов-потомков от ClassLoader намного проще, чем написание целого загрузчика классов. Обратите внимание на использование monitor() для отладки. Комментарии в листинге кода простые, так что я не буду давать здесь детальное объяснение. Если оно, однако, необходимо, прочитайте статью Чака. Наиболее важная вещь, которую делает loadClass() , это выполнение ряда шагов в правильном порядке, чтобы соответствовать конструкции загрузчиков классов. Если эти шаги не в порядке, какой-либо из них отсутствует или осуществлен ненадлежащим образом, Вы явитесь свидетелем неправильного поведения или нарушений защиты. Удовлетворение этих требований нельзя недооценивать. Обратите внимание на блок "<код> // ----- Попытка загрузить его из предпочтительного источника". Это сердцевина всего класса, иллюстрирующая красоту концепции загрузчика классов: всё, что делает загрузчик классов, это получение массива байтов из источника где-нибудь и соответствующая передача этих байтов к Java. Так просто! В нашем случае мы вызываем абстрактный метод, имплементированный классами-потомками URLClassLoader илиFileClassLoader . Обратите внимание, что Вы можете легко создавать классы-потомки, чтобы обрабатывать другие источники, например, реляционные базы данных. Исходный текст классов-потомков здесь: URLClassLoader.java и FileClassLoader.java . Рассмотрим ключевой метод в URLClassLoder : protected byte[] loadClassBytes(String className) { className = formatClassName(className); try { URL url = new URL(urlString + className); URLConnection connection = url.openConnection(); if (sourceMonitorOn) { print("Loading from URL: " + connection.getURL() ); } monitor("Content type is: " + connection.getContentType()); InputStream inputStream = connection.getInputStream(); int length = connection.getContentLength(); monitor("InputStream length = " + length); // Неудача, если -1 byte[] data = new byte[length]; inputStream.read(data); // Реальная передача байтов inputStream.close(); return data; } catch(Exception ex) { print("### URLClassLoader.loadClassBytes() — Exception:"); ex.printStackTrace(); return null; } } Создать loadClassBytes() совершенно не трудно, благодаря множеству удобных классов Java. Всю работу делает единственная строка кода: Напоследок предостережение: каждый класс-потомок MultiClassLoader должен использоваться только для одного источника. Не меняйте источник загрузчика классов после того, как создан экземпляр загрузчика классов. Это предотвратит такие бедствия как класс с тем же самым именем, но иным источником, загружаемый из неверного источника, что может случиться, если источник был изменен динамически. Это также затруднит нарушения безопасности. Избегайте искушения переопределять источник. Вместо этого создавайте собственные подклассы, чтобы установить предпочтительный источник только однажды, и да поможет Вам Java! Как выполнить собственный тестСначала загрузите отсюда следующие файлы: Hello.class test_store_Person.class test_store_Worker.class Предупреждение
Эти файлы выполняются как приложение, а не как апплет. Они заслуживают доверия и просто являются скомпилированными классами из исходных файлов, упомянутых выше. Загрузите MultiClassLoader , URLClassLoader , FileClassLoader , и TestLoader (все .java-файлы). Поместите их в один каталог «test», и скомпилируйтеTestLoader , благодаря чему также автоматически скомпилируются другие классы. Подключитесь к Интернету, скажем, по коммутируемому доступу. Затем запустите TestLoader стандартным способом — java TestLoader . Вы должны увидеть: Loading from URL: http://www.mindspring.com/~happyjac/Hello.class Loaded class test.store.Hello Hello class instantiated Hello.run() called Loading from URL: http://www.mindspring.com/~happyjac/test_store_Worker.class Loading from URL: http://www.mindspring.com/~happyjac/test_store_Person.class Worker class instantiated Worker.startWorking() called Worker class instantiated Person first name is FirstName Tested casting Worker to Person Test complete Чтобы проверить способность загружать классы из файла, добавьте подкаталог к каталогу «test» с именем «store». Загрузите файлы Hello.java , Person.java иWorker.java . Скомпилируйте их. Переименуйте Person.class в test_store_Person.class и Worker.class в test_store_Worker.class . Затем в каталоге «test» выполните команду java TestLoader x без подключения к Интернету. Вы должны увидеть сообщения, подобные первому тесту, за исключением того, что теперь он загружен локально. Вот и всё. Наслаждайтесь собственным загрузчиком классов. Он предоставляет возможность (с разрешения) загрузить класс из любого места сети. Это означает, что если, как заявляет Sun, «сеть это компьютер», Вы теперь абсолютный хозяин сети. Как только Вы осознаете мощь индивидуальных загрузчиков классов, они станут жизнеспособной альтернативой браузерам для тонких клиентов. Спасибо Чаку Мак-Манису за идеи в его статье в JavaWorld о загрузчиках классов! Об автореДжек Харич, aka «Happy Jack», жизнерадостный, разносторонний человек, занявшийся программным обеспечением после того, как карьера скульптора неожиданно закончилась из-за повреждения шеи. В настоящее время он консультант в Атланте и очень активно участвует в деятельности группы пользователей Java в Атланте, её специальной группе «Java как второй язык» и Java-консорциуме Атланты. Ресурсы
Reprinted with permission of JavaWorld. Copyright © ITworld.com, Inc., an IDG Communications company. Source: http://www.javaworld.com/javaworld/javatips/jw-javatip39.html | |
| |
Views: 2904 | |