业无高卑志当坚,男儿有求安得闲?这篇文章主要讲述推荐学java——Spring第一课相关的知识,希望能为你提供帮助。
Spring简介
官网地址:https://spring.io/
框架先来一张官网的全局框架图:
文章图片
这里的
Spring Boot
和 Spring Cloud
相信各位都听过,后面我们也会学习,今天的主角是 Spring Framework
也就是我们平时所说的 Spring框架
,后面本文都称之为Spring
.Spring Framework
官网地址:https://spring.io/projects/spring-framework
Spring Framework
API文档:https://docs.spring.io/spring-framework/docs/current/javadoc-api/
Spring Framework
的架构图:文章图片
核心简介
Spring
将它理解为一个容器,这个容器的功能是可以创建java对象、给java对象赋值、控制对象的生命周期。这个过程就叫做反转。而创建java对象、给java对象赋值、控制对象的生命周期这个过程叫做控制。这就是Spring
的第一个核心内容 IoC
(Inversion of Control的缩写,意为 控制反转),简言之,就是把对象的控制者交给了Spring
。IoC
是一个理论思想,自己能理解就可以,而它的技术实现方案叫:DI
(Dependency injection 的缩写,意为 依赖注入).另外一个核心内容是:AOP(面向切面编程)。我们在学习过程中始终把握这两个核心,不要偏移重点。
第一个Spring项目
创建流程1、我这里为了和前面学习的
MyBatis
作区分,先新建EmptyProject
然后添加名为spring-01
的 Module
,这样每次新建module即可。2、添加Spring依赖(在pom.xml中)
<
!-- 添加Spring依赖-->
<
dependency>
<
groupId>
org.springframework<
/groupId>
<
artifactId>
spring-context<
/artifactId>
<
version>
5.3.14<
/version>
<
/dependency>
3、定义接口和实现类
接口:
public interface SomeService void doSome();
实现类:
public class SomeServiceImpl implements SomeService @Override
public void doSome()
System.out.println("实现接口 SomeService");
4、在
resources
目录下创建Spring配置文件beans.xml
,作用是声明java对象,通过&
lt;
bean&
gt;
标签,把对象交给Spring创建和管理。<
?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">
<
!--声明java Bean对象
id:必须唯一,如果不自定义,Spring会自动指定
class:接口实现类(可以是实体类本身,也可以是实体类的子类,还可以是接口的实现类)
-->
<
bean id="someService" class="com.javafirst.service.impl.SomeServiceImpl"/>
<
/beans>
5、通过容器使用对象
String config = "beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
// 根据id 获取对象
SomeService someService = (SomeService) applicationContext.getBean("someService");
someService.doSome();
Spring是通过对象的无参构造方法来创建对象的,Spring是一次全部映射
beans.xml
中配置的对象。Spring这种管理对象的方式本质是反射技术。拿到容器之后,我们就可以通过容器提供的方法来操作Java对象了。容器可以映射的对象前面我们自定义了一个实现接口的Java类,Spring是可以拿到该类并调用方法的,那么系统类呢?Spring是否也可以拿到,我们在
beans.xml
中添加下面这样一个系统类,来测试结果。<
bean id="javaDate" class="java.util.Date"/>
测试代码和上面的类似:
// 系统类
Date date = (Date) applicationContext.getBean("javaDate");
System.out.println(date);
结果当然是肯定的。那么我们自定义一个类,但没有实现接口,是否可以呢?答案也是可以的。各位感兴趣可以自己尝试,这里不做演示了。
Spring给属性赋值
DI分类:
- set注入,也叫设值注入(推荐使用)
- 构造方法注入
语法很简单:
<
!--set注入方式
适合基本数据类型和String;
这种方式执行的是java实体类中的setXXX()方法;
-->
<
bean id="myStudent" class="com.javafirst.ba01.Student">
<
property name="name" value="https://www.songbingjia.com/android/张三丰"/>
<
property name="age" value="https://www.songbingjia.com/android/208"/>
<
/bean>
学生类Student中只有两个属性,分别是
name
和age
. 测试代码如下:@Test
public void testSet_student()
String config = "ba01/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
Student student = (Student) context.getBean("myStudent");
System.out.println(student);
结果各位自行测试,能正确输出我们在xml中的赋值即可,当然这是我们自定义的java对象,如果使用系统的类呢?大家可以自行尝试我们前面提到的
java.util.Date
类。对象类型set注入
我们再定义一个Java对象
School
,有属性name
和address
,然后在Student类中增加该对象。看具体代码:<
bean id="myStudent" class="com.javafirst.ba01.Student">
<
property name="name" value="https://www.songbingjia.com/android/张三丰"/>
<
property name="age" value="https://www.songbingjia.com/android/208"/>
<
!--引用类型-->
<
property name="school" ref="mySchool"/>
<
/bean>
在原来的基础上增加了引用类型对象的赋值,与简单类型不同的是,这里使用了
ref
这个标签属性,很明显,这是引用的意思,也就是引用了下面这段代码:<
bean id="mySchool" class="com.javafirst.ba02.School">
<
property name="name" value="https://www.songbingjia.com/android/北京大学"/>
<
property name="address" value="https://www.songbingjia.com/android/北京市海淀区颐和园路5号"/>
<
/bean>
这个时候测试结果接口看到我们的赋值,测试代码相同的,就不贴了。
构造方法注入
我们给Student添加构造方法如下:
public Student(String name, int age, School school)
this.name = name;
this.age = age;
this.school = school;
接着是
applicationContext.xml
中的代码:<
!--构造注入-->
<
bean id="myStudent" class="com.javafirst.ba03.Student">
<
!--使用name属性-->
<
constructor-arg name="age" value="https://www.songbingjia.com/android/55"/>
<
constructor-arg name="name" value="https://www.songbingjia.com/android/东邪黄药师"/>
<
constructor-arg name="school" ref="mySchool"/>
<
!--使用index属性-->
<
!--<
constructor-arg index="1" value="https://www.songbingjia.com/android/75"/>
-->
<
!--<
constructor-arg index="0" value="https://www.songbingjia.com/android/重阳真人王重阳"/>
-->
<
!--<
constructor-arg index="2" ref="mySchool"/>
-->
<
/bean>
<
bean id="mySchool" class="com.javafirst.ba03.School">
<
property name="name" value="https://www.songbingjia.com/android/北京理工大学"/>
<
property name="address" value="https://www.songbingjia.com/android/北京市海淀区中关村南大街5号"/>
<
/bean>
构造方法的形参可以自定义,没有必要和类属性保持一致,也可以通过构造方法的形参的索引来赋值(如果不写
name
或者index
,只给value赋值也是可以的,默认就是按照索引来的)。测试代码和前面一样的,能输出我们赋值的结果即为正确。引用类型自动注入
byName
按名称注入。Java对象中引用类型的属性名和Spring容器中bean的id名称一样,且数据类型也相同,这些bean能够赋值给引用类型。byType
按类型注入。java对象中引用类型的数据类型和Spring容器中bean的class值是同源关系,这些bean能够赋值给引用类型。
<
bean id="myStudent" class="com.javafirst.ba04.Student" autowire="byName">
<
property name="name" value="https://www.songbingjia.com/android/老顽童"/>
<
property name="age" value="https://www.songbingjia.com/android/112"/>
<
/bean>
<
bean id="school" class="com.javafirst.ba04.School">
<
property name="name" value="https://www.songbingjia.com/android/北京交通大学"/>
<
property name="address" value="https://www.songbingjia.com/android/北京市海淀区高梁桥斜街44号"/>
<
/bean>
两个注意点:增加了
autowire
属性,按名称注入就是byName
;引用对象的注册&
lt;
bean&
gt;
的id需要和Java实体类中引用对象的名称保持一致,也就是这里的 school
.看第二种方式前,我们先搞清楚什么是同源关系?
- Java实体类中引用类型的数据类型和bean的class值是一样的
- Java实体类中引用类型的数据类型和bean的class值是父子类关系
- Java实体类中引用类型的数据类型和bean的class值是接口和实现类的关系
<
bean id="myStudent" class="com.javafirst.ba05.Student" autowire="byType">
<
property name="name" value="https://www.songbingjia.com/android/紫阳真人"/>
<
property name="age" value="https://www.songbingjia.com/android/102"/>
<
/bean>
<
bean id="my_school" class="com.javafirst.ba05.School">
<
property name="name" value="https://www.songbingjia.com/android/北京航空航天大学"/>
<
property name="address" value="https://www.songbingjia.com/android/北京市海淀区学院路37号"/>
<
/bean>
区别就是这里的
autowire
属性值需要是 byType
;第二点就是引用类型的 class (也就是这里id为my_school
的 class值)需要和Java对象中声明的引用类型是同一个。测试代码都是相同的,各位自行验证哈。
import的使用
前面我们都是例子比较简单的情况下,写一个 Spring 配置文件就完成了所有工作,但实际开发中远不止这点业务,可能需要我们将业务按照模块划分,这就需要我们提供独立的配置文件,在现有基础上,我们将
Student
和 School
进行独立的配置,然后保留主配置文件,看流程:- 新建
spring-student.xml
和spring-school.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"> < bean id="myStudent" class="com.javafirst.ba06.Student" autowire="byType"> < property name="name" value="https://www.songbingjia.com/android/元始天尊"/> < property name="age" value="https://www.songbingjia.com/android/1002"/> < /bean> < /beans>
< ?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"> < bean id="my_school" class="com.javafirst.ba06.School"> < property name="name" value="https://www.songbingjia.com/android/凌霄宫"/> < property name="address" value="https://www.songbingjia.com/android/三十三重天"/> < /bean> < /beans>
- 修改原来的配置文件
applicationContext.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"> < !-- 这里因为是在同一个包下,完整的路径应该是:classpath:ba06/spring-school.xml--> < import resource="spring-student.xml"/> < import resource="classpath:ba06/spring-school.xml"/> < /beans>
这里其实还可以使用通配符,个人建议初学容易犯小错误,还是暂时都用全路径,这样比较清晰明了,错误容易排查,也容易理解。
- 测试代码如下:
/** * 测试 import 引入bean配置 */ @Test public void testImport() String config = "ba06/applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(config); com.javafirst.ba06.Student student = (com.javafirst.ba06.Student) context.getBean("myStudent"); System.out.println(student);
测试代码其实没有变化,结果各位自行验证。
@Component
注解Java对象类前,创建java对象,给java对象起id,不写其 value 值的话,默认是类名小写。@Repository
放在 dao 接口的实现类上面,创建java对象,是持久层的,可以访问数据库。@Service
放在业务层接口的实现类上面,创建业务层java对象,业务层对象有事务功能。@Controller
放在控制器上面,创建控制器java对象,是视图层,能把请求结果显示给用户。@Value
简单类型属性赋值。@Autowired
引用类型属性赋值。
@Component(value = "https://www.songbingjia.com/android/my_student") // 不指定value,默认是类名小写
public class Student private String name;
private int age;
@Override
public String toString()
return "Student信息:" +
"name=" + name + \\ +
", age=" + age +
;
和之前的区别少了属性对应的
setXXX()
和 getXXX()
方法。applicationConext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<
!--注解扫描器
base-package 添加了注解的Java对象,扫描器会自动扫描该路径下的类以及子包中的类
-->
<
context:component-scan base-package="com.javafirst.ba01"/>
<
!--扫描多个包 中间的分隔符可以是;
也可以是,还可以是空格-->
<
!--<
context:component-scan base-package="com.javafirst.ba01;
com.javafirst.ba02"/>
-->
<
!--扫描多个包 还可以直接指定父包(因为扫描器本身会对子文件夹进行扫描)-->
<
!--<
context:component-scan base-package="com.javafirst"/>
-->
<
/beans>
这里和之前的变化是不再需要手动添加
&
lt;
bean&
gt;
标签了,而是只需要注解扫描器即可,注解扫描器管理Java对象的方式有以上三种。推荐大家选择第二种。测试代码和之前没什么区别:
@Test
public void testComponent()
String config = "ba01/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
// 注意这里的 my_student 就是注解 @Component 的 value 值
Student student = (Student) context.getBean("my_student");
System.out.println(student);
结果大家自行验证即可。
@Value使用针对简单类型的属性赋值使用。
我们在上面的基础上,给 Student 对象的两个属性赋值,代码如下:
@Value(value = "https://www.songbingjia.com/android/司马如意")
private String name;
@Value("26") // 当注解只使用一个属性的时候 注解属性名可省略
private int age;
这种在属性上面加注解的方式推荐使用,此外,还可以在对应的
setXXX()
方法上加注解,比如下面这样:@Value("诸葛如意")
public void setName(String name)
this.name = name;
如果两个同时加上,则会取后者的值。结果大家自行测试即可。
当然
@Value
注解还可以给属性赋值来自于外部文件中的值,我们在 resources
目录下新建 myconfig.properties
,该文件中的内容如下:my_name=南极贤文
my_age=55
对应的,加载该文件的操作需要我们再来学习一个标签,在
applicationContext.xml
中添加如下代码:<
!--读取属性配置文件xxx.properties-->
<
context:property-placeholder location="classpath:myconfig.properties" file-encoding="utf-8"/>
这个其实和前面学习 MyBatis 的时候类似,我们也同样配置过加载属性文件的标签。接下来看java对象代码的变化:
/**
* 给属性赋值 来自于外部文件中的值
*/
@Value("$my_name")
private String name;
@Value("$my_age")
private int age;
测试代码没有变化,各位自行尝试哈。
@Autowired使用引用类型属性赋值
我们在现有代码基础上增加一个
School
对象,并在原来的 Student
中声明 School
属性,对于 School
本身属性的赋值,就是简单类型赋值操作,其实各位已经学会了,主要看下 Student
中如何给引用类型添加注解:@Component(value = "https://www.songbingjia.com/android/my_student")
public class Student /**
* 给属性加注解 推荐使用这种方式
*/
@Value(value = "https://www.songbingjia.com/android/上官婉儿")
private String name;
@Value("22")
private int age;
/**
* @Autowired 支持 byName 和 byType(默认)
* 注解位置可在属性上面,也可在属性的 set 方法上面,推荐使用前者
*/
@Autowired
private School school;
/**
* @Autowired byName 方式
* 这种方式要结合 @Qualifier(value = "https://www.songbingjia.com/android/对象的@Component注解的value值") 注解一起使用,但在数写上无先后顺序
* 这种方式如果把 @Qualifier 注解的 value 值写错了,程序会终止(因为 @Autowired 注解的 required 值默认是true)
*/
// @Autowired
// @Qualifier(value = "https://www.songbingjia.com/android/my_school")
// private School school;
@Override
public String toString()
return "Student信息:" +
"name=" + name + \\ +
", age=" + age +
", school=" + school +
;
对应的
School
中代码如下:@Component("my_school")
public class School @Value("乾坤学院")
private String name;
@Value("长安城光华街西北角")
private String address;
@Override
public String toString()
return "School信息:" +
"name=" + name + \\ +
", address=" + address + \\ +
;
结果各位自行测试哈,很简单。
@Resource使用jdk1.8本身自带的,如果使用高于1.8,那么需要手动添加依赖
<
dependency>
<
groupId>
javax.resource<
/groupId>
<
artifactId>
javax.resource-api<
/artifactId>
<
version>
1.7.1<
/version>
<
/dependency>
该注解同样支持 byName(默认) 和
byType
两种赋值方式, 不同于 @Autowired
的是 该注解如果 byName
赋值失败,会自动使用 byType
赋值.我们看下核心的注解代码:
/**
* @Resource 注解是jdk1.8本身自带的,如果使用高于1.8,那么需要手动添加依赖
* <
p>
* 该注解同样支持 byName(默认) byType两种赋值方式,
* 不同于 @Autowired 的是 该注解如果 byName 赋值失败,会自动使用 byType 赋值.
<
p>
* 如果只想使用 byName 方式,
* 那么只需要给 @Resource 注解的 name属性值指定为对象类型的 @Component 注解的value值(此时value值没有必要和声明的引用对象名相同)
*/
@Resource
private School school;
【推荐学java——Spring第一课】我们知道,
byName
方式是通过 id
匹配的,所以我们 School
类的 @Conponent
注解的 value 值应改为 school
. 结果各位自己测试,相信能看到这里,说明你已经很熟练了,哈哈~总结
- 文本开头也提到了,
Spring
中最重要的两个知识点IoC
和AOP
,本节主要学习了前者 - 通过学习
MyBatis
,现在学Spring
就相对轻松一些了,因为很多内容似乎是相同的,其实这个感觉也可以用于学习不同编程语言之间 - 注解开发方式现在流行起来了,所以相对要重点掌握,但也要看自己所处的“环境”。
推荐阅读
- RENIX发送固定个数报文——网络测试仪实操
- Spring认证中国教育管理中心-Apache Solr 的 Spring 数据教程一
- #yyds干货盘点# springboot配置@Async异步任务的线程池
- 文件解压缩及文件打包
- 实验(LAMP安装论坛)
- k8s故障排查指南
- 微灯手握寸笔,细谈内存管理
- 源码编译构建LAMP
- C# 设置或验证 PDF中的文本域格式