1、Spring
1.1、简介
-
Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。
-
功能:使用基本的JavaBean代替EJB(Enterprise Java Beans)
-
目的:解决企业应用开发的复杂性
-
Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。
-
Rod Johnson,Spring Framework创始人,著名作者,悉尼大学的音乐学博士。
-
Spring官网:https://spring.io/
1.2、优点
-
轻量、非侵入式。
-
控制反转(IoC)、面向切面(AOP)
-
支持事务的处理,对框架整合的支持
-
为什么使用Spring
Spring使每个人都可以更快,更轻松和更安全地进行Java编程。Spring对速度,简单性和生产率的关注使其成为世界上最受欢迎的 Java框架。
“我们使用了Spring框架随附的许多工具,并获得了许多现成的解决方案的好处,而不必担心编写大量额外的代码-这样确实为我们节省了一些时间和能量。”
Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simplicity, and productivity has made it the world’s most popular Java framework.
“We use a lot of the tools that come with the Spring framework and reap the benefits of having a lot of the out of the box solutions, and not having to worry about writing a ton of additional code—so that really saves us some time and energy.”
——https://spring.io/why-spring
1.3、组成
- Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC
2、控制反转(IOC)
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。
采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。
2.1、引入
- Java开发的编程规范——dao层、service层、controller层
- DAO层:
DAO层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表的增删改查,也就是说某个DAO一定是和数据库的某一张表一一对应的,其中封装了增删改查基本操作,建议DAO只做原子操作,增删改查。 - Service层:
Service层叫服务层,被称为服务,粗略的理解就是对一个或多个DAO进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事务控制。 - Controler层:
Controler负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面。
2.2、示例
假如我们设计一个Say类,在里面设计一个方法用于输出传入的值,然后再Main中调用该方法。
2.2.1、编写示范
Say类设计:
public class Say {
private String note;
public Say(String note) {
this.note = note;
}
public Say() {
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public void sayNote(){
System.out.println(note);
}
}
调用:
public class Main {
public static void main(String[] args) {
//调用Say类中的toString
Say unit = new Say(); //先创建对象
unit.setNote("This is a note."); //对象赋值
unit.sayNote(); //调用对象方法
}
}
-
实际情况
- 根据往常编写经验可知若需要多次实现sayNote,则需要多次new出Say对象
-
解决方案:IOC
- 从当初程序主动创建对象变成被动注入对象
- 同一个对象可以注入到多个类中
为了实现上述内容,需要一个容器负责管理所有的组件。那么就需要告诉容器如何创建组件以及依赖关系。
2.3、xml编写介绍
- 创建Spring项目的时候,可以自动创建一个空的.xml文件,该文件便用于告知组件创建。
- 在编写xml文件之前需要向其中写入部分配置内容
<?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">
</beans>
根据不同的需求还可以写入不同的内容如C命名空间注入和P命名空间注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
2.4、重新编写
- 首先配置xml基本内容
<?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">
</beans>
- 向其中添加bean
<bean id="unit" class="func.Say">
<constructor-arg name="note" value="This is a note."/>
</bean>
- 重新编写Main
添加import
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
在main中添加 spring-config.xml 配置文件的读取(文件名随意)
ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml");
随后从容器中获取编辑好的bean
Say unit = (Say) Context.getBean("unit"); //使用(say)进行强制转换,将获取到的object转换成Say类型
Say say = Context.getBean("unit",Say.class); //使用getBean的设计,在后面补充class类型
//以上两种方法均可从容器中获取bean
最后正常调用即可
unit.sayNote();
say.sayNote();
Main代码展示:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml");
Say unit = (Say) Context.getBean("unit");
unit.sayNote();
}
}
3、关于xml的编写
3.1、可直接看到的标签
- bean
- alias:别名
- description
- import:导入。用于多人开发场景,导入他人的xml
- beans:整个配置文件的根节点,包含一个或多个Bean元素。
3.2、bean常用标签
-
id:起名,定义对象名。类似于 int a 中的 a
-
class:对象所属类的全路径。类似 String b 中的String (若定义String对象则需要写 java.lang.String )
-
scope:作用域。常用为singleton和prototype
-
singleton:若不单独设置scope属性则默认为singleton,单例对象
<bean id="unit" class="func.Say"/>
public class Main { public static void main(String[] args) { ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml"); Say unit1 = Context.getBean("unit",Say.class); Say unit2 = Context.getBean("unit",Say.class); System.out.println(unit1 == unit2); } }
输出结果:true
-
prototype:需要单独设置该属性,多实例对象
<bean id="unit" class="func.Say" scope="prototype"/>
public class Main { public static void main(String[] args) { ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml"); Say unit1 = Context.getBean("unit",Say.class); Say unit2 = Context.getBean("unit",Say.class); System.out.println(unit1 == unit2); } }
输出结果:false
-
-
constructor-arg:用以传入构造参数进行实例化,是注入依赖关系的一种方式。
<constructor-arg name="note" value="This is a note."/>
public Say(String note) { this.note = note; }
-
index:指定构造参数的序号(从0开始)
<constructor-arg index="0" value="This is a note."/> <constructor-arg index="1" value="8"/>
public Say(String note, int num) { this.note = note; this.num = num; }
-
type:指定构造参数的类型(当类型重复时,会根据配置的顺序对应赋值)
- 正常示例
<constructor-arg type="int" value="9"/> <constructor-arg type="java.lang.String" value="This is a note."/>
public Say(String note, int num) { this.note = note; this.num = num; }
- 重复类型示例
<constructor-arg type="int" value="9"/> <constructor-arg type="int" value="8"/> <constructor-arg type="int" value="7"/> <constructor-arg type="java.lang.String" value="This is a note1."/> <constructor-arg type="java.lang.String" value="This is a note2."/> <constructor-arg type="java.lang.String" value="This is a note3."/>
public class Say { private String note1; private String note2; private int num1; private int num2; private String note3; private int num3; public Say(String note1, String note2, int num1, int num2, String note3, int num3) { this.note1 = note1; this.note2 = note2; this.num1 = num1; this.num2 = num2; this.note3 = note3; this.num3 = num3; } @Override public String toString() { return "Say{" + "note1='" + note1 + '\'' + ", note2='" + note2 + '\'' + ", num1=" + num1 + ", num2=" + num2 + ", note3='" + note3 + '\'' + ", num3=" + num3 + '}'; } }
输出结果:Say{note1=‘This is a note1.’, note2=‘This is a note2.’, num1=9, num2=8, note3=‘This is a note3.’, num3=7}
-
property:以调用Bean实例中的相关Set方法完成属性值的赋值
-
ref/value:ref指注入对象,value指注入具体值
-
list:以封装List或数组类型属性的依赖注入,具体的元素通过ref或value子标记
-
set:以封装Set类型属性的依赖注入,具体的元素通过ref或value子标记指定
-
map:以封装Map类型属性的依赖注入,具体的元素通过entry子标记指定
-
entry:用做map标记的子标记,用以设置一个“键/值”对。key属性接收字符串类型的“键”名称,“值”则可由ref或value子标记指定
-
props:以封装Properties类型属性的依赖注入,具体的元素通过prop子标记指定
-
代码实现展示
-
设计
student类设计(部分)
private String name; //姓名 private Address address; //家庭地址(Address对象注入) private String[] subjects; //各类学科 private List<String> hobbies; //爱好 private Map<String,String> idCard; //学生证:姓名+学生证号 private Set<String> games; //常玩游戏 private Properties info; //其他信息 private String lover; //恋爱状况(用于null注入)
Address类设计
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return address; } }
-
注入实现参考 (以下内容均写在 ··· 中)
- value注入
<property name="name" value="张三"/>
- ref注入
<!--该段bean独立于stu--> <bean id="add" class="func.Address"> <property name="address" value="山东"/> </bean> <!--该段属于stu中--> <property name="address" ref="add"/>
- array注入
<property name="subjects"> <array> <value>C语言</value> <value>Python语言</value> <value>Java语言</value> <value>离散数学</value> </array> </property>
- List注入
<property name="hobbies"> <list> <value>唱</value> <value>跳</value> <value>rap</value> <value>篮球</value> </list> </property>
- Map注入
<property name="idCard"> <map> <entry key="姓名" value="法外狂徒"/> <entry key="学号" value="1900000101"/> </map> </property>
- Set注入
<property name="games"> <set> <value>DNF</value> <value>clx</value> <value>QQSpeed</value> </set> </property>
- Properties注入
<property name="info"> <props> <prop key="QQ号">123456789</prop> <prop key="电话号">11122223333</prop> <prop key="邮箱地址">123456789@qq.com</prop> </props> </property>
- null注入
<property name="lover"> <null/> </property>
- main设计:
public class Main { public static void main(String[] args) { ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml"); student stu = Context.getBean("stu",student.class); System.out.println(stu.toString()); } }
- 输出结果
student{ name='张三' address=山东 subjects=[C语言, Python语言, Java语言, 离散数学] hobbies=[唱, 跳, rap, 篮球] idCard={姓名=法外狂徒, 学号=1900000101} games=[DNF, clx, QQSpeed] info={QQ号=123456789, 邮箱地址=123456789@qq.com, 电话号=11122223333} lover='null' }
-
3.3、<c>标签与<p>标签
在xml里编写注入的时候,除了上述2.3.2.2内容中的各类注入方法,此外还有<c>命名空间注入和<p>命名空间注入。
首先,使用<c>标签和<p>标签之前,需要在配置文件中写入以下内容。
<c>标签需要写入
xmlns:c="http://www.springframework.org/schema/c"
<p>标签需要写入
xmlns:p="http://www.springframework.org/schema/p"
与普通的注入方法相似,<c>指<constructor-arg>即有参注入,<p>指<property>即无参注入。
以下为C命名空间注入,两种写法的结果是一致的。
<bean id="cp" class="func.CandP" c:note="This is C."/>
<bean id="cp2" class="func.CandP">
<constructor-arg name="note" value="This is C."/>
</bean>
以下为P命名空间注入,两种写法的结果也是一致的。
<bean id="p" class="func.CandP" p:note="This is P."/>
<bean id="p2" class="func.CandP">
<property name="note" value="This is P."/>
</bean>
【注意】
-
若使用P标签,在class的设计时,需要对所需要被注入的成员设计set方法,否则无法在xml文件中利用p标签获取成员信息。
private String note; private String book; public void setNote(String note) { this.note = note; }
<bean id="p" class="func.CandP" p:note="This is P."/> <!--该代码有效--> <bean id="p2" class="func.CandP" p:book="This is p2."/> <!--因为在class中没有写book的set函数,故该代码无效-->
-
若使用C标签,在class的设计时,需要对类进行有参构造,构造需要的参数可以被xml文件中的c标签读取。
private String note; private String book; public CandP(String note) { this.note = note; }
<bean id="c" class="func.CandP" c:note="This is C."/> <!--该代码有效--> <bean id="c2" class="func.CandP" c:book="This is C."/> <!--因为在class中的有参构造里没有book成员,故该代码无效-->
-
在使用p标签和c标签时,除了直接赋值注入以外,也可以利用ref注入对象
-
在使用c标签时也可以使用index的方式注入,不过需要先写下划线"_"后面再跟数字0、1、2、3···
public class CandP {
private String note;
public CandP(String note) {
this.note = note;
}
}
<!--使用C标签的index-->
<bean id="c" class="func.CandP" c:_0="This is C."/>
<!--使用C标签的普通方法-->
<bean id="c2" class="func.CandP" c:note="This is C."/>
<!--使用constructor的name方法-->
<bean id="c3" class="func.CandP">
<constructor-arg name="note" value="This is C."/>
</bean>
<!--使用constructor的index方法-->
<bean id="c4" class="func.CandP">
<constructor-arg index="0" value="This is C."/>
</bean>
3.4、bean的自动装配
Spring会在配置文件上下文中自动寻找并给bean装配属性
自动装配的关键字为 autowire ,包含5个子参数——byName, byType, constructor, default, no
在解释之前先看一下接下来要用到的三个类的设计
public class classRoom {
private String num;
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
@Override
public String toString() {
return "classRoom{" +
"num='" + num + '\'' +
'}';
}
}
public class dormitory {
private String No;
public String getNo() {
return No;
}
public void setNo(String no) {
No = no;
}
@Override
public String toString() {
return "dormitory{" +
"No='" + No + '\'' +
'}';
}
}
public class student {
classRoom cr;
dormitory dor;
public classRoom getCr() {
return cr;
}
public void setCr(classRoom cr) {
this.cr = cr;
}
public dormitory getDor() {
return dor;
}
public void setDor(dormitory dor) {
this.dor = dor;
}
@Override
public String toString() {
return "student{" +
"cr=" + cr.toString() +
", dor=" + dor.toString() +
'}';
}
}
以及main代码
public class Main {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml");
student stu = Context.getBean("student",student.class);
System.out.println(stu.toString());
}
}
3.4.1通常写法
在不使用自动装配的时候,xml文件我们为手动装配
<bean id="dormitory" class="func.dormitory" p:no="北1A101"/>
<bean id="classRoom" class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student">
<property name="cr" ref="classRoom"/>
<property name="dor" ref="dormitory"/>
</bean>
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
3.4.2使用byName
使用byName的好处在于,不用担心配置时变量名的输入错误(字母缺失或者大小写混淆),变量名只需和类中的成员名相同即可
byName运行流程
若一个bean含有autowire byName 。首先自动查找类中所有的set方法并获取set后面的字符串且转化为小写,例如setclassRomm()自动获取classroom。随后前往Spring容器中寻找id为classroom的对象。如果对象存在,则取出并注入,否则报空指针异常。
<bean id="dor" class="func.dormitory" p:no="北1A101"/>
<bean id="cr" class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student" autowire="byName"/>
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
【错误示范】
<!--在设计类的时候,成员名我并没有写成全拼,byName取出的字符串为dor和cr,无法上下文匹配-->
<bean id="dormitory" class="func.dormitory" p:no="北1A101"/>
<bean id="classRoom" class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student" autowire="byName"/>
输出结果:Exception in thread “main” java.lang.NullPointerException
3.4.3使用byType
使用byName的好处在于,不用担心配置时变量名的输入错误,系统自动按对象类型进行匹配
由于byType是根据bean需要的对象的类型进行上下文查找,所以id可以任意编写甚至可以不写。
而byType存在的明显缺陷,同一类型的对象,必须在spring容器中唯一否则会报不唯一的异常,并且该方法并不像前文提到的<type>标签。
type:指定构造参数的类型(当类型重复时,会根据配置的顺序对应赋值)
<bean id="dormitory" class="func.dormitory" p:no="北1A101"/>
<bean id="classRoom" class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student" autowire="byType"/>
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
【错误示范】
<!--student中仅含有1个classRoom成员,此处编写了2个属性为classRoom的bean,发生报错-->
<!--哪怕将第三行的class移到最后一行,也是一样会发生报错-->
<bean id="dormitory" class="func.dormitory" p:no="北1A101"/>
<bean id="classRoom" class="func.classRoom" p:num="明德101"/>
<bean id="class" class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student" autowire="byType"/>
<!--
错误信息:Could not autowire. There is more than one bean of 'classRoom' type. Beans: class,classRoom.
-->
哪怕是重新设计student类,多写一个dormintory尝试让他按顺序装配
classRoom cr;
dormitory dor;
dormitory dor2;
<bean id="dormitory" class="func.dormitory" p:no="北1A101"/>
<bean id="dor" class="func.dormitory" p:no="南A101"/>
<bean id="classRoom" class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student" autowire="byType"/>
同样会报出错误信息:Could not autowire. There is more than one bean of ‘dormitory’ type. Beans: dor,dormitory.
3.4.4使用注解@Autowired
使用注解之前需要在xml中添加约束和支持
xmlns:context="http://www.springframework.org/schema/context"
以及
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<context:annotation-config/>
xml配置预览:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
Autowired运行流程
在使用@Autowired时,首先在容器中查询对应类型的bean
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据
如果查询的结果不止一个,那么@Autowired会根据名称来查找。
Autowired使用方法
xml代码:
<bean class="func.dormitory" p:no="北1A101"/>
<bean class="func.classRoom" p:num="明德101"/>
<bean id="student" class="func.student"/>
将Autowired写在对应的成员名上面即可,由于要注入给student,故只修改student.class
public class student {
@Autowired
classRoom cr;
@Autowired
dormitory dor;
//--------------以下内容不变--------------
public classRoom getCr() {
return cr;
}
public void setCr(classRoom cr) {
this.cr = cr;
}
public dormitory getDor() {
return dor;
}
public void setDor(dormitory dor) {
this.dor = dor;
}
@Override
public String toString() {
return "student{" +
"cr=" + cr.toString() +
", dor=" + dor.toString() +
'}';
}
}
运行结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
若查询的bean有多个,则按成员名匹配,匹配规则与byName相同
<bean id="dor" class="func.dormitory" p:no="北1A101"/>
<bean id="cr" class="func.classRoom" p:num="明德101"/>
<bean id="dormitory" class="func.dormitory" p:no="北1A666"/>
<bean id="classRoom" class="func.classRoom" p:num="明德666"/>
<bean id="student" class="func.student"/>
@Autowired
classRoom cr;
@Autowired
dormitory dor;
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
若使用Autowired,则可以在设计类的时候不再设计成员的set方法,前提是属性在容器中存在且符合byName规则
public class student {
@Autowired
classRoom cr;
@Autowired
dormitory dor;
@Override
public String toString() {
return "student{" +
"cr=" + cr.toString() +
", dor=" + dor.toString() +
'}';
}
}
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
拓展
有的时候我们不一定会在配置文件中写入bean,但是在成员设计时却添加了Autowired注解,那么此时就会产生无bean的异常抛出。
解决方案:
@Autowired(required = false)
classRoom cr;
@Autowired(required = false)
dormitory dor;
以下为Autowired的源代码片段
public @interface Autowired {
boolean required() default true;
}
3.4.5使用注解@Qualifier
若在xml配置中,写入了不唯一且相同类型的bean时,可以使用Autowired的自动匹配,得到id相同的bean并注入。相当于A类中的成员a需要被注入对象时,会自动寻找与其相符合的对象B并注入给a。倘若使用Qualifier,则可以使a主动寻找合适的对象并将对象注入给a。
举例
<bean id="dor" class="func.dormitory" p:no="北1A101"/>
<bean id="cr" class="func.classRoom" p:num="明德101"/>
<bean id="dormitory" class="func.dormitory" p:no="北1A666"/>
<bean id="classRoom" class="func.classRoom" p:num="明德666"/>
<bean id="student" class="func.student"/>
@Autowired
private classRoom cr;
@Autowired
private dormitory dor;
//存在相同类型的bean,根据成员名注入dor和cr两个bean
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
此时xml文件不变,修改class文件
@Autowired
@Qualifier(value = "classRoom")
private classRoom cr;
@Autowired
@Qualifier(value = "dormitory")
private dormitory dor;
输出结果:student{cr=classRoom{num=‘明德666’}, dor=dormitory{No=‘北1A666’}}
总结
如果Autowired的自动装配环境复杂,无法通过一个注解@Autowired完成时,可以额外使用注解@Qualifier(value = “xxx”)指定唯一bean对象注入。
3.4.6使用注解@Resource
前文提到的注解@Autowired和@Qualifier均为spring中的注解,本分支的内容为注解@Resource,该注解为Java原生注解。
与@Autowired相似,使用@Resource是在bean中先按照id进行对象匹配和注入,若没有可以匹配的id则按照类型进行匹配注入。
<!--按id匹配-->
<bean id="dor" class="func.dormitory" p:no="北1A101"/>
<bean id="cr" class="func.classRoom" p:num="明德101"/>
<bean id="dormitory" class="func.dormitory" p:no="北1A666"/>
<bean id="classRoom" class="func.classRoom" p:num="明德666"/>
<bean id="student" class="func.student"/>
@Resource
private classRoom cr;
@Resource
private dormitory dor;
输出结果:student{cr=classRoom{num=‘明德101’}, dor=dormitory{No=‘北1A101’}}
<!--按类型匹配-->
<bean id="dormitory" class="func.dormitory" p:no="北1A666"/>
<bean id="classRoom" class="func.classRoom" p:num="明德666"/>
<bean id="student" class="func.student"/>
输出结果:student{cr=classRoom{num=‘明德666’}, dor=dormitory{No=‘北1A666’}}
<!--按类型匹配-->
<bean class="func.dormitory" p:no="北1A666"/>
<bean class="func.classRoom" p:num="明德666"/>
<bean id="student" class="func.student"/>
输出结果:student{cr=classRoom{num=‘明德666’}, dor=dormitory{No=‘北1A666’}}
与@Qualifier相似,@Resource也可以跟一个限制条件获取唯一对象
<bean id="dor" class="func.dormitory" p:no="北1A101"/>
<bean id="cr" class="func.classRoom" p:num="明德101"/>
<bean id="dormitory" class="func.dormitory" p:no="北1A666"/>
<bean id="classRoom" class="func.classRoom" p:num="明德666"/>
<bean id="student" class="func.student"/>
@Resource(name = "classRoom")
private classRoom cr;
@Resource(name = "dormitory")
private dormitory dor;
输出结果:student{cr=classRoom{num=‘明德666’}, dor=dormitory{No=‘北1A666’}}
3.4.7、Resource与Autowired的异同
- @Autowired与@Resource都可以用来装配bean,都可以写在字段或set方法上
- @Autowired默认按类型装配,默认情况下必须要求依赖对象存在,如果要允许null值,可以设置它的required属性为false。如果想使用名称装配可以结合@Qualifier注解进行使用。
- @Resource,默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
4、注解开发
【自Spring4开始,若要使用注解开发,需要导入AOP包】
4.1、组件(@Component)
先利用前面的知识写一个学生类并输出
//学生类
public class student {
private String name;
public student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
'}';
}
}
//Main入口
public class Main {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml");
student stu = Context.getBean("student",student.class);
System.out.println(stu.toString());
}
}
<!--bean编写-->
<bean id="student" class="func.student">
<constructor-arg name="name" value="Anne"/>
</bean>
输出结果:student{name=‘Anne’}
在使用注解开发之前需要在xml文件中写入
<context:component-scan base-package="xxx"/> <!--xxx指需要扫描组件的包名-->
注解@Component写在类的上方,表示该类已经交付于Spring进行管理。属性注入使用@Value(“xxx”)即可。在使用注解开发时,不需要在xml文件中写bean标签,运行到component-scan时会将后面包名中的组件注册到容器中,默认的id是类的名称的小写,直接getBean即可取出。
@Component
public class student {
@Value("Anne") //该代码也可以写到Set方法上面
private String name;
public String getName() {
return name;
}
//可以在这里写@Value
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
'}';
}
}
<context:component-scan base-package="func"/>
public class Main {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml");
student stu = Context.getBean("student",student.class);
System.out.println(stu.toString());
}
}
输出结果:student{name=‘Anne’}
4.2、衍生注解
开发的三层架构:DAO、Service、Controller
- DAO层:@Repository
- Service层:@Service
- Controller层:@Controller
包括@Component共4个注解,功能均一样,只是在不同的地方使用不同的注解。本质都是把某个类注册到Spring容器中。
4.3、自动装配
内容重复,请移步参考
- 3.4.4、@Autowired
- 3.4.5、@Qualifier
- 3.4.6、@Resource
4.4、作用域
xml文件中的作用域使用可以移步 3.2、 scope 查看
例如设置一个bean为多实例对象,在xml文件中可以写成
<bean id="student" class="func.student" scope="prototype"/>
脱离xml文件,若直接在Java代码中可以写成
@Component
@Scope("prototype")
public class student {
/*代码省略*/
}
4.5、总结
5、使用JavaConfig代替xml实现配置
在编码过程中,若要实现Spring容器功能,未必使用xml配置文件,直接编写Java代码也可以实现效果
- 配置文件入口
在之前,我们使用ApplicationContext.xml作为配置文件,在程序入口写入:
ApplicationContext Context = new ClassPathXmlApplicationContext("spring-config.xml");
用来使程序解析xml。
现在我们可以直接创建一个类,命名为config.class,在程序入口写入:
ApplicationContext context = new AnnotationConfigApplicationContext(config.class);
- 配置文件编写
若要使某个类称为配置文件,需要在该类上面写上**@Configuration**
若要扫描某个包中的组件,只需要写上**@ComponentScan(“xxx”)**
在配置文件中编写Bean,只需在类方法的上面写上**@Bean**
若要实现xml的多人开发,即第三方xml文件导入,与其相似的只需在类方法上面写上**@Import(xxx.class)**
其中,方法的名字相当于bean标签中的id;方法的返回值相当于bean标签中的class;方法的返回结果就是要注入到bean的对象。
//config.class
@Configuration
@ComponentScan("func")
@Import(config2.class)
public class config {
@Bean
public student getStudent(){
return new student();
}
}
//Main.class
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(config.class);
student stu = context.getBean("getStudent",student.class);
System.out.println(stu.toString());
}
}
//student.class
@Configuration
public class config {
@Bean
public student getStudent(){
return new student();
}
}
输出结果:student{name=‘John’}
参考文献
-
Java中DAO层、Service层和Controller层的区别 https://blog.youkuaiyun.com/qq_22771739/article/details/82344336
-
Sping-xml文件中Bean标签(属性和作用域) https://blog.youkuaiyun.com/tommy5553/article/details/82184360
-
【狂神说Java】Spring5最新完整教程IDEA版通俗易懂 https://www.bilibili.com/video/BV1WE411d7Dv?p=8
-
Spring 自动装配及其注解
https://blog.youkuaiyun.com/zcywell/article/details/88397158 -
@Autowired用法详解
https://www.cnblogs.com/fnlingnzb-learner/p/9723834.html -
@Autowired注解与@Resource注解的区别
https://www.jianshu.com/p/a74707581ffa