一 Spring概述
(一) Spring是什么(掌握)
话术:
Spring是一个分层的Java SE/EE full-stack(一站式)轻量级开源框架,以 IoC(Inverse Of Control:反转控制,控制翻转, 控制反向)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。
在java三层架构当中分别提供了相应技术:
表现层(web层) :SpringMVC 框架,servlet
业务层(service层) :Bean管理(IOC容器,在三层都有体现) 声明式事务
持久层(dao层) :JdbcTemplate模板对象以及提供了ORM模块整合其他优秀的持久层技术
ps:
工具统一配置:
文件的忽略~
设置文件的编码: utf-8 必须设置
工程统一maven: maven的版本信息 maven的配置信息 maven 的本地仓库 【必须配置】
配置当前工程的自动编译: compile当中~
配置自动导报:
忽略大小写进行代码提示:
(二) Spring框架发展历程(了解)
话术:
oracle: java Mysql oracle websphere
IBM: DB2 eclipse
1997 年,IBM提出了EJB 的思想
1998 年,SUN制定开发标准规范 EJB1.0
1999 年,EJB1.1 发布
2001 年,EJB2.0 发布
2003 年,EJB2.1 发布
2006 年,EJB3.0 发布
Rod Johnson ( Spring 之父)
Expert One-to-One J2EE Design and Development(2002)
阐述了 J2EE 使用EJB 开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了 J2EE 开发不使用 EJB的解决方式(Spring 雏形)
2017 年 9 月份发布了 Spring 的最新版本 Spring5.0 通用版(GA)
(三)Spring框架的优势(掌握)
方便解耦,简化开发:Spring 就是一个大工厂,可以管理所有对象的创建和依赖关系维护,交给Spring管理
AOP 编程的支持:可以方便的实现对程序进行权限拦截,日志记录,运行的监控
声明式事务的支持:通过配置方式完成对事务的管理,无需手动编程
方便程序的测试:对Junit支持,可以通过注解方便的对Spring程序进行测试
整合外部优秀技术:Spring内部提供了对各种优秀框架(Hibernate,Mybatis,Quartz等)的直接支持
javaEE技术的封装 :Spring对javaEE开发当中复杂难用的API(JavaEmail, RMI等)进行封装,降低了这些API的使用难度。
(四) Spring的体系结构(掌握)
Spring的核心是容器(IOC): Beans Core Context SpEL表达式
中间层技术:AOP Aspects AOP
web: servler springmvc
data Access: 数据访问层: jdbc ORM 整合外部的优秀框架 Transaction 事务
基于Test测试: spring 整合junit junit4.X
官方网站:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
二 程序的耦合和解耦合
(一) 什么是程序的耦合
话术:
程序的耦合是程序之间的关联性,也就是多个类的联系是否紧密,多个对象之间的关系是否密切。
耦合度高:
(1)不利于扩展
(2)不利于程序的维护。
生活中的案例:
你的房子里面有窗子,那么房子和窗子就有了关联
耦合度是松还是紧就看你的关联是强还是弱,也就是修改的代价,比如你窗子是扣死在墙里的那么你修改窗子就必须修改墙 这就比较紧密了,反应在程序上就是耦合度高,不利于程序的扩展和维护。
但是如果你窗子是按照某种规格的 可以自由拆装的,那么修改的代价就小,耦合度也就低了,反应在程序上就是耦合度低,利于程序的扩展和维护。
我们写程序的目标就是 高内聚 低耦合!
这样修改起来 就不会有太多的联系,不用改一个地方其他的都要修改。
(二) 解决程序耦合的思路
1 编译不依赖,运行时才依赖
当我们讲解jdbc时,是通过反射来注册驱动的,代码如下:
Class.forName("com.mysql.jdbc.Driver");//使用的驱动类是指定了一个字符串
底层: DriverManager.regist(new com.mysql.jdbc.Driver());
Connection connection = DriverManager.getConnection("","","");
此时的好处式,我们的类中在编译阶段不再需要具体的驱动类,就算删除mysql的驱动jar包,依然可以通过编译。在运行阶段才会依赖驱动包。实际开发当中,我们应该做到编译不依赖,运行时才依赖。
上述代码产生的新问题,mysql驱动类的全限定类名作为一个字符串java类中是写死的,一旦发生改变,还需要修改源码。
使用配置文件结合反射就可以解决上述问题。
2 使用工厂模式解耦合
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的 方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。
那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
service 调用dao:
原生: new xxxDaoImpl(); 问题: service实现类当中出现了 new关键字, 出现了dao 层具体的实现类。 不满足java设计原则: 面向接口开发~ 总结: 耦合度高~
工厂模式: XXXFactory.getInstance(); 获得接口的实现类: xxxDao接口接收~ 解决了: new关键字, 业务层的实现类当中, 不在出现dao层的实现类。 总结: 松耦合~
三 SpringIOC 机制的详解
(一) IOC概述及作用【重要】
1 IOC的简介,设计思想
SpringIOC:IOC 是 Inversion of Control 的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。
就是将对象的创建和维护的权利交给spring 框架。
1996 年,Michael Mattson 在一篇有关探讨面向对象框架的文章中,首先提出了 IOC 这个概念。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。
IOC 理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦 如下图
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了, 全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合 在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。 我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话, 当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是 一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!
总结:
IOC容器就是创建对象, 并且维护对象和对象之间的关联关系。
2 IOC作用
IOC本质上就是一个大工厂,大容器。主要作用就是创建和管理对象的依赖关系,削减计算机程序的耦合(解除我们代码中的依赖关系),提高程序的可扩展性和可维护性。
(二) SpringIOC入门案例前期准备
1 构建maven工程,添加Spring框架的依赖
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
</properties>
<!--导入spring的context坐标,context依赖core、beans、expression aop-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
2 创建持久层接口和实现类
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userDao save method running...... ");
}
}
3 创建业务层接口和实现类
public interface UserService {
public void saveService();
}
public class UserServiceImpl implements UserService {
@Override
public void saveService() {
System.out.println("userSerivce save method running......");
}
}
4 在resources文件夹中创建一个任意名称的xml文件
xml文件名称: 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">
</beans>
5 让Spring管理资源,在配置文件中配置service和dao
<?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">
<!--配置userDaoImpl-->
<bean id="userDao" class="com.ujiuye.dao.impl.UserDaoImpl"></bean>
<!--配置userServiceImpl-->
<bean id="userService" class="com.ujiuye.service.UserServiceImpl"></bean>
</beans>
6 测试IOC 配置是否成功
@Test
public void test1(){
//1:获得spring IOC容器对象:
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
//2:从容器当中根据id获得UserDaoImpl 对象,执行userDao对象的save方法
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
//3:从容器当中根据id获得UserServiceImpl 对象,执行userService对象的saveService方法
UserService userService = (UserService)applicationContext.getBean("userService");
userService.saveService();
}
运行结果:
(三)Spring基于XML的IOC细节
1 IOC配置文件详解:配置标签书写规范,配置标签的属性
话术:
bean标签: 用于配置对象交给Spring 来创建。
默认情况下他会调用类中无参数的构造器,如果没有无参数构造器则不能成功创建
基本属性:
id : Bean实例对象在Spring容器当中的唯一标识
class : Bean的全限定类名
name : 了解,给对象命名操作
2 SpringIOC机制源码解析
2.1 IOC容器解析 (面试题目)
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂,Spring 中工厂的类结构图如下:
话术:
1)BeanFactory :该接口是顶层接口, IOC容器的基本实现,是Spring内部使用的接口,不提供开发人员使用,加载配置文件时,不会创建对象,在获得(使用)对象时才采取创建对象。对象的创建时机,getBean()创建【稍后演示】
2)HierarchicalBeanFactory:这个工厂接口非常简单,实现了Bean工厂的分层。 工厂接口也是继承自BeanFacotory,也是一个二级接口,相对于父接口,它只扩展了一个重要的功能——工厂分层
3)AutowireCapableBeanFactory:该接口有自动装配能力,需要注意的是,ApplicationContext接口并没有实现此接口,因为应用代码很少用到此功能,如果确实需要的话,可以调用ApplicationContext的getAutowireCapableBeanFactory方法,来获取此接口的实例。
4)ListableBeanFactory:获取bean时,Spring 鼓励使用这个接口定义的api,如查看Bean的个数、获取某一类型Bean的配置名、查看容器中是否包括某一Bean等方法。
5)ApplicationContext: BeanFactory接口的子接口,提供更多强大的功能,一般由开发人员使用.接口提供了bean基础性操作同时,扩展了国际化等功能。ApplicationContext接口在加载配置文件时候就会配置文件当中的对象进行创建,存放在IOC容器当中
6)AnnotationConfigApplicationContext:
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
7)ClassPathXmlApplicationContext:
它是从类的根路径下加载配置文件 推荐使用这种
8)FileSystemXmlApplicationContext: 【不用~】
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
重点掌握: BeanFactory ApplicationContext
面试题目: BeanFactory & ApplicationContext 联系和区别:
BeanFactory : 顶层接口, 接口当中实现了基础性的功能。 提供给框架设计者使用。
加载配置文件的时候, 对象不创建。 getBean();才会创建对象。
ApplicationContext :是BeanFactory 接口的子接口。 提供了基础性功能之外, 功能进行了扩展,开发者使用。
加载配置文件的时候, 对象就创建成功, 并且存放在IOC 容器当中。
ClassPathXmlApplicationContext: 从类路径下获得资源文件。
AnnotationConfigApplicationContext: 基于主机模式使用的实现类。
2.2 IOC容器底层bean初始化过程(了解)
话术:
1)BeanFactoryPostProcessor
作用:定义了在bean工厂对象创建后,bean对象创建前执行的动作,用于对工厂进行创建后业务处理
运行时机: 操作用于对工厂进行处理,仅运行一次
2)BeanPostProcessor
作用: 定义了所有bean初始化前后进行的统一动作,用于对bean进行创建前业务处理与创建后业务处理
运行时机:当前操作伴随着每个bean的创建过程,每次创建bean均运行该操作
3)InitializingBean 【了解】
作用:定义了每个bean的初始化前进行的动作,属于非统一性动作,用于对bean进行创建前业务处理。类似于 init-method。
运行时机:当前操作伴随着任意一个bean的创建过程,保障其个性化业务处理
(四) 手动实现自己的IOC容器
1 分析IOC 实现思路
讲解思路:
1、创建工厂对象,加载Spring核心配置文件
2、使用xml解析技术,获得bean标签当中id属性以及class属性对应的值
3、底层使用反射技术,创建对象
4、准备临时容器,存放创建的对象
5、提供getBean方法,对外提供对象
6、测试自定义的工厂对象
2 IOC原理实现-环境搭建:构建maven工程,引入依赖
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<!--导入spring的context坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--导入junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3 设计接口和类以及编写配置文件
//设定UserDao接口
public interface UserDao {
public void save();
}
//设定UserDao接口实现类
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userDao save method running...... ");
}
}
<!--配置userDaoImpl-->
<bean id="userDao" class="com.ujiuye.dao.impl.UserDaoImpl"></bean>
4 使用xml技术解析配置文件
<!--引入dom4J 是xml解析。 -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
/**
* 创建自己的工厂类
*/
public class MyBeanFactory {
//创建一个map集合,模拟IOC容器
private static Map<String,Object> map = new HashMap<>();
static{
try {
//使用dom4J 解析xml文件:
//第一步:获得一个解析器:
SAXReader reader = new SAXReader();
//第二: 读取外部的配置文件:
String path = "src/main/resources/applicationContext.xml";
//第三: 读取了整个文档对象
Document document = reader.read(path);
//第四: 获得根节点:
Element rootElement = document.getRootElement();//beans
//第五: 获得根节点下的所有的bean 标签对应的节点:
List<Element> bean = rootElement.elements("bean");// bean
for (Element element : bean) {
//获得id属性对应的值:
String id1 = element.attributeValue("id");
//获得class属性对应的值:【全限定类名】
String aClass = element.attributeValue("class");//获得class对应的值: 全限定类名。
//通过反射创建对象:
Class clz = Class.forName(aClass);
Object object = clz.newInstance();
//存容器 id做key,创建出来的对象value
map.put(id1,object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据id从容器当中获得对象
* @param id id的名称
* @return 返回Object类型对象
*/
public static Object getBean(String id){
Object o = map.get(id);
return o;
}
}
总结: IOC容器自定义时候;
xml解析技术:
反射技术
工厂模式
5 编写测试文件,展示测试结果
@Test
public void testMyFactory(){
//1:创建工厂对象
MyBeanFactory factory =new MyBeanFactory();
//2:从容器当中根据id获得对象
UserDao userDao = (UserDao) factory.getBean("userDao");
System.out.println(userDao);
userDao.save();
}
运行结果:
(五)Spring管理bean细节
1 bean实例化介绍:
bean 对象创建~
2 bean实例化方式【面试问题】
2.1 构造方法的方式
它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败
2.1.1 创建User类
public class User implements Serializable {
public User(){
System.out.println("user created...");
}
}
2.1.2 配置Spring容器管理user 类型对象
<!--配置user对象-->
<bean id="user" class="com.ujiuye.pojo.User"></bean>
2.1.3 测试容器实例化User对象是否成功
@Test
public void testUser(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
2.2 静态工厂方式
2.2.1 创建静态工厂ItemFactory
public class ItemFactory {
//静态方法返回实例bean
public static User createUser(){
System.out.println("static method running create bean ......");
return new User();
}
}
2.2.2 配置Spring容器管理User类型对象
<!--静态工厂实例化对象:
工厂方法的返回值存放在IOC容器当中
-->
<bean id="user" class="com.ujiuye.factory.ItemFactory" factory-method="createUser"></bean>
2.2.3 测试容器实例化User对象是否成功
@Test
public void testItemFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
2.3 实例工厂方式
2.3.1 创建实例工厂NewItemFactory
public class NewItemFactory {
//工厂的非静态方法返回bean实例
public User createUser(){
System.out.println("Dynamic method running create bean ......");
return new User();
}
}
2.3.2 配置Spring容器管理NewItemFactory类型对象
<!--实例工厂:
步骤一; 创建对象
步骤二: 通过对象调用方法,方法的返回值存在IOC容器当中。
-->
<bean id="itemFactory" class="com.ujiuye.factory.NewItemFactory"></bean>
<bean id="user" factory-bean="itemFactory" factory-method="createUser"></bean>
2.3.3 测试容器实例化User对象是否成功
@Test
public void testNewItemFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
3 bean作用域【面试问题】
3.1 bean作用域介绍
web的四大域:
pageContext request session application :
jsp九大内置对象:
~
话术:
所谓Bean的作用域其实就是指Spring给我们创建出的对象的存活范围,在配置文件中通过bean的scope属性指定
<bean id="" class="" scope="">
scope:指对象的作用范围,取值如下:
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
3.2 bean作用域的解析
3.2.1 当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
3.2.2 当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
注意: 多例对象,不会随容器而创建, 不会随容器销毁而销毁。
在内存当中, 由垃圾回收器定期回收~
3.2.3 当Scope的取值为其他值
scope指定为其他值,需要在特定的环境下使用, 只需要作为一个了解知识,面试能够回答出来即可
4 bean的生命周期
4.1 bean生命周期介绍
话术:
在这里所谓的Bean的生命周期其实指的是Bean创建到销毁的这么一段时间。
在Spring中可以通过配置的形式,指定bean在创建后和销毁前要调用的方法。
属性:
init-method:指定bean在创建后调用的方法
destroy-method:对象在销毁前调用的方法
<bean id="xxx" class="xxx" init-method="" destroy-method=""></bean>
4.2 单例对象的生命周期
4.2.1 在UserService中添加方法init和destory
public class UserServiceImpl implements UserService {
public void init(){
System.out.println("init method running...");
}
@Override
public void saveService() {
System.out.println("userSerivce save method running......");
}
public void destroy(){
System.out.println("destroy method running...");
}
}
4.2.2 在Spring.xml配置文件中配置UserService
<!--配置userServiceImpl-->
<bean id="userService" class="com.ujiuye.service.UserServiceImpl" init-method="init" destroy-method="destroy">
</bean>
4.2.3 测试代码验证单例对象的生命周期
@Test
public void test1(){
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.saveService();
//关闭工厂:
applicationContext.close();
}
单例对象总结:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
4.3 多例对象的生命周周期
4.3.1 修改Spring配置文件scope属性
<!--配置userServiceImpl-->
<bean id="userService" class="com.ujiuye.service.UserServiceImpl" init-method="init" destroy-method="destroy" scope="prototype"></bean>
4.3.2 测试代码验证多例对象的生命周期
@Test
public void test1(){
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.saveService();
//关闭工厂:
applicationContext.close();
}
多例对象总结:
对象创建:当使用对象时(getBean),创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了, 对destroy-method属性针对多例不生效。
四 Spring依赖注入【~~~】
(一) 依赖注入的介绍:什么是依赖注入,为什么需要依赖注入
话术:
依赖注入(Dependency Injection):简称DI,它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。
例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取
简单理解: 依赖注入就是spring IOC 容器在创建对象的时候, 给属性赋值。
(二) Spring中依赖注入方式
1 构造函数注入
1.1 构建Account类,提供所有属性的构造方法
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置 的方式,让 Spring 框架来为我们注入。具体代码如下
public class Account {
private String name;
private Integer age;
private Date birthday;
public Account(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
1.2 在Spring.xml文件中配置Account类(使用构造方法注入依赖数据)
<!--使用构造函数的方式:给account中的属性赋值
要求:
类中需要提供一个对应参数列表的构造器函数
涉及的标签:
constructor-arg:
属性:
name: 执行参数在构造器中的名称
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
-->
<bean id="now" class="java.util.Date"></bean>
<bean id="account" class="com.ujiuye.pojo.Account">
<constructor-arg name="name" value="王达"></constructor-arg>
<constructor-arg name="age" value="20"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
1.3 构造函数注入测试方法
@Test
public void testDI(){
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
Account account = (Account) applicationContext.getBean("account");
System.out.println("name:"+account.getName()+" age:"+account.getAge()+" birthday:"+account.getBirthday());
//打印结果: name:王达 age:20 birthday:Thu Feb 04 13:53:45 CST 2021
}
2 setter注入
2.1 修改Account类,添加属性的setter方法
顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:
public class Account {
private String name;
private Integer age;
private Date birthday;
public Account() {
}
public Account(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
2.2 在Spring.xml文件中配置Account类(利用setter方法注入依赖数据)
<!--使用set方法的方式给属性赋值
涉及的标签:
property
属性:
name:找的是类中set方法后面的部分
ref: 给属性赋值是其他bean类型的
value:给属性赋值是基本数据类型和 string 类型的
实际开发当中, 此种方式用的比较多,推荐使用
-->
<bean id="now" class="java.util.Date"></bean>
<bean id="account" class="com.ujiuye.pojo.Account">
<property name="name" value="张三丰"></property>
<property name="age" value="31"></property>
<property name="birthday" ref="now"></property>
</bean>
2.3 setter注入测试方法
@Test
public void testDI(){
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
Account account = (Account) applicationContext.getBean("account");
System.out.println("name:"+account.getName()+" age:"+account.getAge()+" birthday:"+account.getBirthday());
//测试结果: name:张三丰 age:31 birthday:Thu Feb 04 14:05:19 CST 2021
}
补充:
P名称空间注入: 语法: p:属性名称=“”
在xml当中引入p名称空间
xmlns:p="http://www.springframework.org/schema/p"
语法:
<bean id="account" class="com.offcn.pojo.Account" p:name="唐晓飞" p:age="21" p:birthday-ref="birthday"></bean>
SPEL 表达式注入:
<!--SPEL注入: spring el expression
(1) 底层是调用了set方法
(2) #{} spring EL 语法
(3) 不管是普通属性或者是对象属性, 都使用value 赋值。
(4)功能最强大 , 属性赋值的时候,可以调用对象的属性, 可以调用对象的方法。
-->
<bean id="account" class="com.offcn.pojo.Account">
<property name="name" value="#{'admin'}"></property>
<property name="age" value="#{account.calcullatorAge()}"></property>
<property name="birthday" value="#{birthday}"></property>
</bean>
3 注入集合数据
3.1 修改Account类,添加集合属性
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。
我们这里介绍注入数组,List,Set,Map,Properties。具体代码如下:
public class Account {
//注入数组,List集合,Set集合,Map集合,Properties集合属性
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public Account() {
}
public String[] getMyStrs() {
return myStrs;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public List<String> getMyList() {
return myList;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public Set<String> getMySet() {
return mySet;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public Map<String, String> getMyMap() {
return myMap;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public Properties getMyProps() {
return myProps;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
}
3.2 在Spring.xml文件中配置Account类(利用setter注入依赖集合数据)
<!--注入集合类型数据:
涉及到标签:
List结构: array list set
Map结构: map entry props prop
-->
<bean id="account" class="com.ujiuye.pojo.Account">
<!--注意:在注入集合数据时,只要是结构相同,标签可以互换-->
<!--注入数组数据-->
<property name="myStrs">
<array>
<value>array-AAA</value>
<value>array-BBB</value>
<value>array-CCC</value>
</array>
</property>
<!--注入List集合数据-->
<property name="myList">
<list>
<value>list-AAA</value>
<value>list-BBB</value>
<value>list-CCC</value>
</list>
</property>
<!--注入Set集合数据-->
<property name="mySet">
<list>
<value>set-AAA</value>
<value>set-BBB</value>
<value>set-CCC</value>
</list>
</property>
<!--注入Map集合-->
<property name="myMap">
<map>
<entry key="map-a" value="AAA"></entry>
<entry key="map-b">
<value>BBB</value>
</entry>
</map>
</property>
<!--注入Properties集合-->
<property name="myProps">
<props>
<prop key="pro-a">AAA</prop>
<prop key="pro-b">BBB</prop>
</props>
</property>
</bean>
3.3 集合注入测试方法
@Test
public void testDI(){
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
Account account = (Account) applicationContext.getBean("account");
System.out.println("array:"+ Arrays.toString(account.getMyStrs()));
System.out.println("list:"+account.getMyList());
System.out.println("set:"+account.getMySet());
System.out.println("map:"+account.getMyMap());
System.out.println("props:"+account.getMyProps());
/*
测试结果:
array:[array-AAA, array-BBB, array-CCC]
list:[list-AAA, list-BBB, list-CCC]
set:[set-AAA, set-BBB, set-CCC]
map:{map-a=AAA, map-b=BBB}
props:{pro-b=BBB, pro-a=AAA}
*/
}
五 Spring配置文件模块化
讲解思路:
1、配置文件模块化的业务背景,为什么要进行配置文件模块化
2、配置文件模块化的具体实现
方式一: 创建多个并列的配置文件
方式二: 创建一个主配置,多个从配置,主配置当中引入从配置
(一) Spring模块化的介绍:为什么需要配置文件进行模块操作,如何实现配置文件模块化
话术:
我们现在的配置都集中配在了一个applicationContext.xml文件中,当开发人员过多时, 如果所有bean都配 置到同一个配置文件中,会使这个文件巨大,而且也不方便维护。 针对这个问题,Spring提供了多配置文件的方式,也就是所谓的配置文件模块化
(二) Spring模块化的配置
1 Spring模块化配置方式一
并列的多个配置文件 直接编写多个配置文件,比如说beans1.xml,beans2.xml……, 然后在创建ApplicationContext的时候,直接传入多个配置文件。
ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
2 Spring模块化配置方式二
主从配置文件 先配置一个主配置文件,然后在里面导入其它的配置文件。
<import resource="beans1.xml" />
<import resource="beans2.xml" />
注意:
同一个xml文件中不能出现相同名称的bean,如果出现会报错
多个xml文件如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽 量保证bean的名称是唯一的。
六 模板设计模式解析
讲解思路::
1、了解什么是设计模式,将常见的模式简单复习
2、模板设计模式的介绍
3、模板设计模式的使用场景,结合已经学习到技术解释模板设计模式
4、模板设计模式具体案例的实现
5、引出Spring中JdbcTemplate模板对象
设计模式是java编程当中非常重要的思想, 不管是实际开发还是面试过程当中,经常涉及。所以凡是涉及到设计模式的内容都是重点。
(一)模板设计模式介绍
话术:
模板类:
模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
模板方法模式的静态结构图如下:
(二) 模板设计模式的应用场景
在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中
程序主框架相同,细节不同的情况下,也可以使用模板方法
举例说明:
a:使用过Servlet的人都清楚,除了要在web.xml做相应的配置外,还需继承一个叫HttpServlet的抽象类。HttpService类提供了一个service()方法,这个方法调用七个do方法中的一个或几个,完成对客户端调用的响应。这些do方法需要由HttpServlet的具体子类提供,因此这是典型的模板方法模式。
b:持久层对数据库的操作,Spring提供了JdbcTemplate模板对象, 完成CRUD操作。
(三) 模板设计模式实现
1 需求:用模板方法模式实现出国留学手续设计程序。
分析:出国留学手续一般经过以下流程:索取学校资料,提出入学申请,办理因私出国护照、出境卡和公证,申请签证,体检、订机票、准备行装,抵达目标学校等,其中有些业务对各个学校是一样的,但有些业务因学校不同而不同,所以比较适合用模板方法模式来实现。
在本实例中,我们先定义一个出国留学的抽象类 StudyAbroad,里面包含了一个模板方法 TemplateMethod(),该方法中包含了办理出国留学手续流程中的各个基本方法,其中有些方法的处理由于各国都一样,所以在抽象类中就可以实现,但有些方法的处理各国是不同的,必须在其具体子类(如美国留学类 StudyInAmerica)中实现。如果再增加一个国家,只要增加一个子类就可以了。
图 2 所示是其结构图
2 程序代码实现
//抽象类: 出国留学
public abstract class StudyAbroad {
//定义抽象方法,索取学校资料
public abstract void LookingForSchool();
//定义抽象方法,定义入学申请
public abstract void ApplyForEnrol();
//定义入学申请方法:
public void ApplyForPassport() {
System.out.println("三.办理因私出国护照、出境卡和公证:");
System.out.println(" 1)持录取通知书、本人户口簿或身份证向户口所在地公安机关申请办理因私出国护照和出境卡。");
System.out.println(" 2)办理出生公证书,学历、学位和成绩公证,经历证书,亲属关系公证,经济担保公证。");
}
//定义申请签证方法
public void ApplyForVisa() {
System.out.println("四.申请签证:");
System.out.println(" 1)准备申请国外境签证所需的各种资料,包括个人学历、成绩单、工作经历的证明;个人及家庭收入、资金和财产证明;家庭成员的关系证明等;");
System.out.println(" 2)向拟留学国家驻华使(领)馆申请入境签证。申请时需按要求填写有关表格,递交必需的证明材料,缴纳签证。有的国家(比如美国、英国、加拿大等)在申请签证时会要求申请人前往使(领)馆进行面试。");
}
//体检、订机票、准备行装 方法
public void ReadyGoAbroad() {
System.out.println("五.体检、订机票、准备行装:");
System.out.println(" 1)进行身体检查、免疫检查和接种传染病疫苗;");
System.out.println(" 2)确定机票时间、航班和转机地点。");
}
//定义抵达抽象方法
public abstract void Arriving();
//定义模板方法:
public void templateMethod(){
(1)(2)....
}
}
//定义具体的子类,美国留学
public class StudyInAmerica extends StudyAbroad {
//索取资料的具体实现
@Override
public void LookingForSchool() {
System.out.println("一.索取学校以下资料:");
System.out.println(" 1)对留学意向国家的政治、经济、文化背景和教育体制、学术水平进行较为全面的了解;");
System.out.println(" 2)全面了解和掌握国外学校的情况,包括历史、学费、学制、专业、师资配备、教学设施、学术地位、学生人数等;");
System.out.println(" 3)了解该学校的住宿、交通、医疗保险情况如何;");
System.out.println(" 4)该学校在中国是否有授权代理招生的留学中介公司?");
System.out.println(" 5)掌握留学签证情况;");
System.out.println(" 6)该国政府是否允许留学生合法打工?");
System.out.println(" 8)毕业之后可否移民?");
System.out.println(" 9)文凭是否受到我国认可?");
}
//入学申请的具体实现
@Override
public void ApplyForEnrol() {
System.out.println("二.入学申请:");
System.out.println(" 1)填写报名表;");
System.out.println(" 2)将报名表、个人学历证明、最近的学习成绩单、推荐信、个人简历、托福或雅思语言考试成绩单等资料寄往所申请的学校;");
System.out.println(" 3)为了给签证办理留有充裕的时间,建议越早申请越好,一般提前1年就比较从容。");
}
//抵达的具体实现
@Override
public void Arriving() {
System.out.println("六.抵达目标学校:");
System.out.println(" 1)安排住宿;");
System.out.println(" 2)了解校园及周边环境。");
}
}
public class StudyAbroadProcess {
public static void main(String[] args) {
StudyAbroad tm = new StudyInAmerica();
tm.TemplateMethod();
}
}
3 运行结果如下:
一.索取学校以下资料:
1)对留学意向国家的政治、经济、文化背景和教育体制、学术水平进行较为全面的了解;
2)全面了解和掌握国外学校的情况,包括历史、学费、学制、专业、师资配备、教学设施、学术地位、学生人数等;
3)了解该学校的住宿、交通、医疗保险情况如何;
4)该学校在中国是否有授权代理招生的留学中介公司?
5)掌握留学签证情况;
6)该国政府是否允许留学生合法打工?
8)毕业之后可否移民?
9)文凭是否受到我国认可?
二.入学申请:
1)填写报名表;
2)将报名表、个人学历证明、最近的学习成绩单、推荐信、个人简历、托福或雅思语言考试成绩单等资料寄往所申请的学校;
3)为了给签证办理留有充裕的时间,建议越早申请越好,一般提前1年就比较从容。
三.办理因私出国护照、出境卡和公证:
1)持录取通知书、本人户口簿或身份证向户口所在地公安机关申请办理因私出国护照和出境卡。
2)办理出生公证书,学历、学位和成绩公证,经历证书,亲属关系公证,经济担保公证。
四.申请签证:
1)准备申请国外境签证所需的各种资料,包括个人学历、成绩单、工作经历的证明;个人及家庭收入、资金和财产证明;家庭成员的关系证明等;
2)向拟留学国家驻华使(领)馆申请入境签证。申请时需按要求填写有关表格,递交必需的证明材料,缴纳签证。有的国家(比如美国、英国、加拿大等)在申请签证时会要求申请人前往使(领)馆进行面试。
五.体检、订机票、准备行装:
1)进行身体检查、免疫检查和接种传染病疫苗;
2)确定机票时间、航班和转机地点。
六.抵达目标学校:
1)安排住宿;
2)了解校园及周边环境。
七 Spring整合JDBC实现用户的CRUD
讲解思路:
1、简单复习JDBC技术操作数据库的步骤,JDBC开发的弊端问题
2、介绍Spring在持久层提供的JdbcTemplate模板对象
3、JdbcTemplate模板对象的使用步骤,常见的读写方法
4、扩展JdbcDaoSupport工具类当中注入JdbcTemplate原理
(一) 整合思路分析
话术:
Spring提供了ioc容器,管理jdbc操作数据库的过程中需要的数据库连接对象,同时Spring提供了整合jdbc操作数据库的工具类JdbcDaoSupport 和模板工具 JdbcTemplate,在JdbcTemplate中提供了大量的操作数据库的方式供用户使用。所以我们只需要获取模板工具类然后调用方法就可以完成Jdbc的操作了。
(二)构建maven工程,添加技术依赖
<dependencies>
<!--导入spring的context坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--导入Jdbc模块依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--导入Mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--导入C3P0连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--导入junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
(三) 构建数据库表并编写实体类Account
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
(四)编写持久层代码AccountDao以及实现类AccountDaoImp
public interface AccountDao {
public void save(Account account);
public void delete(Integer id);
public void update(Account account);
public Account findById(Integer id);
public Integer getTotalRecords();
public List<Account> findAll();
}
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void save(Account account) {
String sql ="insert into account(name,money) values(?,?)";
jdbcTemplate.update(sql,account.getName(),account.getMoney());
}
@Override
public void delete(Integer id) {
String sql ="delete from account where id = ? ";
jdbcTemplate.update(sql,id);
}
@Override
public void update(Account account) {
String sql ="update account set money = ? , name=? where id= ?";
jdbcTemplate.update(sql,account.getMoney(),account.getName(),account.getId());
}
@Override
public Account findById(Integer id) {
String sql ="select * from account where id = ? ";
Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class),id);
return account;
}
@Override
public Long getTotalRecords() {
Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(count);
return count;
}
@Override
public List<Account> findAll() {
String sql ="select * from account";
List<Account> accountList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
return accountList;
}
}
(五)编写业务层代码AccountService以及实现类AccountServiceImpl
public interface AccountService {
public void save(Account account);
public void delete(Integer id);
public void update(Account account);
public Account findById(Integer id);
public Long getTotalRecords();
public List<Account> findAll();
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void save(Account account) {
accountDao.save(account);
}
@Override
public void delete(Integer id) {
accountDao.delete(id);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public Account findById(Integer id) {
return accountDao.findById(id);
}
@Override
public Long getTotalRecords() {
return accountDao.getTotalRecords();
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
(六)创建并编写配置文件:配置容器管理对象
将数据库的连接信息抽取到外部配置文件中,和spring的配置文件分离开,有利于后期维护
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置JdbcTemplate模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置AccountDaoImpl对象-->
<bean id="accountDao" class="com.ujiuye.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--配置AccountServiceImpl对象-->
<bean id="accountService" class="com.ujiuye.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
(七)测试代码
//测试save方法
@Test
public void testJdbcTemplateSave(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Account account = new Account();
account.setName("jack");
account.setMoney(1001D);
service.save(account);
}
//测试update方法
@Test
public void testJdbcTemplateUpdate(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Account account = new Account();
account.setName("jack2");
account.setMoney(999D);
account.setId(1008);
service.update(account);
}
//测试delete方法
@Test
public void testJdbcTemplateDelete(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
service.delete(1001);
}
//测试唯一性查询findById
@Test
public void testJdbcTemplateFindById(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Account account = service.findById(1001);
System.out.println(account);
}
//测试总记录数
@Test
public void testJdbcTemplateGetTotalRecords(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
Long totalRecords = service.getTotalRecords();
System.out.println("表当中的总记录数为:"+totalRecords);
}
//测试账户列表
@Test
public void testJdbcTemplateGetAll(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
List<Account> accountList = service.findAll();
accountList.forEach((account -> {
System.out.println(account);
}));
}