查看blade源码,分析IOC依赖注入 IocApplication
blade是一个轻量级的JavaWeb框架,进行嵌入Jetty开发的,其实不管是否嵌入开发道理都是差不多的,至于Jetty这个怎么实现的,没有看过源码,难度估计有点大,有时间慢慢的静下心来看看,这里说的嵌入了Jeety启动服务之前在Jeety中注入了一个Listener,这个Listener和我们在原始的Web工程的原理其实是一样的,只是他们给我们规范了添加的格式的方式,我们自己写的时候,可以按照自己的方式处理自己的代码的逻辑。服务器启动的时候,我们处理IOC的逻辑也将在这里开始,设置我么你的根路径,IOC的扫描逻辑,这些就是我们学习的意义,更加的了解这些逻辑,自己即使使用其他的MVC框架也是一样的道理。
webAppContext.addEventListener(new BladeInitListener());
看看这个Listener简单的结构
import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import static com.blade.Blade.$; / * Blade Web Context Listener */ public class BladeInitListener implements ServletContextListener, HttpSessionListener {
@Override public void sessionCreated(HttpSessionEvent event) { int timeout = $().config().getInt("server.timeout", 15); event.getSession().setMaxInactiveInterval(timeout * 60); } @Override public void contextInitialized(ServletContextEvent sce) { } @Override public void contextDestroyed(ServletContextEvent sce) { WebContextHolder.destroy(); } @Override public void sessionDestroyed(HttpSessionEvent event) { } }
这里面的WebContextHolder处理是保存线程所有的Request和Response,有原始的HttpServletRequest 进行组合封装,在客户端进行请求的时候进行拦截到将这两个变量保存下来,在任何的地方都是可以访问的。平时我们处理的时候也是可以这样去处理的,非常的方便,实用。
public class WebContextHolder {
/ * BladeWebContext object for the current thread */ private static final ThreadLocal<WebContextHolder> ctx = new ThreadLocal<>(); //这个组合了我们的HttpServletRequest private Request request; / * Response */ private Response response; private WebContextHolder() { } public static WebContextHolder me() { return ctx.get(); } public static void init(Request request, Response response) { WebContextHolder bladeWebContext = new WebContextHolder(); bladeWebContext.request = request; bladeWebContext.response = response; ctx.set(bladeWebContext); } / * 移除当前线程的Request、Response对象 */ public static void remove() { ctx.remove(); } public static Request request() { return me().request; } public static Response response() { return me().response; } public static Session session() { return request().session(); } public static ServletContext servletContext() { return request().raw().getServletContext(); } public static void destroy() { ctx.remove(); } }
好像扯歪了,好吧!回来我们的主要的是IOC
处理IOC就是注入依赖,我们要得到依赖对象的实例,注入类的实例,所以一般的步骤是什么?反正不管,就是使用了反射哈哈,非常强大的工具,具体步骤如下。
- 配置文件,我们需要扫描包的位置在哪里com.xxx.contorl,这个就是举个例子
- 然后根据包名 String packageDirName = packageName.replace(‘.’, ‘/’);得到包的Dir路径。根据这个我们可以获得当前文件路径的URL,可以获得当前路径下的所有URL的枚举。 Enumeration dirs = this.getClass().getClassLoader().getResources(packageDirName);
- 获取了URL这个是文件协议+路径的地址,这样我们可以一个个的枚举得到文件的物理路径信息,比如:String filePath = URLDecoder.decode(url.getFile(), “UTF-8”);
- 找的了文件的路径,我们就可以创建文件File dir = new File(packagePath);
- 这里就可以递归的获取当前文件夹下面的所有的文件啊,对了我们可以过滤下文件只有当前文件下的文件夹和.class才是我们选择的范围哦
- 当我们遍历到当前不是文件的夹的时候就可以处理啦,因为获取到了file的实例,我们可以获取到文件的名称,每次递归的时候我们把包的名称也是传递下去(packageName + “.” + file.getName()),还有当前文件的绝对路径(file.getAbsolutePath()),通过这个我们又去创建文件,然后过滤哦。
- 上面那一步我们得到了当前类在项目中的包名称+类名(稍微处理一下),这样就可以反射到该类的Class
public class ClassInfo { private String className; private Class<?> clazz; public ClassInfo(String className) { this.className = className; } public ClassInfo(Class<?> clazz) { this.clazz = clazz; this.className = clazz.getName(); } public ClassInfo(String className, Class<?> clazz) { this.clazz = clazz; this.className = className; } public String getClassName() { return className; } public Class<?> getClazz() { return clazz; } public Object newInstance() { try { return clazz.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } }
收集到的包集合->ClassInfo集合->一步步的遍历——>Class中的方法
我们知道的是,我们现在写程序都喜欢使用注解,IOC等等都是使用注解去处理,自动化的给我们处理了很多的麻烦事。简单的方便啊!我们处理完了Class的集合还的需要去处理CLass上面的注解。有主要集中Controller,Compent,Service,RestController,Intercept等等
- 判断当前类的信息,接口和抽象类不要 ( !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) 根据返回值判断.
- 看看返回值中是否为NULL 然后进行相应的处理哦,就代表时候有注解在上面
Service service = clazz.getAnnotation(Service.class); Controller controller = clazz.getAnnotation(Controller.class); RestController restController = clazz.getAnnotation(RestController.class); Component component = clazz.getAnnotation(Component.class); @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Component { String value() default ""; }
- 这里主要是Compent讲解,这个比较简单.既然都说了讲解IOC容器说白了就是一个Map吧,哈哈!SimpleIoc 组合了HashMap,这个并不需要做并发处理
public class SimpleIoc implements Ioc {
private final Map<String, BeanDefine> pool = new HashMap<>(32); public void addBean(Object bean) { addBean(bean.getClass().getName(), bean); } public void addBean(Class<?> beanClass, Object bean) { Assert.notNull(beanClass); addBean(beanClass.getName(), bean); } public void addBean(String name, Object bean) { Assert.notNull(bean); BeanDefine beanDefine = new BeanDefine(bean); addBean(name, beanDefine); // add interface Class<?>[] interfaces = beanDefine.getType().getInterfaces(); if (interfaces.length > 0) { for (Class<?> interfaceClazz : interfaces) { this.addBean(interfaceClazz.getName(), beanDefine); } } } / * Register @Component marked objects */ public Object addBean(Class<?> type, boolean singleton) { Assert.notNull(type); return addBean(type.getName(), type, singleton); } public Object addBean(String name, Class<?> beanClass, boolean singleton) { Assert.notNull(name); Assert.notNull(beanClass); Assert.isFalse(beanClass.isInterface(), "Must not be interface: %s", beanClass.getName()); Assert.isFalse(Modifier.isAbstract(beanClass.getModifiers()), "Must not be abstract class: %s", beanClass.getName()); // LOGGER.debug("addBean: {} = {}", name, beanClass.getName()); BeanDefine beanDefine = this.getBeanDefine(beanClass, singleton); if (pool.put(name, beanDefine) != null) { LOGGER.warn("Duplicated Bean: {}", name); } // add interface Class<?>[] interfaces = beanClass.getInterfaces(); if (interfaces.length > 0) { for (Class<?> interfaceClazz : interfaces) { if (null != this.getBean(interfaceClazz)) { break; } this.addBean(interfaceClazz.getName(), beanDefine); } } return beanDefine.getBean(); } }
BeanDefine 这个就是简单的对于这个进行封装。
public class BeanDefine {
private Object bean; private Class<?> type; private boolean isSignle; public BeanDefine(Object bean) { this(bean, bean.getClass()); } public BeanDefine(Object bean, Class<?> type) { this.bean = bean; this.type = type; this.isSignle = true; } public BeanDefine(Object bean, Class<?> type, boolean isSingle) { this.bean = bean; this.type = type; this.isSignle = isSingle; } public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public Class<?> getType() { return type; } public void setType(Class<?> type) { this.type = type; } public boolean isSignle() { return isSignle; } public void setSignle(boolean isSignle) { this.isSignle = isSignle; }
- 看了源码发现我们在定义Compent上定义的值无效的,这里没有进行处理仅仅根据当前类的包的名称进行定义的,没有去管理这个值。
Component component = clazz.getAnnotation(Component.class); 看到没有这里没有去处理这个的值哦! if (null != service || null != component) { ioc.addBean(clazz); }
收集到的包集合->ClassInfo集合——>Class中的方法->SimpleIoc中的Map
现在我们该往里面注入注解存在的Object吧!这些注入进去的Service等等,之前我们在扫描包的时候都是得到实例了的,遍历一遍每个Class中的定义的Field然后注入就好了.
List<BeanDefine> beanDefines = ioc.getBeanDefines();//得到IOC-Map中的值的部分 if (null != beanDefines) { beanDefines.forEach(b -> IocKit.injection(ioc, b)); } //这里就是遍历处理,对于每一个private XXXX xxx;这种类注入哈哈!
public static void injection(Ioc ioc, BeanDefine beanDefine) { ClassDefine classDefine = ClassDefine.create(beanDefine.getType()); //对于Class进行封装 List<FieldInjector> fieldInjectors = IocKit.getInjectFields(ioc, classDefine); Object bean = beanDefine.getBean(); for (FieldInjector fieldInjector : fieldInjectors) { fieldInjector.injection(bean); } }
看看 ClassDefine
在说这个源码的时候,这里有很多的封装,将CLass对象进行了包装,能够很实用。
import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; public final class ClassDefine {
private static final ConcurrentHashMap<Class<?>, ClassDefine> pool = new ConcurrentHashMap<Class<?>, ClassDefine>(128); private final Class<?> clazz; private ClassDefine(Class<?> type) { this.clazz = type; } public static ClassDefine create(Class<?> clazz){ ClassDefine classDefine = pool.get(clazz); if (classDefine == null) { classDefine = new ClassDefine(clazz); ClassDefine old = pool.putIfAbsent(clazz, classDefine); if (old != null) { classDefine = old; } } return classDefine; } @SuppressWarnings("unchecked") public <T> Class<T> getType() { return (Class<T>) clazz; } public String getName() { return clazz.getName(); } public String getSimpleName() { return clazz.getSimpleName(); } public ClassDefine getSuperKlass() { Class<?> superKlass = clazz.getSuperclass(); return (superKlass == null) ? null : ClassDefine.create(superKlass); } public List<ClassDefine> getInterfaces() { Class<?>[] interfaces = clazz.getInterfaces(); if (interfaces.length == 0) { return Collections.emptyList(); } List<ClassDefine> results = new ArrayList<ClassDefine>(interfaces.length); for (Class<?> intf : interfaces) { results.add(ClassDefine.create(intf)); } return results; } // ------------------------------------------------------------------ public Annotation[] getAnnotations() { return clazz.getAnnotations(); } public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return clazz.getAnnotation(annotationClass); } public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationClass) { return clazz.isAnnotationPresent(annotationClass); } public Field[] getDeclaredFields() { return clazz.getDeclaredFields(); } // ------------------------------------------------------------------ public int getModifiers() { return clazz.getModifiers(); } public boolean isInterface() { return Modifier.isInterface(getModifiers()); } public boolean isAbstract() { return Modifier.isAbstract(getModifiers()); } public boolean isStatic() { return Modifier.isStatic(getModifiers()); } public boolean isPrivate() { return Modifier.isPrivate(getModifiers()); } public boolean isProtected() { return Modifier.isProtected(getModifiers()); } public boolean isPublic() { return Modifier.isPublic(getModifiers()); } }
List< FieldInjector> fieldInjectors = IocKit.getInjectFields(ioc, classDefine); 看看定义的FileInJector
这个定义很简单的,就是将我们过滤过的Field(需要进行注入的Field抓出来),getInjectFields就是这样处理的啊,看看上面有没有某个注解,有的话装到List中去。
public class FieldInjector implements Injector {
private Ioc ioc; private Field field; public FieldInjector(Ioc ioc, Field field) { this.ioc = ioc; this.field = field; } @Override public void injection(Object bean) { try { Class<?> fieldType = field.getType(); Object value = ioc.getBean(fieldType); if (value == null) { throw new IllegalStateException("Can't inject bean: " + fieldType.getName() + " for field: " + field); } field.setAccessible(true);//可访问性 field.set(bean, value);//当前类的实例变量,当前field的值 } catch (Exception e) { throw new RuntimeException(e); } } }
将满足某个注解的留下来
public static List<FieldInjector> getInjectFields(Ioc ioc, ClassDefine classDefine) { List<FieldInjector> injectors = new ArrayList<FieldInjector>(8); for (Field field : classDefine.getDeclaredFields()) { for (Annotation annotation : field.getAnnotations()) { InjectWith with = annotation.annotationType().getAnnotation(InjectWith.class); if (with != null) { injectors.add(new FieldInjector(ioc, field)); } } } if (injectors.size() == 0) { return Collections.emptyList(); } return injectors; }
再来看看
public static void injection(Ioc ioc, BeanDefine beanDefine) { ClassDefine classDefine = ClassDefine.create(beanDefine.getType()); List<FieldInjector> fieldInjectors = IocKit.getInjectFields(ioc, classDefine); Object bean = beanDefine.getBean(); //便利所有需要注入的field的封装,然后注入当前类的实例变量,搞定 for (FieldInjector fieldInjector : fieldInjectors) { fieldInjector.injection(bean); } }
大工告成,基于注解的IOC
今天的文章 分析balde源码,查看Web工程处理Ioc注入的背后的过程,ThreadLocal 使用保存线程所有的request and respond,分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/101540.html