早上看到一个朋友的个人空间说到自己面试被问及@autowired的实现。结果是卡壳了,回来之后觉得应该是反射之类的方式实现。个人感觉他说的也不能算错,不过不是我想看到的答案,自己查了查资料,顺便记下。
本段文字将包含如下几个方面的内容:
BeanPostProcessor接口的例子
BeanPostProcessor接口结合注解的例子
Autowired注解
Autowired是个注解类,
在实际使用中,它能很方便地帮我们自动装配Bean直接的关系,而不用自己制定。
它是Java注解和Spring扩展AutowiredAnnotationBeanPostProcessor的结合使用。
首先我们先定义一个自己注解类,通过结合BeanPostProcessor扩展功能实现通过注解修改Bean属性值的简单功能。
声明类:AutowiredAnnotationBeanPostProcessor ,该类是BeanPostProcessor的实现类。
BeanPostProcessor接口,BeanFactory预留出来的供开发者扩展功能。
- 先看一个BeanPostProcessor接口的例子
1.定义一个自己的BeanPostProcessor实现类:
public class MyBeanProcessor implements BeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("初始化之前 - bean"+beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("初始化之后 - bean"+beanName);
return bean;
}
}
2.然后配置在application.xml中,声明BeanPostProcessor和Cat类,并给Cat属性name赋值为tomcat。
<bean class="foo.beans.MyBeanProcessor"/>
<bean id="cat" class="foo.beans.Cat">
<property name="name" value="tomcat"></property>
</bean>
3.在main方法中调用:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
((DefaultListableBeanFactory)context.getBeanFactory()).getBeanDefinitionNames();
Cat cat = (Cat)context.getBean("cat");
System.out.println("Cat name is: "+ cat.getName());
输出结果:
初始化之前 - beancat
初始化之后 - beancat
初始化之前 - beanzookeeper
初始化之后 - beanzookeeper
I feed the cat: tomcat
可见,可以在每个bean的生成前后都做处理。
有了这样的机制,可以进行一下扩展,生成一个注解,可以在类的属性初始化之后,属性变成在注解中指定的值。
可以做如下的设计:
- BeanPostProcessor接口结合注解的例子
1.定义一个注解:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
String setValue() default "";
}
2.将注解应用到MyBeanProcessor 中:
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("初始化之后 - bean"+beanName);
ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException,
IllegalAccessException {
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, ((MyValue)field.getAnnotation(MyValue.class)).setValue());
}
}, new FieldFilter(){
public boolean matches(Field field) {
return field.getAnnotation(MyValue.class) != null;
}
});
return bean;
}
3.在Cat类中使用注解:
@MyValue(setValue="maleXXXXXXXXXX")
private String gender;
4.在Zookeeper类中使用:
public class ZooKeeper {
@Autowired
private Cat cat;
public void feedCat(){
System.out.println("I feed the cat: "+cat.getName()+" and the gender is "+cat.getGender());
}
}
结果:
初始化之前 - beancat
初始化之后 - beancat
初始化之前 - beanzookeeper
初始化之后 - beanzookeeper
I feed the cat: tomcat and the gender is maleXXXXXXXXXX
- Autowired注解
上面的两个例子其实就是@autowired注解最朴素的原理,定义注解,通过BeanPostProcessor加载并实现。
最原始使用@autowired的方式就是直接使用Spring定义的AutowiredAnnotationBeanPostProcessor类。
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
这样就可以在Spring项目中使用自动装配。Spring自己实现了很多这样的扩展和注解。同样,如果需要使用其他的注解,如@Required,就需要加上这样一句配置:
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
不过这样也不算简便,感觉有点繁琐。
Spring还提供了一种更简单的方式,可以一次性将AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor等BeanPostProcessor注入。
配置:
<context:annotation-config/>
深究其实现,查看ContextNamespaceHandler类中的实现:
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
AnnotationConfigBeanDefinitionParser类的parse方法如下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// Obtain bean definitions for all relevant BeanPostProcessors.
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
// Register component for the surrounding <context:annotation-config> element.
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
// Nest the concrete beans in the surrounding component.
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
}
// Finally register the composite component.
parserContext.popAndRegisterContainingComponent();
return null;
}
这样极大地简化了配置。
在我们使用注解时一般都会配置扫描包路径选项:
<context:component-scan base-package="package path"/>
这个配置会去base-package中加载bean对象,并且和<context:annotation-config/>
一样,将内置的PostBeanProcessor注入,并且自动装配。