首页 > 学院 > 开发设计 > 正文

Spring应用教程-1

2019-11-14 22:58:16
字体:
来源:转载
供稿:网友
SPRing应用教程-1作者:禅楼望月(http://www.VEVb.com/yaoyinglong)

Spring是Web框架,是容器框架,用于配置bean,并维护bean之间的关系的框架。

1. Spring在整个项目层次中的位置:

915034e5-9dec-43b8-96f9-8f2e6c6d1a3b

2. 快速入门

1、引入spring开发包(最小配置)如下:

964d8d0d-002c-483b-996d-b877f150b3cb

2、创建spring的一个核心文件(如struts中的struts-config.xmlapplicationContext.xml文件。该文件一般放在src目录下,该文件需引入xsd文件:可以从下载下来的开发包中的例子中粘贴过来。

b795f18f-4a26-4a43-bb56-d1a72cb5c504

上面我们看了配置单个bean的做法,但是当两个bean之间是嵌套的(就像某人有一只狗),该怎么设置呢?

7da42827-1198-4fbb-b91b-d3981dba7275

3. 细节讨论

使用spring ,没有new对象,我们把创建对象的任务交给spring框架

spring的运行原理图:

5fccf830-f4de-4a9f-a31f-b15d574dda12

spring开发提倡接口编程,配合di技术可以层与层的解耦

现在我们体验一下spring的di配合接口编程

例:

建立一个用户验证接口,使用两种不同的验证方法。两种不同的验证方法都继承用户验证接口:

a8a40345-371d-4bdc-b96e-3af9e27eb050

4. bean工厂容器

可以从ApplicationContext 应用上下文容器中获取bean也可以从BeanFactory中获取bean。那他们有什么区别呢?

当从ApplicationContext 中取bean时,是这样的:

15f1b3b2-129d-4013-bee4-1d891e00d3cd

怎么验证呢?很简单,因为它是利用反射机制来实现的,所以在实例化对象时会调用该bean的默认构造函数:

在该bean中加入默认构造函数:

62c0ee9b-4285-4c66-9036-77ccc17e98f0

执行该语句时:

cb3f3b6f-a278-45fe-baa2-ad21d7a76d90

假如我们有需求:用我们自己的构造函数来实例化这个bean。该怎么办呢?→在该bean的配置中添加<constructor-arg>标签(通过构造函数来注入值)

1a14e1db-9da0-41ff-9ce3-cb8817746cf0

注意:

c2d07443-4b47-4b70-9d8b-6864fe3bc850

当从BeanFactory中获取bean时:

f69dff68-3f07-4ae9-a32d-86d3886c39d8

当我们开始使用bean时:

fc9e5d90-3f0c-4e50-888f-698d0997ae27

由此可见:

1.如果使用ApplicationContext ,并且配置的bean如果是 singlton(默认),不管你用不用,都被实例化.(好处就是可以预先加载,缺点就是耗内存)

2.如果是 BeanFactory ,则当你获取beanfacotry时候,配置的bean不会被马上实例化,当你使用的时候,才被实例(好处节约内存,缺点就是速度)

3.规定: 一般没有特殊要求,应当使用ApplicatioContext完成(90%)

5. ApplicationContext 对象有3种应用方法

1. ClassPathXmlApplicationContext -> 通过类路径

2. FileSystemXmlApplicationContext -> 通过文件路径

举例:

ApplicationContext ac=new FileSystemXmlApplicationContext("文件路径beans.xml / applicationContext.xml");

3. XmlWebApplicationContext

6. bean生命周期6.1 利用ApplicationContext获取bean时,bean的生命周期

① 实例化(当我们的程序加载beans.xml文件),把我们的bean(前提是scope=singleton)实例化到内存

② 调用set方法设置属性

③ 如果你实现了bean名字关注接口(BeanNameAware) 则,可以通过setBeanName获取Bean的id属性值

6aa13451-81ab-49c8-9eaa-1dfbba52fa45

④ 如果你实现了bean工厂关注接口(BeanFactoryAware),则可以获取BeanFactory

⑤ 如果你实现了 ApplicationContextAware接口,则调用方法

//该方法传递ApplicationContext

public void setApplicationContext(ApplicationContext arg0) throws BeansException {

System.out.println("setApplicationContext"+arg0);

}

⑥ 如果bean 和 一个BeanPostProcessor(前置处理器)关联,则会自动去调用 Object postProcessBeforeInitialization方法

⑦ 如果你实现InitializingBean 接口,则会调用 afterPropertiesSet

⑧ 如果自己在<bean init-method=”init”/> 则可以在bean定义自己的初始化方法.


publicclass Chinese implements InitializingBean { private String name; privateintage; publicvoid setName(String name) { System.out.println("正在设置name属性&hellip;…"); this.name = name; } publicvoid setAge(int age) { System.out.println("正在设置age属性……"); this.age = age; } @Override publicvoid afterPropertiesSet() throws Exception { System.out.println("在执行我之前,该Bean 实例已创建,并且通过setter方法将所有属性都设置完毕"); } publicvoid init(){ System.out.println("我的功能和InitializingBean接口一样"); }

}


配置:


<beanid="chinese"class="smzq.Chinese"init-method="init"> <propertyname="name"value="小明"/> <propertyname="age"value="23"/></bean>
测试:

image

通过实例我们可以看出,在同时配置了init-method属性和实现了InitializingBean接口,会先执行接口方法然后执行init-method属性配置的方法。使用init-method属性配置时,类依旧是普通的java类,没有受到污染。在实际开发中使用其中的任何一个就可以,但这里推荐使用init-method属性。

⑨ 如果bean 和 一个BeanPostProcessor(后置处理器)关联,则会自动去调用 Object postProcessAfterInitialization方法

⑩ 使用我们的bean

11. 容器关闭

12. 可以通过实现DisposableBean 接口来调用方法 destory

13. 可以在<bean destory-method=”fun1”/> 调用定制的销毁方法

12和13的使用和7、8一样。

记住他们的顺序。

小结: 我们实际开发中往往,没有用的这么的过程,常见的是:

1->2->6->10->9->11

d5c572a6-4916-436b-8498-3f3af1e73766

6.2 利用BeanFactory获取bean时,bean的生命周期

只经历了以下的接口:

BeanNameAware,BeanFactoryAware,InitializingBean和自己配置的init-method方法。

7. 配置bean的细节7.1 scope 的说明

singleton(默认):在整个Spring Ioc容器中,使用 scope="singleton" 的bean将只有一个实例。

prototype:每次通过容器的getBean方法获取scope="prototype"的bean时,都将产生一个新的bean实例。

request:每次HTTP请求,使用 scope=" request" 的bean都将产生一个新的bean实例。

session:对于每个HttpSession,使用 scope=" session" 的bean都将产生一个新的bean实例。

global session:每个全局的HttpSession对应一个Bean实例。

em15[3]? 尽量使用 scope=”singleton” ,不要使用prototype,因为这样对我们的性能影响较大.

对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

em11注意

Spring不能对一个prototype bean的整个生命周期负责。程序每次请求该bean,Spring都会创建一个新的Bean实例,然后交给程序,然后就对该Bean实例不闻不问了。这就意味着,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

7.2 给集合类型注入值

①给List注入值:

748e77ff-8818-4dce-a446-0895ab6cef8a

②给数组注入值

和List一样。

③给set注入值

1d026c15-1b2a-4864-9e84-2330fb8111db

④向Map注入值

ccae56c3-648e-4433-bba7-eb3cf7e052c3

⑤向属性集合注入

8b80a1e0-309c-47e2-ab8c-a26fbdd1550d

7.3 集合的合并

子集合的值是从其父集合中继承和覆盖而来的。

bec31d25-e9d2-4cfe-9c33-35dbec0625d0

集合合并只能在spring2.0以后使用并且不同集合是不能合并的。

7.4 强类型集合

在Java5以后才能使用。

public class Foo {                   private Map<String, Float> accounts;        public void setAccounts(Map<String, Float> accounts) {        this.accounts = accounts;    }}
<beans>    <bean id="foo" class="x.y.Foo">        <property name="accounts">            <map>                <entry key="one" value="9.99"/>                <entry key="two" value="2.75"/>                <entry key="six" value="3.99"/>            </map>        </property>    </bean></beans>
7.5 嵌套Bean

如果某个Bean依赖的Bean不想被Spring容器直接访问,可以使用嵌套bean。

<bean id="date" class="date.Date_">    <property name="year">        <bean class="date.Year_">            <property name="data" value="2015"/>        </bean>    </property>    <property name="month">        <bean class="date.Month_">            <property name="data" value="2"/>        </bean>    </property>    <property name="day">        <bean class="date.Day_">            <property name="data" value="7"/>        </bean>    </property></bean>

image

这样,内部的Bean不能被Spring容器访问,因此不用担心其他程序 修改嵌套的Bean,提高了程序的内聚性,但降低了程序的灵活性。因此只有确定某个Bean实例无需通过Spring容器来访问时,才考虑使用嵌套Bean。

7.6 组合属性注入
public class Date_ {    private Year_ year=new Year_();    public Year_ getYear() {        return year;    }    public void setYear(Year_ year) {        this.year = year;    }}
<bean id="next" class="date.Date_">    <property name="year.data" value="2015"/></bean>

image

7.7 使用抽象Bean

设置属性abstract="true"的Bean是抽象Bean,容器不会创建抽象Bean的实例,也因此,抽象Bean可以没有class属性。


<beanid="dateTemplate "abstract="true">

<!-- 定义依赖注入的属性 --> <propertyname="month"ref="month"/>

</bean>


它主要是用来被继承的。7.8 使用子Bean7.8.1 继承自抽象Bean

当我们有大量的Bean要配置到Spring的配置文件中,而这些Bean的大部分配置信息都完全一样,只有少量的信息不一样,这时使用Bean继承是一个很好的办法:

将大部分相同信息配置为Bean模板(抽象Bean)后,将实际的Bean实例配置成为该Bean模板的子Bean即可。

子Bean无法从父Bean继承如下属性:depends-on、autowire、scope、lazy-init,这些属性总是从子Bean中获得,或者采用默认值。子Bean配置可以增加新的配置信息,当子Bean指定的配置信息与父Bean模板信息不一致时,子Bean所制定的配置信息将覆盖父Bean指定的配置信息。


<beanid="month"class="date.Month_"> <propertyname="data"value="12"/></bean><beanid="day"class="date.Day_"> <propertyname="data"value="23"/></bean><beanid="dateTemplate"abstract="true"> <!-- 定义依赖注入的属性 --> <propertyname="month"ref="month"/> <propertyname="day"ref="day"/></bean><beanid="date"class="date.Date_"parent="dateTemplate"> <propertyname="year"> <beanclass="date.Year_"> <propertyname="data"value="2015"/> </bean> </property>

</bean>


image

如果父Bean配置中有class属性,则子Bean在和父Bean类相同的情况下可以省略掉class属性:


<beanid="dateTemplate"abstract="true"class="date.Date_"> <!-- 定义依赖注入的属性 --> <propertyname="month"ref="month"/> <propertyname="day"ref="day"/> </bean> <beanid="date"parent="dateTemplate"> <propertyname="year"> <beanclass="date.Year_"> <propertyname="data"value="2015"/> </bean> </property>

</bean>


em15[7]注意,继承自抽象Bean继承只是为了配置参数的复用,并且子Bean的类型不必与抽象Bean的类型(配置了的话)一样。

7.8.2 继承实例bean

13a02a8a-c410-479d-81e5-f758feb2497b

这种继承与Java语言的继承一样。

7.9 容器中的工厂Bean

实现了FactoryBean接口的Bean成为工厂Bean,并且该Bean只能做为工厂Bean来使用。


publicclass PersonFactory implements FactoryBean<Person> { private Person person=null; @Override //返回工厂Bean生成的Java实例 public Person getObject() throws Exception { returnperson==null? person=new Person():person; } @Override //返回该工厂Bean能生成那种类型的实例 public Class<? extends Person> getObjectType() { return Person.class; } @Override //该工厂Bean所生成的Java实例是否是单例模式 publicboolean isSingleton() { returntrue; }

}


配置,和普通bean的配置完全一样:


<beanid="briton"class="factoryBean.PersonFactory"/>


测试:


publicstaticvoid main(String[] args) throws BeansException, Exception { ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); //虽然请求的FactoryBean的id,但是返回来的却是有FactoryBean生成的实例,而非FactoryBean本身 Person p1=ctx.getBean("briton", Person.class); p1.setName("Tom");p1.setAge(20); p1.say(); Person p2=ctx.getBean("briton", Person.class); System.out.println(p1==p2); //如需获取FactoryBean本身,指引在bean的id前加上& ((PersonFactory)ctx.getBean("&briton")).getObject().say();

}


f5999ebb-392b-41f4-bb72-5dd5dbdcf480

由此可见,工厂Bean已经无法作为正常的Bean使用了,客户端请求该工厂Bean的id时,得到是工厂Bean的产品(上面是一个Person)而非工厂Bean的本身。

7.10 获得Bean本身的Id

在前面的程序中,程序总是通过Bean的id从BeanFactory中获取Bean实例。但是现在的需求是,我已经知道了Bean,需要知道配置该Bean时指定的id属性。

BeanNameAware接口提供了setBeanName方法来获取部署在Spring配置文件中的Bean。


publicclass Chinese implements BeanNameAware { //保存部署该Bean时指定的id属性 private String beanName; @Override publicvoid setBeanName(String beanName) { this.beanName=beanName; } publicvoid info(){ System.out.println("Chinese 实现类,部署该Bean时指定的id为"+beanName); }

}


配置Bean:


<beanid="chinese"class="beanNameAware.Chinese"/>


获取Bean:


publicstaticvoid main(String[] args) throws BeansException, Exception { ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); Chinese ch=ctx.getBean("chinese", Chinese.class); ch.info();

}


image

从程序中我们看到,和获取、使用普通Bean没有任何区别。从程序执行来看,Spring在实例化该Bean的时候,也调用了该Bean的setBeanName方法。

7.11 depengs-on

该属性用于指定当前bean初始化之前或销毁之前需要强制初始化的bean。该属性只对singleton bean有效。指定多个bean是,多个bean之间可以用逗号,空格,分号来分开。

71450ebd-f68b-4227-b509-a9c8b2127000[1]

8. Spring中的Null值

在spring中空值和<null/>是不一样的。

4861013f-4e45-41cb-b3d1-ad5ac4820fb5

9. XML配置文件的简写

①<property>、<constructor-arg>、<entry>都支持value属性,尽量使用。

②ref也可以是属性。

③使用p命名空间配置属性

xmlns:p="http://www.springframework.org/schema/p"

b335b35a-9e2c-4e4d-baf8-6bd94f7e8e8f

10. 自动装配bean的属性值

可以通过元素<beans>的default-autowire属性指定,也可以通过<bean>元素的autowire属性

image

①byName

3bbe1a32-f916-4545-a525-cd1f3df6396c

②byType

寻找和属性的类型相同的bean,找不到,装不上;找到多个,出现异常。

③constructor

1909f32e-0d6f-4c7b-8851-878cfbbe5c1e

④autodetect

bf6306f9-1be3-433e-bae6-01cddb1902a4

再如:

eaa8d3f6-e946-4414-abb0-244c6be1d206

⑤no

不自动装配

11. 分散配置

有两种方法:

PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,可以将BeanFactory定义中的一些属性值放到另一个单独的标准Java Properties文件中。这就允许用户在部署应用时只需要在属性文件中对一些关键属性(例如数据库URL,用户名和密码)进行修改,而不用对主XML定义文件或容器所用文件进行复杂和危险的修改。

考虑下面的XML配置元数据定义,它用占位符定义了DataSource。我们在外部的Properties文件中配置一些相关的属性。在运行时,我们为元数据提供一个PropertyPlaceholderConfigurer,它将会替换dataSource的属性值。

 1 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 2  <property name="locations"> 3  <value>classpath:com/foo/jdbc.properties</value> 4  </property> 5 </bean> 6 <bean id="dataSource" destroy-method="close" 7  class="org.apache.commons.dbcp.BasicDataSource"> 8  <property name="driverClassName" value="${jdbc.driverClassName}"/> 9  <property name="url" value="${jdbc.url}"/>10  <property name="username" value="${jdbc.username}"/>11  <property name="passWord" value="${jdbc.password}"/>12 </bean>
View Code

实际的值来自于另一个标准Java Properties格式的文件:

jdbc.driverClassName=org.hsqldb.jdbcDriver

jdbc.url=jdbc:hsqldb:hsql://production:9002

jdbc.username=sa

jdbc.password=root

②使用<context:property-placeholder

但是使用这个的时候必须将命名空间引进来,如下所示:

1 <beans xmlns="http://www.springframework.org/schema/beans"2  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"3  xmlns:context="http://www.springframework.org/schema/context"4  xsi:schemaLocation="http://www.springframework.org/schema/beans 5  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd6  http://www.springframework.org/schema/context7  http://www.springframework.org/schema/context/spring-context-2.5.xsd">
View Code

这样我们的bean配置文件就会很简单em4

11adeec9-2e39-46ed-8899-0a8b0e76f968

在Spring 2.5中,context名字空间可能采用单一元素属性占位符的方式(多个路径提供一个逗号分隔的列表)

<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>

PropertyPlaceholderConfigurer如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找。这个行为可以通过设置systemPropertiesMode属性来定制,它有三个值:让配置一直覆盖、让它永不覆盖及让它仅仅在属性文件中找不到该属性时才覆盖。请参考PropertiesPlaceholderConfigurer的JavaDoc以获得更多的信息。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表