在没有引入spring框架前,在类与类之间的调用关系通过new关键字进行实例化对象,进而调用对象的方法或者属性。在引入spring框架后,我们开始使用spring容器进行IOC注入,在spring的配置文件applicationContext.xml文件里,配置相应类的bean节点,在不配置懒加载bean节点的前提下,当配置文件applicationContext加载后,会自动实例化所有的singleton的bean并缓存在容器中供我们类的调用。再后来,我们不再在xml文件中配置bean节点,而是配置<context:component-scan/>标签自动扫描包,通过注解的方式,被spring容器进行管理。
现在我来简单描述一下这三个过程:
1· 没有引入spring之前:
HelloWorld a=new HelloWorld();
2·在引入Spring容器后,在applicationContext.xml文件里,配置类HelloWorld节点,
<beanid="helloWorld"class="com.dynamic.study1.HelloWorld"></bean>
3·在使用注解后:
@Component
public class HelloWorld{}
这三个声明方式虽然变了,但是原理还是一样的。
在2的过程中,是通过反射机制动态加载的类HelloWorld,其实质和new实例化是一样的,即:
Class t=Class.forName("package.A");
t.newInstance();
和
A a = new A();
实现的效果是一致的。所以说1和2的变化过程实质是一样的。
那在2到3的变化过程中,是怎么工作机制法呢?换句话说,spring注解是如何工作的呢?Spring是如何读取注解信息,并注入到bean容器中的?
先来看以下demo:
A 自定义注解:
package com.dynamic.spring.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyComponent {
String value() default "";
}
B java普通类:
package com.dynamic.spring.annotations;
@MyComponent
public class User {
public User(){
System.out.println("the User class.......");
}
}
C Spring配置文件applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置自动扫描包 -->
<context:component-scan base-package="com.dynamic.spring.annotations"></context:component-scan>
</beans>
D 客户端:
package com.dynamic.spring.annotations;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args){
ApplicationContext atc=new ClassPathXmlApplicationContext("applicationContext.xml");
atc.getBean(User.class);
}
}
执行这个例子,就会发现,自定义注解@MyComponent被spring容器加载进来了。
执行结果:
解析:为什么自定义的注解也能够像@Service,@Respository等注解被spring的配置文件扫描到?
查看Spring的源码会发现,Spring是使用ClassPathScanningCandidateComponentProvider扫描package,
这个类的 registerDefaultFilters 方法有这样几行代码:
/**
* Register the default filter for {@link Component @Component}.
* <p>This will implicitly register all annotations that have the
* {@link Component @Component} meta-annotation including the
* {@link Repository @Repository}, {@link Service @Service}, and
* {@link Controller @Controller} stereotype annotations.
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
* JSR-330's {@link javax.inject.Named} annotations, if available.
*
*/
@SuppressWarnings("unchecked")
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(当然还要有Stringvalue() default"";的方法,因为Spring的Bean都是有beanName唯一标示的),都可以被Spring扫描到,并注入容器内。