AopContext接口
beanMake(Class<?> clz)
使用场景:
在开发插件(或在一些特殊条件下),自动扫描组件没有被扫描到,一般是因为要注册的组件没有在启动类的包下。
// 启动类所在包为 org.xxx
package org.xxx;
import org.noear.solon.Solon;
public class Main {
public static void main(String[] args) {
Solon.start(Main.class,args);
}
}
// 组要注册的 controller 组件却在 com.test 包下,此时TestController 组件是不会被扫描到的
package com.test;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Mapping;
@Controller
public class TestController {
@Mapping("t1")
public String t1(){
return "t1";
}
}
由此可以使用 context.beanMake(TestController.class);
来注册组件,并且会触发@Controller 的注解功能
beanBuilderAdd(Class anno, BeanBuilder builder)
使用场景:给带指定注解的类赋能(自定义注解),比如@MyController,@MyService 注解等
anno
参数为 注解类,
builder
参数为 一个函数式接口,可以传递lambda 表达式或者实现了 BeanBuilder 接口的实现类
BeanBuilder类中的参数列表
void doBuild(Class<?> clz, BeanWrap bw, T anno) throws Throwable;
源码示例:
/** AopContext.java
* ::初始化(独立出 initialize,方便重写)
*/
protected void initialize() {
// ...
//注册 @Controller 构建器
beanBuilderAdd(Controller.class, (clz, bw, anno) -> {
new HandlerLoader(bw).load(Solon.app());
});
//注册 @ServerEndpoint 构建器
beanBuilderAdd(ServerEndpoint.class, (clz, wrap, anno) -> {
if (Listener.class.isAssignableFrom(clz)) {
Listener l = wrap.raw();
Solon.app().router().add(Utils.annoAlias(anno.value(), anno.path()), anno.method(), l);
}
});
//注册 @Inject 注入器
beanInjectorAdd(Inject.class, ((fwT, anno) -> {
beanInject(fwT, anno.value(), anno.required(), anno.autoRefreshed());
}));
}
执行了beanBuilderAdd
方法后,传入的lambda 表达式 或者 实现了BeanBuilder 的类中的内容不会立即执行,它们会全部添加到beanBuilders 的Map集合中
/** BeanContainer.java
* bean 构建器
*/
protected final Map<Class<?>, BeanBuilder<?>> beanBuilders = new HashMap<>();
//
public <T extends Annotation> void beanBuilderAdd(Class<T> anno, BeanBuilder<T> builder) {
beanBuilders.put(anno, builder);
}
那它们执行的时机是什么?通过查看beanBuilders.get()方法,可以看到获取它的时机。
源码分析
// AopContext.java
// 片段1
public void beanScan(Class<?> source) {
//确定文件夹名
if (source.getPackage() != null) {
beanScan(source.getClassLoader(), source.getPackage().getName()); //跟踪这里
}
}
/** AopContext.java
* // 片段2
* ::扫描源下的所有 bean 及对应处理
*/
public void beanScan(ClassLoader classLoader,String basePackage) {
if (Utils.isEmpty(basePackage)) {
return;
}
if (classLoader == null) {
return;
}
String dir = basePackage.replace('.', '/');
//扫描类文件并处理(采用两段式加载,可以部分bean先处理;剩下的为第二段处理)
ScanUtil.scan(classLoader, dir, n -> n.endsWith(".class"))
.stream()
.sorted(Comparator.comparing(s -> s.length()))
.forEach(name -> {
String className = name.substring