Autowired注解闲谈

早上看到一个朋友的个人空间说到自己面试被问及@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注入,并且自动装配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值