Spring自定義註解載入
在工作中經常使用Spring的相關框架,免不了去看一下Spring的實現方法,瞭解一下Spring內部的處理邏輯。特別是開發Web應用時,我們會頻繁的定義@Controller,@Service等JavaBean元件,通過註解,Spring自動掃描載入了這些元件,並提供相關的服務。
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyComponent {
String value() default "";
}
@Configuration 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(); } @MyComponent 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掃描到,並注入容器內。
下面具體講解關於掃描的類
類圖如下:
其中ClassPathBeanDefinitionScanner建立時需要傳入BeanDefinitionRegistry,當我們掃描完時他會判斷是否將掃描的註解放到Spring上下文中;而ClassPathScanningCandidateComponentProvider則只是返回多個BeanDefinition
2 自定義掃描並注入到Spring容器中
2.1 還是定義一個註解,只不過不再需要@Component了
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomizeComponent {
String value() default "";
}
2.2 註解修飾的類
@CustomizeComponent
public class ScanClass1 {
public void print() {
System.out.println("scanClass1");
}
}
2.3 BeanScannerConfigurer用於嵌入到Spring的載入過程的中,這裡用到了BeanFactoryPostProcessor 和 ApplicationContextAware。
Spring提供了一些的介面使程式可以嵌入Spring的載入過程。這個類中的繼承ApplicationContextAware介面,Spring會讀取ApplicationContextAware型別的的JavaBean,並呼叫setApplicationContext(ApplicationContext applicationContext)傳入Spring的applicationContext。
同樣繼承BeanFactoryPostProcessor介面,Spring會在BeanFactory的相關處理完成後呼叫postProcessBeanFactory方法,進行定製的功能。
@Component
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");
}
}
2.4 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());
}
}
2.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()就通過代理了原始類的方法,自定義類的方法。
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);
}
}
2.6 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();
}
}
3 自定義註解掃描
public static void main(String[] args) {
ClassPathScanningCandidateComponentProvider scan = new ClassPathScanningCandidateComponentProvider(false);
TypeFilter fypeFilter = new AnnotationTypeFilter(CustomizeComponent.class);
scan.addIncludeFilter(fypeFilter);
Set<BeanDefinition> beanDefinitions = scan.findCandidateComponents("com.gz.scan");
for(BeanDefinition beanDefinition : beanDefinitions) {
System.out.println(beanDefinition.getBeanClassName());
}
}