Many people marvel at Solon's ability to inject. How can one annotation inject everything?
I. Annotation injectors
One of the four wonders of Solon Ioc: the annotation injector (BeanInjector<T extends Annotation>
). When scanning, Solon will check the field or parameter of the component in question, does it have any annotations on it? If there is an annotation, has the corresponding injector been registered? If so, the injector is executed.
1. What is a note?
Annotations, also commonly called metadata, are code-level descriptive content. Compilers can do a lot of magic with annotations at compile time; Solon Ioc does a lot of magic with annotations at runtime.
One of the four magic tricks of Solon Ioc is the annotation injector.
2. What does the injector interface look like?
@FunctionalInterface
public interface BeanInjector<T extends Annotation> {
void doInject(VarHolder vh, T anno);
}
Among them:
- vh for receiving variable data
- anno, on the other hand, is the annotation of the assertion
3. Solon Ioc's injector registration interface
void beanInjectorAdd(Class<T> annoClz, BeanInjector<T> injector);
void beanInjectorAdd(Class<T> annoClz, Class<?> targetClz, BeanInjector<T> injector);
II. Why can it also be called a "void" injector?
The reason for this is that Solon's injections enforce an interface, not a defined content. The content can be ready-made or dynamically constructed. So it's very "empty".
1. Decomposition of the built-in@Inject
annotation implementation
@Inject
A simple example of the use of the
//Primary Input Configuration
@Component
public class DemoService{
@Inject("${}")
String trackUrl;
@Inject("${track.db1}")
HikariDataSource trackDs;
}
//pour into bean
@Component
public class DemoService{
@Inject
private static TrackService trackService;
@Inject("userService")
private UserService userService;
}
Anatomy of an injector capability implementation (simple schematic implementation, the framework implementation is more complex than this)
(, (vh, anno) -> {
// Affirmation: must it be injected?
(());
if (()) {
//No value, means it's bean type injection
().getBeanAsync((), bean->{ //() i.e. context. may be different when "hotplugging"
(bean); }); //() i.e. context.
}); } else { //() i.e. context.
} else {
if(().startsWith("${")) {
// indicates that it's configuration injection
String val = ().cfg().getByExpr((()); }); } else { if().startsWith("${")) {/ indicates that it's a configuration injection
(val);
} else {
//Indicates bean name injection
().getBeanAsync((), bean->{
(bean).
});
}
}
});
2. "Type enhancement" injector. Magic upgrades!
Solon's built-in injector, don't you like it?
Can I change my realization? Yes! Is it too much to replace the code completely and want to add injection for specific types? Yes! For example, let's design aEsMapper<T>
Used to operate Elasticsearch. which can then be freely extended:
public interface AuthorMapper extends EsMapper<Author> {
}
public interface CommentMapper extends EsMapper<Comment> {
}
public interface ContactMapper extends EsMapper<Contact> {
}
public interface DocumentMapper extends EsMapper<Document> {
}
Presumably there will be a desire to extend more subclasses?"Type Enhancement" Injector in hand, I have everything.。
EsMapperFactory esMapperFactory;
(, , (vh, anno) -> {
EsMapper mapper = (());
(mapper);
});
Can then borrow the container's "cache" feature, the same type of injection performance is improved:
EsMapperFactory esMapperFactory;
(, , (vh, anno) -> {
EsMapper mapper = ().getBean(());
if (mapper == null) {
mapper = (());
().wrapAndPut((), bean); //It's possible it's being proxied.,relationship between type and () not the same
().wrapAndPut((), bean);
}
(mapper);
});
If there is a notion of "multiple sources", we can also support@Inject("name")
:
EsMapperFactory esMapperFactory.
(, , (vh, anno) -> {
EsMapper mapper = null;
if (()) {
// Fetch by type
mapper = ().getBean(());
} else {
// by name
mapper = ().getBean(()); } else { //fetch by name
}
if (mapper == null) {
mapper = ((), ()); } if (mapper == null) {
if (()) {
// inject by class injection; just cache by type
().wrapAndPut((), bean); //It's possible that it's being proxied with a different type than ()
().wrapAndPut((), bean);
} else {
// Inject by class name; just cache by name
().wrapAndPut((), bean); }
}
}
(mapper);
}).
Now we're ready to use it. (Dinner's ready, work's ready!) :
//Primary Input Configuration
@Component
public class DemoService{
@Inject
DocumentMapper documentMapper;
@Inject("es2")
DocumentMapper documentMapper2;
}