Spring 基础入门(一)

本文代码部分来自于《spring in action》,本文讲的是使用!!

Spring 是为了解决什么

       一个框架的存在是为了解决某个问题的,那么Spring这个框架是为了解决什么问题呢?主要就是解耦,spring主要要把握两方面的知识,

  • DI(依赖注入 dependency injection) : 解耦, 方便测试
  • AOP(面向切面编程 aspect-oriented programming)

 

      关于 DI Ioc 和 AOP 可以先阅读这篇文章,本文讲的主要是使用。

DI (依赖注入 depency injection)

     先看一个例子:

  1 //定义一个勇士类
  2 public class DamselRescuingKnight implements Knight {
  3 
  4 	private RescueDamselQuest quest;
  5 
  6 	//营救任务
  7 	public DamselRescuingKnight() {
  8 		this.quest = new RescueDamselQuest();
  9 	}
 10 
 11 	public void embarkOnQuest() {
 12 		quest.embark();
 13 	}
 14 }

      可以看到这里,勇士的 embarkOnQuest ( ) 方法执行的传入的特定的任务,那么当勇士需要执行其他任务的时候就无法进行了,于是Queue 应该做成一个接口.这样的好处是解耦合和方便测试

  1 public class BraveKnight implements Knight {
  2 	private Quest quest;
  3 
  4 	public BraveKnight(Quest quest) {
  5 		this.quest = quest;
  6 	}
  7 
  8 	public void embarkOnQuest() {
  9 		quest.embark();
 10 	}
 11 }
  1 public class SlayDragonQuest implements Quest {
  2 	private PrintStream stream;
  3 
  4 	public SlayDragonQuest(PrintStream stream) {
  5 		this.stream = stream;
  6 	}
  7 
  8 	public void embark() {
  9 		stream.println("Embarking on quest to slay the dragon!");
 10 	}
 11 }

     上面使用依赖注入并且在构造器解耦被称为构造器解耦。 在spring中如何组装这些组件呢?(将queue配置给knight)

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 	xsi:schemaLocation="http://www.springframework.org/schema/beans
  5 	http://www.springframework.org/schema/beans/spring-beans.xsd">
  6 
  7 	<bean id="knight" class="com.springinaction.knights.BraveKnight">
  8 		<constructor-arg ref="quest" />
  9 	</bean>
 10 
 11 	<bean id="quest" class="com.springinaction.knights.SlayDragonQuest">
 12 		<constructor-arg value="#{T(System).out}" />
 13 	</bean>
 14 </beans>

 

  依赖注入

        图一. 依赖注入会将所依赖的关系自动交给目标对象, 而不是让对象自己去获取依赖

 

AOP(面向切面编程)

     加入一个勇士需要一个历史记录着纪录它的英雄事迹,那么

  1 public class Recorder {
  2 	private PrintStream stream;
  3 
  4 	public Recorder(PrintStream stream) {
  5 		this.stream = stream;
  6 	}
  7 
  8 	public void singBeforeQuest() {
  9 		stream.println("Fa la la, the knight is so brave!");
 10 	}
 11 
 12 	public void singAfterQuest() {
 13 		stream.println("Tee hee hee, the brave knight " +
 14 		"did embark on a quest!");
 15 	}
 16 }
  1 public class BraveKnight implements Knight {
  2 	private Quest quest;
  3 	private Recorder recorder;
  4 
  5 	public BraveKnight(Quest quest, Recorder recorder) {
  6 		this.quest = quest;
  7 		this.recorder= recorder;
  8 	}
  9 
 10 	public void embarkOnQuest() throws QuestException {
 11 		recorder.singBeforeQuest();
 12 		quest.embark();
 13 		recorder.singAfterQuest();
 14 	}
 15 }

     但是这样似乎有点不合常理,当我需要一名勇士时是否每次都要匹配以为记录者?假如记录着可以当我需要的时候出现那么就好了。使用spring的AOP 编程,

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4        xmlns:aop="http://www.springframework.org/schema/aop"
  5        xsi:schemaLocation="http://www.springframework.org/schema/aop
  6 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
  7 http://www.springframework.org/schema/beans
  8 http://www.springframework.org/schema/beans/spring-beans.xsd">
  9 
 10     <bean id="knight" class="com.springinaction.knights.BraveKnight">
 11         <constructor-arg ref="quest"/>
 12     </bean>
 13 
 14     <bean id="quest" class="com.springinaction.knights.SlayDragonQuest">
 15         <constructor-arg value="#{T(System).out}"/>
 16     </bean>
 17 
 18     <bean id="minstrel" class="com.springinaction.knights.Recorder">
 19         <constructor-arg value="#{T(System).out}"/>
 20     </bean>
 21 
 22     <aop:config>
 23         <aop:aspect ref="recorder">
 24             <aop:pointcut id="embark"
 25                           expression="execution(* *.embarkOnQuest(..))"/>
 26             <aop:before pointcut-ref="embark"
 27                         method="singBeforeQuest"/>
 28             <aop:after pointcut-ref="embark"
 29                        method="singAfterQuest"/>
 30         </aop:aspect>
 31     </aop:config>
 32 </beans>
 33 

Spring 容器

     spring容器是使用DI(依赖注入)去管理组成应用的组件,其中包括两大类,

  • Application Cotext
  • Bean Factories

Bean Factories 主要是低水准的应用, 下面主要介绍application context,它包括

  • AnnotationConfigApplicationContext :从一个或多个java形式的class文件中加载一个spring 应用
  • AnnotationConfigWebApplicationContext  : 从一个或多个java形式的class文件中加载一个spring web应用
  • FileSystemXmlApplication : 加载一个上下文定义(context)从文件路径中的xml文件
  • XmlWebApplicationConte
  • ClassPathXmlApplication

AnnotationConfigWebApplicationContext 和 XmlWebApplicationContext 我们使用比较多的

 

装配组件

  三种方式

  • XML 形式
  • JavaConfig形式
  • 隐式的bean发现机制 和 自动装配

   三种方式的选择: 根据《spring in action》的描述,隐式的bean 发现和自动装配方式强与显式装配, 假若不得不显式装配,优先选择类型安全的java编码形式的,最后才是XML形式

 

自动配置

Spring从两个角度来实现自动化装配:(一个是生成独立的bean, 一个是将他们组装起来)

  • 组件扫描(component scanning) : Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring) : Spring自动满足bean之间的依赖。

    @Component 属性表明这个类是个组件,并且告诉spring 因为为它创建一个been

  1 @Component
  2 public class SgtPeppers implements CompactDisc {
  3 	private String title = "Sgt. Pepper's Lonely Hearts Club Band";
  4 	private String artist = "The Beatles";
  5 
  6 	public void play() {
  7 		System.out.println("Playing " + title + " by " + artist);
  8 	}
  9 }
  1 @Configuration
  2 @ComponentScan
  3 public class CDPlayerConfig {
  4 
  5 }

     @ComponentScan 表示组件扫描,在这个包下的组件都会被扫描出来并且创建为been,而@Configuration 将会在下面讲到,同时上面也可以通过xml的形式创建

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4        xmlns:context="http://www.springframework.org/schema/context"
  5        xsi:schemaLocation="http://www.springframework.org/schema/beans
  6             http://www.springframework.org/schema/beans/spring-beans.xsd
  7             http://www.springframework.org/schema/context
  8             http://www.springframework.org/schema/context/spring-context.xsd">
  9     <context:component-scan base-package="soundsystem" />
 10 </beans>

      组件被scan 后会生成一个bean ,然后生成一个id,默认为类名,那么下面展示的如何自定义这个id的名字,和如何指定spring在特定的路径下进行扫描

  1   1 //组件命名
  2   2 @Component("lonelyHeartsClub")
  3   3 public class SgtPeppers implements CompactDisc {
  4   4 ...
  5   5 }
  6   6
  7   7 //扫描基本路径,多个基本路径
  8   8 @Configuration
  9   9 @ComponentScan(basePackages= {"soundsystem", "video"})
 10  10 public class CDPlayerConfig {}

     扫描基类下的所有类

  1 @Configuration
  2 @ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
  3 public class CDPlayerConfig {}

     扫描完了,下面展示如何自动装配,使用@Autowired 注解,spring会自动将bean 加载进来,但是当bean 不存在时,spring加载时会抛出错误,为了防止spring直接抛出错误,可以设置 required = false

  1 @Component
  2 public class CDPlayer implements MediaPlayer {
  3     private CompactDisc cd;
  4     @Autowired
  5     public CDPlayer(CompactDisc cd) {
  6         this.cd = cd;
  7     }
  8     public void play() {
  9         cd.play();
 10     }
 11 
 12     @Autowired(required=false)
 13     public OtherCDPlayer(CompactDisc cd) {
 14         this.cd = cd;
 15     }
 16 }

 

JAVA 编码装配bean

      当我们需要使用到第三方库时,我们就不能使用自动装配机制,可以使用XML形式和JavaConfig的形式来进行显式装配,java编码比xml形式的重构更加友好,类型安全,更加强大。

  1 @Configuration
  2 public class CDPlayerConfig {
  3 }

    使用@Configuration 注解表明这个类通过javaConfig形式告知spring为它创建一个bean。另外加入我们调用一个方法,而方法返回值是个对象,我们希望这个对象被spring生成一个bean,这又怎么写呢?

  1 @Bean(name="lonelyHeartsClubBand")
  2 public CompactDisc sgtPeppers() {
  3 	return new SgtPeppers();
  4 }

    和上面自动扫描一样,我们创建了bean, 是时候把它们装配起来了,假如CDPlayer 这个对象依赖CompactDisc 这个对象,我们需要对它进行注入。上面的代码可以知道sgtPeppers( ) 方法可以获取一个bean对象。

  1 @Bean
  2 public CDPlayer cdPlayer() {
  3 	return new CDPlayer(sgtPeppers());
  4 }

    可以看到我们同样调用了sgtPeppers( ) 方法,使用了@Bean注解,这个需要注意的是spring创建的bean都会是单例,什么意思呢?

  1 @Bean
  2 public CDPlayer cdPlayer() {
  3 	return new CDPlayer(sgtPeppers());
  4 }
  5 @Bean
  6 public CDPlayer anotherCDPlayer() {
  7 	return new CDPlayer(sgtPeppers());
  8 }

     在sgtPeppers( )方法中即使我们使用new 一个新的对象,调用这两个方法返回的CDPlay( )却是相同的,都是spring 创建的bean对象。我们可以使用一个简单的方式写法。

  1 @Bean
  2 public CDPlayer cdPlayer(CompactDisc compactDisc) {
  3 	return new CDPlayer(compactDisc);
  4 }

XML 方式装配bean

       使用XML 形式进行装配bean,

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4        xsi:schemaLocation="http://www.springframework.org/schema/beans
  5        http://www.springframework.org/schema/beans/spring-beans.xsd
  6        http://www.springframework.org/schema/context">
  7 
  8        <bean id="cdPlay" class="soundsystem.SgtPeppers">
  9 </beans>

        初始化bean构造器注入有两种方式

  • 使用<constructor-arg>
  • 使用c-命名空间在Spring 3.0

       下面使用的时第一种,ref是传入的引用,是某个bean 的id

  1 <bean id="cdPlayer" class="soundsystem.CDPlayer">
  2 	<constructor-arg ref="compactDisc" />
  3 </bean>

       如果存在下面的类

  1 public class BlankDisc implements CompactDisc {
  2         private String title;
  3         private String artist;
  4         public BlankDisc(String title, String artist) {
  5             this.title = title;
  6             this.artist = artist;
  7         }
  8         public void play() {
  9             System.out.println("Playing " + title + " by " + artist);
 10         }
 11     }

       那么构造器依赖注入就是

  1 <bean id="compactDisc" class="soundsystem.BlankDisc">
  2     <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band"/>
  3     <constructor-arg value="The Beatles"/>
  4 </bean>
  5 
  6 <!--下面是c-命名空间,下划线后加参数名字-->
  7 <bean id="compactDisc"
  8       class="soundsystem.BlankDisc"
  9       c:_title="Sgt. Pepper's Lonely Hearts Club Band"
 10       c:_artist="The Beatles" />
 11 
 12 <!--也可以这样子写-->
 13 <bean id="compactDisc"
 14       class="soundsystem.BlankDisc"
 15       c:_0="Sgt. Pepper's Lonely Hearts Club Band"
 16       c:_1="The Beatles" />
 17 

      如果构造参数中有List,或是set,要是set 的话,只需要改节点为<set>

  1 <bean id="compactDisc" class="soundsystem.BlankDisc">
  2     <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
  3     <constructor-arg value="The Beatles" />
  4     <constructor-arg>
  5         <list>
  6             <value>Sgt. Pepper's Lonely Hearts Club Band</value>
  7             <value>With a Little Help from My Friends</value>
  8             <value>Lucy in the Sky with Diamonds</value>
  9             <value>Getting Better</value>
 10             <value>Fixing a Hole</value>
 11             <!-- ...other tracks omitted for brevity... -->
 12         </list>
 13     </constructor-arg>
 14 </bean>

        如果使用c命名空间的构造器注入,必须向在开头声明命令空间

  1 
  2 <?xml version="1.0" encoding="UTF-8"?>
  3 <beans xmlns="http://www.springframework.org/schema/beans"
  4        xmlns:c="http://www.springframework.org/schema/c"
  5        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6        xsi:schemaLocation="http://www.springframework.org/schema/beans
  7 http://www.springframework.org/schema/beans/spring-beans.xsd">
  8     ...
  9 </beans>

      下面是如何使用

 

 

构造器注入

  1 <bean id="cdPlayer" class="soundsystem.CDPlayer"
  2 c:cd-ref="compactDisc" />

    

 

        假如一个类没有构造方法,需要对属性进行注入

  1 public class BlankDisc implements CompactDisc {
  2     private String title;
  3     private String artist;
  4     private List<String> tracks;
  5     public void setTitle(String title) {
  6         this.title = title;
  7     }
  8     public void setArtist(String artist) {
  9         this.artist = artist;
 10     }
 11 
 12     public void setTracks(List<String> tracks) {
 13         this.tracks = tracks;
 14     }
 15     public void play() {
 16         System.out.println("Playing " + title + " by " + artist);
 17         for (String track : tracks) {
 18             System.out.println("-Track: " + track);
 19         }
 20     }
 21 }
  1 <bean id="compactDisc"
  2       class="soundsystem.BlankDisc">
  3     <property name="title"
  4               value="Sgt. Pepper's Lonely Hearts Club Band" />
  5     <property name="artist" value="The Beatles" />
  6     <property name="tracks">
  7         <list>
  8             <value>Sgt. Pepper's Lonely Hearts Club Band</value>
  9             <value>With a Little Help from My Friends</value>
 10             <value>Lucy in the Sky with Diamonds</value>
 11             <value>Getting Better</value>
 12             <value>Fixing a Hole</value>
 13             <!-- ...other tracks omitted for brevity... -->
 14         </list>
 15     </property>
 16 </bean>
 17 

      和c-命名空间相同, p-命名空间用来标识属性注入的,但是需要注意的是p-命名空间不能注入List,只能用spring-util 命名空间对集合进行注入,同样,在使用命令空间时需要在xml开头声明

  1 xmlns:p="http://www.springframework.org/schema/p"
  2 xmlns:util="http://www.springframework.org/schema/util"
  1 <bean id="compactDisc"
  2       class="soundsystem.BlankDisc"
  3       p:title="Sgt. Pepper's Lonely Hearts Club Band"
  4       p:artist="The Beatles"
  5       p:tracks-ref="trackList" />
  1 <util:list id="trackList">
  2     <value>Sgt. Pepper's Lonely Hearts Club Band</value>
  3     <value>With a Little Help from My Friends</value>
  4     <value>Lucy in the Sky with Diamonds</value>
  5     <value>Getting Better</value>
  6     <value>Fixing a Hole</value>
  7     <!-- ...other tracks omitted for brevity... -->
  8 </util:list>

      即使当显示声明bean时,我们更趋向javaCofig 形式装配,但是有些时候XML 形式的才是最好的选择,spring允许两者混合使用,如下

在javaCofig形式中引入xml配置

      加入存在以下图的依赖关系

依赖

  1 @Configuration
  2 public class CDConfig {
  3     @Bean
  4     public CompactDisc compactDisc() {
  5         return new SgtPeppers();
  6     }
  7 }
  8 
  9 
 10 //我们直接使用 @Import 注解通过引入另外一个类,来装配bean,
 11 @Configuration
 12 @Import(CDConfig.class)
 13 public class CDPlayerConfig {
 14     @Bean
 15     public CDPlayer cdPlayer(CompactDisc compactDisc) {
 16         return new CDPlayer(compactDisc);
 17     }
 18 }
 19 
 20 
 21 //或是我们创建多一个类,直接引入两个类,来装配bean
 22 @Configuration
 23 @Import({CDPlayerConfig.class, CDConfig.class})
 24 public class SoundSystemConfig {
 25 }
 26 

        假如BlankDisc 是定义在XML 中,如何通过Java形式来进行装配,使用@ImportResource()注解

  1 <bean id="compactDisc"
  2       class="soundsystem.BlankDisc"
  3       c:_0="Sgt. Pepper's Lonely Hearts Club Band"
  4       c:_1="The Beatles">
  5     <constructor-arg>
  6         <list>
  7             <value>Sgt. Pepper's Lonely Hearts Club Band</value>
  8             <value>With a Little Help from My Friends</value>Importing and mixing configurations 61
  9             <value>Lucy in the Sky with Diamonds</value>
 10             <value>Getting Better</value>
 11             <value>Fixing a Hole</value>
 12             <!-- ...other tracks omitted for brevity... -->
 13         </list>
 14     </constructor-arg>
 15 </bean>
  1 @Configuration
  2 @Import(CDPlayerConfig.class)
  3 @ImportResource("classpath:cd-config.xml")
  4 public class SoundSystemConfig {
  5 }

      其中,classpath :cd-config.xml 表示xml的位置。假如需要从另外的XML 文件中引入一个bean

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4        xmlns:c="http://www.springframework.org/schema/c"
  5        xsi:schemaLocation="http://www.springframework.org/schema/beans
  6        http://www.springframework.org/schema/beans/spring-beans.xsd">
  7 
  8     <import resource="cd-config.xml" />
  9     <bean id="cdPlayer"
 10           class="soundsystem.CDPlayer"
 11           c:cd-ref="compactDisc" />
 12 </beans>
 13 

      从java形式装配引入到XML 中,直接使用bean

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4        xmlns:c="http://www.springframework.org/schema/c"
  5        xsi:schemaLocation="http://www.springframework.org/schema/beans
  6     http://www.springframework.org/schema/beans/spring-beans.xsd">
  7     <bean class="soundsystem.CDConfig" />
  8     <bean id="cdPlayer"
  9           class="soundsystem.CDPlayer"
 10           c:cd-ref="compactDisc" />
 11 </beans>

      当需要装配一个bean来自XML ,一个来自javaConfig,那么按照之前的想法就是将多一个更加高层次的类对两个bean进行装配。

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4        xmlns:c="http://www.springframework.org/schema/c"
  5        xsi:schemaLocation="http://www.springframework.org/schema/beans
  6        http://www.springframework.org/schema/beans/spring-beans.xsd">
  7     <bean class="soundsystem.CDConfig" />
  8     <import resource="cdplayer-config.xml" />
  9 </beans>

       好了,这一篇讲了bean 装配的问题,下一篇继续学习!加油

posted @ 2018-02-12 20:53  float123  阅读(396)  评论(0编辑  收藏  举报