依赖注入
我们在 Spring — 循环依赖 中谈到 Spring 的两种依赖注入方式
- 构造器注入
- 属性注入(setter注入也归属于此)
@Service public class HelloService { /** * 属性注入 */ @Autowired private BeanFactory beanFactory; /** * 构造器注入 */ public HelloService(ApplicationContext applicationContext) { } /** * 属性注入 * */ @Autowired public void setEnvironment(Environment environment) { System.out.println(""); } } 复制代码
关于构造函数实例化策略的文章已经在 Spring 源码--Bean 实例化 和 Spring 实例化--谁是我的候选人 已经谈及过
那么 @Autowired
放在属性上和放在 setter 方法上有什么不一样呢 ? 其实对于 Spring 来说都是一样的、都是一个注入元素。
AutowiredAnnotationBeanPostProcessor#postProcessProperties 该方法在 bean 实例化之后被调用
那么构造函数注入和属性注入的差别和适用场景是啥?
- 构造函数注入注入的时机是先于属性注入的、并且是强依赖的
- 对于那些非必需的依赖、推荐使用属性注入
方法注入
Lookup
方法注入的本质实际上是 Spring 继承你当前类、通过CGLib 产生子类并且重写需要注入的方法。
可能这么说有的不明所以,看个例子
@Service @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class PrintService { private int printCount; public void print(String name) { System.out.println("hi! " + name + " " + printCount++); } } @Service public class HelloService { @Autowired private PrintService printService; public void sayHi(String name) { printService.print(name); } } 复制代码
PrintService 是一个 prototype 类型的 bean、HelloService 是一个 singleton 、我的本意是每次调用 sayHi 方法的时候都能使用一个新创建的 PrintService 对象,但是单纯靠属性注入或者构造函数注入都是不能够实现的。
或许你可能会想到使用 ApplicationContext / BeanFactory 每次使用 PrintService 的时候去 Spring 中获取,这样子就能每次获取到一个新创建的实例。这样当然可以、但是既然逻辑都是这样、框架能不能帮我们做呢 ? 当然是可以的
@Service public class HelloService { public void sayHi(String name) { getPrintService().print(name); } @Lookup("printService") protected PrintService getPrintService(){ return null; } } 复制代码
@Lookup 修饰的方法必须是可被子类覆盖的、如果没有设置 beanName 给 @Lookup 注解、那么则根据方法返回类型从 Spring 中获取该 bean
public @interface Lookup { String value() default ""; } 复制代码
这段代码其实我们在 Spring 源码--Bean 实例化 和 Spring 实例化--谁是我的候选人 曾经遇到过、只是没有点进来详细了解。
如果你用 @Lookup 修饰的方法的类是通过配置类去实例化的话、那么这个注解则会失效。只有通过构造函数实例化的 bean 才能被 Spring 处理、使用 CGLib 产生子类。
MethodReplacer
第二种则是 MethodReplacer
<bean name="replacer" class="springroad.deomo.chap4.MethodReplace"> </bean> <bean name="testBean" class="springroad.deomo.chap4.LookupMethodBean"> <replaced-method name="test" replacer="replacer"> </replaced-method> </bean> 复制代码
public class LookupMethodBean { public void test() { System.out.println("原始方法!"); } } public class MethodReplace implements MethodReplacer { public Object reimplement(Object obj, Method method, Object[] args) throws Throwable { System.out.println("方法已经被替换!"); return null; } } 复制代码
暂时没有找到 MethodReplacer 相关的注解、只能用原始的 xml 配置
原理跟 Lookup 也是一样的
是不是跟 AOP 很像、但是其实是不一样的、MethodReplacer 你是永远没有办法调回被覆盖的方法的、它是完全覆盖的、而不是像 AOP 那样可以前置后置拦截。所以实际业务上很少地方可以用到这个 MethodReplacer 、能用到它的 AOP 都能做到、甚至比它灵活太多