Spring入门案例代码 1。创建Maven的java项目 2。pom。xml添加Spring的依赖jar包lt;?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0http:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersiongroupIdcom。itheimagroupIdspring01quickstartartifactIdversion1。0SNAPSHOTversiondependenciesdependencygroupIdorg。springframeworkgroupIdspringcontextartifactIdversion5。2。10。RELEASEversiondependencydependencygroupIdjunitgroupIdjunitartifactIdversion4。12versionscopetestscopedependencydependenciesproject 3。创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类packagecom。itheima。dao;publicinterfaceBookDao{publicvoidsave();}packagecom。itheima。dao。impl;importcom。itheima。dao。BookDao;publicclassBookDaoImplimplementsBookDao{publicvoidsave(){System。out。println(bookdaosave。。。);}}packagecom。itheima。service;publicinterfaceBookService{publicvoidsave();}packagecom。itheima。service。impl;importcom。itheima。dao。BookDao;importcom。itheima。dao。impl。BookDaoImpl;importcom。itheima。service。BookService;publicclassBookServiceImplimplementsBookService{5。删除业务层中使用new的方式创建的dao对象privateBookDaobookDao;publicvoidsave(){System。out。println(bookservicesave。。。);bookDao。save();}6。提供对应的set方法publicvoidsetBookDao(BookDaobookDao){this。bookDaobookDao;}} 4。resources下添加spring配置文件,并完成bean的配置 applicationContext。xmlllt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsd!1。导入spring的坐标springcontext,对应版本是5。2。10。RELEASE!2。配置bean!bean标签标示配置beanid属性标示给bean起名字class属性表示给bean定义类型beanidbookDaoclasscom。itheima。dao。impl。BookDaoImplbeanidbookServiceclasscom。itheima。service。impl。BookServiceImpl!7。配置server与dao的关系!property标签表示配置当前bean的属性name属性表示配置哪一个具体的属性ref属性表示参照哪一个beanpropertynamebookDaorefbookDaobeanbeans 注意:配置中的两个bookDao的含义是不一样的 namebookDao中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setBookDao()方法进行对象注入 refbookDao中bookDao的作用是让Spring能在IOC容器中找到id为bookDao的Bean对象给 bookService进行注入 综上所述,对应关系如下: 5。从容器中获取对象进行方法调用packagecom。itheima;importcom。itheima。dao。BookDao;importcom。itheima。service。BookService;importorg。springframework。context。ApplicationContext;importorg。springframework。context。support。ClassPathXmlApplicationContext;publicclassApp{publicstaticvoidmain(String〔〕args){获取IOC容器ApplicationContextctxnewClassPathXmlApplicationContext(applicationContext。xml);BookServicebookService(BookService)ctx。getBean(bookService);bookService。save();}} 以上完成了入门案例代码。 bean基础配置 bean基础配置(id与class) bean的name属性 name为bean指定别名,别名可以有多个,使用逗号,空格、分号隔开。 根据名称容器中获取bean对象 publicclassAppForName{ publicstaticvoidmain(String〔〕args){ ApplicationContextctxnew ClassPathXmlApplicationContext(applicationContext。xml); 此处根据bean标签的id属性和name属性的任意一个值来获取bean对象 BookServicebookService(BookService)ctx。getBean(service4); bookService。save(); } } bean作用范围scope配置 bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象 bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高 bean在容器中是单例的,会不会产生线程安全问题? 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。 封装实例的域对象,因为会引发线程安全问题,所以不适合。 哪些bean对象适合交给容器进行管理? 表现层对象业务层对象数据层对象工具对象 bean实例化方法 构造方法实例化!方式一:构造方法实例化beanbeanidbookDaoclasscom。itheima。dao。impl。BookDaoImpl Spring底层使用的是类的无参构造方法。关于Spring的构造方法实例化就已经学习完了,因为每一个类默认都会提供一个无参构造函数,所以其实真正在使用这种方式的时候,我们什么也不需要做。这也是我们以后比较常用的一种方式。 静态工厂实例化!方式二:使用静态工厂实例化beanbeanidorderDaoclasscom。itheima。factory。OrderDaoFactoryfactorymethodgetOrderDao 看到这,可能有人会问了,你这种方式在工厂类中不也是直接new对象的,和我自己直接new没什么太大的区别,而且静态工厂的方式反而更复杂,这种方式的意义是什么? 主要的原因是: 在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少,如: publicclassOrderDaoFactory{ publicstaticOrderDaogetOrderDao(){ System。out。println(factorysetup。。。。);模拟必要的业务操作 returnnewOrderDaoImpl(); } } 之前new对象的方式就无法添加其他的业务内容。 实例工厂与FactoryBean 接下来继续来研究Spring的第三种bean的创建方式实例工厂实例化: 环境准备 (1)准备一个UserDao和UserDaoImpl类 publicinterfaceUserDao{ publicvoidsave(); } publicclassUserDaoImplimplementsUserDao{ publicvoidsave(){ System。out。println(userdaosave。。。); } } (2)创建一个工厂类OrderDaoFactory并提供一个普通方法,注意此处和静态工厂的工厂类不一样的地方是方法不是静态方法 publicclassUserDaoFactory{ publicUserDaogetUserDao(){ returnnewUserDaoImpl(); } } (3)编写AppForInstanceUser运行类,在类中通过工厂获取对象 创建实例工厂对象 UserDaoFactoryuserDaoFactorynewUserDaoFactory(); 通过实例工厂对象创建对象 UserDaouserDaouserDaoFactory。getUserDao(); userDao。save(); 实例工厂实例化 具体实现步骤为: (1)在spring的配置文件中添加以下内容: beaniduserFactoryclasscom。itheima。factory。UserDaoFactory beaniduserDaofactorymethodgetUserDaofactorybeanuserFactory 实例化工厂运行的顺序是: 创建实例化工厂对象,对应的是第一行配置 调用对象中的方法来创建bean,对应的是第二行配置 factorybean:工厂的实例对象 factorymethod:工厂对象中的具体创建对象的方法名,对应关系如下: factorymehod:具体工厂类中创建对象的方法名 (2)在AppForInstanceUser运行类,使用从IOC容器中获取bean的方法进行运行测试publicclassAppForInstanceUser{publicstaticvoidmain(String〔〕args){ApplicationContextctxnewClassPathXmlApplicationContext(applicationContext。xml);UserDaouserDao(UserDao)ctx。getBean(userDao);userDao。save();}} 实例工厂实例化的方式就已经介绍完了,配置的过程还是比较复杂,所以Spring为了简化这种配置方式就提供了一种叫FactoryBean的方式来简化开发。 FactoryBean的使用 具体的使用步骤为: (1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法publicclassUserDaoFactoryBeanimplementsFactoryBeanUserDao{代替原始实例工厂中创建对象的方法publicUserDaogetObject()throwsException{returnnewUserDaoImpl();}返回所创建类的Class对象publicClasslt;?getObjectType(){returnUserDao。class;}} (2)在Spring的配置文件中进行配置beaniduserDaoclasscom。itheima。factory。UserDaoFactoryBean (3)AppForInstanceUser运行类不用做任何修改,直接运行 查看源码会发现,FactoryBean接口其实会有三个方法,分别是:TgetObject()throwsException;Classlt;?getObjectType();defaultbooleanisSingleton(){returntrue;} 方法一:getObject(),被重写后,在方法中进行对象的创建并返回 方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象 方法三:没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单例,默认true。 那如果想改成单例具体如何实现? 只需要将isSingleton()方法进行重写,修改返回为false,即可FactoryBean创建对象publicclassUserDaoFactoryBeanimplementsFactoryBeanUserDao{代替原始实例工厂中创建对象的方法publicUserDaogetObject()throwsException{returnnewUserDaoImpl();}publicClasslt;?getObjectType(){returnUserDao。class;}publicbooleanisSingleton(){returnfalse;}} 但是一般情况下我们都会采用单例,也就是采用默认即可。所以isSingleton()方法一般不需要进行重写。 bean的生命周期 首先理解下什么是生命周期? 从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。 bean生命周期是什么?bean对象从创建到销毁的整体过程。 bean生命周期控制是什么?在bean创建后到销毁前做一些事情。 生命周期设置 添加生命周期的控制方法,具体的控制有两个阶段: bean创建之后,想要添加内容,比如用来初始化需要用到资源 bean销毁之前,想要添加内容,比如用来释放用到的资源 步骤1:添加初始化和销毁方法 针对这两个阶段,我们在BooDaoImpl类中分别添加两个方法,方法名任意publicclassBookDaoImplimplementsBookDao{publicvoidsave(){System。out。println(bookdaosave。。。);}表示bean初始化对应的操作publicvoidinit(){System。out。println(init。。。);}表示bean销毁前对应的操作publicvoiddestory(){System。out。println(destory。。。);}} 步骤2:配置生命周期 在配置文件添加配置,如下:beanidbookDaoclasscom。itheima。dao。impl。BookDaoImplinitmethodinitdestroymethoddestory 步骤3:运行程序 从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢? Spring的IOC容器是运行在JVM中运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了所以没有调用对应的destroy方法。 知道了出现问题的原因,具体该如何解决呢? close关闭容器ApplicationContext中没有close方法需要将ApplicationContext更换成ClassPathXmlApplicationContextClassPathXmlApplicationContextctxnewClassPathXmlApplicationContext(applicationContext。xml);调用ctx的close()方法ctx。close();运行程序,就能执行destroy方法的内容 注册钩子关闭容器在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器调用ctx的registerShutdownHook()方法ctx。registerShutdownHook(); 注意:registerShutdownHook在ApplicationContext中也没有 两种方式介绍完后,close和registerShutdownHook选哪个? 相同点:这两种都能用来关闭容器 不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多也比较乱。 Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置initmethod和 destroymethod 接下来在BookServiceImpl完成这两个接口的使用: 修改BookServiceImpl类,添加两个接口InitializingBean,DisposableBean并实现接口中的两个方法afterPropertiesSet和destroypublicclassBookServiceImplimplementsBookService,InitializingBean,DisposableBean{privateBookDaobookDao;publicvoidsetBookDao(BookDaobookDao){this。bookDaobookDao;}publicvoidsave(){System。out。println(bookservicesave。。。);bookDao。save();}publicvoiddestroy()throwsException{System。out。println(servicedestroy);}publicvoidafterPropertiesSet()throwsException{System。out。println(serviceinit);}} 那第二种方式的实现,我们也介绍完了。 小细节对于InitializingBean接口中的afterPropertiesSet方法,翻译过来为属性设置之后。对于BookServiceImpl来说,bookDao是它的一个属性setBookDao方法是Spring的IOC容器为其注入属性的方法afterPropertiesSet和setBookDao是setBookDao方法先执行 DI依赖注入 setter注入注入引用类型对象在bean中定义引用类型属性,并提供可访问的set方法publicclassBookServiceImplimplementsBookService{privateBookDaobookDao;publicvoidsetBookDao(BookDaobookDao){this。bookDaobookDao;}} 配置中使用property标签ref属性注入引用类型对象beanidbookServiceclasscom。itheima。service。impl。BookServiceImplpropertynamebookDaorefbookDaobeanbeanidbookDaoclasscom。itheima。dao。imipl。BookDaoImpl 2。注入简单数据类型 步骤1:声明属性并提供setter方法publicclassBookDaoImplimplementsBookDao{privateStringdatabaseName;privateintconnectionNum;publicvoidsetConnectionNum(intconnectionNum){this。connectionNumconnectionNum;}publicvoidsetDatabaseName(StringdatabaseName){this。databaseNamedatabaseName;}publicvoidsave(){System。out。println(bookdaosave。。。databaseName,connectionNum);}} 步骤2:配置文件中进行注入配置lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidbookDaoclasscom。itheima。dao。impl。BookDaoImplpropertynamedatabaseNamevaluemysqlpropertynameconnectionNumvalue10beanbeaniduserDaoclasscom。itheima。dao。impl。UserDaoImplbeanidbookServiceclasscom。itheima。service。impl。BookServiceImplpropertynamebookDaorefbookDaopropertynameuserDaorefuserDaobeanbeans value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换。 对于setter注入方式的基本使用就已经介绍完了,对于 引用数据类型使用的是 对于简单数据类型使用的是 构造器注入构造器注入引用数据类型 步骤1:提供构造方法publicclassBookServiceImplimplementsBookService{privateBookDaobookDao;publicBookServiceImpl(BookDaobookDao){this。bookDaobookDao;}publicvoidsave(){System。out。println(bookservicesave。。。);bookDao。save();}} 步骤2:配置文件中进行配置构造方式注入 在applicationContext。xml中配置lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidbookDaoclasscom。itheima。dao。impl。BookDaoImplbeanidbookServiceclasscom。itheima。service。impl。BookServiceImplconstructorargnamebookDaorefbookDaobeanbeans 说明: 标签中name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。ref属性指向的是spring的IOC容器中其他bean对象。 构造器注入多个引用数据类型 步骤1:提供多个属性的构造函数 步骤2:配置文件中配置多参数注入 构造器注入多个简单数据类型 步骤1:添加多个简单属性并提供构造方法publicclassBookDaoImplimplementsBookDao{privateStringdatabaseName;privateintconnectionNum;publicBookDaoImpl(StringdatabaseName,intconnectionNum){this。databaseNamedatabaseName;this。connectionNumconnectionNum;}publicvoidsave(){System。out。println(bookdaosave。。。databaseName,connectionNum);}} 步骤2:配置完成多个属性构造器注入lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidbookDaoclasscom。itheima。dao。impl。BookDaoImplconstructorargnamedatabaseNamevaluemysqlconstructorargnameconnectionNumvalue666beanbeaniduserDaoclasscom。itheima。dao。impl。UserDaoImplbeanidbookServiceclasscom。itheima。service。impl。BookServiceImplconstructorargnamebookDaorefbookDaoconstructorargnameuserDaorefuserDaobeanbeans当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变这两块存在紧耦合,具体该如何解决? 方式一:删除name属性,添加type属性,按照类型注入beanidbookDaoclasscom。itheima。dao。impl。BookDaoImplconstructorargtypeintvalue10constructorargtypejava。lang。Stringvaluemysqlbean这种方式可以解决构造函数形参名发生变化带来的耦合问题但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了 方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始beanidbookDaoclasscom。itheima。dao。impl。BookDaoImplconstructorargindex1value100constructorargindex0valuemysqlbean这种方式可以解决参数类型重复问题但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题 介绍完两种参数的注入方式,具体我们该如何选择呢?强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现强制依赖指对象在创建的过程中必须要注入指定的参数可选依赖使用setter注入进行,灵活性强可选依赖指对象在创建过程中注入的参数可有可无Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入自己开发的模块推荐使用setter注入 依赖自动装配IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配 自动装配方式有哪些?按类型(常用)按名称按构造方法不启用自动装配 自动装配只需要修改applicationContext。xml配置文件即可: (1)将标签删除 (2)在标签中添加autowire属性lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanclasscom。itheima。dao。impl。BookDaoImpl!autowire属性:开启自动装配,通常使用按类型装配beanidbookServiceclasscom。itheima。service。impl。BookServiceImplautowirebyTypebeans 注意事项: 需要注入属性的类中对应属性的setter方法不能省略被注入的对象必须要被Spring的IOC容器管理按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException 一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanclasscom。itheima。dao。impl。BookDaoImpl!autowire属性:开启自动装配,通常使用按类型装配beanidbookServiceclasscom。itheima。service。impl。BookServiceImplautowirebyNamebeans 最后对于依赖注入,需要注意一些其他的配置特征: 1。自动装配用于引用类型依赖注入,不能对简单类型进行操作 2。使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用 3。使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用 4。自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效 集合注入 前面我们已经能完成引入数据类型和简单数据类型的注入,但是还有一种数据类型集合,集合中既可以装简单数据类型也可以装引用数据类型,对于集合,在Spring中该如何注入呢? 先来回顾下,常见的集合类型有哪些?数组ListSetMapProperties 举例 (1)项目中添加添加BookDao、BookDaoImpl类publicinterfaceBookDao{publicvoidsave();}publicclassBookDaoImplimplementsBookDao{publicclassBookDaoImplimplementsBookDao{privateint〔〕array;privateListStringlist;privateSetStringset;privateMapString,Stringmap;privatePropertiesproperties;publicvoidsave(){System。out。println(bookdaosave。。。);System。out。println(遍历数组:Arrays。toString(array));System。out。println(遍历Listlist);System。out。println(遍历Setset);System。out。println(遍历Mapmap);System。out。println(遍历Propertiesproperties);}setter。。。。方法省略,自己使用工具生成} (2)resources下提供spring的配置文件,applicationContext。xml 下面的所以配置方式,都是在bookDao的bean标签中使用进行注入lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidbookDaoclasscom。itheima。dao。impl。BookDaoImplbeanbeans 注入数组类型数据propertynamearrayvalue100valuevalue200valuevalue300valuearrayproperty 注入List类型数据propertynamelistlistvalueitcastvaluevalueitheimavaluevalueboxueguvaluevaluechuanzhihuivaluelistproperty 注入Set类型数据propertynamesetsetvalueitcastvaluevalueitheimavaluevalueboxueguvaluevalueboxueguvaluesetproperty 注入Map类型数据propertynamemapmapentrykeycountryvaluechinaentrykeyprovincevaluehenanentrykeycityvaluekaifengmapproperty 注入Properties类型数据propertynamepropertiespropspropkeycountrychinaproppropkeyprovincehenanproppropkeycitykaifengproppropsproperty 说明:property标签表示setter方式注入,构造方式注入constructorarg标签内部也可以写、list、set、map、props标签List的底层也是通过数组实现的,所以和标签是可以混用集合中要添加引用类型,只需要把标签改成标签,这种方式用的比较少