先看个案例
实体类ServiceA和ServiceB
public class ServiceA {
}
public class ServiceB {
private ServiceA serviceA;
public ServiceA getServiceA() {
return serviceA;
}
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
xml 配置:
注意:ServiceA配置是多例的,ServiceB默认是单例
<bean id="serviceA" class="com.spring.lookupMethod.ServiceA" scope="prototype"/>
<bean id="serviceB" class="com.spring.lookupMethod.ServiceB">
<property name="serviceA" ref="serviceA"/>
</bean>
测试:
@Test
public void lookupMethod() {
String path = "classpath:/bean/lookupMethod.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
Object serviceA1 = context.getBean("serviceA");
Object serviceA2 = context.getBean("serviceA");
System.out.println("serviceA --> " + serviceA1);
System.out.println("serviceA --> " + serviceA2);
ServiceB serviceB1 = (ServiceB)context.getBean("serviceB");
ServiceB serviceB2 = (ServiceB)context.getBean("serviceB");
System.out.println("serviceB --> " + serviceB1);
System.out.println("serviceB --> " + serviceB2);
ServiceA serviceA = serviceB1.getServiceA();
ServiceA serviceA3 = serviceB2.getServiceA();
System.out.println("serviceB.serviceA --> " + serviceA);
System.out.println("serviceB.serviceA --> " + serviceA3);
}
执行结果:
serviceA --> com.spring.lookupMethod.ServiceA@7c137fd5
serviceA --> com.spring.lookupMethod.ServiceA@183ec003
serviceB --> com.spring.lookupMethod.ServiceB@7d9d0818
serviceB --> com.spring.lookupMethod.ServiceB@7d9d0818
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@221a3fa4
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@221a3fa4
结论:
因为ServiceA我们配置的是多例,所以每次从容器中获取的ServiceA都是不一样的。ServiceB是单例的,在ServiceB中的ServiceA也是同一个对象。
如果我们想每次获取ServiceB中的ServiceA都是不一样的,该如何处理。可以使用ApplicationContextAware#setApplicationContext,我们就可以获取applicationContext
修改ServiceB,是它实现ApplicationContextAware接口:
public class ServiceB implements ApplicationContextAware {
ApplicationContext applicationContext;
private ServiceA serviceA;
public ServiceA getServiceA() {
return applicationContext.getBean(ServiceA.class);
}
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
测试方法不变,运行结果如下:
serviceA --> com.spring.lookupMethod.ServiceA@7c137fd5
serviceA --> com.spring.lookupMethod.ServiceA@183ec003
serviceB --> com.spring.lookupMethod.ServiceB@7d9d0818
serviceB --> com.spring.lookupMethod.ServiceB@7d9d0818
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@24569dba
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@5ddeb7cb
这样就可以实现,但是对spring的api有耦合的作用。所以我们使用方法拦截,spring中的lookup-method就可以实现这样的功能。
我们改造ServiceB:
public class ServiceB {
private ServiceA serviceA;
//直接返回null
public ServiceA getServiceA() {
return null;
}
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
xml 改造:
<bean id="serviceA" class="com.spring.lookupMethod.ServiceA" scope="prototype"/>
<bean id="serviceB" class="com.spring.lookupMethod.ServiceB">
<lookup-method name="getServiceA" bean="serviceA"/>
</bean>
测试方法不变,我们运行看结果:
serviceA --> com.spring.lookupMethod.ServiceA@1787f2a0
serviceA --> com.spring.lookupMethod.ServiceA@7de62196
serviceB --> com.spring.lookupMethod.ServiceB$$EnhancerBySpringCGLIB$$2f4980fe@163370c2
serviceB --> com.spring.lookupMethod.ServiceB$$EnhancerBySpringCGLIB$$2f4980fe@163370c2
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@51bf5add
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@7905a0b8
spring还提供了一个功能replaced-method,需要实现MethodReplacer接口
案例:
MyMethodReplacer
public class MyMethodReplacer implements ApplicationContextAware , MethodReplacer {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public Object reimplement(Object o, Method method, Object[] objects) throws Throwable {
return this.applicationContext.getBean(ServiceA.class);
}
}
xml 配置:
<bean id="serviceA" class="com.spring.lookupMethod.ServiceA" scope="prototype"/>
<bean id="myMethodReplacer" class="com.spring.lookupMethod.MyMethodReplacer"/>
<bean id="serviceB" class="com.spring.lookupMethod.ServiceB">
<!--name 为拦截的方法名 myMethodReplacer:实现MethodReplacer接口的bean名-->
<replaced-method name="getServiceA" replacer="myMethodReplacer"/>
</bean>
ServiceB#getServiceA,还是返回null,测试方法不变,运行结果如下:
serviceA --> com.spring.lookupMethod.ServiceA@7905a0b8
serviceA --> com.spring.lookupMethod.ServiceA@35a3d49f
serviceB --> com.spring.lookupMethod.ServiceB$$EnhancerBySpringCGLIB$$2f4980fe@389b0789
serviceB --> com.spring.lookupMethod.ServiceB$$EnhancerBySpringCGLIB$$2f4980fe@389b0789
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@1b410b60
serviceB.serviceA --> com.spring.lookupMethod.ServiceA@2462cb01
总结
lookup-method:方法查找,可以对指定的bean的方法进行拦截,然后从容器中查找指定的bean作为被拦截方法的返回值
replaced-method:方法替换,可以实现bean方法替换的效果,整体来说比lookup-method更灵活一些