License: Apache 2.0
jBeanBox is a micro-scale IOC/AOP tool. Except third-party libraries, its core source code is only about 3000 lines. It uses the "Box" programming model, using pure Java classes as a configuration. jBeanBox runs on JDK 1.6 or above.
The purpose of jBeanBox development is to overcome some of the problems of other IOC/AOP tools:
Add the following configuration in pom.xml:
<dependency>
<groupId>com.github.drinkjava2</groupId>
<artifactId>jbeanbox</artifactId>
<version>4.0.0</version> <!-- Or newest version -->
</dependency>
jBeanBox does not depend on any third-party libraries. To avoid package conflicts, third-party libraries such as CGLIB that it uses are included in jBeanBox by source code.
jBeanBox jar size is large, about 460K, if you do not need AOP feature, you can only use its DI kernel, called "jBeanBoxDI", only 49k size, put below in pom.xml:
<dependency>
<groupId>com.github.drinkjava2</groupId>
<artifactId>jbeanboxdi</artifactId>
<version>4.0.0</version> <!-- Or newest version -->
</dependency>
The demo shows 10 different injection methods:
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: User5
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
}
}
The output of this example is to print out "User1", "User2"... to "User10" in sequence. Here is the explanation:
Above example mainly demonstrates the Java method configuration of jBeanBox. The Java method can be executed dynamically, or it can be executed as a fixed configuration in the defined BeanBox class. The fixed configuration can lay down the configuration keynote. When you need to change, you can use the same Java method to adjust (because it is the same BeanBox object) or even temporarily create a new configuration, so jBeanBox has the advantages of fixed configuration and dynamic configuration. In addition, when there is no source code, for example, to configure an instance of a third-party library, all annotation methods are not used at this time, and the only Java configuration method that can be used is the only one.
The value() method in above example is a global method statically introduced from the JBEANBOX class. The source code for this example is located in HelloWorld.java under the unit test directory.
jBeanBox not only supports Java mode configuration, but also supports annotation mode configuration. It supports the following annotations:
@INJECT is similar to the @Inject annotation in JSR, but allows the addition of the target class as a parameter
@POSTCONSTRUCT is equivalent to the @PostConstruct annotation in JSR
@PREDESTROY is equivalent to @PreDestroy annotation in JSR
@VALUE is similar to the @Value annotation in Spring
@PROTOTYPE is equivalent to @Prototype annotation in Spring
@AOP is used to customize AOP annotations. See the AOP section for details.
@NAMED is equivalent to @Named annotation in JSR
@QUALIFIER is equivalent to @Qulifier annotation in JSR
Because everyone is familiar with annotation method configuration, here has no detailed introduction, in jBeanBox\test directory can find an "AnnotationInjectTest.java" file, demonstrating the use of various annotation mode configuration. To disable JSR, Spring annotations, can use ctx.setAllowSpringJsrAnnotation(false) method. To disable all annotations (that means only Java configurationcan be used) use ctx.setAllowAnnotation(false) method.
Below are some demos of annotation configruation:
//Class inject
@PROTOTYPE
@VALUE("3")
public static class Demo4 { }//ctx.getBean(Demo4.class) will return "3"
@INJECT(Demo4.class) @PROTOTYPE
public static class Demo5 { } //prototype
@INJECT(value=Demo4.class )
public static class Demo6 { } //singleton
@INJECT(value=Demo4.class )
public static interface inf1{}//singleton
@INJECT(value=Demo4.class, pureValue=true) //return Demo4.class
public static interface inf2{}
//Constructor inject
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; } }
//Field inject
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;
}
//Method inject
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;
}
}
The example one is a general demonstration of the Java mode configuration of jBeanBox, and new let's go back to explain all Java configuration mothods in detail:
The Java configuration, there are 2 special methods in BeanBox class: createºÍconfig. See below:
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);
}
}
The above example shows that the bean created in DemoBox is generated by the create method and modified by the config method. The Caller parameter in the create and config methods can be omitted if you don't need to use this Caller parameter to load other beans.
Most of the jBeanBox functions can be implemented in either Java configuration or annotation configuration. Similarly, there are two ways to support 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);
}
The above naming rules use "*" as a fuzzy matching character, representing any length, any character.
The annotation method AOP has only two types, for method and for class. The annotation method requires a special annotation @AOP, which is used to customize the AOP annotations. The usage examples are as follows:
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);
}
The AOP mentioned in this article is for the interface of the Aop alliance federation standard. It has been included in the jBeanBox and does not need to be introduced separately (of course, there is no problem with repeated introduction). The Aop alliance federation standard is a useful interface to achieve interchangeability between various AOP implementations. Based on it, jBeanBox can replace Spring's kernel and use its declarative transaction. This interchangeability can be achieved. The premise is that Spring's declarative transaction implementation (such as TransactionInterceptor) also implements the Aop alliance federation standard interface MethodInterceptor.
Since the 2.4.8 version, the ABean function has been cut off, and the unused pre-, post-, and abnormal aspect functions have been removed. Only the functions of the AOP alliance standard interface MethodInterceptor have been retained (note that there is also an interface with the same name in CGLIB). , don't confuse). The class that implements the MethodInterceptor interface, usually called Interceptor, but saves it in the jBeanBox, also called it AOP, after all, writing addBeanAop is simpler than writing addBeanInterceptor.
jBeanBox supports loop dependency detection. If a loop dependency injection is found (such as injecting B in the A constructor and injecting A in the constructor of B), a BeanBoxException runtime exception will be thrown.
However, circular dependency injections that occur in such fields or methods are allowed in 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 supports multiple context instances (BeanBoxContext), and each context instance does not. For example, a User.class can generate three singletons in different contexts (annotations, Java) in three contexts. These three "singletons" are unique relative to the current context, and their properties and their respective Configuration related.
The JBEANBOX.getBean() method takes advantage of a default global context, which can be retrieved using the JBEANBOX.ctx() method, so if you don't need multiple contexts in a project, you can use the JBEANBOX.getBean() method directly. Get the instance, which saves a line of code that creates a new context.
Each instance of BeanBoxContext maintains configuration information, singleton cache, etc. internally. After the close method of the BeanBoxContext instance is called, its configuration information and singleton are cleared. Of course, before the emptying, all singletons of PreDestroy are cleared. The method (if any) is called to run. So for the context that needs to call back the PreDestroy method, don't forget to call the close method when closing. For the default global context, this is the JBEANBOX.close() method.
Detailed methods of BeanBoxContext are explained in detail:
The following is the comparison of the performance of jBeanBox with other IOC tools (only compare the DI injection function, build an instance tree composed of 6 objects), it can be seen that jBeanBox creates a non-singleton bean half speed of Guice and 45 times faster than Spring. The test project is located in: [di-benchmark] (https://github.com/drinkjava2/di-benchmark)
Runtime benchmark, fetch new bean for 500000 times:
---------------------------------------------------------
Vanilla| 31ms
Guice| 1154ms
Feather| 624ms
Dagger| 312ms
Genie| 609ms
Pico| 4555ms
jBeanBoxNormal| 2075ms
jBeanBoxTypeSafe| 2371ms
jBeanBoxAnnotation| 2059ms
SpringJavaConfiguration| 92149ms
SpringAnnotationScanned| 95504ms
Split Starting up DI containers & instantiating a dependency graph 4999 times:
-------------------------------------------------------------------------------
Vanilla| start: 0ms fetch: 0ms
Guice| start: 1046ms fetch: 1560ms
Feather| start: 0ms fetch: 109ms
Dagger| start: 46ms fetch: 173ms
Pico| start: 376ms fetch: 217ms
Genie| start: 766ms fetch: 247ms
jBeanBoxNormal| start: 79ms fetch: 982ms
jBeanBoxTypeSafe| start: 0ms fetch: 998ms
jBeanBoxAnnotation| start: 0ms fetch: 468ms
SpringJavaConfiguration| start: 51831ms fetch: 1834ms
SpringAnnotationScanned| start: 70712ms fetch: 4155ms
Runtime benchmark, fetch singleton bean for 5000000 times:
---------------------------------------------------------
Vanilla| 47ms
Guice| 1950ms
Feather| 624ms
Dagger| 2746ms
Genie| 327ms
Pico| 3385ms
jBeanBoxNormal| 188ms
jBeanBoxTypeSafe| 187ms
jBeanBoxAnnotation| 171ms
SpringJavaConfiguration| 1061ms
SpringAnnotationScanned| 1045ms
Although most of the IOC tools are used in singleton cases, the performance is almost the same (because it is taken from the cache), but if you encounter a situation where you must generate a non-single instance, such as generating a new page instance each time, Spring is not fast enough. And for the starting speed, it is pretty slow.
The above is the introduction of jBeanBox, there is no other documents, because after all, jBeanBox's core source code is only ~1500 lines (third-party tools such as CGLIB, JSR interface etc. are not counted). If you have any questions of jBeanBox, to check its source code is a easy solution.
More demos of jBeanBox can also be seen in the jSqlBox project (data source configuration, declarative transaction examples, etc.).
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。