精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

笑傲江湖,通過注解配置和包自動掃描的方式完成Bean對象的注冊

開發 前端
你經歷過618和雙11嗎?你加入過大促時候那么多復雜的營銷活動賺幾毛錢嗎?你開發過連讀明白玩法都需要一周但只使用3天的大促需求嗎?

 [[413653]]

本文轉載自微信公眾號「bugstack蟲洞棧」,作者小傅哥。轉載本文請聯系bugstack蟲洞棧公眾號。

目錄

  • 一、前言
  • 二、目標
  • 三、方案
  • 四、實現
    • 1. 工程結構
    • 2. 處理占位符配置
    • 3. 定義攔截注解
    • 4. 處理對象掃描裝配
    • 5. 解析xml中調用掃描
  • 五、測試
    • 1. 事先準備
    • 2. 屬性配置文件
    • 3. spring.xml 配置對象
    • 4. 單元測試(占位符)
    • 5. 單元測試(包掃描)
  • 六、總結

一、前言

忒復雜,沒等搞明白大促都過去了!

你經歷過618和雙11嗎?你加入過大促時候那么多復雜的營銷活動賺幾毛錢嗎?你開發過連讀明白玩法都需要一周但只使用3天的大促需求嗎?有時候對于有些產品的需求真的是太復雜了,復雜到開發、測試都需要在整個過程中不斷的學習最后才可能讀懂產品為啥這樣的玩,要是一個長期的活動可能也就算了,培養用戶心智嗎!但這一整套拉新、助力、激活、下單、投保、領券、消費、開紅包等等一連串的騷操作下來,如果在線上只用3天呢,或者是只用1天,那TM連參與的用戶都沒弄明白呢,活動就結束了,最后能打來什么樣好的數據呢?對于這樣流程復雜,估計連羊毛黨都看不上!!!

以上只是舉個例子,大部分時候并不會搞的這么惡心,評審也是過不去的!而同樣的道理用在程序設計開發和使用中也是一樣的,如果你把你的代碼邏輯實現的過于分散,讓外部調用方在使用的時候,需要調用你的接口多個和多次,還沒有消息觸達,只能定時自己輪訓你的接口查看訂單狀態,每次還只能查10條,查多了你說不行,等等反人類的設計,都會給調用方帶來要干你的體會。

所以,如果我們能在完成目的的情況下,都是希望盡可能流程簡單、模式清晰、自動服務。那這在Spring的框架中也是有所體現的,這個框架的普及使用程度和它所能帶來的方便性是分不開的,而我們如果能做到如此的方便,那肯定是一種好的設計和實現。

二、目標

其實到本章節我們已經把關于 IOC 和 AOP 全部核心內容都已經實現完成了,只不過在使用上還有點像早期的 Spring 版本,需要一個一個在 spring.xml 中進行配置。這與實際的目前使用的 Spring 框架還是有蠻大的差別,而這種差別其實都是在核心功能邏輯之上建設的在更少的配置下,做到更簡化的使用。

這其中就包括:包的掃描注冊、注解配置的使用、占位符屬性的填充等等,而我們的目標就是在目前的核心邏輯上填充一些自動化的功能,讓大家可以學習到這部分的設計和實現,從中體會到一些關于代碼邏輯的實現過程,總結一些編碼經驗。

三、方案

首先我們要考慮??,為了可以簡化 Bean 對象的配置,讓整個 Bean 對象的注冊都是自動掃描的,那么基本需要的元素包括:掃描路徑入口、XML解析掃描信息、給需要掃描的Bean對象做注解標記、掃描Class對象摘取Bean注冊的基本信息,組裝注冊信息、注冊成Bean對象。那么在這些條件元素的支撐下,就可以實現出通過自定義注解和配置掃描路徑的情況下,完成 Bean 對象的注冊。除此之外再順帶解決一個配置中占位符屬性的知識點,比如可以通過 ${token} 給 Bean 對象注入進去屬性信息,那么這個操作需要用到 BeanFactoryPostProcessor,因為它可以處理 在所有的 BeanDefinition 加載完成后,實例化 Bean 對象之前,提供修改 BeanDefinition 屬性的機制 而實現這部分內容是為了后續把此類內容結合到自動化配置處理中。整體設計結構如下圖:

結合bean的生命周期,包掃描只不過是掃描特定注解的類,提取類的相關信息組裝成BeanDefinition注冊到容器中。

在XmlBeanDefinitionReader中解析標簽,掃描類組裝BeanDefinition然后注冊到容器中的操作在ClassPathBeanDefinitionScanner#doScan中實現。

  • 自動掃描注冊主要是掃描添加了自定義注解的類,在xml加載過程中提取類的信息,組裝 BeanDefinition 注冊到 Spring 容器中。
  • 所以我們會用到 配置包路徑并在 XmlBeanDefinitionReader 解析并做相應的處理。這里的處理會包括對類的掃描、獲取注解信息等
  • 最后還包括了一部分關于 BeanFactoryPostProcessor 的使用,因為我們需要完成對占位符配置信息的加載,所以需要使用到 BeanFactoryPostProcessor 在所有的 BeanDefinition 加載完成后,實例化 Bean 對象之前,修改 BeanDefinition 的屬性信息。這一部分的實現也為后續處理關于占位符配置到注解上做準備

四、實現

1. 工程結構

  1. small-spring-step-12 
  2. └── src 
  3.     ├── main 
  4.     │   └── java 
  5.     │       └── cn.bugstack.springframework 
  6.     │           ├── aop 
  7.     │           │   ├── aspectj 
  8.     │           │   │   └── AspectJExpressionPointcut.java 
  9.     │           │   │   └── AspectJExpressionPointcutAdvisor.java 
  10.     │           │   ├── framework  
  11.     │           │   │   ├── adapter 
  12.     │           │   │   │   └── MethodBeforeAdviceInterceptor.java 
  13.     │           │   │   ├── autoproxy 
  14.     │           │   │   │   └── MethodBeforeAdviceInterceptor.java 
  15.     │           │   │   ├── AopProxy.java 
  16.     │           │   │   ├── Cglib2AopProxy.java 
  17.     │           │   │   ├── JdkDynamicAopProxy.java 
  18.     │           │   │   ├── ProxyFactory.java 
  19.     │           │   │   └── ReflectiveMethodInvocation.java 
  20.     │           │   ├── AdvisedSupport.java 
  21.     │           │   ├── Advisor.java 
  22.     │           │   ├── BeforeAdvice.java 
  23.     │           │   ├── ClassFilter.java 
  24.     │           │   ├── MethodBeforeAdvice.java 
  25.     │           │   ├── MethodMatcher.java 
  26.     │           │   ├── Pointcut.java 
  27.     │           │   ├── PointcutAdvisor.java 
  28.     │           │   └── TargetSource.java 
  29.     │           ├── beans 
  30.     │           │   ├── factory 
  31.     │           │   │   ├── config 
  32.     │           │   │   │   ├── AutowireCapableBeanFactory.java 
  33.     │           │   │   │   ├── BeanDefinition.java 
  34.     │           │   │   │   ├── BeanFactoryPostProcessor.java 
  35.     │           │   │   │   ├── BeanPostProcessor.java 
  36.     │           │   │   │   ├── BeanReference.java 
  37.     │           │   │   │   ├── ConfigurableBeanFactory.java 
  38.     │           │   │   │   ├── InstantiationAwareBeanPostProcessor.java 
  39.     │           │   │   │   └── SingletonBeanRegistry.java 
  40.     │           │   │   ├── support 
  41.     │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java 
  42.     │           │   │   │   ├── AbstractBeanDefinitionReader.java 
  43.     │           │   │   │   ├── AbstractBeanFactory.java 
  44.     │           │   │   │   ├── BeanDefinitionReader.java 
  45.     │           │   │   │   ├── BeanDefinitionRegistry.java 
  46.     │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java 
  47.     │           │   │   │   ├── DefaultListableBeanFactory.java 
  48.     │           │   │   │   ├── DefaultSingletonBeanRegistry.java 
  49.     │           │   │   │   ├── DisposableBeanAdapter.java 
  50.     │           │   │   │   ├── FactoryBeanRegistrySupport.java 
  51.     │           │   │   │   ├── InstantiationStrategy.java 
  52.     │           │   │   │   └── SimpleInstantiationStrategy.java   
  53.     │           │   │   ├── support 
  54.     │           │   │   │   └── XmlBeanDefinitionReader.java 
  55.     │           │   │   ├── Aware.java 
  56.     │           │   │   ├── BeanClassLoaderAware.java 
  57.     │           │   │   ├── BeanFactory.java 
  58.     │           │   │   ├── BeanFactoryAware.java 
  59.     │           │   │   ├── BeanNameAware.java 
  60.     │           │   │   ├── ConfigurableListableBeanFactory.java 
  61.     │           │   │   ├── DisposableBean.java 
  62.     │           │   │   ├── FactoryBean.java 
  63.     │           │   │   ├── HierarchicalBeanFactory.java 
  64.     │           │   │   ├── InitializingBean.java 
  65.     │           │   │   ├── ListableBeanFactory.java 
  66.     │           │   │   └── PropertyPlaceholderConfigurer.java 
  67.     │           │   ├── BeansException.java 
  68.     │           │   ├── PropertyValue.java 
  69.     │           │   └── PropertyValues.java  
  70.     │           ├── context 
  71.     │           │   ├── annotation 
  72.     │           │   │   ├── ClassPathBeanDefinitionScanner.java  
  73.     │           │   │   ├── ClassPathScanningCandidateComponentProvider.java  
  74.     │           │   │   └── Scope.java  
  75.     │           │   ├── event 
  76.     │           │   │   ├── AbstractApplicationEventMulticaster.java  
  77.     │           │   │   ├── ApplicationContextEvent.java  
  78.     │           │   │   ├── ApplicationEventMulticaster.java  
  79.     │           │   │   ├── ContextClosedEvent.java  
  80.     │           │   │   ├── ContextRefreshedEvent.java  
  81.     │           │   │   └── SimpleApplicationEventMulticaster.java  
  82.     │           │   ├── support 
  83.     │           │   │   ├── AbstractApplicationContext.java  
  84.     │           │   │   ├── AbstractRefreshableApplicationContext.java  
  85.     │           │   │   ├── AbstractXmlApplicationContext.java  
  86.     │           │   │   ├── ApplicationContextAwareProcessor.java  
  87.     │           │   │   └── ClassPathXmlApplicationContext.java  
  88.     │           │   ├── ApplicationContext.java  
  89.     │           │   ├── ApplicationContextAware.java  
  90.     │           │   ├── ApplicationEvent.java  
  91.     │           │   ├── ApplicationEventPublisher.java  
  92.     │           │   ├── ApplicationListener.java  
  93.     │           │   └── ConfigurableApplicationContext.java 
  94.     │           ├── core.io 
  95.     │           │   ├── ClassPathResource.java  
  96.     │           │   ├── DefaultResourceLoader.java  
  97.     │           │   ├── FileSystemResource.java  
  98.     │           │   ├── Resource.java  
  99.     │           │   ├── ResourceLoader.java 
  100.     │           │   └── UrlResource.java 
  101.     │           ├── stereotype 
  102.     │           │   └── Component.java 
  103.     │           └── utils 
  104.     │               └── ClassUtils.java 
  105.     └── test 
  106.         └── java 
  107.             └── cn.bugstack.springframework.test 
  108.                 ├── bean 
  109.                 │   ├── IUserService.java 
  110.                 │   └── UserService.java 
  111.                 └── ApiTest.java 

工程源碼:公眾號「bugstack蟲洞棧」,回復:Spring 專欄,獲取完整源碼

在Bean的生命周期中自動加載包掃描注冊Bean對象和設置占位符屬性的類關系,如圖 14-2

圖 14-2

  • 整個類的關系結構來看,其實涉及的內容并不多,主要包括的就是 xml 解析類 XmlBeanDefinitionReader 對 ClassPathBeanDefinitionScanner#doScan 的使用。
  • 在 doScan 方法中處理所有指定路徑下添加了注解的類,拆解出類的信息:名稱、作用范圍等,進行創建 BeanDefinition 好用于 Bean 對象的注冊操作。
  • PropertyPlaceholderConfigurer 目前看上去像一塊單獨的內容,后續會把這塊的內容與自動加載 Bean 對象進行整合,也就是可以在注解上使用占位符配置一些在配置文件里的屬性信息。

2. 處理占位符配置

  1. public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor { 
  2.  
  3.     /** 
  4.      * Default placeholder prefix: {@value} 
  5.      */ 
  6.     public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"
  7.  
  8.     /** 
  9.      * Default placeholder suffix: {@value} 
  10.      */ 
  11.     public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"
  12.  
  13.     private String location; 
  14.  
  15.     @Override 
  16.     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
  17.         // 加載屬性文件 
  18.         try { 
  19.             DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); 
  20.             Resource resource = resourceLoader.getResource(location); 
  21.             Properties properties = new Properties(); 
  22.             properties.load(resource.getInputStream()); 
  23.  
  24.             String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); 
  25.             for (String beanName : beanDefinitionNames) { 
  26.                 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); 
  27.  
  28.                 PropertyValues propertyValues = beanDefinition.getPropertyValues(); 
  29.                 for (PropertyValue propertyValue : propertyValues.getPropertyValues()) { 
  30.                     Object value = propertyValue.getValue(); 
  31.                     if (!(value instanceof String)) continue
  32.                     String strVal = (String) value; 
  33.                     StringBuilder buffer = new StringBuilder(strVal); 
  34.                     int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX); 
  35.                     int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX); 
  36.                     if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) { 
  37.                         String propKey = strVal.substring(startIdx + 2, stopIdx); 
  38.                         String propVal = properties.getProperty(propKey); 
  39.                         buffer.replace(startIdx, stopIdx + 1, propVal); 
  40.                         propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buffer.toString())); 
  41.                     } 
  42.                 } 
  43.             } 
  44.         } catch (IOException e) { 
  45.             throw new BeansException("Could not load properties", e); 
  46.         } 
  47.     } 
  48.  
  49.     public void setLocation(String location) { 
  50.         this.location = location; 
  51.     } 
  52.  

依賴于 BeanFactoryPostProcessor 在 Bean 生命周期的屬性,可以在 Bean 對象實例化之前,改變屬性信息。所以這里通過實現 BeanFactoryPostProcessor 接口,完成對配置文件的加載以及摘取占位符中的在屬性文件里的配置。

這樣就可以把提取到的配置信息放置到屬性配置中了,buffer.replace(startIdx, stopIdx + 1, propVal); propertyValues.addPropertyValue

3. 定義攔截注解

cn.bugstack.springframework.context.annotation.Scope

  1. @Target({ElementType.TYPE, ElementType.METHOD}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. public @interface Scope { 
  5.  
  6.     String value() default "singleton"
  7.  
  • 用于配置作用域的自定義注解,方便通過配置Bean對象注解的時候,拿到Bean對象的作用域。不過一般都使用默認的 singleton

cn.bugstack.springframework.stereotype.Component

  1. @Target(ElementType.TYPE) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. public @interface Component { 
  5.  
  6.     String value() default ""
  7.  

Component 自定義注解大家都非常熟悉了,用于配置到 Class 類上的。除此之外還有 Service、Controller,不過所有的處理方式基本一致,這里就只展示一個 Component 即可。

4. 處理對象掃描裝配

cn.bugstack.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

  1. public class ClassPathScanningCandidateComponentProvider { 
  2.  
  3.     public Set<BeanDefinition> findCandidateComponents(String basePackage) { 
  4.         Set<BeanDefinition> candidates = new LinkedHashSet<>(); 
  5.         Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class); 
  6.         for (Class<?> clazz : classes) { 
  7.             candidates.add(new BeanDefinition(clazz)); 
  8.         } 
  9.         return candidates; 
  10.     } 
  11.  
  • 這里先要提供一個可以通過配置路徑 basePackage=cn.bugstack.springframework.test.bean,解析出 classes 信息的工具方法 findCandidateComponents,通過這個方法就可以掃描到所有 @Component 注解的 Bean 對象了。

cn.bugstack.springframework.context.annotation.ClassPathBeanDefinitionScanner

  1. public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { 
  2.  
  3.     private BeanDefinitionRegistry registry; 
  4.  
  5.     public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { 
  6.         this.registry = registry; 
  7.     } 
  8.  
  9.     public void doScan(String... basePackages) { 
  10.         for (String basePackage : basePackages) { 
  11.             Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 
  12.             for (BeanDefinition beanDefinition : candidates) { 
  13.                 // 解析 Bean 的作用域 singleton、prototype 
  14.                 String beanScope = resolveBeanScope(beanDefinition); 
  15.                 if (StrUtil.isNotEmpty(beanScope)) { 
  16.                     beanDefinition.setScope(beanScope); 
  17.                 } 
  18.                 registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition); 
  19.             } 
  20.         } 
  21.     } 
  22.  
  23.     private String resolveBeanScope(BeanDefinition beanDefinition) { 
  24.         Class<?> beanClass = beanDefinition.getBeanClass(); 
  25.         Scope scope = beanClass.getAnnotation(Scope.class); 
  26.         if (null != scope) return scope.value(); 
  27.         return StrUtil.EMPTY; 
  28.     } 
  29.  
  30.     private String determineBeanName(BeanDefinition beanDefinition) { 
  31.         Class<?> beanClass = beanDefinition.getBeanClass(); 
  32.         Component component = beanClass.getAnnotation(Component.class); 
  33.         String value = component.value(); 
  34.         if (StrUtil.isEmpty(value)) { 
  35.             value = StrUtil.lowerFirst(beanClass.getSimpleName()); 
  36.         } 
  37.         return value; 
  38.     } 
  39.  

ClassPathBeanDefinitionScanner 是繼承自 ClassPathScanningCandidateComponentProvider 的具體掃描包處理的類,在 doScan 中除了獲取到掃描的類信息以后,還需要獲取 Bean 的作用域和類名,如果不配置類名基本都是把首字母縮寫。

5. 解析xml中調用掃描

cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader

  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { 
  2.  
  3.     protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException { 
  4.         SAXReader reader = new SAXReader(); 
  5.         Document document = reader.read(inputStream); 
  6.         Element root = document.getRootElement(); 
  7.  
  8.         // 解析 context:component-scan 標簽,掃描包中的類并提取相關信息,用于組裝 BeanDefinition 
  9.         Element componentScan = root.element("component-scan"); 
  10.         if (null != componentScan) { 
  11.             String scanPath = componentScan.attributeValue("base-package"); 
  12.             if (StrUtil.isEmpty(scanPath)) { 
  13.                 throw new BeansException("The value of base-package attribute can not be empty or null"); 
  14.             } 
  15.             scanPackage(scanPath); 
  16.         } 
  17.         
  18.         // ... 省略其他 
  19.              
  20.         // 注冊 BeanDefinition 
  21.         getRegistry().registerBeanDefinition(beanName, beanDefinition); 
  22.     } 
  23.  
  24.     private void scanPackage(String scanPath) { 
  25.         String[] basePackages = StrUtil.splitToArray(scanPath, ','); 
  26.         ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry()); 
  27.         scanner.doScan(basePackages); 
  28.     } 
  29.  
  • 關于 XmlBeanDefinitionReader 中主要是在加載配置文件后,處理新增的自定義配置屬性 component-scan,解析后調用 scanPackage 方法,其實也就是我們在 ClassPathBeanDefinitionScanner#doScan 功能。
  • 另外這里需要注意,為了可以方便的加載和解析xml,XmlBeanDefinitionReader 已經全部替換為 dom4j 的方式進行解析處理。

五、測試

1. 事先準備

  1. @Component("userService"
  2. public class UserService implements IUserService { 
  3.  
  4.     private String token; 
  5.  
  6.     public String queryUserInfo() { 
  7.         try { 
  8.             Thread.sleep(new Random(1).nextInt(100)); 
  9.         } catch (InterruptedException e) { 
  10.             e.printStackTrace(); 
  11.         } 
  12.         return "小傅哥,100001,深圳"
  13.     } 
  14.  
  15.     public String register(String userName) { 
  16.         try { 
  17.             Thread.sleep(new Random(1).nextInt(100)); 
  18.         } catch (InterruptedException e) { 
  19.             e.printStackTrace(); 
  20.         } 
  21.         return "注冊用戶:" + userName + " success!"
  22.     } 
  23.  
  24.     @Override 
  25.     public String toString() { 
  26.         return "UserService#token = { " + token + " }"
  27.     } 
  28.  
  29.     public String getToken() { 
  30.         return token; 
  31.     } 
  32.  
  33.     public void setToken(String token) { 
  34.         this.token = token; 
  35.     } 

給 UserService 類添加一個自定義注解 @Component("userService") 和一個屬性信息 String token。這是為了分別測試包掃描和占位符屬性。

2. 屬性配置文件

  1. token=RejDlI78hu223Opo983Ds 

這里配置一個 token 的屬性信息,用于通過占位符的方式進行獲取

3. spring.xml 配置對象

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.        xmlns:context="http://www.springframework.org/schema/context" 
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  6.           http://www.springframework.org/schema/beans/spring-beans.xsd 
  7.    http://www.springframework.org/schema/context"> 
  8.  
  9.     <bean class="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer"
  10.         <property name="location" value="classpath:token.properties"/> 
  11.     </bean> 
  12.  
  13.     <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService"
  14.         <property name="token" value="${token}"/> 
  15.     </bean> 
  16.  
  17. </beans> 
  • 加載 classpath:token.properties 設置占位符屬性值 ${token}

spring-scan.xml

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.        xmlns:context="http://www.springframework.org/schema/context" 
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  6.           http://www.springframework.org/schema/beans/spring-beans.xsd 
  7.    http://www.springframework.org/schema/context"> 
  8.  
  9.     <context:component-scan base-package="cn.bugstack.springframework.test.bean"/> 
  10.  
  11. </beans> 

添加 component-scan 屬性,設置包掃描根路徑

4. 單元測試(占位符)

  1. @Test 
  2. public void test_property() { 
  3.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-property.xml"); 
  4.     IUserService userService = applicationContext.getBean("userService", IUserService.class); 
  5.     System.out.println("測試結果:" + userService); 

測試結果

  1. 測試結果:UserService#token = { RejDlI78hu223Opo983Ds } 
  2.  
  3. Process finished with exit code 0 

通過測試結果可以看到 UserService 中的 token 屬性已經通過占位符的方式設置進去配置文件里的 token.properties 的屬性值了。

5. 單元測試(包掃描)

  1. @Test 
  2. public void test_scan() { 
  3.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml"); 
  4.     IUserService userService = applicationContext.getBean("userService", IUserService.class); 
  5.     System.out.println("測試結果:" + userService.queryUserInfo()); 

測試結果

  1. 測試結果:小傅哥,100001,深圳 
  2.  
  3. Process finished with exit code 0 

通過這個測試結果可以看出來,現在使用注解的方式就可以讓 Class 注冊完成 Bean 對象了。

六、總結

通過整篇的內容實現可以看出來,目前的功能添加其實已經不復雜了,都是在 IOC 和 AOP 核心的基礎上來補全功能。這些補全的功能也是在完善 Bean 的生命周期,讓整個功能使用也越來越容易。

在你不斷的實現著 Spring 的各項功能時,也可以把自己在平常使用 Spring 的一些功能想法融入進來,比如像 Spring 是如何動態切換數據源的,線程池是怎么提供配置的,這些內容雖然不是最基礎的核心范圍,但也非常重要。

可能有些時候這些類實現的內容對新人來說比較多,可以一點點動手實現逐步理解,在把一些稍微較有難度的內容實現后,其實后面也就沒有那么難理解了。

 

責任編輯:武曉燕 來源: bugstack蟲洞棧
相關推薦

2012-11-05 09:53:27

云存儲SaaSPaaS

2016-02-26 12:05:06

華為

2011-09-22 09:26:49

PowerAIX

2010-07-16 10:22:19

金山雷軍

2011-07-14 10:51:40

飛視美視頻會議

2011-06-20 11:02:35

激光打印機推薦

2011-10-14 10:51:05

桌面虛擬化服務器

2021-08-04 11:39:17

Bean對象配置

2010-05-13 14:59:23

移動互聯網新浪模式

2012-04-11 15:06:48

投影機評測

2023-05-10 08:29:28

Spring配置原理

2023-09-28 09:17:18

SpringBootBean

2023-03-08 09:59:39

SpringBean注入

2025-08-05 01:45:00

XXL-JOB自動注冊運維

2023-12-01 08:00:57

微信運動SpringBean

2024-07-31 11:26:05

反射BeanXML

2009-07-01 14:09:24

Servlet和BeaJSP

2019-07-02 11:01:35

SpringBean配置

2021-06-07 08:39:58

SpringBootMyBatisMapper

2015-09-16 11:13:51

ChefWindows集群運維
點贊
收藏

51CTO技術棧公眾號

国产女人水真多18毛片18精品视频| 一本久道久久综合婷婷鲸鱼| 欧美精品色一区二区三区| 宅男噜噜99国产精品观看免费| 97人妻人人澡人人爽人人精品| 欧美高清不卡| 亚洲欧美日本精品| 在线a免费观看| 人成在线免费网站| 国产欧美日韩视频一区二区| 亚洲精品欧美日韩专区| 成人免费区一区二区三区| 色88久久久久高潮综合影院| 日韩欧美电影在线| 国产成人精品无码播放| 色婷婷在线播放| 欧美激情在线一区二区| 国产精品成人观看视频免费| 最新国产中文字幕| 亚洲精品女人| 久久综合伊人77777尤物| 法国伦理少妇愉情| 51亚洲精品| 欧美日韩高清在线| 久久黄色片视频| av大片在线| 中文字幕乱码久久午夜不卡| 国产欧美日韩一区二区三区| 一区二区三区免费在线视频| 久久精品国产清高在天天线| 久久久久久综合网天天| 中文字幕美女视频| 亚洲综合小说图片| 欧美精品一区二区高清在线观看| 亚洲精品视频三区| 国产a级一级片| 亚洲一区制服诱惑| wwwav国产| 欧美一区二区性| 亚洲电影免费观看高清完整版在线| 欧美wwwwwww| 91超碰碰碰碰久久久久久综合| 偷拍亚洲欧洲综合| 高清无码一区二区在线观看吞精| 午夜视频在线观看免费视频| 国产三级精品视频| 欧美一区二区三区在线播放 | 日本韩国欧美国产| 国产中文字幕免费观看| 精品众筹模特私拍视频| 亚洲男人电影天堂| 美女黄色片网站| 麻豆av在线导航| 中文字幕亚洲成人| 综合国产精品久久久| 亚洲麻豆精品| 亚洲欧洲精品天堂一级 | melody高清在线观看| 国产三级三级三级精品8ⅰ区| 热re99久久精品国产99热| 男人天堂亚洲二区| 国产亚洲欧美一级| 亚洲巨乳在线观看| 毛片av在线| 一区二区三区在线播放| 亚洲激情免费视频| 超碰在线网站| 午夜欧美2019年伦理| 日韩中字在线观看| 亚洲一区资源| 欧美亚洲一区三区| 自拍偷拍一区二区三区四区| 97色婷婷成人综合在线观看| 欧美一级电影网站| 中文字幕无人区二| 四虎884aa成人精品最新| 亚洲视频在线观看网站| 亚洲图片第一页| 中国精品18videos性欧美| 欧美激情成人在线视频| 国产精品suv一区二区三区| 性伦欧美刺激片在线观看| 国产成人小视频在线观看| 男人搞女人网站| 户外露出一区二区三区| 制服丝袜中文字幕亚洲| 久久久久久久穴| 亚洲三级网页| 精品国产欧美一区二区三区成人| 欧美交换国产一区内射| 亚洲影院免费| 亚洲a区在线视频| 天堂资源最新在线| 中文字幕五月欧美| 欧美极品欧美精品欧美| 日本国产一区| 亚洲国产精彩中文乱码av在线播放 | 蜜臀av一级做a爰片久久| 91香蕉电影院| 手机福利小视频在线播放| 国产精品久久久久aaaa樱花| 精品无码一区二区三区爱欲| 亚洲精品555| 亚洲成人1234| 免费成人深夜蜜桃视频| 一区二区三区福利| 成人网在线免费观看| 日本福利片高清在线观看| 亚洲欧洲日韩女同| 国产青青在线视频| 国产在线不卡一区二区三区| 亚洲三级 欧美三级| 久久久综合久久久| 免费亚洲电影在线| 久久99精品久久久久久三级| 二区三区四区高清视频在线观看| 色综合久久久久综合| 亚洲综合中文网| 日韩免费看片| 国产91网红主播在线观看| www.com在线观看| 国产精品久久久久9999吃药| 99精品视频播放| 国产精品午夜av| 欧美精品免费在线| 亚洲一区二区人妻| 久久精品免视看| 国产午夜福利在线播放| 亚洲一区二区三区免费| 久久韩国免费视频| 五月天中文字幕| 99久久精品国产一区| 国产亚洲精品久久久久久久| 日韩伦理一区二区| 日韩精品黄色网| 四虎永久在线精品| 成人黄色大片在线观看| 国产成人免费高清视频| 亚洲欧美专区| 精品国内亚洲在观看18黄 | 日韩一区二区麻豆国产| 免费成人美女女在线观看| 日本麻豆一区二区三区视频| 日本不卡一区二区三区视频| 中文字幕一区久| 国产婷婷97碰碰久久人人蜜臀 | 中文字幕在线观看网站| 7777精品伊人久久久大香线蕉最新版| 国产三级黄色片| 男人操女人的视频在线观看欧美| 日本不卡二区高清三区| av高清一区| 伊人久久久久久久久久久| 亚洲 欧美 中文字幕| 久久欧美中文字幕| 国产成人精品视频ⅴa片软件竹菊| 私拍精品福利视频在线一区| 日本不卡免费高清视频| 国产乱理伦片a级在线观看| 色噜噜狠狠一区二区三区果冻| www.中文字幕av| 三级影片在线观看欧美日韩一区二区| 乱一区二区三区在线播放| 女生影院久久| 中文字幕欧美日韩va免费视频| 又污又黄的网站| 亚洲丝袜自拍清纯另类| 中文字幕第六页| 99亚洲一区二区| 欧美xxxx黑人又粗又长精品| 精品123区| 久青草国产97香蕉在线视频| 免费观看国产精品| 欧美性猛交xxxx乱大交3| 99久久久无码国产精品衣服| 麻豆久久久久久久| 欧美人与动牲交xxxxbbbb| 国产精品xxxav免费视频| 日本精品视频在线观看| 日本暖暖在线视频| 欧美va亚洲va| 中文字幕69页| 亚洲精品视频在线看| 在线看黄色的网站| 免费观看成人av| 韩日视频在线观看| 成人羞羞视频播放网站| 风间由美久久久| 亚洲mmav| 欧美激情一区二区久久久| 国产精品一级伦理| 日韩欧美中文一区二区| 久久久久久在线观看| 亚洲欧美一区二区不卡| 国产精品亚洲无码| 国产精一区二区三区| 国产成人无码一二三区视频| 欧美残忍xxxx极端| 精品综合在线| 精品国产一区二区三区2021| 欧美一区二区三区免费视| 高潮毛片在线观看| 夜夜嗨av色综合久久久综合网| 亚洲av无码国产精品永久一区| 色一情一伦一子一伦一区| 黄色一级视频免费观看| 国产午夜精品一区二区 | 国产欧美精品区一区二区三区 | 欧美交受高潮1| 懂色av中文在线| 亚洲精品suv精品一区二区| 一区二区三区免费在线| 色94色欧美sute亚洲13| 久久免费少妇高潮99精品| 国产精品久久久久久久岛一牛影视 | 青青草免费观看视频| 亚洲欧美日韩综合aⅴ视频| 精品人伦一区二区| 91亚洲精品乱码久久久久久蜜桃 | 久久亚洲国产精品| 国产在线中文字幕| 亚洲精品不卡在线| 亚洲黄色小说网| 日韩一区二区三区在线观看| 中文字幕永久在线| 欧美性xxxxxx| 精品91久久久| 亚洲国产成人91porn| 国产人妻精品一区二区三区不卡| 国产欧美一区二区精品性色| 中文人妻一区二区三区| av在线播放成人| 人妻 日韩 欧美 综合 制服| 国产一区二区三区免费看 | 久久夜夜久久| 国产精品免费久久久久影院| 黄色综合网址| 欧洲精品在线视频| 色是在线视频| 欧美中文字幕在线播放| 麻豆网站免费在线观看| 45www国产精品网站| 麻豆免费在线| 欧美专区在线视频| 成人性生交大片免费网站| 日韩av免费看| 国产精品字幕| 国产精品直播网红| 伦一区二区三区中文字幕v亚洲| 国产精品入口免费视| 亚洲va中文在线播放免费| 国产精品高潮在线| 黄色成人小视频| 成人性教育视频在线观看| 成人黄色理论片| 91九色极品视频| 成人另类视频| 久久国产日韩欧美| 国产影视一区| 亚洲日本欧美在线| 亚洲国产一成人久久精品| 老司机午夜网站| 在线 亚洲欧美在线综合一区| 18禁免费观看网站| 日韩精品欧美精品| 国产成年人视频网站| 国产一区二区三区久久久| 久久久久中文字幕亚洲精品 | 国产精品免费一区二区三区四区| 久久夜色电影| 天堂资源在线亚洲视频| 婷婷亚洲最大| 亚洲熟妇无码一区二区三区| 狂野欧美一区| 欧美成人乱码一二三四区免费| 国产激情一区二区三区四区 | 99热这里是精品| 欧美不卡123| 经典三级在线| 欧美成人精品三级在线观看| 美女露胸视频在线观看| 国产精品一二区| 亚洲性视频在线| 色播亚洲视频在线观看| 中文字幕亚洲综合久久五月天色无吗''| 久久精品xxx| 日韩国产精品久久久久久亚洲| 一区二区三区四区毛片| 91香蕉视频污在线| 永久免费未视频| 欧美日韩一区二区在线播放| 91精品人妻一区二区三区果冻| 欧美精品一区二区三区四区 | 欧洲亚洲视频| 永久久久久久| 亚洲自啪免费| www.污网站| 久久久久国产精品厨房| 久久久久亚洲av无码专区 | 欧美中文字幕在线播放| 精品三级国产| 手机看片福利永久国产日韩| 伊人久久久大香线蕉综合直播| www亚洲成人| 99麻豆久久久国产精品免费| 麻豆天美蜜桃91| 在线观看免费成人| 人妻少妇精品无码专区| 久久精品视频免费播放| 韩国成人动漫| 久久久久久九九| 欧美视频在线观看| 天天干天天操天天做| 久久久99精品久久| 欧美a∨亚洲欧美亚洲| 日韩欧美电影在线| 免费av在线| 国产精品旅馆在线| 一区二区三区韩国免费中文网站| 国产一级做a爰片久久毛片男| 久久精品国产免费| 蜜桃av乱码一区二区三区| 天天综合色天天| 日韩一级免费毛片| 欧美激情亚洲综合一区| 亚洲精品a区| 好吊色视频988gao在线观看| 蜜桃av一区二区三区| 成都免费高清电影| 欧美日韩国产中字| 少妇无码一区二区三区| 欧美大片免费看| 国产一区二区高清在线| 国产卡一卡二在线| 精品一区二区三区视频 | 性欧美大战久久久久久久久| 精品国产免费无码久久久| 久久人人爽人人爽人人片亚洲| 亚洲成人高清| 国产一区一区三区| 韩国av一区二区三区在线观看| 三级黄色录像视频| 欧美精品丝袜中出| 久草中文在线观看| 91深夜福利视频| 欧美国产日本| 2018国产精品| 亚洲成人激情自拍| 人妻无码一区二区三区久久99| 久久久久久久久久久av| 久久资源综合| 无码人妻丰满熟妇区毛片18| 久久亚洲二区三区| 天天干天天操天天操| 永久免费毛片在线播放不卡| av免费在线一区| 中国成人在线视频| 国产麻豆视频精品| 国产在线视频在线观看| 亚洲国产成人精品女人久久久| 中日韩脚交footjobhd| 三区精品视频| 国内精品久久久久影院一蜜桃| 久久免费看少妇高潮v片特黄| 日韩三级免费观看| 大菠萝精品导航| 欧美精品123| 久久97超碰国产精品超碰| 欧美日韩中文字幕在线观看| 亚洲成人久久一区| 欧美magnet| 中国人体摄影一区二区三区| 国产aⅴ综合色| 美女又爽又黄免费视频| 日韩一区二区欧美| 成人av动漫| 国产视频一区二区三区在线播放| 最新不卡av在线| 色一情一乱一乱一区91av| 国产不卡视频在线| 中文av一区| 无码一区二区三区在线| 5月丁香婷婷综合| 精精国产xxxx视频在线播放| 先锋影音亚洲资源| 大胆亚洲人体视频| 中文在线字幕免费观| 久久99精品久久久久久青青91| 亚洲丁香日韩| www.51色.com| 一本久久a久久精品亚洲| 麻豆传媒视频在线| 精品视频免费观看| 韩日av一区二区| www.国产com| 久久99亚洲精品| 精品久久影院| 国产精品无码永久免费不卡| 3751色影院一区二区三区| 亚洲天堂导航|