1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/drinkjava2-jBeanBox

Клонировать/Скачать
README-ENG.md 31 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 25.11.2024 10:23 daaf42c

jBeanBox

Лицензия: Apache 2.0

jBeanBox — это инструмент IOC/AOP микромасштаба. За исключением сторонних библиотек, основной исходный код составляет около 3000 строк. Он использует модель программирования «Box», используя чистые классы Java в качестве конфигурации. jBeanBox работает на JDK 1.6 или выше.
Цель разработки jBeanBox состоит в том, чтобы преодолеть некоторые проблемы других инструментов IOC/AOP:

  1. Spring: исходный код раздутый, режим Java не гибкий, есть проблемы с динамической конфигурацией, наследованием конфигурации, медленным запуском и режимом не одиночного режима.
  2. Guice: исходный код слегка раздутый (200 классов), не очень удобно использовать, поддержка жизненного цикла Bean плохая.
  3. Feather: исходный код минимальный (несколько сотен строк), но не полностью функциональный. Это просто инструмент DI и не поддерживает AOP.
  4. Dagger: исходный код немного раздутый (300 классов), статическая инъекция во время компиляции, немного неудобно использовать, не поддерживает AOP.
  5. Genie: это ядро ActFramework, просто инструмент DI, не поддерживающий AOP.

Как использовать jBeanBox?

Добавьте следующую конфигурацию в pom.xml:

<dependency>
    <groupId>com.github.drinkjava2</groupId>
    <artifactId>jbeanbox</artifactId>
    <version>4.0.0</version> <!-- Или новейшая версия -->
</dependency>  

jBeanBox не зависит от каких-либо сторонних библиотек. Чтобы избежать конфликтов пакетов, сторонние библиотеки, такие как CGLIB, которые он использует, включены в jBeanBox исходным кодом.
Размер jar-файла jBeanBox большой, около 460 КБ, если вам не нужна функция AOP, вы можете использовать только его ядро ​​DI, называемое «jBeanBoxDI», размером всего 49 КБ, поместите ниже в pom.xml:

<dependency>
    <groupId>com.github.drinkjava2</groupId>
    <artifactId>jbeanboxdi</artifactId>
    <version>4.0.0</version> <!-- Или новейшая версия -->
</dependency>

Первый демо-пример jBeanBox

Демонстрация показывает 10 различных методов внедрения:

public class HelloWorld {
    public static class User {
        String name;
        
        public User() {}
        
        @VALUE("User1")
        public User(String name) {  this.name = name;}
        
        void setName(String name) { this.name = name;}
        
        void init() {this.name = "User6";}
        
        @PreDestroy
        void end() {this.name= "User10";}
    }

    public static class UserBox extends BeanBox {
        Object create() {return new User("User2");}
    }
    
    public static class UserBox7 extends BeanBox {
        {   setBeanClass(User.class);
            setProperty("name", "User7");
        } 
    }

    public static class H8 extends UserBox {{setAsValue("User8");}}
 
    public static void main(String[] args) {
        User u1 = JBEANBOX.getInstance(User.class);
        User u2 = JBEANBOX.getBean(UserBox.class);
        User u3 = JBEANBOX.getBean(new BeanBox().injectConstruct(User.class, String.class, value("User3")));
        User u4 = JBEANBOX.getBean(new BeanBox(User.class).injectValue("name", "User4" ));
        User u5 = JBEANBOX
                .getBean(new BeanBox(User.class).injectMethod("setName", String.class, value("User5")));
        User u6 = JBEANBOX.getBean(new BeanBox().setBeanClass(User.class).setPostConstruct("init"));
        User u7 = new UserBox7().getBean();
        
        BeanBoxContext ctx = new BeanBoxContext(); 
        Interceptor aop=new MethodInterceptor() { 
            public Object invoke(MethodInvocation invocation) throws Throwable { 
                invocation.getArguments()[0]="User9";
                return invocation.proceed();
            }
        };
        User u8 = ctx.rebind(String.class, "8").bind("8", H8.class)
                .getBean(ctx.getBeanBox(User.class).addMethodAop(aop, "setName",String.class).injectField("name", autowired())); 
        System.out.println(u1.name); //Result: User1
        System.out.println(u2.name); //Result: User2
        System.out.println(u3.name); //Result: User3
        System.out.println(u4.name); //Result: User4
        System.out.println(u5.name); //Result:

*Примечание: в тексте запроса присутствуют фрагменты кода на языке Java, они были переведены на русский язык.* ```
System.out.println(u6.name); //Result: User6
System.out.println(u7.name); //Result: User7
System.out.println(u8.name); //Result: User8
u8.setName("");
System.out.println(u8.name); //Result: User9
ctx.close();
System.out.println(u8.name); //Result: User10 
}

Этот фрагмент кода демонстрирует последовательный вывод «User1», «User2»... до «User10». Вот объяснение:

  1. Сначала в примере используется аннотация @VALUE("User1") для внедрения конструктора.
  2. UserBox — это чистый класс конфигурации Java, в этом примере метод create вручную генерирует объект User("User2").
  3. Третий пример демонстрирует динамическое создание конфигурации BeanBox, динамическую настройку его внедрения конструктора и внедрение значения «User3».
  4. Четвёртый пример также представляет собой динамическую конфигурацию, которая демонстрирует внедрение поля со значением константы «User4».
  5. Пятый пример демонстрирует внедрение метода. Параметрами внедрения являются: имя метода, тип параметра и фактические параметры.
  6. Шестой пример устанавливает внедрение setPostConstruct, эквивалентное аннотации @PostConstruct, то есть метод, выполняемый сразу после создания компонента, является методом init().
  7. Седьмой UserBox7 — это общий класс конфигурации BeanBox, который устанавливает тип компонента. Этот метод вызовет свой конструктор без аргументов для генерации экземпляра, а затем внедрит атрибут name в «User7».
  8. Восьмой пример более сложный. ctx — это новый экземпляр контекста. Он сначала получает фиксированную конфигурацию класса User.class, затем добавляет аспект AOP к своему методу setName и затем внедряет поле «name» в тип autowired String, но перед этим класс String привязывается к строке «8», строка «8» привязывается к классу H8.class, H8 наследуется от UserBox, UserBox возвращает «User2», но все они являются облаками, поскольку сам H8 настроен как тип значения «User8», окончательный вывод — «User8».
  9. Девятый пример относительно прост, потому что в метод setName добавлен перехватчик AOP и параметр изменён на «User9».
  10. Десятый пример связан с закрытием контекста ctx, будет вызван метод в синглтонах, помеченный @PreDestroy.

Приведённый выше пример в основном демонстрирует конфигурацию метода Java jBeanBox. Метод Java можно выполнить динамически или как фиксированную конфигурацию в определённом классе BeanBox. Фиксированная конфигурация может заложить основу конфигурации. Когда необходимо внести изменения, можно использовать тот же метод Java для настройки (поскольку это один и тот же объект BeanBox) или даже временно создать новую конфигурацию, поэтому jBeanBox имеет преимущества фиксированной конфигурации и динамической конфигурации. Кроме того, когда нет исходного кода, например, для настройки экземпляра сторонней библиотеки, все методы аннотаций в настоящее время не используются, и единственный доступный метод конфигурации Java — единственный.

Метод value() в приведённом выше примере — это глобальный метод, статически введённый из класса JBEANBOX. Исходный код этого примера находится в файле HelloWorld.java в каталоге модульного тестирования.

Конфигурация аннотаций jBeanBox

jBeanBox поддерживает не только конфигурацию режима Java, но и конфигурацию режима аннотаций. Поддерживаются следующие аннотации:
@INJECT похож на аннотацию @Inject в JSR, но позволяет добавить целевой класс в качестве параметра
@POSTCONSTRUCT эквивалентен аннотации @PostConstruct в JSR
@PREDESTROY эквивалентен аннотации @PreDestroy в JSR
@VALUE похож на аннотацию @Value в Spring
@PROTOTYPE эквивалентен аннотации @Prototype в Spring
@AOP используется для настройки аннотаций AOP. Подробнее см. в разделе AOP.
@NAMED эквивалентен аннотации @Named в JSR @QUALIFIER эквивалентен аннотации @Qulifier в JSR

Поскольку все знакомы с конфигурацией метода аннотирования, здесь нет подробного введения. В каталоге jBeanBox\test можно найти файл «AnnotationInjectTest.java», демонстрирующий использование различных режимов аннотирования. Конфигурация.

Чтобы отключить JSR, можно использовать метод Spring annotations ctx.setAllowSpringJsrAnnotation(false). Чтобы отключить все аннотации (это означает, что можно будет использовать только Java configuration), используйте метод ctx.setAllowAnnotation(false).

Ниже приведены примеры конфигурации аннотаций:

// Класс внедрения
@PROTOTYPE
@VALUE("3")  
public static class Demo4 { } // ctx.getBean(Demo4.class) вернёт «3»

@INJECT(Demo4.class) @PROTOTYPE  
public static class Demo5 { } // прототип
 
@INJECT(value=Demo4.class )
public static class Demo6 { } // синглтон

@INJECT(value=Demo4.class  )
public static interface inf1{}// синглтон

@INJECT(value=Demo4.class,  pureValue=true) // возвращает Demo4.class 
public static interface inf2{}

// Конструктор внедрения
public static class CA {}
public static class CB {}
public static class C1 { int i = 0; @INJECT public C1() { i = 2; } } 
public static class C2 { int i = 0; @INJECT public C2(@VALUE("2") int a) { i = a; } }
public static class C3 { int i = 0; @VALUE("2") public C3(int a) { i = a; } }
public static class C4 { int i = 0; @INJECT public C4(@VALUE("2") Integer a,@VALUE("2") byte b ) { i = b; } }
public static class C5 { Object o ; @INJECT(value=Bar.class, pureValue=true) public C5(Object a) { o = a; } }
public static class C6 { Object o1,o2 ; @INJECT public C6(CA a, CB b) { o1 = a; o2=b; } }

// Внедрение поля
public static class FieldInject2 {
    @INJECT(required = false)
    public String field0 = "aa"; 

    @INJECT(value = ClassABox.class, pureValue = false, required = true)
    private ClassA field1; 
    
    @INJECT(value = ClassABox.class)
    private ClassA field1; 

    @INJECT(HelloBox.class) 
    private String field3;

    @VALUE(value = "true")
    private Boolean field4;

    @VALUE("5")
    private long field5;

    @VALUE("6")
    private Long field6;

    @Autowired(required = false)
    public String field7 = "7"; 

    @Inject
    public CA ca;

    @Autowired
    public CB cb;
}

// Метод внедрения
public static class MethodInject1 {
    public String s1;
    public String s2;
    public long l3;
    public Boolean bl4;
    public String s5;
    public byte bt5;
    public CA a;

    @INJECT(HelloBox.class)
    private void method1(String a) {
        s1 = a;
    }

    @INJECT
    private void method2(@INJECT(value = HelloBox.class) String a) {
        s2 = a;
    }

    @INJECT
    private void method3(@VALUE("3") long a) {
        l3 = a;
    }

    @VALUE("true")
    private void method4(boolean a) {
        bl4 = a;
    }

    @INJECT
    private void method5(@INJECT(HelloBox.class) String a, @VALUE("5") Byte b) {
        s5 = a;
        bt5 = b;
    }

    @INJECT
    private void method6(CA a) {
        this.a = a;
    }
}

Методы конфигурации jBeanBox на Java

Приведённый пример — это общая демонстрация конфигурации Java-режима jBeanBox. Давайте подробнее рассмотрим все методы конфигурации Java:

  • setAsValue(Object) настраивает текущий BeanBox как константное значение, эквивалентное setTarget(Obj)+setPureVale(true);

  • setPrototype(boolean) Если аргумент равен true, это означает, что это не синглтон, в отличие от метода setSingleton.

  • injectConstruct(Class<?>, Object...) Устанавливает внедрение конструктора. Параметрами являются класс, тип параметра конструктора и параметры.

  • injectMethod(String, Object...) Устанавливает внедрение метода. Параметрами являются имя метода, тип параметра и параметры.

  • addAopToMethod(Object, Method) Добавляет AOP к методу, параметрами являются класс или экземпляр AOP, метод.

  • addMethodAop(Object, String, Class<?>...) Добавляет AOP к методу, параметры — класс или экземпляр AOP, имя метода, тип параметра.

  • addBeanAop(Object, String) Добавляет AOP ко всему компоненту. Параметры — класс или экземпляр AOP, правила метода (например, «setUser*»).

  • setPostConstruct(String) устанавливает имя метода PostConstruct с тем же эффектом, что и аннотация @PostConstruct.

  • setPreDestroy(String) устанавливает имя метода PreDestroy с тем же эффектом, что и аннотация @PreDestroy.

  • injectField(String, BeanBox) Внедряет поле, параметр — имя поля. BeanBox и его эквивалентная аннотация @INJECT

  • setProperty(String, Object) эквивалентен методу injectValue.

  • injectValue(String, Object). Метод injectValue внедряет поле, параметр — это имя поля, экземпляр объекта, и аннотация, с которой его можно сравнить, — @VALUE.

  • setTarget(Object) предназначен для цели текущего bean. Кроме того, когда bind("7", User.class), setTarget("7") эквивалентно setTarget(User.class).

  • setPureValue(boolean) указывает, что цель больше не является целью, а возвращается как чистое значение. «7» на восходящем потоке вернёт строку «7».

  • setBeanClass(Class<?>) устанавливает конечный целевой класс текущего BeanBox. Все конфигурации основаны на этом классе.

  • setSingleton(Boolean) — противоположность setPrototype.

  • setConstructor(Constructor<?>) задаёт конструктор.

  • setConstructorParams(BeanBox[]) задаёт параметры конструктора, который используется вместе с восходящим потоком.

  • setPostConstruct(Method) задаёт метод PostConstruct с тем же эффектом, что и аннотация @PostConstruct.

  • setPreDestroy(Method) задаёт имя метода PreDestroy с тем же эффектом, что и аннотация @PreDestroy.

В Java-конфигурации в классе BeanBox есть два специальных метода: create и config. См. ниже:

public static class DemoBox extends BeanBox {

        public Object create(Caller caller) {
            A a = new A();
            a.field1 = caller.getBean(B.class);
            return a;
        }

        public void config(Object o, Caller caller) {
            ((A) o).field2 = caller.getBean(C.class);
        }
    }

Приведённый выше пример показывает, что бин, созданный в DemoBox, генерируется методом create и модифицируется методом config. Параметр Caller в методах create и config можно опустить, если вам не нужно использовать этот параметр Caller для загрузки других бинов.

AOP для jBeanBox (для фасетного программирования)

Большинство функций jBeanBox можно реализовать либо в Java-конфигурации, либо в конфигурации аннотаций. Аналогично существует два способа поддержки AOP:

Конфигурация AOP в режиме Java

  • someBeanBox.addMethodAop(Object, String, Class<?>...) добавляет AOP к методу, параметры — класс или экземпляр AOP, имя метода, тип параметра.
  • someBeanBox.addBeanAop(Object, String) добавляет AOP ко всему бину. Параметры — класс или экземпляр AOP и правила метода (например, «setUser*»).
  • someBeanBoxContext.addGlobalAop(Object, Object, String); добавляет правило AOP ко всему контексту. Параметры — класс или экземпляр AOP, правило класса или имени класса, правило имени метода. Вышеупомянутые три метода соответствуют трём различным уровням правил AOP. Первый метод только для метода, второй метод для всего класса, третий метод для всего контекста. Ниже приведён пример конфигурации Java для AOP:
public static class AopDemo1 {
        String name;
        String address;
        String email;
        //getter & setters...
    }

    public static class MethodAOP implements MethodInterceptor { 
        public Object invoke(MethodInvocation invocation) throws Throwable {
            invocation.getArguments()[0] = "1";
            return invocation.proceed();
        }
    }

    public static class BeanAOP implements MethodInterceptor { 
        public Object invoke(MethodInvocation invocation) throws Throwable {
            invocation.getArguments()[0] = "2";
            return invocation.proceed();
        }
    }

    public static class GlobalAOP implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            invocation.getArguments()[0] = "3";
            return invocation.proceed();
        }
    }

    public static class AopDemo1Box extends BeanBox {
        {
            this.injectConstruct(AopDemo1.class, String.class, value("0"));
            this.addMethodAop(MethodAOP.class, "setName", String.class);
            this.addBeanAop(BeanAOP.class, "setAddr*");
        }
    }

    @Test
    public void aopTest1() {
``` JBEANBOX.ctx().bind("3", GlobalAOP.class);
JBEANBOX.ctx().addGlobalAop("3", AopDemo1.class, "setEm*");
AopDemo1 demo = JBEANBOX.getBean(AopDemo1Box.class);
demo.setName("--");
Assert.assertEquals("1", demo.name);
demo.setAddress("--");
Assert.assertEquals("2", demo.address);
demo.setEmail("--");
Assert.assertEquals("3", demo.email);
}

#### Annotation mode AOP configuration

Режим аннотации AOP имеет только два типа: для метода и для класса.
Метод аннотирования требует специальной аннотации @AOP, которая используется для настройки аннотаций AOP. Примеры использования следующие:

public static class Interceptor1 implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { invocation.getArguments()[0] = "1"; return invocation.proceed(); } }

public static class Interceptor2 implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { invocation.getArguments()[0] = "2"; return invocation.proceed(); } }

@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) @AOP public static @interface MyAop1 { public Class<?> value() default Interceptor1.class;

public String method() default "setNa*";

}

@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) @AOP public static @interface MyAop2 { public Class<?> value() default Interceptor2.class; }

@MyAop1 public static class AopDemo1 { String name; String address;

public void setName(String name) {
    this.name = name;
}

@MyAop2
public void setAddress(String address) {
    this.address = address;
}

}

@Test public void aopTest1() { AopDemo1 demo = JBEANBOX.getBean(AopDemo1.class); demo.setName("--"); Assert.assertEquals("1", demo.name); demo.setAddress("--"); Assert.assertEquals("2", demo.address); }


AOP, упомянутый в этой статье, относится к интерфейсу стандарта федерации AOP альянса. Он был включён в jBeanBox и не нуждается в отдельном представлении (конечно, нет проблем с повторным представлением). Стандарт федерации AOP альянса является полезным интерфейсом для достижения взаимозаменяемости между различными реализациями AOP. На его основе jBeanBox может заменить ядро Spring и использовать его декларативную транзакцию. Эта взаимозаменяемость может быть достигнута. Предпосылка заключается в том, что реализация декларативной транзакции Spring (например, TransactionInterceptor) также реализует интерфейс стандарта федерации AOP альянса MethodInterceptor.

Начиная с версии 2.4.8, функция ABean была отключена, а неиспользуемые функции предварительного, последующего и аномального аспектов были удалены. Были сохранены только функции интерфейса стандарта федерации AOP альянса MethodInterceptor (обратите внимание, что в CGLIB также есть интерфейс с таким же именем, не путайте). Класс, который реализует интерфейс MethodInterceptor, обычно называется Interceptor, но сохраняется в jBeanBox, также называется AOP, в конце концов, писать addBeanAop проще, чем писать addBeanInterceptor.

### О циклических зависимостях
jBeanBox поддерживает обнаружение циклических зависимостей. Если будет обнаружена циклическая зависимость инъекции (например, инъекция B в конструкторе A и инъекция A в конструкторе B), будет выброшено исключение времени выполнения BeanBoxException.
Однако в jBeanBox разрешены циклические зависимости инъекций, которые происходят в таких полях или методах:

public static class A { @Inject public B b; }

public static class B { @Inject public A a; }

A a = JBEANBOX.getBean(A.class); Assert.assertTrue(a == a.b.a);//true

### jBeanBox поддерживает несколько контекстов и жизненный цикл Bean Функция, построить дерево экземпляров из 6 объектов), можно увидеть, что jBeanBox создаёт не-синглтон-бин в два раза быстрее Guice и в 45 раз быстрее Spring. Тестовый проект находится по адресу: [di-benchmark] (https://github.com/drinkjava2/di-benchmark).

Бенчмарк времени выполнения, получение нового бина 500 000 раз:
---------------------------------------------------------
                     Vanilla|    31мс
                       Guice|  1154мс
                     Feather|   624мс
                      Dagger|   312мс
                       Genie|   609мс
                        Pico|  4555мс
              jBeanBoxNormal|  2075мс
            jBeanBoxTypeSafe|  2371мс
          jBeanBoxAnnotation|  2059мс
     SpringJavaConfiguration| 92149мс
     SpringAnnotationScanned| 95504мс

Разделение запуска контейнеров DI и создания графа зависимостей 4999 раз:
-------------------------------------------------------------------------------
                     Vanilla| запуск:     0мс   получение:     0мс
                       Guice| запуск:  1046мс   получение:  1560мс
                     Feather| запуск:     0мс   получение:   109мс
                      Dagger| запуск:    46мс   получение:   173мс
                        Pico| запуск:   376мс   получение:   217мс
                       Genie| запуск:   766мс   получение:   247мс
              jBeanBoxNormal| запуск:    79мс   получение:   982мс
            jBeanBoxTypeSafe| запуск:     0мс   получение:   998мс
          jBeanBoxAnnotation| запуск:     0мс   получение:   468мс
     SpringJavaConfiguration| запуск: 51831мс   получение:  1834мс
     SpringAnnotationScanned| запуск: 70712мс   получение:  4155мс

Бенчмарк времени выполнения, получение синглтона 5 000 000 раз:
---------------------------------------------------------
                     Vanilla|    47мс
                       Guice|  1950мс
                     Feather|   624мс
                      Dagger|  2746мс
                       Genie|   327мс
                        Pico|  3385мс
              jBeanBoxNormal|   188мс
            jBeanBoxTypeSafe|   187мс
          jBeanBoxAnnotation|   171мс
     SpringJavaConfiguration|  1061мс
     SpringAnnotationScanned|  1045мс

Хотя большинство инструментов IOC используются в случаях с синглтонами, производительность почти такая же (потому что она берётся из кэша), но если вы сталкиваетесь с ситуацией, когда необходимо создать не-одиночный экземпляр, такой как генерация нового экземпляра страницы каждый раз, Spring недостаточно быстр. А для начальной скорости он довольно медленный.

Выше представлено введение в jBeanBox, других документов нет, потому что, в конце концов, основной исходный код jBeanBox составляет всего около 1500 строк (сторонние инструменты, такие как CGLIB, интерфейс JSR и т. д., не учитываются). Если у вас есть какие-либо вопросы о jBeanBox, проверить его исходный код — это простое решение.

Дополнительные демонстрации jBeanBox также можно увидеть в проекте jSqlBox (конфигурация источника данных, примеры декларативных транзакций и т.д.).

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/drinkjava2-jBeanBox.git
git@api.gitlife.ru:oschina-mirror/drinkjava2-jBeanBox.git
oschina-mirror
drinkjava2-jBeanBox
drinkjava2-jBeanBox
master