当前位置: 首页 > news >正文

做设计做网站信息技术制作网站首页

做设计做网站,信息技术制作网站首页,智能网站建设模板售后,厦门市建设工程造价协会官方网站前置知识 《【Spring专题】Spring底层核心原理解析》 思路整理 我们在上一节《【Spring专题】Spring底层核心原理解析》课里面有简单分析过一个Spring容器的一般流程#xff0c;所以#xff0c;本节课我们这里尝试写一下简易的Spring容器。 手写源码示例 一、手写前的准…前置知识 《【Spring专题】Spring底层核心原理解析》 思路整理 我们在上一节《【Spring专题】Spring底层核心原理解析》课里面有简单分析过一个Spring容器的一般流程所以本节课我们这里尝试写一下简易的Spring容器。 手写源码示例 一、手写前的准备 1.1 注解 既然是需要手写Spring容器那我们肯定需要自定义一个MyApplicationContext类以及自定义注解ComponentScanComponentAutowired和Scope代码如下 Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public interface Component {String value() default ; }Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public interface ComponentScan {String value() default ; }Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public interface Scope {String value() default ; }Retention(RetentionPolicy.RUNTIME) Target(ElementType.FIELD) public interface Autowired { }其中Scope是为了验证多例Bean跟单例Bean的。当然需要准备的接口不止于此后面我会随着方法的完善逐渐引出其余需要的接口或者注解。 1.2 测试Bean 另外就是测试用的Bean代码如下 /*** author zhangshen* tag 【手写】* Date 2023/8/7 19:57* slogan 编码即学习**/ Component(userService) public class MyUserService implements InitializingBean, BeanPostProcessor {private int afterPropertiesSet 0;private int postProcessBeforeInitialization 0;private int postProcessAfterInitialization 0;private static int count 0;public void showYourFace() {System.out.println(手写的spring。。。。。 test一下);System.out.println(初始化是第几步答案是 afterPropertiesSet);System.out.println(初始化前是第几步答案是 postProcessBeforeInitialization);System.out.println(初始化后是第几步答案是 postProcessAfterInitialization);}Overridepublic void afterPropertiesSet() throws Exception {System.out.println(这里是初始化是在aware回调之后);count;afterPropertiesSet count;}Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println(这里是初始化前);count;postProcessBeforeInitialization count;return bean;}Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println(这里是初始化后);count;postProcessAfterInitialization count;return bean;} } 1.3 调用实例 调用实例 /*** 手写Spring容器测试** author zhangshen* tag 【手写】* Date 2023/8/7 19:55* slogan 编码即学习**/ public class MyApplicationContextTest {public static void main(String[] args) {MyApplicationContext context new MyApplicationContext(MyAppConfig.class);MyUserService userService (MyUserService) context.getBean(userService);userService.showYourFace();} }二、构造方法构建基本流程 在上节课中我们说过在容器启动的构造方法里面大致的流程可以分为三步 所以我们代码起手式是新建一个MyApplicationContext类然后一个默认的无参构造方法代码如下 public class MyApplicationContext {public MyApplicationContext(Class clazz) {// 步骤一扫描scan(clazz);// 步骤二IOCioc();// 步骤三AOPaop();} }但事实上我们前面说过虽然看似大过程是3步不过AOP是需要发生在IOC内部的Bean放入单例池之前所以后面我们会把这个AOP的过程写在IOC里面如下 public class MyApplicationContext {public MyApplicationContext(Class clazz) {// 步骤一扫描scan(clazz);// 步骤二IOCioc();}private void doIOC() {// 步骤三AOPaop();} }三、实现scan()方法 先上扫描的简易流程图 接下来我们只需要在doScan()方法里面实现这些步骤就好了。 /*** 扫描方法** param clazz 配置类*/private void scan(Class clazz) {// 步骤一获取扫描路径String basePackage doGetScanPackage(clazz);if (StrUtil.isEmpty(basePackage)) {return;}// 步骤二、三从电脑磁盘中加载文件并且扫描doLoadClassFromDiskAndScan(basePackage, clazz);}3.1 doGetScanPackage()获取扫描路径 /*** 获取包扫描路径** param clazz 配置类* return 扫描路径*/private String doGetScanPackage(Class clazz) {if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation (ComponentScan) clazz.getAnnotation(ComponentScan.class);return componentScanAnnotation.value();}return null;}3.2 doLoadClassFromDiskAndScan()从电脑磁盘中加载文件并且扫描 /*** 从磁盘中加载class并且扫描** param basePackage 扫描路径*/private void doLoadClassFromDiskAndScan(String basePackage) {// 将Java包名转换为电脑路径名basePackage basePackage.replace(., /);// 加载ClassLoader classLoader MyApplicationContext.class.getClassLoader();URL resource classLoader.getResource(basePackage);File file new File(resource.getFile());if (file.isDirectory()) {for (File listFile : file.listFiles()) {// 获取全限定名String fullyQualifiedClassName transferToFullyQualifiedClassName(listFile);try {Class? beanClass classLoader.loadClass(fullyQualifiedClassName);if (!beanClass.isAnnotationPresent(Component.class)) {continue;}// 记录Bean信息doRecordBeanInfo(beanClass);} catch (ClassNotFoundException e) {e.printStackTrace();}}}}方法解读 在上一步我们已经获取到了扫描路径但是扫描路径是我们Java包名现在需要从磁盘重读取的话定然是需要转换成电脑磁盘重的路径的。大家可能对classLoader这一块比较难理解这是属于JVM那一块的。不过这么说我们自己写的业务代码除非特别指定不然都是使用的同一个类加载器这里还有2个关键方法调用一个是transferToFullyQualifiedClassName()是将文件名转换为类加载器能识别的类全限定名另一个是doRecordBeanInfo()方法记录Bean定义信息的。代码会在后面 3.3 transferToFullyQualifiedClassName() 获取全限定名 /*** 将电脑磁盘上的文件转换为AppClassLoader能识别的类全限定名包 类名** p* 由于JVM的类加载器有三种默认加载用户自定义class文件的其实是ApplicationClassLoader* 该类加载器所能识别的其实是包名* /p** param listFile 电脑磁盘上的文件*/private String transferToFullyQualifiedClassName(File listFile) {String absolutePath listFile.getAbsolutePath();absolutePath absolutePath.substring(absolutePath.indexOf(org), absolutePath.indexOf(.class));absolutePath absolutePath.replace(\\, .);return absolutePath;}*3.4 doRecordBeanInfo() 记录Bean信息 /*** 过滤并且记录Bean信息*/private void doRecordBeanInfo(Class? beanClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 获取bean的名字String beanName doGetBeanName(beanClass);// 记录BeanPostProcessorif (BeanPostProcessor.class.isAssignableFrom(beanClass)) {BeanPostProcessor instance (BeanPostProcessor) beanClass.getConstructor().newInstance();beanPostProcessorList.add(instance);}BeanDefinition beanDefinition new BeanDefinition();beanDefinition.setType(beanClass);if (beanClass.isAnnotationPresent(Scope.class)) {Scope scopeAnnotation beanClass.getAnnotation(Scope.class);String value scopeAnnotation.value();beanDefinition.setScope(value);}// 记录BeanDefinitionbeanDefinitionMap.put(beanName, beanDefinition);}这里引入了一个新的类BeanDefinition类源码如下 /*** 手写Bean定义** author zhanghuitong* tag 【手写】 【Bean定义】 【图纸】* Date 2023/8/8 10:54* slogan 编码即学习**/ Getter Setter public class BeanDefinition {private static final String SINGLETON singleton;/*** Bean类型*/private Class type;/*** 作用域* 是原型还是单例*/private String scope SINGLETON; }也许大家会很奇怪为什么需要引入这么一个东西呢我都已经扫描好了我直接根据类信息生成不就行了吗如果你也有这个想法那么我反问你一个问题如果我后面想修改怎么办还别说真的有可能修改Spring提供了那么多拓展机制其中就有对类信息修改的拓展点至于具体应用场景我还没彻底学清楚我后面学到了我会回来更新的。但是为了方便大家理解这个玩意的存在我举个通俗的例子如下 BeanDefinition的存在更像是一份家具定制的图纸Bean是具体的某个家具。而Spring里面在后面我们会学到的ApplicationContext则是生产家具的厂家。这样类比的话你应该能想明白为什么需要BeanDefinition了吧。 总结一句话ApplicationContext厂家根据BeanDefinition图纸生成具体的某个家具Bean。PSApplicationContext 包含 BeanFactory都是Bean工厂 四、实现ioc()方法 先看看ioc简易流程图 PS下面的手写源码没有实现【推断构造方法】的逻辑 /*** 进行IOC过程*/private void ioc() {// 循环遍历beanDefinitionMapSetMap.EntryString, BeanDefinition entries beanDefinitionMap.entrySet();for (Map.EntryString, BeanDefinition entry : entries) {String beanName entry.getKey();BeanDefinition beanDefinition entry.getValue();if (beanDefinition.getScope().equals(BeanDefinition.SINGLETON)) {// 创建BeanObject bean createBean(beanName, beanDefinition);// AOPaop();singletonPool.put(beanName, bean);}}}方法解读 在这里就是遍历我们上一步获得的beanDefinitionMap图纸Map。接着就开始根据图纸生成Bean了createBean()方法接着看看是否需要AOP了在放入单例池之前最后就是放入单例池里面了 *4.1 createBean() 创建Bean /*** 创建bean** param beanName bean名称* param beanDefinition 对应的bean定义*/private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz beanDefinition.getType();Object instance null;try {// 实例化。本应该实现推断构造方法但是这里从简使用默认的instance clazz.getConstructor().newInstance();// 依赖注入doDependecyInjection(clazz, instance);// 各种Aware回调doAwareCallBack(beanName, instance);// 初始化前instance doInitBefore(beanName, instance);// 初始化doInit(instance);// 初始化后instance doInitAfter(beanName, instance);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}// 返回return instance;}方法解读 虽然上面的代码是简单的实现但也稍微能窥见一点IOC的生命周期了大家需要好好记住。 实例化 - 依赖注入 - 各种Aware回调 - 初始化前 - 初始化 - 初始化后 因为是简单实现所以上面这个流程不全应该是缺了点其他拓展点的实现的。但是局部上的顺序是没啥问题而且这不是我吹的而是在Spirng接口上注释的。 4.2 doDependecyInjection() 依赖注入 /*** 依赖注入** param clazz 需要被注入的对象的类信息* param instance 需要被注入的对象*/private void doDependecyInjection(Class clazz, Object instance) throws IllegalAccessException {Field[] declaredFields clazz.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.isAnnotationPresent(Autowired.class)) {declaredField.setAccessible(true);declaredField.set(instance, getBean(declaredField.getName()));}}}4.3 doAwareCallBack() 各种Aware回调 /*** 实现各种Aware的回调处理** param beanName bean名称* param instance bean对象*/private void doAwareCallBack(String beanName, Object instance) {if (instance instanceof BeanNameAware) {((BeanNameAware) instance).setBeanName(beanName);}}方法解读 在这里引入了一个新的接口BeanNameAware它实现自Aware接口。其实除了这个我们用来展示的BeanNameAware还有很多其他的XxxAware主要的目的是在实例化之后让我们获得感知一些Spring组件等等能力。下面是BeanNameAware接口的代码示例其中注释源于Spring源码 /*** 手写Spring的BeanNameAware接口** author zhangshen* tag 【手写】* Date 2023/8/8 19:04* slogan 编码即学习**/ public interface BeanNameAware {/*** 在创建该bean的bean工厂中设置该bean的名称。* 在填充普通bean属性之后但在初始化回调(如InitializingBean.afterPropertiesSet())或自定义初始化方法之前调用。* 参数:* 名称-工厂中bean的名称。请注意这个名称是工厂中使用的实际bean名称它可能与最初指定的名称不同:特别是对于内部bean名称实际bean名称可能通过附加“#…”后缀来使其唯一。如果需要使用BeanFactoryUtils.originalBeanName(String)方法提取原始bean名称(不带后缀)。*/void setBeanName(String name); } 4.4 doInitBefore() 初始化前 /*** 实施初始化之前** param beanName bean名称* param instance bean对象*/private Object doInitBefore(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance beanPostProcessor.postProcessBeforeInitialization(instance, beanName);}return instance;}方法解读 初始化前这是属于SpringIOC提供的一个拓展点需要一个非常非常非常重要的接口BeanPostProcessor 它的定义如下其中注释源于Spring源码 /*** 手写Spring的ABeanPostProcessor接口** author zhangshen* tag 【手写】* Date 2023/8/8 19:04* slogan 编码即学习**/ public interface BeanPostProcessor {/*** 在任何bean初始化回调(如InitializingBean的afterPropertiesSet或自定义初始化方法)之前将此BeanPostProcessor应用于给定的新bean实例。这个bean已经被属性值填充了。返回的bean实例可能是原始bean实例的包装器。* 默认实现按原样返回给定的bean。* 参数:* Bean——新的Bean实例* beanName—bean的名称* 返回:* 要使用的bean实例无论是原始的还是包装的;如果为空则不会调用后续的BeanPostProcessors* 抛出:* BeansException -在错误的情况下*/default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}/*** 在任何bean初始化回调(如InitializingBean的afterPropertiesSet或自定义init-method)之后将此BeanPostProcessor应用于给定的新bean实例。这个bean已经被属性值填充了。返回的bean实例可能是原始bean实例的包装器。* 对于FactoryBean将为FactoryBean实例和由FactoryBean创建的对象调用这个回调(从Spring 2.0开始)。后处理器可以通过相应的FactoryBean instanceof检查来决定是应用于FactoryBean还是已创建的对象或者两者都应用。* 这个回调也将在由InstantiationAwareBeanPostProcessor触发的短路之后被调用。postProcessBeforeInstantiation方法与所有其他BeanPostProcessor回调相反。* 默认实现按原样返回给定的bean。* 参数:* Bean——新的Bean实例* beanName—bean的名称* 返回:* 要使用的bean实例无论是原始的还是包装的;如果为空则不会调用后续的BeanPostProcessors* 抛出:* BeansException -在错误的情况下* 参见:* org.springframework.beans.factory.InitializingBean。afterPropertiesSet, org.springframework.beans.factory.FactoryBean* 以上翻译结果来自有道神经网络翻译YNMT· 通用场景*/default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;} }从上面的注释希望大家在后面真的阅读源码的时候能知道BeanPostProcessor这个拓展点就是作用在初始化前后的 4.5 doInit() 初始化 /*** 实施初始化方法** param instance bean对象*/private void doInit(Object instance) throws Exception {if (instance instanceof InitializingBean) {((InitializingBean) instance).afterPropertiesSet();}}方法解读 初始化方法在这里又是需要一个新的接口InitializingBean在工作使用频率挺高的。当然也可以通过自定义初始化方法不一定就是这个。接口代码如下其中注释源于Spring源码 /*** 手写Spring的ABeanPostProcessor接口** author zhangshen* tag 【手写】* Date 2023/8/8 19:04* slogan 编码即学习**/ public interface InitializingBean {/*** 在设置了所有bean属性并满足BeanFactoryAware、ApplicationContextAware等要求后由包含bean的BeanFactory调用。* 此方法允许bean实例在设置了所有bean属性后对其总体配置和最终初始化执行验证。* 抛出:* 异常-在配置错误的情况下(例如设置基本属性失败)或者由于任何其他原因导致初始化失败*/void afterPropertiesSet() throws Exception; }4.6 doInitAfter() 初始化后 /*** 实施初始化之后** param beanName bean名称* param instance bean对象*/private Object doInitAfter(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance beanPostProcessor.postProcessAfterInitialization(instance, beanName);}return instance;} 方法解读 初始化后跟初始化前一样是属于SpringIOC提供的一个拓展点。并且使用的也是BeanPostProcessor接口 五、实现aop()方法 这个就不实现了大家知道有这个东西就好 六、MyApplicationContext完整源码 package org.tuling.spring.handwriten;import cn.hutool.core.util.StrUtil; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.NoSuchBeanDefinitionException;import java.beans.Introspector; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.*;/*** 手写一个简易的Spring容器** author zhangshen* tag 【Spring】 【手写】* Date 2023/8/7 19:37* slogan 编码即学习**/ public class MyApplicationContext {private MapString, BeanDefinition beanDefinitionMap new HashMap();private MapString, Object singletonPool new HashMap();private ListBeanPostProcessor beanPostProcessorList new ArrayList();public MyApplicationContext(Class clazz) {// 扫描scan(clazz);// IOCioc();}/*** 进行IOC过程*/private void ioc() {// 循环遍历beanDefinitionMapSetMap.EntryString, BeanDefinition entries beanDefinitionMap.entrySet();for (Map.EntryString, BeanDefinition entry : entries) {String beanName entry.getKey();BeanDefinition beanDefinition entry.getValue();if (beanDefinition.getScope().equals(BeanDefinition.SINGLETON)) {// 创建BeanObject bean createBean(beanName, beanDefinition);// AOPaop();singletonPool.put(beanName, bean);}}}/*** 创建bean** param beanName bean名称* param beanDefinition 对应的bean定义*/private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz beanDefinition.getType();Object instance null;try {// 实例化。本应该实现推断构造方法但是这里从简使用默认的instance clazz.getConstructor().newInstance();// 依赖注入doDependecyInjection(clazz, instance);// 各种Aware回调doAwareCallBack(beanName, instance);// 初始化前instance doInitBefore(beanName, instance);// 初始化doInit(instance);// 初始化后instance doInitAfter(beanName, instance);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}// 返回return instance;}/*** 实施初始化之后** param beanName bean名称* param instance bean对象*/private Object doInitAfter(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance beanPostProcessor.postProcessAfterInitialization(instance, beanName);}return instance;}/*** 实施初始化方法** param instance bean对象*/private void doInit(Object instance) throws Exception {if (instance instanceof InitializingBean) {((InitializingBean) instance).afterPropertiesSet();}}/*** 实施初始化之前** param beanName bean名称* param instance bean对象*/private Object doInitBefore(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance beanPostProcessor.postProcessBeforeInitialization(instance, beanName);}return instance;}/*** 实现各种Aware的回调处理** param beanName bean名称* param instance bean对象*/private void doAwareCallBack(String beanName, Object instance) {if (instance instanceof BeanNameAware) {((BeanNameAware) instance).setBeanName(beanName);}}/*** 依赖注入** param clazz 需要被注入的对象的类信息* param instance 需要被注入的对象*/private void doDependecyInjection(Class clazz, Object instance) throws IllegalAccessException {Field[] declaredFields clazz.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.isAnnotationPresent(Autowired.class)) {declaredField.setAccessible(true);declaredField.set(instance, getBean(declaredField.getName()));}}}/*** 进行AOP过程*/private void aop() {}/*** 根据beanName获取Bean** param beanName bean名称* return bean对象*/public Object getBean(String beanName) {if (StrUtil.isEmpty(beanName)) {return null;}// 不存在beanDefinition则肯定不是beanif (!beanDefinitionMap.containsKey(beanName)) {throw new NoSuchBeanDefinitionException(没有对应的bean定义);}BeanDefinition beanDefinition beanDefinitionMap.get(beanName);// 创建对象if (beanDefinition.getScope().equals(BeanDefinition.SINGLETON)) {// 获取单例Object singletonBean singletonPool.get(beanName);if (singletonBean null) {singletonBean createBean(beanName, beanDefinition);singletonPool.put(beanName, singletonBean);}return singletonBean;} else {// 获取多例Object prototype createBean(beanName, beanDefinition);return prototype;}}/*** 扫描方法** param clazz 配置类*/private void scan(Class clazz) {// 获取扫描路径String basePackage doGetScanPackage(clazz);if (StrUtil.isEmpty(basePackage)) {return;}// 从电脑磁盘中加载文件并且扫描doLoadClassFromDiskAndScan(basePackage);}/*** 从磁盘中加载class并且扫描** param basePackage 扫描路径*/private void doLoadClassFromDiskAndScan(String basePackage) {// 将Java包名转换为电脑路径名basePackage basePackage.replace(., /);// 加载ClassLoader classLoader MyApplicationContext.class.getClassLoader();URL resource classLoader.getResource(basePackage);File file new File(resource.getFile());if (file.isDirectory()) {for (File listFile : file.listFiles()) {// 获取全限定名String fullyQualifiedClassName transferToFullyQualifiedClassName(listFile);try {Class? beanClass classLoader.loadClass(fullyQualifiedClassName);if (!beanClass.isAnnotationPresent(Component.class)) {continue;}// 记录Bean信息try {doRecordBeanInfo(beanClass);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}} catch (ClassNotFoundException e) {e.printStackTrace();}}}}/*** 过滤并且记录Bean信息*/private void doRecordBeanInfo(Class? beanClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 获取bean的名字String beanName doGetBeanName(beanClass);// 记录BeanPostProcessorif (BeanPostProcessor.class.isAssignableFrom(beanClass)) {BeanPostProcessor instance (BeanPostProcessor) beanClass.getConstructor().newInstance();beanPostProcessorList.add(instance);}BeanDefinition beanDefinition new BeanDefinition();beanDefinition.setType(beanClass);if (beanClass.isAnnotationPresent(Scope.class)) {Scope scopeAnnotation beanClass.getAnnotation(Scope.class);String value scopeAnnotation.value();beanDefinition.setScope(value);}// 记录BeanDefinitionbeanDefinitionMap.put(beanName, beanDefinition);}/*** 获取当前Bean的名字** param aClass 要加载的类* return bean的名字*/private String doGetBeanName(Class? aClass) {Component componentAnnotaion aClass.getAnnotation(Component.class);String beanName componentAnnotaion.value();if (StrUtil.isEmpty(beanName)) {beanName Introspector.decapitalize(aClass.getSimpleName());}return beanName;}/*** 将电脑磁盘上的文件转换为AppClassLoader能识别的类全限定名包 类名** p* 由于JVM的类加载器有三种默认加载用户自定义class文件的其实是ApplicationClassLoader* 该类加载器所能识别的其实是包名* /p** param listFile 电脑磁盘上的文件*/private String transferToFullyQualifiedClassName(File listFile) {String absolutePath listFile.getAbsolutePath();absolutePath absolutePath.substring(absolutePath.indexOf(org), absolutePath.indexOf(.class));absolutePath absolutePath.replace(\\, .);return absolutePath;}/*** 获取包扫描路径** param clazz 配置类* return 扫描路径*/private String doGetScanPackage(Class clazz) {if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation (ComponentScan) clazz.getAnnotation(ComponentScan.class);return componentScanAnnotation.value();}return null;} }
http://www.tj-hxxt.cn/news/132499.html

相关文章:

  • 网站的空间和域名是啥湖北联诺建设网站
  • 做网站的目的与意义做做网页
  • 友情链接有哪些展现形式株洲seo优化官网
  • 邯郸市教育考试院网站网站首页适配规则
  • 国家骨干高职院校建设网站网站建设数据库软件英文
  • html网站登陆注册怎么做海南代理注册公司
  • 查询网站空间的服务商今天泰安刚刚发生的新闻
  • 成都网站建设 Vr功能 卓 公司php做网站技术
  • 新网站如何做搜索引擎收录上海哪家做网站关键词排名
  • 宝塔wordpress ip访问全网最低价seo
  • 海口免费做网站信息服务平台怎么赚钱
  • 漂亮的网站改版中 html代码网站建设和前端开发的区别
  • 网站建设与域名建设微信代运营
  • 企业网站名称怎么写企业邮箱域名解析
  • 重庆做木门网站公司简介本地营销型网站建设
  • 桂林的网站建设公司cms开发教程
  • 房地产网站的设计要求接兼职建设网站
  • 网站被加黑链免费推广网站排名
  • 网站的设计理念专业网站制作需要多少钱
  • 招聘网站开发需要多长时间什么响应式网站
  • 网站建设视频教程最新外贸网站高端定做
  • 黄岛网站建设个人注册公司代理
  • 馀姚网站建设wordpress怎么填写
  • 郑州 做网站宝和网站建设
  • 哪里有做网站系统的app平台搭建
  • 做网站接电话一般要会什么问题济南英文网站建设
  • 新网站如何做搜索引擎收录校园网站建设重要性
  • 额敏网站建设汕头seo建站
  • 二手车网站开发没有基础学做网站
  • wordpress 无法创建目录 linux南通百度网站快速优化