前言
在工作中經常使用Spring的相關框架,免不了去看一下Spring的實現方法,了解一下Spring內部的處理邏輯。特別是開發Web應用時,我們會頻繁的定義@Controller,@Service等JavaBean組件,通過注解,Spring自動掃描加載了這些組件,並提供相關的服務。
Spring是如何讀取注解信息,並注入到bean容器中的,本文就是通過嵌入Spring的Bean加載,來描述Spring的實現方法。完整的例子都在Github上了。
自定義注解
先看一個最簡單的例子,在使用SpringWeb應用中的過程中,大家免不了會使用@Controller,@Service,@Repository等注解來定義JavaBean。那么怎么自己定義一個注解,Spring可以自動加載呢。所以就有了第一個例子。
-
-
-
-
-
public
-
String value() default "";
-
}
-
-
public 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();
-
}
-
-
public static class InjectClass {
-
public void print() {
-
System.out.println("hello world");
-
}
-
}
-
}
運行這個例子,就會發現,@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 方法有這樣幾行代碼
-
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.
-
}
-
}
這里就會發現Spring在掃描類信息的使用只會判斷被@Component注解的類,所以任何自定義的注解只要帶上@Component(當然還要有String value() default "";的方法,因為Spring的Bean都是有beanName唯一標示的),都可以被Spring掃描到,並注入容器內。
定制功能
但上面的方法太局限了,沒辦法定制,而且也沒有實際的意義。如何用特殊的注解來實現定制的功能呢,一般有兩種方式:
-
還是用上面的方法,在注入Spring的容器后,再取出來做自己定制的功能,Spring-MVC就是使用這樣的方法。AbstractDetectingUrlHandlerMapping 中的detectHandlers方法,這個方法取出了所有的bean,然后循環查找帶有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");
-
}
-
}
-
}
-
}
-
-
不依賴@Component,自定義掃描。所以就有了第二個例子。
自定義掃描
結構比較復雜,可以參考完整的例子,這里是關鍵的幾個類
-
還是定義一個注解,只不過不再需要@Component了
-
-
-
-
public
-
String value() default "";
-
}
-
-
注解修飾的類
-
@CustomizeComponent
-
public class ScanClass1 {
-
public void print() {
-
System. out.println("scanClass1");
-
}
-
}
-
-
BeanScannerConfigurer用於嵌入到Spring的加載過程的中,這里用到了BeanFactoryPostProcessor 和ApplicationContextAware。
Spring提供了一些的接口使程序可以嵌入Spring的加載過程。這個類中的繼承ApplicationContextAware接口,Spring會讀取ApplicationContextAware類型的的JavaBean,並調用setApplicationContext(ApplicationContext applicationContext)傳入Spring的applicationContext。
同樣繼承BeanFactoryPostProcessor接口,Spring會在BeanFactory的相關處理完成后調用postProcessBeanFactory方法,進行定制的功能。-
-
public 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");
-
}
-
}
-
- Scanner繼承的ClassPathBeanDefinitionScanner是Spring內置的Bean定義的掃描器。
includeFilter里定義了類的過濾器,newAnnotationTypeFilter(CustomizeComponent.class)表示只取被CustomizeComponent修飾的類。
doScan里掃面了包底下的讀取道德BeanDefinitionHolder,自定義GenericBeanDefinition相關功能。-
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());
-
}
-
}
-
- 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
-
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);
-
}
-
}
-
- main函數
-
@Configuration
-
public 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();
-
}
-
}
-
至此一個完整的例子就完成了,這里主要用到了BeanFactoryPostProcessor,ApplicationContextAware,FactoryBean等Spring內置的接口,來嵌入Spring的加載和使用過程,這樣就實現了自定義注解,和自定義代理了。
原文鏈接:http://www.jianshu.com/p/7c2948f64b1c
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。