Spring高级程序设计
第3章 控制反转
1、控制反转和依赖注入
2、控制反转类型
3、Spring中的控制反转
4、基于Spring的依赖注入
1控制反转和依赖注入
控制反转(IoC)。依赖注入(DI)。
实际上依赖注入是控制反转的一种特殊形式。
控制反转和依赖注入的核心目标是提供一个更为简单的机制去规定和组件之间的依赖关系(他通常被称作为对象的协助者),并在他们的生命周期中管理这些依赖。
一个需要某些特定依赖的组件通常被称为依赖对象或者目标。控制反转提供的服务使一个组件能够在他的生命周期中访问他的依赖和服务,使用这个这种方法与他的依赖进行交互,这是一件非常美妙的事情。
大体来说,控制反转可以分为两个子类型:依赖注入和依赖查找。这些字类型又能划分成控制反转服务的若干具体实现。以此定义我们可以清楚的知道,当我们谈论依赖注入时我们总是在谈论控制反转,反之则不然。
2控制反转类型
依赖查找有两种类型:依赖拖拽和上下文依赖查找(CDL)。
依赖注入两种类型:构造方法依赖注入和设置方法依赖注入。
依赖拖拽示例:
上下文依赖查找(CDL):
在某些方面跟依赖拖拽相似,但是上下文依赖查找中,查找的过程是在容器管理的资源中进行的,而不是从集中注册表中,并且通常是作用在某些设置点上。
构造方法依赖注入:
在构造方法依赖注入中,组件的依赖从他们的构造方法中提供。组件声明一个或一组构造方法,将他们的依赖作为参数,控制反转容器就会降依赖在组件实例化时传给他。
设置方法依赖注入:
在设置方法依赖注入中,控制反转容器通过JavaBean风格的设置方法为组件注入依赖。一个组件的设置方法向反转容器公开一组依赖。
4基于Spring的依赖注入
XmlBeanFactoryDemo:
XmlBeanFactory源于DefaultListableBeanFactory且简单的扩展了他,利用XmlBeanDefinitionReader进行自动配置。
对比:
bean的实例化模式:
默认情况下,Spring中所有的bean均为单例。也就是Spring只维护一个单独的bean实例,所有的依赖对象都是用同一个实例,并且所有对BeanFactory.getBean(XXX)方法的调用都返回相同的实例。
配置文件:
测试:
总结:所以,是不是发现了很严重的问题,交给spring的bean只要是默认的sington模式都不要有全局变量。
非单例实例化模式:
prototype:每次调用getBean()方法都返回一个bean的新实例。
request:每次在Web应用中调用getBean()方法将为每一个Http请求返回bean的唯一实例。此行为只在WebApplicationContext和他的子接口中实现。
session:每次调用getBean()方法将为每个Http Session返回bean的唯一实例。跟request一样,此作用范围只在WebApplicatoinContext和他的子接口中有效。
global session:每次在portlet上下文中调用getBean()方法将为全局Http Session返回bean的唯一实例。跟request和session一样,此实例化方式只被WebApplicationContext和他的子接口所支持。
prototype测试:
选择一种实例化模式:
单例适用:
无状态的共享对象:当有一个无状态且关联很多依赖的对象时,使用单例。如果无状态的差别,你就不需要做同步处理,当某个依赖对象在工作中需要使用这个bean时,你就不需要创建新的实例。
只读状态共享对象:这跟前一点相似,但是有一些只读的状态,比如说一个只读的属性。这种情况下,你仍然不需要做同步处理,因此为每一个请求创建bean的实例只会增加额外的开销而已。
共享状态的共享对象:如果你有一个必须共享状态的bean,单例便是理想选择。这种情况下,你需要确保状态写的同步吹尽量原子化。
具有可读状态的高吞吐量对象:如果某一个bean在你的应用程序中被大量的使用,你可能会发现保持单例并且对bean状态的所有写访问进行同步会比持续的创建上百个bean实例具有更好的性能。使用此方法时,在不牺牲连贯性的前提下尽量保持同步的原子性。当应用程序在一个较大的时间跨度内创建了大量的实例,或者当你的共享对象只有少量可写状态,再或者当创建新实例花销太大时,你会发现这个方法尤其有用。
使用非单例:
具有可写状态的对象:如果你的bean有大量的可写状态,你会发现用以同步的成本比创建新实例来处理依赖对象的每一个请求的成本要高。
具有私有状态的对象:有时,依赖对象需要一个包含私有状态的bean以便他们能够同其他依赖此bean的对象区分开来独立运行。在这样的情况下,单例显然是不合适的。
解析依赖:
bean的自动装配:
或者注解:
@Autowired
(不过,我还是一般会明确的指明依赖哪些具体的bean。)
依赖检查:
Simple方法检查是否所有属性或者内建类型都有值。使用这种方法,Spring不会对其他类型的属性做检查。
objects方法检查所有simple方法没有检查到的属性,比如一个int一个Foo类型,他就会检查这个没有被simple检查到的Foo类型。
all方法也就是执行上面两张检查。
bean的继承:
第3章 控制反转
1、控制反转和依赖注入
2、控制反转类型
3、Spring中的控制反转
4、基于Spring的依赖注入
1控制反转和依赖注入
控制反转(IoC)。依赖注入(DI)。
实际上依赖注入是控制反转的一种特殊形式。
控制反转和依赖注入的核心目标是提供一个更为简单的机制去规定和组件之间的依赖关系(他通常被称作为对象的协助者),并在他们的生命周期中管理这些依赖。
一个需要某些特定依赖的组件通常被称为依赖对象或者目标。控制反转提供的服务使一个组件能够在他的生命周期中访问他的依赖和服务,使用这个这种方法与他的依赖进行交互,这是一件非常美妙的事情。
大体来说,控制反转可以分为两个子类型:依赖注入和依赖查找。这些字类型又能划分成控制反转服务的若干具体实现。以此定义我们可以清楚的知道,当我们谈论依赖注入时我们总是在谈论控制反转,反之则不然。
2控制反转类型
依赖查找有两种类型:依赖拖拽和上下文依赖查找(CDL)。
依赖注入两种类型:构造方法依赖注入和设置方法依赖注入。
依赖拖拽示例:
- /**
- * 控制反转--依赖查找--依赖拖拽
- * @author partner4java
- *
- */
- public class DependencyPullDemo {
- public static void main(String[] args) {
- BeanFactory beanFactory = getBeanFactory();
- MessageService messageService = (MessageService) beanFactory.getBean("service");
- messageService.execute();
- }
- private static BeanFactory getBeanFactory(){
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- BeanDefinitionReader reader = new PropertiesBeanDefinitionReader(beanFactory);
- reader.loadBeanDefinitions(new ClassPathResource("/META-INF/spring/ioc-pull-context.properties"));
- return beanFactory;
- }
- }
上下文依赖查找(CDL):
在某些方面跟依赖拖拽相似,但是上下文依赖查找中,查找的过程是在容器管理的资源中进行的,而不是从集中注册表中,并且通常是作用在某些设置点上。
- /**
- * 控制反转--依赖查找--上下文依赖查找(CDL)
- * @author partner4java
- *
- */
- public class ContextualizedDependencyLookupDemo {
- private static Set<ManagedComponent> components = new HashSet<ManagedComponent>();
- private static class MessageServiceComponent implements ManagedComponent {
- private MessageService messageService;
- public void lookup(BeanFactory beanFactory) {
- this.messageService = (MessageService) beanFactory.getBean("service");
- }
- public void run(){
- this.messageService.execute();
- }
- }
- public static void main(String[] args) {
- BeanFactory beanFactory = getBeanFactory();
- MessageServiceComponent messageServiceComponent = new MessageServiceComponent();
- registerCompontent(messageServiceComponent);
- allowComponentsToLookup(beanFactory);
- messageServiceComponent.run();
- }
- private static void allowComponentsToLookup(BeanFactory beanFactory){
- for(ManagedComponent component:components){
- component.lookup(beanFactory);
- }
- }
- private static void registerCompontent(ManagedComponent managedComponent){
- components.add(managedComponent);
- }
- private static BeanFactory getBeanFactory(){
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- BeanDefinitionReader reader = new PropertiesBeanDefinitionReader(beanFactory);
- reader.loadBeanDefinitions(new ClassPathResource("/META-INF/spring/ioc-pull-context.properties"));
- return beanFactory;
- }
- }
构造方法依赖注入:
在构造方法依赖注入中,组件的依赖从他们的构造方法中提供。组件声明一个或一组构造方法,将他们的依赖作为参数,控制反转容器就会降依赖在组件实例化时传给他。
设置方法依赖注入:
在设置方法依赖注入中,控制反转容器通过JavaBean风格的设置方法为组件注入依赖。一个组件的设置方法向反转容器公开一组依赖。
4基于Spring的依赖注入
XmlBeanFactoryDemo:
XmlBeanFactory源于DefaultListableBeanFactory且简单的扩展了他,利用XmlBeanDefinitionReader进行自动配置。
- public class XmlBeanFactoryDemo {
- public static void main(String[] args) {
- XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("/META-INF/spring/beanfactorydemo2-context.xml"));
- Oracle oracle = (Oracle) bf.getBean("oracle");
- System.out.println("Meaning of life is " + oracle.defineMeaningOfLife());
- }
- }
对比:
- DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
- BeanDefinitionReader reader = new PropertiesBeanDefinitionReader(bf);
- reader.loadBeanDefinitions(new ClassPathResource("/META-INF/spring/helloworld-context.properties"));
bean的实例化模式:
默认情况下,Spring中所有的bean均为单例。也就是Spring只维护一个单独的bean实例,所有的依赖对象都是用同一个实例,并且所有对BeanFactory.getBean(XXX)方法的调用都返回相同的实例。
配置文件:
- <?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="messageCon1" class="cn.partner4java.singleton.MessageCon1"></bean>
- <bean id="messageCon2" class="cn.partner4java.singleton.MessageCon2"></bean>
- </beans>
测试:
- public class MessageCon1 {
- private String message = "box";
- public void sysMessage(String message){
- if(message != null){
- this.message = message;
- }
- System.out.println(this.message);
- }
- }
- /**
- * 证明了单例,返回的是同一个对象,就是同一个内存指针,一个改变了公共变量,另一个就会获取到
- */
- public static void testMessageCon1(){
- BeanFactory beanFactory = getBeanFactory();
- MessageCon1 messageCon11 = (MessageCon1) beanFactory.getBean("messageCon1");
- messageCon11.sysMessage("hello world");
- MessageCon1 messageCon12 = (MessageCon1) beanFactory.getBean("messageCon1");
- messageCon12.sysMessage(null);
- if(messageCon11 == messageCon12){
- System.out.println("相等");
- }else {
- System.out.println("不相等");
- }
- // 后台打印:
- // hello world
- // hello world
- // 相等
- }
总结:所以,是不是发现了很严重的问题,交给spring的bean只要是默认的sington模式都不要有全局变量。
非单例实例化模式:
prototype:每次调用getBean()方法都返回一个bean的新实例。
request:每次在Web应用中调用getBean()方法将为每一个Http请求返回bean的唯一实例。此行为只在WebApplicationContext和他的子接口中实现。
session:每次调用getBean()方法将为每个Http Session返回bean的唯一实例。跟request一样,此作用范围只在WebApplicatoinContext和他的子接口中有效。
global session:每次在portlet上下文中调用getBean()方法将为全局Http Session返回bean的唯一实例。跟request和session一样,此实例化方式只被WebApplicationContext和他的子接口所支持。
prototype测试:
- public class MessageCon3 {
- private static String message = "box";
- public void sysMessage(String message){
- if(message != null){
- this.message = message;
- }
- System.out.println(this.message);
- }
- }
- <?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="messageCon1" class="cn.partner4java.singleton.MessageCon1" scope="prototype"></bean>
- <bean id="messageCon3" class="cn.partner4java.singleton.MessageCon3" scope="prototype"></bean>
- </beans>
- public static void testMessageCon1(){
- BeanFactory beanFactory = getBeanFactory();
- MessageCon1 messageCon11 = (MessageCon1) beanFactory.getBean("messageCon1");
- messageCon11.sysMessage("hello world");
- MessageCon1 messageCon12 = (MessageCon1) beanFactory.getBean("messageCon1");
- messageCon12.sysMessage(null);
- if(messageCon11 == messageCon12){
- System.out.println("相等");
- }else {
- System.out.println("不相等");
- }
- // 后台打印:
- // hello world
- // box
- // 不相等
- }
- /**
- * 即使设置了scope="prototype",static的全局变量也是个麻烦事
- */
- public static void testMessageCon3(){
- BeanFactory beanFactory = getBeanFactory();
- MessageCon3 messageCon31 = (MessageCon3) beanFactory.getBean("messageCon3");
- messageCon31.sysMessage("hello world");
- MessageCon3 messageCon32 = (MessageCon3) beanFactory.getBean("messageCon3");
- messageCon32.sysMessage(null);
- if(messageCon31 == messageCon32){
- System.out.println("相等");
- }else {
- System.out.println("不相等");
- }
- // 后台打印:
- // hello world
- // hello world
- // 不相等
- }
选择一种实例化模式:
单例适用:
无状态的共享对象:当有一个无状态且关联很多依赖的对象时,使用单例。如果无状态的差别,你就不需要做同步处理,当某个依赖对象在工作中需要使用这个bean时,你就不需要创建新的实例。
只读状态共享对象:这跟前一点相似,但是有一些只读的状态,比如说一个只读的属性。这种情况下,你仍然不需要做同步处理,因此为每一个请求创建bean的实例只会增加额外的开销而已。
共享状态的共享对象:如果你有一个必须共享状态的bean,单例便是理想选择。这种情况下,你需要确保状态写的同步吹尽量原子化。
具有可读状态的高吞吐量对象:如果某一个bean在你的应用程序中被大量的使用,你可能会发现保持单例并且对bean状态的所有写访问进行同步会比持续的创建上百个bean实例具有更好的性能。使用此方法时,在不牺牲连贯性的前提下尽量保持同步的原子性。当应用程序在一个较大的时间跨度内创建了大量的实例,或者当你的共享对象只有少量可写状态,再或者当创建新实例花销太大时,你会发现这个方法尤其有用。
使用非单例:
具有可写状态的对象:如果你的bean有大量的可写状态,你会发现用以同步的成本比创建新实例来处理依赖对象的每一个请求的成本要高。
具有私有状态的对象:有时,依赖对象需要一个包含私有状态的bean以便他们能够同其他依赖此bean的对象区分开来独立运行。在这样的情况下,单例显然是不合适的。
解析依赖:
- <?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="b" class="com.apress.prospring2.ch03.beandependency.B" depends-on="a"/>
- <bean id="a" class="com.apress.prospring2.ch03.beandependency.A"/>
- </beans>
bean的自动装配:
- <?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="foo" class="com.apress.prospring2.ch03.autowiring.Foo"/>
- <bean id="bar" class="com.apress.prospring2.ch03.autowiring.Bar"/>
- <bean id="byName" autowire="byName" class="com.apress.prospring2.ch03.autowiring.Target"/>
- <bean id="byType" autowire="byType" class="com.apress.prospring2.ch03.autowiring.Target"/>
- <bean id="constructor" autowire="constructor" class="com.apress.prospring2.ch03.autowiring.Target"/>
- <bean id="autodetect" autowire="autodetect" class="com.apress.prospring2.ch03.autowiring.Target"/>
- </beans>
或者注解:
@Autowired
(不过,我还是一般会明确的指明依赖哪些具体的bean。)
依赖检查:
Simple方法检查是否所有属性或者内建类型都有值。使用这种方法,Spring不会对其他类型的属性做检查。
objects方法检查所有simple方法没有检查到的属性,比如一个int一个Foo类型,他就会检查这个没有被simple检查到的Foo类型。
all方法也就是执行上面两张检查。
- <?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="target1" class="com.apress.prospring2.ch03.dependencycheck.SimpleBean"
- dependency-check="simple">
- <property name="someInt" value="1"/>
- </bean>
- <bean id="target2" class="com.apress.prospring2.ch03.dependencycheck.SimpleBean"
- dependency-check="objects">
- <property name="nestedSimpleBean" ref="nestedSimpleBean"/>
- </bean>
- <bean id="target3" class="com.apress.prospring2.ch03.dependencycheck.SimpleBean"
- dependency-check="all">
- <property name="nestedSimpleBean" ref="nestedSimpleBean"/>
- <property name="someInt" value="1"/>
- </bean>
- <bean id="nestedSimpleBean" class="com.apress.prospring2.ch03.dependencycheck.SimpleBean"/>
- </beans>
bean的继承:
- <?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="parent" class="com.apress.prospring2.ch03.inheritance.SimpleBean" abstract="true">
- <property name="name" value="Jan Machacek"/>
- </bean>
- <bean id="bean1" class="com.apress.prospring2.ch03.inheritance.SimpleBean" parent="parent">
- <property name="age" value="28"/>
- </bean>
- <bean id="bean2" class="com.apress.prospring2.ch03.inheritance.SimpleBean" parent="parent"/>
- </beans>