久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    深入Spring:自定義注解加載和使用

     一本正經(jīng)地胡鬧 2019-07-03

    前言

    在工作中經(jīng)常使用Spring的相關(guān)框架,免不了去看一下Spring的實(shí)現(xiàn)方法,了解一下Spring內(nèi)部的處理邏輯。特別是開(kāi)發(fā)Web應(yīng)用時(shí),我們會(huì)頻繁的定義@Controller@Service等JavaBean組件,通過(guò)注解,Spring自動(dòng)掃描加載了這些組件,并提供相關(guān)的服務(wù)。
    Spring是如何讀取注解信息,并注入到bean容器中的,本文就是通過(guò)嵌入Spring的Bean加載,來(lái)描述Spring的實(shí)現(xiàn)方法。完整的例子都在Github上了。

    自定義注解

    先看一個(gè)最簡(jiǎn)單的例子,在使用SpringWeb應(yīng)用中的過(guò)程中,大家免不了會(huì)使用@Controller@Service@Repository等注解來(lái)定義JavaBean。那么怎么自己定義一個(gè)注解,Spring可以自動(dòng)加載呢。所以就有了第一個(gè)例子。

    @Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface MyComponent {    String value() default "";
    }
    @Configurationpublic class ComponentAnnotationTest {  public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(ComponentAnnotationTest.class);
        annotationConfigApplicationContext.refresh();
        InjectClass injectClass = annotationConfigApplicationContext.getBean(InjectClass.class);
            injectClass.print();
      }  @MyComponent
      public static class InjectClass {    public void print() {
            System.out.println("hello world");
        }
      }
    }

    運(yùn)行這個(gè)例子,就會(huì)發(fā)現(xiàn),@MyComponent 注解的類(lèi),也被Spring加載進(jìn)來(lái)了,而且可以當(dāng)成普通的JavaBean正常的使用。查看Spring的源碼會(huì)發(fā)現(xiàn),Spring是使用ClassPathScanningCandidateComponentProvider掃描package,這個(gè)類(lèi)有這樣的注釋

    A component provider that scans the classpath from a base package. 
    It then applies exclude and include filters to the resulting classes to find candidates.

    這個(gè)類(lèi)的 registerDefaultFilters 方法有這樣幾行代碼

    protected void registerDefaultFilters() {   
       this.includeFilters.add(new AnnotationTypeFilter(Component.class));
       ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();   try {    
          this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); 
          logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); 
       }   catch (ClassNotFoundException ex) {     
         // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.   
       }   
       try {      
          this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));      
          logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");   
       }  
       catch (ClassNotFoundException ex) {     
       // JSR-330 API not available - simply skip.  
       }
    }

    這里就會(huì)發(fā)現(xiàn)Spring在掃描類(lèi)信息的使用只會(huì)判斷被@Component注解的類(lèi),所以任何自定義的注解只要帶上@Component(當(dāng)然還要有String value() default "";的方法,因?yàn)镾pring的Bean都是有beanName唯一標(biāo)示的),都可以被Spring掃描到,并注入容器內(nèi)。

    定制功能

    但上面的方法太局限了,沒(méi)辦法定制,而且也沒(méi)有實(shí)際的意義。如何用特殊的注解來(lái)實(shí)現(xiàn)定制的功能呢,一般有兩種方式:

    1. 還是用上面的方法,在注入Spring的容器后,再取出來(lái)做自己定制的功能,Spring-MVC就是使用這樣的方法。AbstractDetectingUrlHandlerMapping 中的 detectHandlers方法,這個(gè)方法取出了所有的bean,然后循環(huán)查找?guī)в?em>Controller的bean,并提取其中的RequestMapping信息

    protected void detectHandlers() throws BeansException {        if (logger.isDebugEnabled()) {
                logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
            }        String[] beanNames = (this.detectHandlersInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                    getApplicationContext().getBeanNamesForType(Object.class));        // Take any bean name that we can determine URLs for.
            for (String beanName : beanNames) {            String[] urls = determineUrlsForHandler(beanName);            if (!ObjectUtils.isEmpty(urls)) {                // URL paths found: Let's consider it a handler.
                    registerHandler(urls, beanName);
                }            else {                if (logger.isDebugEnabled()) {
                        logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
                    }
                }
            }
        }
    1. 不依賴(lài)@Component,自定義掃描。所以就有了第二個(gè)例子。

    自定義掃描

    結(jié)構(gòu)比較復(fù)雜,可以參考完整的例子,這里是關(guān)鍵的幾個(gè)類(lèi)

    1. 還是定義一個(gè)注解,只不過(guò)不再需要@Component

    @Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface CustomizeComponent {     String value() default "";
    }
    1. 注解修飾的類(lèi)

    @CustomizeComponentpublic class ScanClass1 {    public void print() {
            System.out.println("scanClass1");
        }
    }
    1. BeanScannerConfigurer用于嵌入到Spring的加載過(guò)程的中,這里用到了BeanFactoryPostProcessorApplicationContextAware
      Spring提供了一些的接口使程序可以嵌入Spring的加載過(guò)程。這個(gè)類(lèi)中的繼承ApplicationContextAware接口,Spring會(huì)讀取ApplicationContextAware類(lèi)型的的JavaBean,并調(diào)用setApplicationContext(ApplicationContext applicationContext)傳入Spring的applicationContext
      同樣繼承BeanFactoryPostProcessor接口,Spring會(huì)在BeanFactory的相關(guān)處理完成后調(diào)用postProcessBeanFactory方法,進(jìn)行定制的功能。

    @Componentpublic static class BeanScannerConfigurer implements  BeanFactoryPostProcessor, ApplicationContextAware {    private ApplicationContext applicationContext;    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {      this.applicationContext = applicationContext;
        }    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
          Scanner scanner = new Scanner((BeanDefinitionRegistry) beanFactory);
          scanner.setResourceLoader(this.applicationContext);
          scanner.scan("org.wcong.test.spring.scan");
        }
      }
    1. Scanner繼承的ClassPathBeanDefinitionScanner是Spring內(nèi)置的Bean定義的掃描器。
      includeFilter里定義了類(lèi)的過(guò)濾器,newAnnotationTypeFilter(CustomizeComponent.class)表示只取被CustomizeComponent修飾的類(lèi)。
      doScan里掃面了包底下的讀取道德BeanDefinitionHolder,自定義GenericBeanDefinition相關(guān)功能。

    public final static class Scanner extends ClassPathBeanDefinitionScanner {      public Scanner(BeanDefinitionRegistry registry) {          super(registry);
          }      public void registerDefaultFilters() {          this.addIncludeFilter(new AnnotationTypeFilter(CustomizeComponent.class));
          }      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
              Set<BeanDefinitionHolder> beanDefinitions =   super.doScan(basePackages);          for (BeanDefinitionHolder holder : beanDefinitions) {
                  GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
                  definition.getPropertyValues().add("innerClassName", definition.getBeanClassName());
                  definition.setBeanClass(FactoryBeanTest.class);
              }          return beanDefinitions;
          }      public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {         return super.isCandidateComponent(beanDefinition) && beanDefinition.getMetadata()
    .hasAnnotation(CustomizeComponent.class.getName());
          }
    }
    1. FactoryBean是Spring中比較重要的一個(gè)類(lèi)。它的描述如下

    Interface to be implemented by objects used within a BeanFactory which are themselves factories. 
    If a bean implements this interface, it is used as a factory for an object to expose, not directly as a bean* instance that will be exposed itself

    普通的JavaBean是直接使用類(lèi)的實(shí)例,但是如果一個(gè)Bean繼承了這個(gè)借口,就可以通過(guò)getObject()方法來(lái)自定義實(shí)例的內(nèi)容,在FactoryBeanTest的getObject()就通過(guò)代理了原始類(lèi)的方法,自定義類(lèi)的方法。

    public static class FactoryBeanTest<T> implements InitializingBean, FactoryBean<T> {      private String innerClassName;      public void setInnerClassName(String innerClassName) {          this.innerClassName = innerClassName;
          }      public T getObject() throws Exception {
              Class innerClass = Class.forName(innerClassName);          if (innerClass.isInterface()) {              return (T) InterfaceProxy.newInstance(innerClass);
              } else {
                  Enhancer enhancer = new Enhancer();
                  enhancer.setSuperclass(innerClass);
                  enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
                  enhancer.setCallback(new MethodInterceptorImpl());              return (T) enhancer.create();
              }
          }      public Class<?> getObjectType() {          try {                return Class.forName(innerClassName);
              } catch (ClassNotFoundException e) {
                    e.printStackTrace();
              }          return null;
          }      public boolean isSingleton() {          return true;
          }      public void afterPropertiesSet() throws Exception {
          }
    }public static class InterfaceProxy implements InvocationHandler {      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("ObjectProxy execute:" + method.getName());          return method.invoke(proxy, args);
          }      public static <T> T newInstance(Class<T> innerInterface) {
              ClassLoader classLoader = innerInterface.getClassLoader();
              Class[] interfaces = new Class[] { innerInterface };
              InterfaceProxy proxy = new InterfaceProxy();          return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
          }
         }     public static class MethodInterceptorImpl implements MethodInterceptor {          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
              System.out.println("MethodInterceptorImpl:" + method.getName());          return methodProxy.invokeSuper(o, objects);
          }
    }
    1. main函數(shù)

    @Configurationpublic class CustomizeScanTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();                
            annotationConfigApplicationContext.register(CustomizeScanTest.class);
            annotationConfigApplicationContext.refresh();
            ScanClass1 injectClass = annotationConfigApplicationContext.getBean(ScanClass1.class);
            injectClass.print();
        }
     }

    至此一個(gè)完整的例子就完成了,這里主要用到了BeanFactoryPostProcessorApplicationContextAwareFactoryBean等Spring內(nèi)置的接口,來(lái)嵌入Spring的加載和使用過(guò)程,這樣就實(shí)現(xiàn)了自定義注解,和自定義代理了。


    作者:wcong
    鏈接:https://www.jianshu.com/p/7c2948f64b1c

      本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評(píng)論

      發(fā)表

      請(qǐng)遵守用戶(hù) 評(píng)論公約

      類(lèi)似文章 更多

      主站蜘蛛池模板: 国产亚洲精品国产福APP| 国产成人AV大片大片在线播放| 国产亚洲综合欧美视频| 日韩精品无码一区二区三区AV| 成人午夜在线观看刺激| 国产午夜亚洲精品不卡下载| 成人区人妻精品一区二区不卡| 四虎在线播放亚洲成人| 67194熟妇在线直接进入| 无码乱码AV天堂一区二区| 乱码视频午夜在线观看| 呦系列视频一区二区三区| 亚洲欧美人成电影在线观看| 国产精品无码久久久久成人影院| 精品视频不卡免费观看| 亚洲精品成人久久久 | 欧美熟妇乱子伦XX视频| 国产猛男猛女超爽免费视频| 国产线播放免费人成视频播放| 99久久99久久免费精品小说| 国产福利姬喷水福利在线观看| 丰满人妻一区二区三区视频53| 毛片大全真人在线| 中文字幕人妻无码一夲道| 午夜色大片在线观看| 国产999久久高清免费观看| 亚洲影院丰满少妇中文字幕无码| 不卡一区二区国产在线| 亚洲精品55夜色66夜色| 欧美黑人又粗又大又硬免费视频| 最新国产精品亚洲| 欧产日产国产精品精品| 亚洲欧洲精品日韩av| 国产精品无码久久久久成人影院| 亚洲国产福利成人一区二区| 婷婷四房播播| 久久久久无码精品国产| 久久毛片少妇高潮| 四虎成人精品永久网站| av中文字幕在线二区| 精品人妻少妇嫩草AV无码专区 |