Spring的@Autowired与自动代理

我想Spring框架的优点,第一条应该就是Spring的依赖注入了吧。尤其@Autowired这个注解,甚至可以直接注解在私有成员变量上而不用去写setter方法。之前看《Spring实战》这书的时候,里面说autowired自动注入是byType的,当时一直以为就是byClass,就是根据变量的类类型,去Spring context中找这样类型的bean。但当我写Dao层、Service层代码的时候,发现情况根本不是这样的。

比如说我现有有一个BookmakerDao接口,以及接口的实现类BookmakerDaoHibernate,并用@Repository注解将实现类注解为Spring bean。

public interface BookmakerDao {
    /**
     * 
     */ 
}
@Repository
public class BookmakerDaoHibernate implements BookmakerDao {
    /**
     *
     */
}

那么我在单元测试的代码中,发现如下情况:

public class BookmakerDaoTest extends SpringTest {
    @Autowired
//    private BookmakerDaoHibernate bookmakerDao; // 无法注入
    private BookmakerDao bookmakerDao;  // 成功注入
    
    @Test
    public void test() {
        /**
         */
    }
}

使用类名定义bookmakerDao变量无法自动注入,而使用接口名定义的变量则可以成功注入。

实际上,@Autowired的byType是包括了byClass以及byInterface的。

但上面这个例子为什么无法根据类名主动注入呢?先来看一下错误输出:

nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.funway.betspider.domain.BookmakerDaoHibernate] found for dependency

没有找到这个BookmakerDaoHibernate类型的bean?wtf!我们明明已经用Repository注解为一个Spring bean了呀。那我就打印出所有bean来看看。我们可以写一个钩子类实现Spring的BeanPostProcessor接口,这个接口会在每个Spring bean初始化的时候被调用。

@Component
public class TestAware implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.err.println("after init: [" + beanName  + "]" + bean.getClass());
        return bean;
    }
    
}

然后观察输出的beanNmae与bean的类类型,会发现在Spring context中name叫做bookmakerDaoHibernate的bean,它的类类型根本不是BookmakerDaoHbiernate,而是一个Proxy!!!

屏幕快照 2016-03-12 下午4.53.58

这是因为Spring在运行时使用自动代理将BookmakerDaoHibernate对象放在了一个代理对象的内部,所以当我们以为可以byClass注入的时候,却发现在Spring容器中无法找到这个BookmakerDaoHibernate类型的bean了。

Spring会使用两种自动代理生成方式,一种是基于jdk的动态代理,一种是基于cglib的动态代理。

上图这种com.sun.proxy.$ProxyXX就是jdk动态代理(java.lang.reflect.Proxy)生成的代理,这种代理模式生成的代理对象依赖于目标对象的接口(目标对象必须有上级接口才可以使用jdk动态代理)

cglib生成的代理如下图所示,这种代理模式生成的代理对象是目标对象的子类。(可以被@Autowired根据父类名自动注入)

屏幕快照 2016-03-12 下午5.12.38

至于Spring根据什么判断是否要为一个bean自动生成代理(被AOP切入的肯定会被自动代理),这得花更多时间去了解细节了。暂时不想了。。 =。=#

 

关于jdk以及cglib代理方式,可参考:

http://www.cnblogs.com/yejg1212/p/3234377.html

http://www.cnblogs.com/yejg1212/p/3234423.html

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top