分析balde源码,查看Web工程处理Ioc注入的背后的过程,ThreadLocal 使用保存线程所有的request and respond,

分析balde源码,查看Web工程处理Ioc注入的背后的过程,ThreadLocal 使用保存线程所有的request and respond,查看 blade 源码 分析 IOC 依赖注入 IocApplicati 是一个轻量级的 JavaWeb 框架 进行嵌入 Jetty 开发的 其实不管是否嵌入开发道理都是差不多的 至于 Jetty 这个怎么实现的 没有看过源码 难度估计有点大 有时间慢慢的静下心来看看 这里说的嵌入了 Jeety 启动服务之前在 Jeety 中注入了一个 Listener 这个 Listener 和我们在原始的 Web 工程的原理其实是一样 javabalde 框架

查看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就是注入依赖,我们要得到依赖对象的实例,注入类的实例,所以一般的步骤是什么?反正不管,就是使用了反射哈哈,非常强大的工具,具体步骤如下。

  1. 配置文件,我们需要扫描包的位置在哪里com.xxx.contorl,这个就是举个例子
  2. 然后根据包名 String packageDirName = packageName.replace(‘.’, ‘/’);得到包的Dir路径。根据这个我们可以获得当前文件路径的URL,可以获得当前路径下的所有URL的枚举。 Enumeration dirs = this.getClass().getClassLoader().getResources(packageDirName);
  3. 获取了URL这个是文件协议+路径的地址,这样我们可以一个个的枚举得到文件的物理路径信息,比如:String filePath = URLDecoder.decode(url.getFile(), “UTF-8”);
  4. 找的了文件的路径,我们就可以创建文件File dir = new File(packagePath);
  5. 这里就可以递归的获取当前文件夹下面的所有的文件啊,对了我们可以过滤下文件只有当前文件下的文件夹和.class才是我们选择的范围哦
  6. 当我们遍历到当前不是文件的夹的时候就可以处理啦,因为获取到了file的实例,我们可以获取到文件的名称,每次递归的时候我们把包的名称也是传递下去(packageName + “.” + file.getName()),还有当前文件的绝对路径(file.getAbsolutePath()),通过这个我们又去创建文件,然后过滤哦。
  7. 上面那一步我们得到了当前类在项目中的包名称+类名(稍微处理一下),这样就可以反射到该类的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,分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2025-01-04 16:21
下一篇 2025-01-04 16:17

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/101540.html