概念关系 控制反转(IoC) VS 依赖注入(DI)
控制反转可以分为两种子类型:依赖注入(DI)和依赖查找
1.依赖查找
依赖拉取(dependency pull,DL)
例如:老版的EJB里面 通过JNDI API查找EJB组件
spring基于依赖拉取的典型场景
applicationContext.getBean("renderer");
上下文依赖查找(contextualized dependecy lookup,CDL)
2.依赖注入
构造函数
setter依赖注入
字段注入
BeanFactory接口
涉及到的核心类有
DefaultListableBeanFactory
BeanDefinitionReader
BeanDefinition
BeanDefinitionReader
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
rdr.loadBeanDefinitions(new ClassPathResource("spring/xml-bean-factory-config.xml"));
Oracle oracle = (Oracle) factory.getBean("oracle");
System.out.println(oracle.defineMeaningOfLife());
3.5 配置ApplicationContext
ApplicationContext的嵌套
pro-spring-5 chapter03/nesting demo源码
文件结构.pngpackage com.apress.prospring5.ch3;
import org.springframework.context.support.GenericXmlApplicationContext;
public class HierarchicalAppContextUsage {
public static void main(String... args) {
GenericXmlApplicationContext parent = new GenericXmlApplicationContext();
parent.load("classpath:spring/parent-context.xml");
parent.refresh();
GenericXmlApplicationContext child = new GenericXmlApplicationContext();
child.load("classpath:spring/child-context.xml");
child.setParent(parent);
child.refresh();
Song song1 = (Song) child.getBean("song1");
Song song2 = (Song) child.getBean("song2");
Song song3 = (Song) child.getBean("song3");
System.out.println("from parent ctx: " + song1.getTitle());
System.out.println("from child ctx: " + song2.getTitle());
System.out.println("from parent ctx: " + song3.getTitle());
child.close();
parent.close();
}
}
package com.apress.prospring5.ch3;
public class Song {
private String title;
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
child-context.xml文件内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="song1" class="com.apress.prospring5.ch3.Song"
p:title-ref="parentTitle"/>
<bean id="song2" class="com.apress.prospring5.ch3.Song"
p:title-ref="childTitle"/>
<bean id="song3" class="com.apress.prospring5.ch3.Song">
<property name="title">
<ref parent="childTitle"/>
</property>
</bean>
<bean id="childTitle" class="java.lang.String" c:_0="No Such Thing"/>
</beans>
parent-context.xml文件内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="childTitle" class="java.lang.String" c:_0="Daughters"/>
<bean id="parentTitle" class="java.lang.String" c:_0="Gravity"/>
</beans>
xml配置
注解配置
java配置
@ComponentScan(basePackages = {"com.apress.prospring5.ch3.annotated"})
@Configuration
public class HelloWorldConfiguration {
@Bean
public MessageProvider provider() {
return new HelloWorldMessageProvider();
}
@Bean
public MessageRenderer renderer(){
MessageRenderer renderer = new StandardOutMessageRenderer();
renderer.setMessageProvider(provider());
return renderer;
}
}
@ImportResource(locations = {"classpath:spring/app-context-xml.xml"})
@Configuration
public class HelloWorldConfiguration {
}
3.5.2 方法注入
除了构造函数注入,setter注入,field注入,spring还有一种不常用的方法注入,spring的方法注入功能有两种形式
查找方法注入(Lookup Method Injection)
方法替换(Method Replacement)
查找方法注入
解决场景:解决当bean依赖于另一个具有不同生命周期bean的需求
例如,单例依赖于非单例场景
@Lookup注解 (<lookup-method>标签)
工作原理
spring使用cglib动态生成查找方法注入类的子类,将动态重写该方法
<beanid = ” abstractLookupBean ”
class=” com .apress.prospring5 .ch3 .AbstractLookupDemoBean”>
<lookup-method name="getMySinger" bean=” sinqer"/> </bean>
对于 abstractLookupBean ,需要使用 <lookup-method>标记来配置查找方法。<lookup-method>标记的name属性告诉Spring应该覆盖的bean方法的名称 。该 方法不接收任何参数,并且所返回的类型应该是希望从该方法返回的bean的类型。此时,该方法应返回一个类型为 Singer的类或其子类。 bean属性告诉Spring查找方法应该返回哪个bean。
pro-spring-5 method-injection demo source
image.pngpackage com.apress.prospring5.ch3.annotated;
public interface DemoBean {
Singer getMySinger();
void doSomething();
}
package com.apress.prospring5.ch3.annotated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("standardLookupBean")
public class StandardLookupDemoBean implements DemoBean {
private Singer mySinger;
@Autowired
@Qualifier("singer")
public void setMySinger(Singer mySinger) {
this.mySinger = mySinger;
}
@Override
public Singer getMySinger() {
return this.mySinger;
}
@Override
public void doSomething() {
mySinger.sing();
}
}
原型singerbean
package com.apress.prospring5.ch3.annotated;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* Created by iuliana.cosmina on 2/20/17.
*/
@Component("singer")
@Scope("prototype")
public class Singer {
private String lyric = "I played a quick game of chess with the salt and pepper shaker";
public void sing() {
// commented to avoid console pollution
//System.out.println(lyric);
}
}
package com.apress.prospring5.ch3.annotated;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component("abstractLookupBean")
public class AbstractLookupDemoBean implements DemoBean {
@Lookup("singer")
public Singer getMySinger() {
return null; // This implementation will be overridden by dynamically generated subclass
}
@Override
public void doSomething() {
getMySinger().sing();
}
}
package com.apress.prospring5.ch3.config;
import com.apress.prospring5.ch3.annotated.DemoBean;
import com.apress.prospring5.ch3.annotated.Singer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.StopWatch;
/**
* Created by iuliana.cosmina on 2/19/17.
*/
public class LookupConfigDemo {
@Configuration
@ComponentScan(basePackages = {"com.apress.prospring5.ch3.annotated"})
public static class LookupConfig {}
public static void main(String... args) {
GenericApplicationContext ctx = new AnnotationConfigApplicationContext(LookupConfig.class);
//Arrays.stream(ctx.getBeanDefinitionNames()).forEach(s-> System.out.println(s));
DemoBean abstractBean = ctx.getBean("abstractLookupBean", DemoBean.class);
DemoBean standardBean = ctx.getBean("standardLookupBean", DemoBean.class);
displayInfo("abstractLookupBean", abstractBean);
displayInfo("standardLookupBean", standardBean);
ctx.close();
}
public static void displayInfo(String beanName, DemoBean bean) {
Singer singer1 = bean.getMySinger();
Singer singer2 = bean.getMySinger();
System.out.println("[" + beanName + "]: Singer Instances the Same? "
+ (singer1 == singer2));
StopWatch stopWatch = new StopWatch();
stopWatch.start("lookupDemo");
for (int x = 0; x < 100000; x++) {
Singer singer = bean.getMySinger();
singer.sing();
}
stopWatch.stop();
System.out.println("100000 gets took " + stopWatch.getTotalTimeMillis() + " ms");
}
/**
* execute result:
* [abstractLookupBean]: Singer Instances the Same? false
* 100000 gets took 1004 ms
* [standardLookupBean]: Singer Instances the Same? true
* 100000 gets took 1 ms
*
* Process finished with exit code 0
*/
}
执行结果:
1.单例bean的比较
2.执行效率的比较
方法替换
场景:使用方法替换,可以任务替换任何bean的方法实现,而不需更改所修改bean的源代码
例如:想替换第三方库某个类的方法实现
实现原理:动态创建bean类的子类,将想要替换的方法的调用重定向为另一个实现了MethodReplacer接口的bean
使用
<replaced-method>
<bean id="replacementTarget" class="com.apress.prospring5.ch3.ReplacementTarget">
<replaced-method name="formatMessage" replacer="methodReplacer">
<arg-type>String</arg-type>
</replaced-method>
</bean>
样例
package com.apress.prospring5.ch3;
public class ReplacementTarget {
public String formatMessage(String msg) {
return "<h1>" + msg + "</h1>";
}
public String formatMessage(Object msg) {
return "<h1>" + msg + "</h1>";
}
}
package com.apress.prospring5.ch3;
import org.springframework.beans.factory.support.MethodReplacer;
import java.lang.reflect.Method;
public class FormatMessageReplacer implements MethodReplacer {
@Override
public Object reimplement(Object arg0, Method method, Object[] args)
throws Throwable {
if (isFormatMessageMethod(method)) {
String msg = (String) args[0];
return "<h2>" + msg + "</h2>";
} else {
throw new IllegalArgumentException("Unable to reimplement method "
+ method.getName());
}
}
private boolean isFormatMessageMethod(Method method) {
if (method.getParameterTypes().length != 1) {
return false;
}
if (!("formatMessage".equals(method.getName()))) {
return false;
}
if (method.getReturnType() != String.class) {
return false;
}
if (method.getParameterTypes()[0] != String.class) {
return false;
}
return true;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="methodReplacer" class="com.apress.prospring5.ch3.FormatMessageReplacer"/>
<bean id="replacementTarget" class="com.apress.prospring5.ch3.ReplacementTarget">
<replaced-method name="formatMessage" replacer="methodReplacer">
<!--bean存在重载方法时,可指定参数-->
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="standardTarget" class="com.apress.prospring5.ch3.ReplacementTarget"/>
</beans>
package com.apress.prospring5.ch3;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.util.StopWatch;
public class MethodReplacementDemo {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:spring/app-context-xml.xml");
ctx.refresh();
ReplacementTarget replacementTarget = (ReplacementTarget) ctx
.getBean("replacementTarget");
ReplacementTarget standardTarget = (ReplacementTarget) ctx
.getBean("standardTarget");
displayInfo(replacementTarget);
displayInfo(standardTarget);
ctx.close();
/*
execute result:
<h2>Thanks for playing, try again!</h2>
1000000 invocations took: 426 ms
<h1>Thanks for playing, try again!</h1>
1000000 invocations took: 43 ms
*/
}
private static void displayInfo(ReplacementTarget target) {
System.out.println(target.formatMessage("Thanks for playing, try again!"));
StopWatch stopWatch = new StopWatch();
stopWatch.start("perfTest");
for (int x = 0; x < 1000000; x++) {
String out = target.formatMessage("No filter in my head");
//commented to not pollute the console
//System.out.println(out);
}
stopWatch.stop();
System.out.println("1000000 invocations took: "
+ stopWatch.getTotalTimeMillis() + " ms");
}
}
网友评论