Сегодня внедрение зависимостей (DI) или Inversion of Control (IoC) лежит в основе многих сред, которые поддерживают понятие контейнера или модели компонентов. Мы обсуждали модели компонентов в моей предыдущей статье о Microcontainer (MC) . Старый JMX Kernal от JBoss предоставлял облегченную поддержку DI / IoC, главным образом из-за ограничений доступа к mbeans через сервер mbean; однако с новой моделью компонентов на основе POJO мы ввели несколько новых и интересных функций.
В этой статье я покажу, как вы можете применять различные концепции DI с помощью микроконтейнера JBoss. Эти понятия будут выражены с помощью кода XML (файлы -beans.xml), но большинство этих функций также можно применять с помощью аннотаций.
Поскольку при объяснении DI / IoC ничего не говорит лучше, чем код, мы пойдем шаг за шагом по примерам кода.
Прочитайте другие части эксклюзивной серии микроконтейнеров JBoss от DZone :
Настройка демонстрационной среды
Чтобы обновить демонстрационную конфигурацию из моей предыдущей статьи, давайте быстро рассмотрим, что нам нужно сделать, чтобы запустить эту демонстрацию в нашей IDE.
Прежде чем мы начнем, я хотел бы сначала описать различные части, которые составляют демо. Весь исходный код можно найти по следующему адресу в нашем хранилище Subversion:
* http://anonsvn.jboss.org/repos/jbossas/projects/demos/microcontainer/branches/DZone_1_0/
Проект полностью подготовлен, поэтому он должен легко настроить его под свою IDE.
Сначала я перейду к подпроектам в демоверсии и опишу их использование. В конце серии статей я более подробно расскажу о том, что делают некоторые подпроекты.
Ниже приведены демонстрационные материалы и подпроекты JBoss Microcontainer, относящиеся к этой статье:
* bootstrap (как следует из названия, он загружает микроконтейнер с помощью демонстрационного кода)
* jmx (добавляет понятие JMX / AOP к начальной загрузке демо)
* ioc (источник код нашего ioc примеров)
В демоверсии есть только одна переменная, которую вам нужно установить — demos home — но даже эта может быть необязательной, если вы вынули свой проект в каталог \ projects \ demos. В противном случае вам нужно установить системное свойство demos.home (например, -Ddemos.home = <мой домашний каталог демонстраций>). Теперь вы сможете запускать JMXMain в качестве основного класса. В этом случае достаточно jmx classpath, так как подпроект ioc не требует дополнительной записи в classpath. После загрузки микроконтейнера он начинает сканировать каталог $ {demos.home} / sandbox на наличие изменений. Теперь все, что нам нужно сделать, это предоставить развертываемый модуль и поместить его туда.
Расширенный IoC
Мы не будем рассматривать простые примеры внедрения, поскольку это должно быть очевидно для большинства читателей. Конфигурирование атрибутов / сеттеров с примитивными или простыми значениями или другими компонентами — это то, что мы видим постоянно. Я постараюсь показать вам некоторые вещи, которые мы считаем уникальными (хорошо, не все из них уникальны, некоторые объясненные функции просто очень крутые :-).
Я расскажу, как некоторые примеры связаны друг с другом, где это применимо. Однако большинство примеров не зависят друг от друга.
Фабрика значений (value-factory-beans.xml)
Иногда мы хотим, чтобы какой-то bean-компонент просто действовал как фабрика значений — то есть мы хотим использовать один из его методов для генерации некоторого значения для нас.
<bean name="Binding" class="org.jboss.demos.ioc.vf.PortBindingManager">
<constructor>
<parameter>
<map keyClass="java.lang.String" valueClass="java.lang.Integer">
<entry>
<key>http</key>
<value>80</value>
</entry>
<entry>
<key>ssh</key>
<value>22</value>
</entry>
</map>
</parameter>
</constructor>
</bean>
<bean name="PortsConfig" class="org.jboss.demos.ioc.vf.PortsConfig">
<property name="http"><value-factory bean="Binding" method="getPort" parameter="http"/></property>
<property name="ssh"><value-factory bean="Binding" method="getPort" parameter="ssh"/></property>
<property name="ftp">
<value-factory bean="Binding" method="getPort">
<parameter>ftp</parameter>
<parameter>21</parameter>
</value-factory>
</property>
<property name="mail">
<value-factory bean="Binding" method="getPort">
<parameter>mail</parameter>
<parameter>25</parameter>
</value-factory>
</property>
</bean>
Здесь мы можем увидеть, как bean-компонент PortsConfig использует bean-компонент Binding для получения его значений через вызов метода getPort.
public class PortBindingManager
{
private Map<String, Integer> bindings;
public PortBindingManager(Map<String, Integer> bindings)
{
this.bindings = bindings;
}
public Integer getPort(String key)
{
return getPort(key, null);
}
public Integer getPort(String key, Integer defaultValue)
{
if (bindings == null)
return defaultValue;
Integer value = bindings.get(key);
if (value != null)
return value;
if (defaultValue != null)
bindings.put(key, defaultValue);
return defaultValue;
}
}
Обратные вызовы (callback-beans.xml) Часто
мы хотим «собрать» все bean-компоненты определенного типа. Мы могли бы даже ограничить количество соответствующих бинов, которые имеют смысл для нашего бина.
<bean name="checker" class="org.jboss.demos.ioc.callback.Checker">
<constructor>
<parameter>
<value-factory bean="parser" method="parse">
<parameter>
<array elementClass="java.lang.Object">
<value>http://www.jboss.org</value>
<value>SI</value>
<value>3.14</value>
<value>42</value>
</array>
</parameter>
</value-factory>
</parameter>
</constructor>
</bean>
<bean name="editorA" class="org.jboss.demos.ioc.callback.DoubleEditor"/>
<bean name="editorB" class="org.jboss.demos.ioc.callback.LocaleEditor"/>
<bean name="parser" class="org.jboss.demos.ioc.callback.Parser">
<incallback method="addEditor" cardinality="4..n"/>
<uncallback method="removeEditor"/>
</bean>
<bean name="editorC" class="org.jboss.demos.ioc.callback.LongEditor"/>
<bean name="editorD" class="org.jboss.demos.ioc.callback.URLEditor"/>
В этом случае у нас есть парсер, который собирает всех редакторов. Мы видим это из подписи метода:
public class Parser
{
private Set<Editor> editors = new HashSet<Editor>();
...
public void addEditor(Editor editor)
{
editors.add(editor);
}
public void removeEditor(Editor editor)
{
editors.remove(editor);
}
}
Примечание: смотрите incallback и uncallback — они совпадают по имени метода.
<incallback method="addEditor" cardinality="4..n"/>
<uncallback method="removeEditor"/>
И мы также установили нижний предел для того, сколько редакторов фактически делают этот компонент из состояния Configured.
cardinality=4..n/>
В конце концов, Checker создается и проверяет парсер
public void create() throws Throwable
{
Set<String> strings = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for (Object element : elements)
strings.add(element.toString());
if (expected.equals(strings) == false)
throw new IllegalArgumentException("Illegal expected set: " + expected + "!=" + strings);
}
Режим доступа к бину (access-mode-beans.xml)
По умолчанию мы не проверяем поля бина; однако, если вы укажете другой BeanAccessMode, поля будут частью свойств bean-компонента.
public enum BeanAccessMode
{
STANDARD(BeanInfoCreator.STANDARD), // Getters and Setters
FIELDS(BeanInfoCreator.FIELDS), // Getters/Setters and fields without getters and setters
ALL(BeanInfoCreator.ALL); // As above but with non public fields included
Здесь мы устанавливаем значение String для частного поля String:
<bean name="FieldsBean" class="org.jboss.demos.ioc.access.FieldsBean" access-mode="ALL">
<property name="string">InternalString</property>
</bean>
public class FieldsBean
{
private String string;
public void start()
{
if (string == null)
throw new IllegalArgumentException("Strings should be set!");
}
}
Псевдоним бина (aliases-beans.xml)
Каждый бин может иметь любое количество псевдонимов. Поскольку мы рассматриваем имена компонентов микроконтейнера как объекты, мы не ограничиваем тип псевдонима. По умолчанию мы не выполняем замену системных свойств — вам нужно явно установить флаг замены.
<bean name="SimpleName" class="java.lang.Object">
<alias>SimpleAlias</alias>
<alias replace="true">${some.system.property}</alias>
<alias class="java.lang.Integer">12345</alias>
<alias><javabean xmlns="urn:jboss:javabean:2.0" class="org.jboss.demos.bootstrap.Main"/></alias>
</bean>
Поддержка аннотаций XML (или MetaData) (annotations-beans.xml)
Поддержка AOP является первоклассным гражданином в JBoss Microcontainer, поэтому мы можем смешивать и сопоставлять аспекты AOP и простые компоненты. В этом примере мы попытаемся перехватить вызов метода на основе аннотации. Самое интересное, что нам все равно, откуда эта аннотация. Это может быть настоящая аннотация класса или аннотация, добавленная через конфигурацию xml.
<interceptor xmlns="urn:jboss:aop-beans:1.0" name="StopWatchInterceptor" class="org.jboss.demos.ioc.annotations.StopWatchInterceptor"/>
<bind xmlns="urn:jboss:aop-beans:1.0" pointcut="execution(* @org.jboss.demos.ioc.annotations.StopWatchLog->*(..)) OR execution(* *->@org.jboss.demos.ioc.annotations.StopWatchLog(..))">
<interceptor-ref name="StopWatchInterceptor"/>
</bind>
</interceptor>
public class StopWatchInterceptor implements Interceptor
{
...
public Object invoke(Invocation invocation) throws Throwable
{
Object target = invocation.getTargetObject();
long time = System.currentTimeMillis();
log.info("Invocation [" + target + "] start: " + time);
try
{
return invocation.invokeNext();
}
finally
{
log.info("Invocation [" + target + "] time: " + (System.currentTimeMillis() - time));
}
}
}
И истинный класс аннотированного исполнителя:
<bean name="AnnotatedExecutor" class="org.jboss.demos.ioc.annotations.AnnotatedExecutor">
public class AnnotatedExecutor implements Executor
{
...
@StopWatchLog // <-- Pointcut match!
public void execute() throws Exception
{
delegate.execute();
}
}
Простой исполнитель с xml-аннотацией:
<bean name="SimpleExecutor" class="org.jboss.demos.ioc.annotations.SimpleExecutor">
<annotation>@org.jboss.demos.ioc.annotations.StopWatchLog</annotation> // <-- Pointcut match!
</bean>
public class SimpleExecutor implements Executor
{
private static Random random = new Random();
public void execute() throws Exception
{
Thread.sleep(Math.abs(random.nextLong() % 101));
}
}
Мы добавляем некоторый bean-компонент invoker для активатора, чтобы увидеть этих исполнителей в действии во время развертывания:
JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch — Invocation [org.jboss.demos.ioc.annotations. AnnotatedExecutor @ 4d28c7] начало: 1229345859234
JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch — вызов [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] время: 31
JBoss-MC- Демонстрационная информация [15-12-2008 13:57:39] StopWatch — Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] начало: 1229345859265
JBoss-MC-Demo INFO [15-12-2008 13:57 : 39] StopWatch — вызов [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] время: 47
Autowire (autowire-beans.xml)
Автопроводка, или контекстная инъекция, является общей особенностью каркасов IoC.
Ниже показано, как использовать или исключить bean-компоненты с автопроводкой.
<bean name="Square" class="org.jboss.demos.ioc.autowire.Square" autowire-candidate="false"/>
<bean name="Circle" class="org.jboss.demos.ioc.autowire.Circle"/>
<bean name="ShapeUser" class="org.jboss.demos.ioc.autowire.ShapeUser">
<constructor>
<parameter><inject/></parameter>
</constructor>
</bean>
<bean name="ShapeHolder" class="org.jboss.demos.ioc.autowire.ShapeHolder">
<incallback method="addShape"/>
<uncallback method="removeShape"/>
</bean>
<bean name="ShapeChecker" class="org.jboss.demos.ioc.autowire.ShapesChecker"/>
В обоих случаях — ShapeUser и ShapeChecker — должен использоваться только Circle, так как Square исключается из контекстной привязки.
Bean factory (bean-factory-beans.xml)
Когда нам нужно более одного экземпляра какого-либо bean-компонента, нам нужно использовать шаблон bean factory. Задача микроконтейнера состоит в том, чтобы сконфигурировать и установить фабрику компонентов, как если бы это был простой компонент. Наша задача — вызвать метод createBean фабрики бинов.
По умолчанию микроконтейнер создает экземпляр GenericBeanFactory, но вы можете настроить собственную фабрику. Единственным ограничением является то, что его хуки подписи / конфигурации аналогичны тем, что используются в AbstractBeanFactory.
<bean name="Object" class="java.lang.Object"/>
<beanfactory name="DefaultPrototype" class="org.jboss.demos.ioc.factory.Prototype">
<property name="value"><inject bean="Object"/></property>
</beanfactory>
<beanfactory name="EnhancedPrototype" class="org.jboss.demos.ioc.factory.Prototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory">
<property name="value"><inject bean="Object"/></property>
</beanfactory>
<beanfactory name="ProxiedPrototype" class="org.jboss.demos.ioc.factory.UnmodifiablePrototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory">
<property name="value"><inject bean="Object"/></property>
</beanfactory>
<bean name="PrototypeCreator" class="org.jboss.demos.ioc.factory.PrototypeCreator">
<property name="default"><inject bean="DefaultPrototype"/></property>
<property name="enhanced"><inject bean="EnhancedPrototype"/></property>
<property name="proxied"><inject bean="ProxiedPrototype"/></property>
</bean>
Здесь вы можете увидеть пример такого расширенного использования BeanFactory:
public class EnhancedBeanFactory extends GenericBeanFactory
{
public EnhancedBeanFactory(KernelConfigurator configurator)
{
super(configurator);
}
public Object createBean() throws Throwable
{
Object bean = super.createBean();
Class<?> clazz = bean.getClass();
if (clazz.isAnnotationPresent(SetterProxy.class))
{
Set<Class> interfaces = new HashSet<Class>();
addInterfaces(clazz, interfaces);
return Proxy.newProxyInstance(
clazz.getClassLoader(),
interfaces.toArray(new Class<?>[interfaces.size()]),
new SetterInterceptor(bean)
);
}
else
{
return bean;
}
}
protected static void addInterfaces(Class<?> clazz, Set<Class> interfaces)
{
if (clazz == null)
return;
interfaces.addAll(Arrays.asList(clazz.getInterfaces()));
addInterfaces(clazz.getSuperclass(), interfaces);
}
private class SetterInterceptor implements InvocationHandler
{
private Object target;
private SetterInterceptor(Object target)
{
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
String methodName = method.getName();
if (methodName.startsWith("set"))
throw new IllegalArgumentException("Cannot invoke setters.");
return method.invoke(target, args);
}
}
}
public class PrototypeCreator
{
...
public void create() throws Throwable
{
ValueInvoker vi1 = (ValueInvoker)bfDefault.createBean();
vi1.setValue("default");
ValueInvoker vi2 = (ValueInvoker)enhanced.createBean();
vi2.setValue("enhanced");
ValueInvoker vi3 = (ValueInvoker)proxied.createBean();
try
{
vi3.setValue("default");
throw new Error("Should not be here.");
}
catch (Exception ignored)
{
}
}
Beta Meta Data Builder (builder-util-beans.xml)
При использовании микроконтейнера в вашем коде мы рекомендуем использовать BeanMetaDataBuilder для настройки / создания метаданных вашего компонента.
<bean name="BuilderUtil" class="org.jboss.demos.ioc.builder.BuilderUtil"/>
<bean name="BuilderExampleHolder" class="org.jboss.demos.ioc.builder.BuilderExampleHolder">
<constructor>
<parameter><inject bean="BUExample"/></parameter>
</constructor>
</bean>
Используя эту концепцию, вы не подвергаете свой код каким-либо деталям реализации микроконтейнера.
public class BuilderUtil
{
private KernelController controller;
@Constructor
public BuilderUtil(@Inject(bean = KernelConstants.KERNEL_CONTROLLER_NAME) KernelController controller)
{
this.controller = controller;
}
public void create() throws Throwable
{
BeanMetaDataBuilder builder = BeanMetaDataBuilder.createBuilder("BUExample", BuilderExample.class.getName());
builder.addStartParameter(Kernel.class.getName(), builder.createInject(KernelConstants.KERNEL_NAME));
controller.install(builder.getBeanMetaData());
}
public void destroy()
{
controller.uninstall("BUExample");
}
}
Пользовательский ClassLoader (classloader-beans.xml)
В микроконтейнере вы даже можете определить собственный ClassLoader для каждого компонента.
При определении загрузчика классов для всего развертывания убедитесь, что вы не создаете циклическую зависимость — например, вновь определенный загрузчик классов, который зависит от самого себя.
<classloader><inject bean="custom-classloader:0.0.0"/></classloader>
<!-- this will be explained in future article -->
<classloader name="custom-classloader" xmlns="urn:jboss:classloader:1.0" export-all="NON_EMPTY" import-all="true"/>
<bean name="CustomCL" class="org.jboss.demos.ioc.classloader.CustomClassLoader">
<constructor>
<parameter><inject bean="custom-classloader:0.0.0"/></parameter>
</constructor>
<property name="pattern">org\.jboss\.demos\.ioc\..+</property>
</bean>
<bean name="CB1" class="org.jboss.demos.ioc.classloader.CustomBean"/>
<bean name="CB2" class="org.jboss.demos.ioc.classloader.CustomBean">
<classloader><inject bean="CustomCL"/></classloader>
</bean>
Здесь вы можете видеть, что bean-компонент CB2 использует собственный ClassLoader, который ограничивает область загружаемого пакета:
public class CustomClassLoader extends ClassLoader
{
private Pattern pattern;
public CustomClassLoader(ClassLoader parent)
{
super(parent);
}
public Class<?> loadClass(String name) throws ClassNotFoundException
{
if (pattern == null || pattern.matcher(name).matches())
return super.loadClass(name);
else
throw new ClassNotFoundException("Name '" + name + "' doesn't match pattern: " + pattern);
}
public void setPattern(String regexp)
{
pattern = Pattern.compile(regexp);
}
}
Режим контроллера (controller-mode-beans.xml)
По умолчанию микроконтейнер использует режим контроллера AUTO — это означает, что он проталкивает компоненты в зависимости от их зависимостей. Но есть два других режима — MANUAL и ON_DEMAND, которые делают именно то, что предлагают их имена.
Если bean-компонент помечен как ON_DEMAND, он не будет использоваться / установлен, пока какой-либо другой bean-компонент явно не будет зависеть от него. В ручном режиме пользователь микроконтейнера может толкать боб вперед и назад по лестнице состояния.
<bean name="OptionalService" class="org.jboss.demos.ioc.mode.OptionalService" mode="On Demand"/>
<bean name="OptionalServiceUser" class="org.jboss.demos.ioc.mode.OptionalServiceUser"/>
<bean name="ManualService" class="org.jboss.demos.ioc.mode.ManualService" mode="Manual"/>
<bean name="ManualServiceUser" class="org.jboss.demos.ioc.mode.ManualServiceUser">
<start>
<parameter><inject bean="ManualService" fromContext="context" state="Not Installed"/></parameter>
</start>
</bean>
Примечание: см. Атрибут fromContext объекта inject. Здесь мы можем внедрить не только компоненты, но и их неизменяемое представление компонентов микроконтейнера.
Проверьте код OptionalServiceUser и ManualServiceUser о том, как использовать API-интерфейс Microcontainer для обработки bean-компонентов ON_DEMAND и MANUAL.
Цикл (cycle-beans.xml)
Иногда бины зависят друг от друга в цикле. Например, A зависит от B в конструкции, где B зависит от A в установщике. Благодаря мелкозернистому разделению жизненного цикла микроконтейнера мы можем решить такие проблемы довольно легко.
<bean name="cycleA" class="org.jboss.demos.ioc.cycle.CyclePojo">
<property name="dependency"><inject bean="cycleB"/></property>
</bean>
<bean name="cycleB" class="org.jboss.demos.ioc.cycle.CyclePojo">
<constructor><parameter><inject bean="cycleA" state="Instantiated"/></parameter></constructor>
</bean>
<bean name="cycleC" class="org.jboss.demos.ioc.cycle.CyclePojo">
<property name="dependency"><inject bean="cycleD"/></property>
</bean>
<bean name="cycleD" class="org.jboss.demos.ioc.cycle.CyclePojo">
<property name="dependency"><inject bean="cycleC" state="Instantiated"/></property>
</bean>
Спрос / предложение (demand-supply-beans.xml)
Иногда зависимость явно не известна, например, внедрение; тем не менее, между двумя компонентами все еще может существовать зависимость, такая как использование статического кода. Это должно быть ясно выражено:
<bean name="TMDemand" class="org.jboss.demos.ioc.demandsupply.TMDemander">
<demand>TM</demand>
</bean>
<bean name="SimpleTMSupply" class="org.jboss.demos.ioc.demandsupply.SimpleTMSupplyer">
<supply>TM</supply>
</bean>
Installs (install-beans.xml)
Когда наш компонент движется по состояниям, мы можем захотеть вызвать некоторые методы (действия) для других компонентов или того же компонента.
Здесь мы видим, как Entry вызывает метод add / removeEntry RepositoryManager для регистрации / отмены регистрации.
<bean name="RepositoryManager" class="org.jboss.demos.ioc.install.RepositoryManager">
<install method="addEntry">
<parameter><inject fromContext="name"/></parameter>
<parameter><this/></parameter>
</install>
<uninstall method="removeEntry">
<parameter><inject fromContext="name"/></parameter>
</uninstall>
</bean>
<bean name="Entry" class="org.jboss.demos.ioc.install.SimpleEntry">
<install bean="RepositoryManager" method="addEntry" state="Instantiated">
<parameter><inject fromContext="name"/></parameter>
<parameter><this/></parameter>
</install>
<uninstall bean="RepositoryManager" method="removeEntry" state="Configured">
<parameter><inject fromContext="name"/></parameter>
</uninstall>
</bean>
Ленивый (lazy-beans.xml)
У нас может быть зависимость от бина, который редко используется, но его настройка занимает много времени. Мы можем использовать ленивый макет bean-компонента, чтобы разрешить зависимость, но когда нам действительно понадобится bean-компонент, мы будем вызывать / использовать целевой bean-компонент, надеясь, что он будет установлен к тому времени.
<bean name="lazyA" class="org.jboss.demos.ioc.lazy.LazyImpl">
<constructor>
<parameter>
<lazy bean="lazyB">
<interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface>
</lazy>
</parameter>
</constructor>
</bean>
<bean name="lazyB" class="org.jboss.demos.ioc.lazy.LazyImpl">
<constructor>
<parameter>
<lazy bean="lazyA">
<interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface>
</lazy>
</parameter>
</constructor>
</bean>
<lazy name="anotherLazy" bean="Pojo" exposeClass="true"/>
<bean name="Pojo" class="org.jboss.demos.ioc.lazy.Pojo"/>
Жизненный цикл (lifecycle-beans.xml)
По умолчанию микроконтейнер использует методы create / start / stop / destroy при перемещении по различным состояниям; однако мы можем не захотеть, чтобы микроконтейнер вызывал их. Следовательно, мы можем установить флаг игнорирования.
<bean name="FullLifecycleBean-3" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"/>
<bean name="FullLifecycleBean-2" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean">
<create ignored="true"/>
</bean>
<bean name="FullLifecycleBean-1" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean">
<start ignored="true"/>
</bean>
Summary
In this article, I showed you how to apply different DI concepts with the help of the JBoss Microcontainer. The Microcontainer provides a comprehensive range of DI/IoC capabilities.
Joining that with a modular design, a clean split into metadata driven deployment and an extensible state machine, you can nicely integrate any additional features you feel are missing. Nevertheless, the Microcontainer team still has some ideas on addition features it would like to add and is always open to your suggestions.
In my next article I’ll present the Microcontainer’s Virtual File System (VFS) project. This will provide a nice prelude for subsequent articles on Microcontainer ClassLoading and the Deployment framework.
About the Author
Ales Justin was born in Ljubljana, Slovenia and graduated with a degree in mathematics from the University of Ljubljana. He fell in love with Java seven years ago and has spent most of his time developing information systems, ranging from customer service to energy management. He joined JBoss in 2006 to work full time on the Microcontainer project, currently serving as its lead. He also contributes to JBoss AS and is Seam and Spring integration specialist. He represent JBoss on ‘JSR-291 Dynamic Component Support for Java SE’ and ‘OSGi’ expert groups.