我想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!!!
这是因为Spring在运行时使用自动代理将BookmakerDaoHibernate对象放在了一个代理对象的内部,所以当我们以为可以byClass注入的时候,却发现在Spring容器中无法找到这个BookmakerDaoHibernate类型的bean了。
Spring会使用两种自动代理生成方式,一种是基于jdk的动态代理,一种是基于cglib的动态代理。
上图这种com.sun.proxy.$ProxyXX就是jdk动态代理(java.lang.reflect.Proxy)生成的代理,这种代理模式生成的代理对象依赖于目标对象的接口(目标对象必须有上级接口才可以使用jdk动态代理)
cglib生成的代理如下图所示,这种代理模式生成的代理对象是目标对象的子类。(可以被@Autowired根据父类名自动注入)
至于Spring根据什么判断是否要为一个bean自动生成代理(被AOP切入的肯定会被自动代理),这得花更多时间去了解细节了。暂时不想了。。 =。=#
关于jdk以及cglib代理方式,可参考: