Spring入门案例
Spring入门案例代码
1.创建Maven的java项目
2.pom.xml添加Spring的依赖jar包 <?xml version="1.0" encoding="UTF-8"?> 4.0.0 com.itheima spring_01_quickstart 1.0-SNAPSHOT org.springframework spring-context 5.2.10.RELEASE junit junit 4.12 test
3.创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类 package com.itheima.dao; public interface BookDao { public void save(); } package com.itheima.dao.impl; import com.itheima.dao.BookDao; public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } } package com.itheima.service; public interface BookService { public void save(); } package com.itheima.service.impl; import com.itheima.dao.BookDao; import com.itheima.dao.impl.BookDaoImpl; import com.itheima.service.BookService; public class BookServiceImpl implements BookService { //5.删除业务层中使用new的方式创建的dao对象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } //6.提供对应的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
4.resources下添加spring配置文件,并完成bean的配置
applicationContext.xmll<?xml version="1.0" encoding="UTF-8"?>
注意:配置中的两个bookDao的含义是不一样的
name="bookDao"中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前 面加set找对应的setBookDao()方法进行对象注入
ref="bookDao"中bookDao的作用是让Spring能在IOC容器中找到id为bookDao的Bean对象给
bookService进行注入
综上所述,对应关系如下:
5.从容器中获取对象进行方法调用 package com.itheima; import com.itheima.dao.BookDao; import com.itheima.service.BookService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { //获取IOC容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookService bookService = (BookService) ctx.getBean("bookService"); bookService.save(); } }
以上完成了入门案例代码。
bean基础配置
bean基础配置(id与class)
bean的name属性
name为bean指定别名,别名可以有多个,使用逗号,空格、分号隔开。
根据名称容器中获取bean对象
public class AppForName {
public static void main ( String [] args ) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext ( "applicationContext.xml" );
//此处根据bean标签的id属性和name属性的任意一个值来获取bean对象
BookService bookService = ( BookService ) ctx . getBean ( "service4" );
bookService . save ();
}
}
bean作用范围scope配置
bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题?
如果对象是有状态对象,即该对象有成员变量可以用来存储数据的, 因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的, 因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
封装实例的域对象,因为会引发线程安全问题,所以不适合。
哪些bean对象适合交给容器进行管理?
表现层对象 业务层对象 数据层对象 工具对象
bean实例化方法
构造方法实例化
Spring底层使用的是类的无参构造方法。关于Spring的构造方法实例化就已经学习完了,因为每一个类默认都会提供一个无参构造函 数,所以其实真正在使用这种方式的时候,我们什么也不需要做。这也是我们以后比较常用的一种方式。
静态工厂实例化
看到这,可能有人会问了,你这种方式在工厂类中不也是直接new对象的,和我自己直接new没什么太 大的区别,而且静态工厂的方式反而更复杂,这种方式的意义是什么?
主要的原因是:
在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少,如:
public class OrderDaoFactory {
public static OrderDao getOrderDao (){
System . out . println ( "factory setup...." ); //模拟必要的业务操作
return new OrderDaoImpl ();
}
}
之前new对象的方式就无法添加其他的业务内容。
实例工厂与FactoryBean
接下来继续来研究Spring的第三种bean的创建方式实例工厂实例化:
环境准备
(1)准备一个UserDao和UserDaoImpl类
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
(2)创建一个工厂类OrderDaoFactory并提供一个普通方法,注意此处和静态工厂的工厂类不一样的 地方是方法不是静态方法
public class UserDaoFactory {
public UserDao getUserDao (){
return new UserDaoImpl ();
}
}
(3)编写AppForInstanceUser运行类,在类中通过工厂获取对象
// //创建实例工厂对象
UserDaoFactory userDaoFactory = new UserDaoFactory();
// //通过实例工厂对象创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
实例工厂实例化
具体实现步骤为:
(1)在spring的配置文件中添加以下内容:
实例化工厂运行的顺序是:
创建实例化工厂对象,对应的是第一行配置
调用对象中的方法来创建bean,对应的是第二行配置
factory-bean:工厂的实例对象
factory-method:工厂对象中的具体创建对象的方法名,对应关系如下:
factory-mehod:具体工厂类中创建对象的方法名
(2)在AppForInstanceUser运行类,使用从IOC容器中获取bean的方法进行运行测试 public class AppForInstanceUser { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = (UserDao) ctx.getBean("userDao"); userDao.save(); } }
实例工厂实例化的方式就已经介绍完了,配置的过程还是比较复杂,所以Spring为了简化这种配置方 式就提供了一种叫FactoryBean的方式来简化开发。
FactoryBean的使用
具体的使用步骤为:
(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法 public class UserDaoFactoryBean implements FactoryBean { //代替原始实例工厂中创建对象的方法 public UserDao getObject() throws Exception { return new UserDaoImpl(); } //返回所创建类的Class对象 public Class<?> getObjectType() { return UserDao.class; } }
(2)在Spring的配置文件中进行配置
(3)AppForInstanceUser运行类不用做任何修改,直接运行
查看源码会发现,FactoryBean接口其实会有三个方法,分别是: T getObject() throws Exception; Class<?> getObjectType(); default boolean isSingleton() { return true; }
方法一:getObject(),被重写后,在方法中进行对象的创建并返回
方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象
方法三:没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单例,默 认true.
那如果想改成单例具体如何实现?
只需要将isSingleton()方法进行重写,修改返回为false,即可 //FactoryBean创建对象 public class UserDaoFactoryBean implements FactoryBean { //代替原始实例工厂中创建对象的方法 public UserDao getObject() throws Exception { return new UserDaoImpl(); } public Class<?> getObjectType() { return UserDao.class; } public boolean isSingleton() { return false; } }
但是一般情况下我们都会采用单例,也就是采用默认即可。 所以isSingleton()方法一般不需要进行重写。
bean的生命周期
首先理解下什么是生命周期?
从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。
bean生命周期是什么? bean对象从创建到销毁的整体过程。
bean生命周期控制是什么?在bean创建后到销毁前做一些事情。
生命周期设置
添加生命周期的控制方法,具体的控制有两个阶段:
bean创建之后,想要添加内容,比如用来初始化需要用到资源
bean销毁之前,想要添加内容,比如用来释放用到的资源
步骤1:添加初始化和销毁方法
针对这两个阶段,我们在BooDaoImpl类中分别添加两个方法, 方法名任意 public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } //表示bean初始化对应的操作 public void init(){ System.out.println("init..."); } //表示bean销毁前对应的操作 public void destory(){ System.out.println("destory..."); } }
步骤2:配置生命周期
在配置文件添加配置,如下:
步骤3:运行程序
从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢?
Spring的IOC容器是运行在JVM中 运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方 法执行main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了 所以没有调用对应的destroy方法.
知道了出现问题的原因,具体该如何解决呢?
close关闭容器 ApplicationContext中没有close方法 需要将ApplicationContext更换成ClassPathXmlApplicationContext ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 调用ctx的close()方法 ctx.close();运行程序,就能执行destroy方法的内容
注册钩子关闭容器 在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器 调用ctx的registerShutdownHook()方法 ctx.registerShutdownHook();
注意:registerShutdownHook在ApplicationContext中也没有
两种方式介绍完后,close和registerShutdownHook选哪个?
相同点:这两种都能用来关闭容器
不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。 分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多 也比较乱。
Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置init-method和
destroy-method
接下来在BookServiceImpl完成这两个接口的使用:
修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean并实现接口中的 两个方法afterPropertiesSet和destroy public class BookServiceImpl implements BookService, InitializingBean, DisposableBean { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } public void destroy() throws Exception { System.out.println("service destroy"); } public void afterPropertiesSet() throws Exception { System.out.println("service init"); } }
那第二种方式的实现,我们也介绍完了。
小细节 对于InitializingBean接口中的afterPropertiesSet方法,翻译过来为属性设置之后。 对于BookServiceImpl来说,bookDao是它的一个属性 setBookDao方法是Spring的IOC容器为其注入属性的方法 afterPropertiesSet和setBookDao是setBookDao方法先执行
DI依赖注入
setter注入 注入引用类型对象 在bean中定义引用类型属性,并提供可访问的 set 方法 public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
配置中使用 property 标签 ref 属性注入引用类型对象
2.注入简单数据类型
步骤1:声明属性并提供setter方法 public class BookDaoImpl implements BookDao { private String databaseName; private int connectionNum; public void setConnectionNum(int connectionNum) { this.connectionNum = connectionNum; } public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public void save() { System.out.println("book dao save ..."+databaseName+","+connectionNum); } }
步骤2:配置文件中进行注入配置 <?xml version="1.0" encoding="UTF-8"?>
value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换。
对于setter注入方式的基本使用就已经介绍完了, 对于
引用数据类型使用的是
对于简单数据类型使用的是
构造器注入 构造器注入引用数据类型
步骤1:提供构造方法 public class BookServiceImpl implements BookService{ private BookDao bookDao; public BookServiceImpl(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
步骤2:配置文件中进行配置构造方式注入
在applicationContext.xml中配置 <?xml version="1.0" encoding="UTF-8"?>
说明:
标签中 name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。 ref属性指向的是spring的IOC容器中其他bean对象。
构造器注入多个引用数据类型
步骤1:提供多个属性的构造函数
步骤2:配置文件中配置多参数注入
构造器注入多个简单数据类型
步骤1:添加多个简单属性并提供构造方法 public class BookDaoImpl implements BookDao { private String databaseName; private int connectionNum; public BookDaoImpl(String databaseName, int connectionNum) { this.databaseName = databaseName; this.connectionNum = connectionNum; } public void save() { System.out.println("book dao save ..."+databaseName+","+connectionNum); } }
步骤2:配置完成多个属性构造器注入 <?xml version="1.0" encoding="UTF-8"?> 当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变 这两块存在紧耦合,具体该如何解决?
方式一:删除name属性,添加type属性,按照类型注入 这种方式可以解决构造函数形参名发生变化带来的耦合问题 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了
方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始 这种方式可以解决参数类型重复问题 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
介绍完两种参数的注入方式,具体我们该如何选择呢? 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现 强制依赖指对象在创建的过程中必须要注入指定的参数 可选依赖使用setter注入进行,灵活性强 可选依赖指对象在创建过程中注入的参数可有可无 Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相 对严谨 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选 依赖的注入 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注 入 自己开发的模块推荐使用setter注入
依赖自动装配 IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配方式有哪些? 按类型(常用) 按名称 按构造方法 不启用自动装配
自动装配只需要修改applicationContext.xml配置文件即可:
(1)将标签删除
(2)在标签中添加autowire属性 <?xml version="1.0" encoding="UTF-8"?>
注意事项:
需要注入属性的类中对应属性的setter方法不能省略 被注入的对象必须要被Spring的IOC容器管理 按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException
一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为: <?xml version="1.0" encoding="UTF-8"?>
最后对于依赖注入,需要注意一些其他的配置特征:
1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推 荐使用
4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合注入
前面我们已经能完成引入数据类型和简单数据类型的注入,但是还有一种数据类型 集合 ,集合中既可 以装简单数据类型也可以装引用数据类型,对于集合,在Spring中该如何注入呢?
先来回顾下,常见的集合类型有哪些? 数组 List Set Map Properties
举例——
(1)项目中添加添加BookDao、BookDaoImpl类 public interface BookDao { public void save(); } public class BookDaoImpl implements BookDao { public class BookDaoImpl implements BookDao { private int[] array; private List list; private Set set; private Map map; private Properties properties; public void save() { System.out.println("book dao save ..."); System.out.println("遍历数组:" + Arrays.toString(array)); System.out.println("遍历List" + list); System.out.println("遍历Set" + set); System.out.println("遍历Map" + map); System.out.println("遍历Properties" + properties); } //setter....方法省略,自己使用工具生成 }
(2)resources下提供spring的配置文件,applicationContext.xml
下面的所以配置方式,都是在bookDao的bean标签中使用进行注入 <?xml version="1.0" encoding="UTF-8"?>
注入数组类型数据 100 200 300
注入List类型数据 itcast itheima boxuegu chuanzhihui
注入Set类型数据 itcast itheima boxuegu boxuegu
注入Map类型数据
注入Properties类型数据 china henan kaifeng
说明: property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写 、、、
水花兄弟半场48分打疯了!克莱8记三分挡不住,勇士打爆太阳北京时间3月14日,NBA常规赛迎来一场焦点战,勇士主场迎战太阳,水花兄弟在上半场火力全开,两人联手轰进了48分,勇士半场就轰下75分,领先了17分,打爆了太阳。勇士上一场加时险胜
记者跟深足名宿聊天表示目前问题太难了球迷要做好最坏打算直播吧3月14日讯根据此前的消息,深圳队可能会在新赛季中超开赛前解散,跟队记者程莉表示深圳队目前的情况很艰难。记者表示其实今年以来,关于深足命运的话题,我建议球迷做好最坏的心理准备
人类的屁股有多奇特?极致腰臀比的吸引力在何处?人类目前是世界上最特殊的动物,但人类身上最特殊的器官并不是眼睛鼻子或者耳朵,而是人类的屁股,而且它是人类身体组成最重要的部位之一。在人类漫长的进化史中,我们的祖先由四肢爬行演化成为
大运河系列赛首站!苏州3万人雨中活力开跑现代快报讯(记者徐晓安)3月12日早晨八点半,随着一声激动人心的枪响,第十二届苏州环金鸡湖半程马拉松暨大运河马拉松系列赛(苏州站)热力开跑!三万名跑者迈着有力的步伐,开启了新的金马
2023大运河系列赛苏州启幕3万人活力开跑交汇点讯3月12日上午8时,随着一声激动人心的枪响,2023第十二届苏州环金鸡湖半程马拉松暨大运河马拉松系列赛(苏州站)正式鸣枪开跑。30000名选手在苏州金鸡湖畔激情开跑,由此拉
一分钟读懂荣耀Magic5系列的值与不值前引本文并非发布会内容的复读机,而是各种删繁就简,以提供更有价值的参考指引。对于这代不满意(例如价格或者外观等)的,建议跳过内容直接拉到文尾看主观感受。至于感兴趣的,建议看到客观总
苹果也坚持不住了,iPhone14Pro系列跌至新低,1TB版本直降1700元1TB的固态硬盘很常见,但1TB的手机就不多见了,甚至直到现在很多人都还没玩过1TB储存的手机,其实市面上拥有1TB超大储存的机型也有好几款,但因为它们售价较贵的缘故,消费者们在选
古镇小聚看看设计师怎么卷今天是周末,写点轻松的文章。自从上周去逛了南头古镇,有点意犹未尽。发了视频上来,但是像素压缩得一塌糊涂,还是转成图片的形式好了。深圳虽然是座很年轻的城市,但有些地方也颇有历史,比如
iQOOZ7系列手机官宣3月20日发布IT之家3月13日消息,iQOO手机官方宣布,全新性能续航小超人iQOOZ7系列即将登场。将于3月20日1900新品发布。现在,iQOOZ7系列手机已开始预约。支持120W闪充,仅
iQOOz7系列确定3月20日发布,主打120瓦的快充iQOOz7确定在3月20日发布,这个产品主打的就是快充,另外iQOOz7i也发布了,一个看起来定位明确的产品。从图片看,这次的iQOOz7是打算使用后置双摄的思路,另外还有ois
输得起才会赢WTT新加坡大满贯继樊振东前期不敌韩国赵大成首轮被淘汰后陈梦和王艺迪又爆冷被淘汰!这就是比赛,比赛不会因为你是世界第一世界第二就额外的关照你,每场比赛对每一个队员都是新的开始,都是
如何提高个人的城府每一个职场小白,都有直面人生的时候,大多快言快语,无所掩饰,将自己的所思所想全部坦诚的暴露出去,大多撞的头破血流,有的被一棍子打死,有的反而思而后生,下面和大家谈谈锻炼自己提高城府
落枕别硬拉!揉揉手上的几个穴位,效果特别好!收藏备用睡觉之前还是好好的,一觉醒来,脖子莫名其妙地不敢动了。没错,可能是落枕了。落枕,中医亦称其为失枕,现代医学称之为急性颈椎关节周围炎或颈肩肌筋膜炎,是颈部软组织常见的损伤之一。落枕起
王者荣耀国服鲁班最新出装,两把无尽加末世,实战效果超棒文丹青解说原创文章盗载必究有的时候,当我们常玩一个英雄的时候,总是会被传统的观念所束缚,例如英雄的出装搭配,没有特殊情况一般不会轻易更改,自然而然地遵循一些老旧的思路。其实,英雄的
传奇世界护盾也该提上日程,激战天元秘境大家好,我是知识嗷嗷丰富,嗓音贼拉炫酷,光一个背影往那一杵,就能吸引粉丝无数的小顾。最近装备提升的进度也不是那么着急了,但是其他项目还有有很大提升空间的。就比如说我们这个元神,目前
十条励志语录,送给2022年的你(1)十条励志语录,送给2022年的你(1)世界是纷繁复杂的舞台,所以每一个人的生活,有时是一路风雨,艰难行进有时是一帆风顺,策马奔腾有时是冰天雪地,步履维艰有时是春暖花开沐浴暖阳。不论
北方人去江西旅游2天后,才终于明白江西菜辣得有多深?去江西旅游2天,蔬菜里都放辣椒,北方人我是被辣回来的都说江西人能吃辣,河南小伙张峰自然是不信的,张峰以前在福建上班时,有个同事也是江西的,吃张峰做的油泼辣子结果被辣得哇哇叫。这让张
魔法觉醒手游问题哈利波特运营问题讨论在朋友邀请下又玩了一周(因为可以借衣服)。现在打到了首席。然后禁林基本上全打到了星级10,传说卡有飞蛾罗恩哈利和那个变形课教授麦格,厉火和一个白龙。金回响基本上有几个了。再次卸载。
王者大冒险官方版王者大冒险官方版是由海南舜杰网络科技有限公司打造的一款架空幻想冒险题材放置类型游戏,它以异世界的英雄峡谷作为背景,为了维护英雄峡谷的和平,玩家要从遗迹泉水之中召唤出各类英雄,从星之
热血传奇沙巴克攻城战的背后故事?说起沙巴克攻城战,无人不知,但说起沙巴克的背后故事,知道的人就寥寥无几了。它如何诞生?拥有怎样的过去?史籍记载,沙巴克发现于玛法人最黑暗的时刻。在黑暗势力的影响下,大部分生灵变为嗜
镜KPL限定皮肤最新爆料!上线时间已确定!47块你会买吗?皮肤爆料恭喜镜姐喜提KPL史上第一英雄称号不仅有FMVP皮肤还有KPL限定排面杠杠滴有!一起看看吧匿光追影者故事设定这个故事我很喜欢。它告诉我们,即使被创造者规定了人生,历经残酷,
威客电竞LOL德杯小组赛阶段结束,最终晋级结果出炉随着英雄联盟冬季转会期的结束,一年一度的德杯开始了,今年德杯赛制和去年一样,S11世界赛的四支LPL队伍直接晋级八强,通过小组赛再选出剩下四支战队。目前德杯小组赛阶段已经结束,最终