轉自:https://blog.csdn.net/z69183787/article/details/53784845
前言
在工作中經常使用Spring的相關框架,免不了去看一下Spring的實現方法,了解一下Spring內部的處理邏輯。特別是開發Web應用時,我們會頻繁的定義*@Controller*,*@Service*等JavaBean組件,通過注解,Spring自動掃描加載了這些組件,並提供相關的服務。
Spring是如何讀取注解信息,並注入到bean容器中的,本文就是通過嵌入Spring的Bean加載,來描述Spring的實現方法。完整的例子都在Github上了。
自定義注解
先看一個最簡單的例子,在使用SpringWeb應用中的過程中,大家免不了會使用*@Controller*,*@Service*,*@Repository*等注解來定義JavaBean。那么怎么自己定義一個注解,Spring可以自動加載呢。所以就有了第一個例子。
1 @Target({ ElementType.TYPE }) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Component 5 public @interface MyComponent { 6 String value() default ""; 7 }
1 @Configuration 2 public class ComponentAnnotationTest { 3 public static void main(String[] args) { 4 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); 5 annotationConfigApplicationContext.register(ComponentAnnotationTest.class); 6 annotationConfigApplicationContext.refresh(); 7 InjectClass injectClass = annotationConfigApplicationContext.getBean(InjectClass.class); 8 injectClass.print(); 9 } 10 @MyComponent 11 public static class InjectClass { 12 public void print() { 13 System.out.println("hello world"); 14 } 15 } 16 }
運行這個例子,就會發現,*@MyComponent* 注解的類,也被Spring加載進來了,而且可以當成普通的JavaBean正常的使用。查看Spring的源碼會發現,Spring是使用ClassPathScanningCandidateComponentProvider掃描package,這個類有這樣的注釋
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.
這個類的 registerDefaultFilters 方法有這樣幾行代碼
1 protected void registerDefaultFilters() { 2 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); 3 ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); 4 try { 5 this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); 6 logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); 7 } catch (ClassNotFoundException ex) { 8 // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. 9 } 10 try { 11 this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); 12 logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); 13 } 14 catch (ClassNotFoundException ex) { 15 // JSR-330 API not available - simply skip. 16 } 17 }
這里就會發現Spring在掃描類信息的使用只會判斷被*@Component*注解的類,所以任何自定義的注解只要帶上*@Component*(當然還要有String value() default "";的方法,因為Spring的Bean都是有beanName唯一標示的),都可以被Spring掃描到,並注入容器內。
定制功能
但上面的方法太局限了,沒辦法定制,而且也沒有實際的意義。如何用特殊的注解來實現定制的功能呢,一般有兩種方式:
還是用上面的方法,在注入Spring的容器后,再取出來做自己定制的功能,Spring-MVC就是使用這樣的方法。AbstractDetectingUrlHandlerMapping 中的 detectHandlers方法,這個方法取出了所有的bean,然后循環查找帶有Controller的bean,並提取其中的RequestMapping信息
1 protected void detectHandlers() throws BeansException { 2 if (logger.isDebugEnabled()) { 3 logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); 4 } 5 String[] beanNames = (this.detectHandlersInAncestorContexts ? 6 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : 7 getApplicationContext().getBeanNamesForType(Object.class)); 8 9 // Take any bean name that we can determine URLs for. 10 for (String beanName : beanNames) { 11 String[] urls = determineUrlsForHandler(beanName); 12 if (!ObjectUtils.isEmpty(urls)) { 13 // URL paths found: Let's consider it a handler. 14 registerHandler(urls, beanName); 15 } 16 else { 17 if (logger.isDebugEnabled()) { 18 logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); 19 } 20 } 21 } 22 }
2.不依賴*@Component*,自定義掃描。所以就有了第二個例子。
自定義掃描
結構比較復雜,可以參考完整的例子,這里是關鍵的幾個類
1.還是定義一個注解,只不過不再需要*@Component*了
1 @Target({ ElementType.TYPE }) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 public @interface CustomizeComponent { 5 String value() default ""; 6 }
2.注解修飾的類
1 @CustomizeComponent 2 public class ScanClass1 { 3 public void print() { 4 System.out.println("scanClass1"); 5 } 6 }
3.BeanScannerConfigurer用於嵌入到Spring的加載過程的中,這里用到了BeanFactoryPostProcessor 和 ApplicationContextAware。
Spring提供了一些的接口使程序可以嵌入Spring的加載過程。這個類中的繼承ApplicationContextAware接口,Spring會讀取ApplicationContextAware類型的的JavaBean,並調用setApplicationContext(ApplicationContext applicationContext)傳入Spring的applicationContext。
同樣繼承BeanFactoryPostProcessor接口,Spring會在BeanFactory的相關處理完成后調用postProcessBeanFactory方法,進行定制的功能。
1 @Component 2 public static class BeanScannerConfigurer implements BeanFactoryPostProcessor, ApplicationContextAware { 3 private ApplicationContext applicationContext; 4 5 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 6 this.applicationContext = applicationContext; 7 } 8 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 9 Scanner scanner = new Scanner((BeanDefinitionRegistry) beanFactory); 10 scanner.setResourceLoader(this.applicationContext); 11 scanner.scan("org.wcong.test.spring.scan"); 12 } 13 }
4.Scanner繼承的ClassPathBeanDefinitionScanner是Spring內置的Bean定義的掃描器。
includeFilter里定義了類的過濾器,newAnnotationTypeFilter(CustomizeComponent.class)表示只取被CustomizeComponent修飾的類。
doScan里掃面了包底下的讀取道德BeanDefinitionHolder,自定義GenericBeanDefinition相關功能
1 public final static class Scanner extends ClassPathBeanDefinitionScanner { 2 public Scanner(BeanDefinitionRegistry registry) { 3 super(registry); 4 } 5 public void registerDefaultFilters() { 6 this.addIncludeFilter(new AnnotationTypeFilter(CustomizeComponent.class)); 7 } 8 public Set<BeanDefinitionHolder> doScan(String... basePackages) { 9 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); 10 for (BeanDefinitionHolder holder : beanDefinitions) { 11 GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition(); 12 definition.getPropertyValues().add("innerClassName", definition.getBeanClassName()); 13 definition.setBeanClass(FactoryBeanTest.class); 14 } 15 return beanDefinitions; 16 } 17 public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { 18 return super.isCandidateComponent(beanDefinition) && beanDefinition.getMetadata() 19 .hasAnnotation(CustomizeComponent.class.getName()); 20 } 21 }
5.FactoryBean是Spring中比較重要的一個類。它的描述如下
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是直接使用類的實例,但是如果一個Bean繼承了這個借口,就可以通過getObject()方法來自定義實例的內容,在FactoryBeanTest的getObject()就通過代理了原始類的方法,自定義類的方法。
1 public static class FactoryBeanTest<T> implements InitializingBean, FactoryBean<T> { 2 private String innerClassName; 3 public void setInnerClassName(String innerClassName) { 4 this.innerClassName = innerClassName; 5 } 6 public T getObject() throws Exception { 7 Class innerClass = Class.forName(innerClassName); 8 if (innerClass.isInterface()) { 9 return (T) InterfaceProxy.newInstance(innerClass); 10 } else { 11 Enhancer enhancer = new Enhancer(); 12 enhancer.setSuperclass(innerClass); 13 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); 14 enhancer.setCallback(new MethodInterceptorImpl()); 15 return (T) enhancer.create(); 16 } 17 } 18 public Class<?> getObjectType() { 19 try { 20 return Class.forName(innerClassName); 21 } catch (ClassNotFoundException e) { 22 e.printStackTrace(); 23 } 24 return null; 25 } 26 public boolean isSingleton() { 27 return true; 28 } 29 public void afterPropertiesSet() throws Exception { 30 } 31 } 32 public static class InterfaceProxy implements InvocationHandler { 33 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 34 System.out.println("ObjectProxy execute:" + method.getName()); 35 return method.invoke(proxy, args); 36 } 37 public static <T> T newInstance(Class<T> innerInterface) { 38 ClassLoader classLoader = innerInterface.getClassLoader(); 39 Class[] interfaces = new Class[] { innerInterface }; 40 InterfaceProxy proxy = new InterfaceProxy(); 41 return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); 42 } 43 } 44 public static class MethodInterceptorImpl implements MethodInterceptor { 45 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 46 System.out.println("MethodInterceptorImpl:" + method.getName()); 47 return methodProxy.invokeSuper(o, objects); 48 } 49 }
6.main函數
1 @Configuration 2 public class CustomizeScanTest { 3 public static void main(String[] args) { 4 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); 5 annotationConfigApplicationContext.register(CustomizeScanTest.class); 6 annotationConfigApplicationContext.refresh(); 7 ScanClass1 injectClass = annotationConfigApplicationContext.getBean(ScanClass1.class); 8 injectClass.print(); 9 } 10 }
至此一個完整的例子就完成了,這里主要用到了BeanFactoryPostProcessor,ApplicationContextAware,FactoryBean等Spring內置的接口,來嵌入Spring的加載和使用過程,這樣就實現了自定義注解,和自定義代理了。