本文将通过对 Spring 基础知识的总结,加深对 Spring 的理解与应用。
一、Spring 概述 Spring 的优势 方便解耦,简化开发 AOP编程的支持 声明式事务的支持 方便程序的测试 方便集成各种优秀框架 降低 JavaEE API 的使用难度 Java 源码是经典学习范例
Spring 的体系结构 核心容器
Sping 的开发包 官网https://spring.io/ 下载地址(http://repo.springsource.org/libs-release-local/org/springframework/spring )
二、IOC 的概念和作用 这部分内容可查看之前的文章IOC基本原理及相关实现 对IOC有单独的阐述
三、使用 spring 的 IOC 解决程序耦合 创建持久层接口和实现类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package gavino.spring.dao; /** * @author Yan weigang */ public interface IDemoDao { void print(); } package gavino.spring.dao.impl; import gavino.spring.dao.IDemoDao; /** * @author Yan weigang */ public class DemoDaoImpl implements IDemoDao { public void print() { System.out.println("使用 spring 的 IOC 解决程序耦合"); } }
创建业务层接口和实现类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package gavino.spring.service; /** * @author Yan weigang */ public interface IDemoService { void print(); } package gavino.spring.service.impl; import gavino.spring.dao.IDemoDao; import gavino.spring.dao.impl.DemoDaoImpl; import gavino.spring.service.IDemoService; /** * @author Yan weigang */ public class DemoServiceImpl implements IDemoService { IDemoDao dao = new DemoDaoImpl();//将通过ioc解决这种依赖关系 public void print() { dao.print(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 package gavino.spring.ui; import gavino.spring.service.impl.DemoServiceImpl; /** * @author Yan weigang */ public class View { public static void main(String[] args) { DemoServiceImpl service = new DemoServiceImpl();//将通过ioc解决这种依赖关系 service.print(); } }
基于 XML 的配置 在pom.xml 中导入相关依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>lut.gavino</groupId> <artifactId>day01_spring04_ioc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.9.RELEASE</version> </dependency> </dependencies> </project>
在resources下创建一个任意名称的 xml 文件(不能是中文,我一般命名为bean.xml) 给配置文件导入约束
在之前的 Spring 开发包中,解压以 dist 结尾的压缩包以后,进入该包中找到 docs 文件夹并进入,然后选择 spring-framework-reference 文件进入,在该目录下找到 core.html 文件用浏览器打开,找到如下约束:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions go here --> </beans>
在 bean.xml 文件中导入约束以后 在配置文件中配置 service 和 dao,让 Spring 管理资源
1 2 3 4 5 6 7 8 9 10 11 <?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"> <!-- 配置 dao --> <bean id="demoDao" class="gavino.spring.dao.impl.DemoDaoImpl"></bean> <!-- 配置 service --> <bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl"></bean> </beans>
修改前面的测试,测试配置是否成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package gavino.spring.ui; import gavino.spring.dao.IDemoDao; import gavino.spring.service.IDemoService; import gavino.spring.service.impl.DemoServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author Yan weigang */ public class View { public static void main(String[] args) { // IDemoService service = new DemoServiceImpl();//将通过ioc解决这种依赖关系 // service.print(); //1.使用 ApplicationContext 接口,就是在获取 spring 容器 ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //2.根据 bean 的 id 获取对象 IDemoDao dao = (IDemoDao) context.getBean("demoDao"); IDemoDao demoDao = context.getBean("demoDao", IDemoDao.class); System.out.println(dao); System.out.println(demoDao); IDemoService service = context.getBean("demoService",IDemoService.class); System.out.println(service); service.print(); } } ------------------------------------------------------------------------------------------ 运行结果: gavino.spring.dao.impl.DemoDaoImpl@5e3a8624 gavino.spring.dao.impl.DemoDaoImpl@5e3a8624 gavino.spring.service.impl.DemoServiceImpl@5c3bd550 使用 spring 的 IOC 解决程序耦合
Spring 基于 XML 的 IOC 细节
BeanFactory 是 Spring 的顶层接口
BeanFactory 和 ApplicationContext 的区别
BeanFactory 才是 Spring 容器中的顶层接口。 ApplicationContext 是它的子接口。
BeanFactory 和 ApplicationContext 的区别:
创建对象的时间点不一样。
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:什么使用什么时候创建对象。
ApplicationContext 接口的实现类
ClassPathXmlApplicationContext: 它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext: 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
IOC 中 bean 标签和管理对象细节
bean 标签
作用
用于配置对象让 spring 来创建的。 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
属性
id:给对象在容器中提供一个唯一标识。用于获取对象。 class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。 scope:指定对象的作用范围。 * singleton :默认值,单例的. * prototype :多例的. * request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中. * session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中. * global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么 globalSession 相当于 session. init-method:指定类中的初始化方法名称。 destroy-method:指定类中销毁方法名称。
bean 的作用范围和生命周期
单例对象:scope=”singleton”
一个应用只有一个对象的实例。它的作用范围就是整个引用。 生命周期: 对象出生:当应用加载,创建容器时,对象就被创建了。 对象活着:只要容器在,对象一直活着。 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
多例对象:scope=”prototype”
每次访问对象时,都会重新创建对象实例。 生命周期: 对象出生:当使用对象时,创建新的对象实例。 对象活着:只要对象在使用中,就一直活着。 对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。
实例化 Bean 的三种方式
spring 的依赖注入 依赖注入的概念
spring中的依赖注入 #依赖注入: Dependency Injection #IOC的作用: 降低程序间的耦合(依赖关系) #依赖关系的管理: 以后都交给spring来维护 #在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明 #依赖关系的维护: 就称之为依赖注入。 #依赖注入: *能注入的数据:有三类 基本类型和String 其他bean类型(在配置文件中或者注解配置过的bean) 复杂类型/集合类型 *注入的方式:有三种 第一种:使用构造函数提供 第二种:使用set方法提供 第三种:使用注解提供
构造函数注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private String name;//String类型 private Integer id;//基本数据类型 private Date time;//其它Bean类型 //提供构造函数 public DemoServiceImpl(String name, Integer id, Date time) { this.name = name; this.id = id; this.time = time; } //测试方法 @Test public void print() { System.out.println("----------测试构造函数注入 基本数据类型和String-----------"); System.out.println("id:"+id+" name:"+name+" "); System.out.println("----------测试构造函数注入 其它Bean类型-----------"); System.out.println("time:"+time); }
bean.xml文件中的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置 dao --> <bean id="demoDao" class="gavino.spring.dao.impl.DemoDaoImpl"></bean> <!-- 配置 service --> <bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl"> <constructor-arg name="id" value="101"></constructor-arg> <constructor-arg name="name" value="依赖注入"></constructor-arg> <constructor-arg name="time" ref="date"></constructor-arg> </bean> <bean id="date" class="java.util.Date"></bean> </beans>
在测试类中测试是否注入成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /** * @author Yan weigang */ public class DITest { @Test public void testConstructor(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); IDemoService service = context.getBean("demoService", IDemoService.class); service.print(); } } ########################## 测试输出如下 #################################### ----------测试构造函数注入 基本数据类型和String----------- id:101 name:依赖注入 ----------测试构造函数注入 其它Bean类型----------- time:Mon Aug 26 19:46:18 CST 2019
set 方法注入 注掉构造方法,提供相应的set方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /** * @author Yan weigang */ public class DemoServiceImpl implements IDemoService { private String name; private Integer id; private Date time; // IDemoDao dao = new DemoDaoImpl();//将通过ioc解决这种依赖关系 // private IDemoDao dao; // public DemoServiceImpl(String name, Integer id, Date time) { // this.name = name; // this.id = id; // this.time = time; // } public void setName(String name) { this.name = name; } public void setId(Integer id) { this.id = id; } public void setTime(Date time) { this.time = time; } @Test public void print() { System.out.println("----------测试构造函数注入 基本数据类型和String-----------"); System.out.println("id:"+id+" name:"+name+" "); System.out.println("----------测试构造函数注入 其它Bean类型-----------"); System.out.println("time:"+time); } }
修改bean.xml配置文件
1 2 3 4 5 6 <bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl"> <property name="id" value="020"></property> <property name="name" value="set方法注入"></property> <property name="time" ref="date"></property> </bean> <bean id="date" class="java.util.Date"></bean>
测试类中测试是否注入成功
1 2 3 4 5 6 7 8 9 10 11 @Test public void testConstructor(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); IDemoService service = context.getBean("demoService", IDemoService.class); service.print(); } ============================= 测试输出如下 ==================================== ----------测试构造函数注入 基本数据类型和String----------- id:20 name:set方法注入 ----------测试构造函数注入 其它Bean类型----------- time:Mon Aug 26 20:01:38 CST 2019
使用 p 名称空间注入数据(本质还是调用 set 方法) 使用 p 名称空间注入,使用 p:propertyName 来注入数据,它的本质仍然是调用类中的set 方法实现注入功能。 依然提供相应的set方法,代码如在set 方法中注入一样 修改bean.xml配置文件:
1 xmlns:p="http://www.springframework.org/schema/p"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" 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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- p名称空间注入 --> <bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl" p:id="303" p:name="p名称空间注入" p:time-ref="date"/> <bean id="date" class="java.util.Date"></bean> </beans>
测试类中测试是否注入成功
1 2 3 4 5 ++++++++++++++++++++++++++ 测试输出如下 +++++++++++++++++++++++++++++++++++++ ----------测试构造函数注入 基本数据类型和String----------- id:303 name:p名称空间注入 ----------测试构造函数注入 其它Bean类型----------- time:Mon Aug 26 20:10:11 CST 2019
注入集合属性 注入集合属性,用的也是set方法注入的方式,只不过变量的数据类型都是集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package gavino.spring.service.impl; import gavino.spring.dao.IDemoDao; import gavino.spring.dao.impl.DemoDaoImpl; import gavino.spring.service.IDemoService; import org.junit.Test; import java.util.*; /** * @author Yan weigang */ public class DemoServiceImpl implements IDemoService { private String[] str; private List<String> list; private Set<String> set; private Map<String,String> map; private Properties props; //提供set方法 public void setStr(String[] str) { this.str = str; } public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setProps(Properties props) { this.props = props; } @Test public void print() { System.out.println(Arrays.toString(str)); System.out.println(list); System.out.println(set); System.out.println(map); System.out.println(props); } }
配置bean.xml配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" 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 http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl"> <property name="str"> <array> <value>数组元素1</value> <value>数组元素2</value> <value>数组元素3</value> </array> </property> <property name="list" > <list> <value>列表A</value> <value>列表B</value> <value>列表C</value> </list> </property> <property name="set"> <set> <value>set 1</value> <value>set 2</value> <value>set 3</value> </set> </property> <property name="map"> <map> <entry key="testA" value="aaa"></entry> <entry key="testB"> <value>bbb</value> </entry> </map> </property> <property name="props"> <props> <prop key="username">root</prop> <prop key="password">root</prop> </props> </property> </bean> </beans>
测试类中测试是否注入成功
1 2 3 4 5 6 ============================ 测试输出如下 =================================== [数组元素1, 数组元素2, 数组元素3] [列表A, 列表B, 列表C] [set 1, set 2, set 3] {testA=aaa, testB=bbb} {password=root, username=root}
四、基于注解的 IOC配置 环境搭建 明确: 注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
(一)导入相关依赖 (二)使用@Component 注解配置管理的资源 (三)创建 spring 的 xml 配置文件并开启对注解的支持注意: 基于注解整合时,导入约束时需要多导入一个 context 名称空间下的约束。
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 告知 spring 创建容器时要扫描的包 --> <context:component-scan base-package="gavino.spring"></context:component-scan> </beans>
常用注解
用于创建对象的( 相当于:<bean id=”” class=””> )
@Component
作用: 把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性: value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
@Controller 一般用于表现层的注解。
@Service 一般用于业务层的注解。
@Repository 一般用于持久层的注解。
注意 @Controller、@Service、@Repository 三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。 它们只不过是提供了更加明确的语义化。如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值时可以不写。
用于注入数据的 ( 相当于:<property name=”” ref=””> <property name=”” value=””> )
@Autowired
作用: 自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。
@Qualifier
作用: 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。
属性: value:指定 bean 的 id。
@Resource
作用: 直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
属性: name:指定 bean 的 id。
@Value
作用: 注入基本数据类型和 String 类型数据的
属性: value:用于指定值
用于改变作用范围
@Scope( 相当于:<bean id=”” class=”” scope=””> )
作用: 指定 bean 的作用范围。
属性: value:指定范围的值。 取值:singleton prototype request session globalsession
和生命周期相关的( 相当于:<bean id=”” class=”” init-method=”” destroy-method=”” /> )
@PostConstruct
@PreDestroy
其他新注解
@Configuration
@ComponentScan
@Bean
@PropertySource
@Import
注意
关于Spring 和 XML 的选择问题 注解的优势 配置简单 XML的优势 修改时不用修改源码
五、Spring 整合 Junit 说在前面
spring整合Junit时,Junit4 和 Junit5 的代码形式并不一样
1 2 3 4 5 6 //@RunWith(SpringJUnit4ClassRunner.class)//junit4环境下 @ExtendWith(SpringExtension.class)//junit5环境下 @ContextConfiguration(classes = SpringConfiguration.class) public class AccountServiceTest { ...... }
Spring整合junit的配置
1、导入spring整合junit的jar(坐标) 2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的@Runwith(junit4环境下),若导入的是Junit5的坐标,则应替换为@ExtendWith 3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置 *@ContextConfiguration的说明: locations:指定xml文件的位置,加上classpath关键字,表示在类路径下 classes:指定注解类所在地位置
六、AOP 的相关概念 连接点即方法 被增强的方法才是切入点,切入点一定是连接点,但连接点不一定是切入点 切面 切入点表达式
七、Spring 中的 AOP 八、Spring 中的 JdbcTemplate JdbcTemplate的作用
九、Spring 中的事务控制 十、总结