💞 官方资料
🍊spring5下载
- 进入官网: https://spring.io/
- 这里的版本是Spring5 (Spring框架就是一系列jar包. 即引入jar包, 就可以使用spring)
- 进入Spring5的github(Spring本身也是GitHub的开源项目)
下拉找到A***ess to Binaries, 进入Spring Framework Artifacts
进入到Spring的仓库(这里有Spring的各个版本的jar包)
具体路径 snapshot->org->springframework->spring
下载网址 https://repo.spring.io/artifactory/snapshot/org/springframework/spring/
这里博主已把所有资源上传, 无需再去官网下载, 资料如下
各个jar包的含义
🍊文档介绍
在线文档 | https://docs.spring.io/spring-framework/reference/ |
---|---|
离线文档 | spring-framework-5.3.8\docs\reference\html\index.html |
离线API | spring-framework-5.3.8\docs\javadoc-api\index.html |
💞Spring5
🍊内容介绍
Spring核心学习内容 IOC, AOP, jdbcTemplate, 声明式事务
- IOC: 控制反转, 可以管理java对象
- AOP: 切面编程
- JDBCTemplate: 是spring提供的一套访问数据库的技术. 应用性强, 相对好理解
- 声明式事务: 基于ioc/aop实现事务管理
- IOC, AOP 是重点同时是难点, 需要时间理解
🍊重要概念
-
Spring可以整合其它的框架(解读: Spring是管理框架的框架)
-
Spring有两个核心的概念: IOC 和 AOP
-
IOC [Inversion Of Control 反转控制]
-
传统的开发模式[JDbcUtils / 反射], 程序------>环境 //程序读取环境配置, 然后自己创建对象
以连接到数据库为例
程序员编写程序, 在程序中读取配置信息
创建对象, 使用对象完成任务 -
Spring方式
Spring根据配置文件xml / 注解, 创建对象, 并放入到容器(ConcurrentHashMap). 并且可以完成对象之间的依赖
当需要使用某个对象实例的时候, 就直接从容器中获取即可
这样程序员可以更加关注如何使用对象完成相应的业务(以前是new -> 现在是注解 / 配置) -
DI - Dependency Injection依赖注入, 可以理解成是IOC的别称
Spring最大的价值是 通过配置, 给程序员提供需要使用的对象web层[Servlet (Action/Controller)/ Service / Dao / JavaBean(entity)]对象
这是核心价值所在, 也是ioc的具体体现, 实现解耦
💞快速入门
🍊Spring操作演示
需求: 通过Spring的方式[配置文件], 获取JavaBean-Monster的对象, 并给该对象的属性赋值, 输出该对象的信息
1.下载Spring5开发包, Spring5开发包资源博主已上传
2.创建Java工程, Spring5
3.新建lib目录, 引入开发Spring5的基本包
4.创建JavaBean, 一定要有无参构造器. Spring底层反射创建对象时, 需要使用
package ***.zzw.spring.bean;
public class Monster {
private String monsterId;
private String name;
private String skill;
//无参构造器: Spring底层反射创建对象时, 需要使用
public Monster() {
}
//有参构造器, setter, getter, toString()
}
5.src目录下: 新建一个容器配置文件beans.xml
创建好之后, 右上角进行配置, 不难
说明: xmlns表示xml namespace, 即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">
<!--
1.配置monster对象/javabean
2.在beans中可以配置多个bean
3.bean表示一个java对象
4.class是用于指定类的全路径->Spring底层使用反射创建(所以要有无参构造器)
5.id属性表示该java对象在spring容器中的id, 通过id可以获取到该对象
6.<property name="monsterId" value="100"/> 用于给该对象的属性赋值, String没有赋值就是null
-->
<bean class="***.zzw.spring.bean.Monster" id="monster01">
<property name="monsterId" value="100"/>
<property name="name" value="美猴王"/>
<property name="skill" value="金箍棒"/>
</bean>
</beans>
6.测试
package ***.zzw.spring.test;
public class SpringBeanTest {
@Test
public void getMonster() {
//解读
//1.创建容器 ApplicationContext
//2.该容器和容器配置文件关联
//3.习惯用接口的形式接收
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans.xml");
//3.通过getBean获取对应的对象
// 默认返回的是Object, 但是运行类型是Monster
//Object monster01 = ioc.getBean("monster01");
Monster monster01 = (Monster) ioc.getBean("monster01");
//4.输出
System.out.println("monster01" + monster01 + ", monster01运行类型" + monster01.getClass());
System.out.println("monster01" + monster01 + ", 属性name=" + monster01.getName() + ", monsterId="+ monster01.getMonsterId());
//5.也可以在获取的时候, 直接指定Class类型, 可以再次获取
Monster monster011 = ioc.getBean("monster01", Monster.class);
System.out.println("monster011=" + monster011);
System.out.println("monster011.name=" + monster011.getName());
System.out.println("ok~~~");
}
}
🍊类加载路径
解释类加载路径
ApplicationContext ioc = new ClassPathXmlApplicationContext(“beans.xml”);
//验证类加载路径
@Test
public void classPath() {
File file = new File(this.getClass().getResource("/").getPath());
//看到类的加载路径
System.out.println("file=" + file);
}
🍊Spring容器结构剖析
判断是否是懒加载: 是事先创建好, 还是等到用户使用的时候再创建.
lazyInit: false. 说明beans.xml中对象的创建不是懒加载.
用Debug的方式, 看一下Spring容器的处理机制
ioc->beanFactory->beanDefinitionMap
beanDefinitionMap / table
index=217
table / propertyValues
beanFactory->singletonObjects
singletonObjects / table
beanFactory / beanDefinitionNames
题目: 查看容器注入了哪些bean对象, 输出bean的id
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName=" + beanDefinitionName);
}
🍊Debug配置
小技巧分享
ioc->beanFactory->beanDefinitionMap
💞实现简单基于XML配置程序
需求说明
- 自己写一个简单的Spring容器, 通过读取beans.xml, 获取第1个JavaBean: Monster的对象, 并给该对象的属性赋值, 放入到容器中, 并输出该对象信息
- 也就是说, 不使用Spring原生框架, 我们自己简单模拟实现
- 了解Spring容器的简单机制
思路分析
●代码实现
1.引入dom4j-1.6.1.jar包
2.***.zzw.spring.zzwapplicationcontext.ZzwApplicationContext.java
package ***.zzw.spring.zzwapplicationcontext;
/**
* @author 赵志伟
* @version 1.0
* 1.这个程序用于实现Spring的一个简单容器机制
* 2.后面还会详细实现
* 3.这里我们实现如何将beans.xml文件进行解析, 并生成对象, 放入容器中
* 4.提供一个方法 getBean(id) 返回对应的对象
* 5.这里就是一个开胃小点心, 理解Spring容器的机制
*/
@SuppressWarnings({"all"})
public class ZzwApplicationContext {
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
//构造器
//接收一个容器的配置文件 比如 beans.xml, 该文件默认在src目录下
public ZzwApplicationContext(String iocBeanXmlFile) throws Exception {
//1.得到类加载路径:
// /D:/idea_project/zzw_spring/spring/out/production/spring/
String path = this.getClass().getResource("/").getPath();
//2.创建解析器
SAXReader reader = new SAXReader();
//3.得到document对象
Document document = reader.read(new File(path + iocBeanXmlFile));
//4.获取rootElement
Element rootElement = document.getRootElement();
//5.得到第1个bean-monster01
Element bean = (Element) rootElement.elements("bean").get(0);
//6.获取第一个bean-monster01的相关属性 => beanDefinitionMap
String id = bean.attributeValue("id");
String ClassFullPath = bean.attributeValue("class");
List<Element> properties = bean.elements("property");
//这里不再遍历, 直接获取
Integer monsterId = Integer.parseInt(properties.get(0).attributeValue("value"));
String name = properties.get(1).attributeValue("value");
String skill = properties.get(2).attributeValue("value");
//7.使用反射创建对象 => 回顾反射机制
Class<?> aClass = Class.forName(ClassFullPath);
//这里instance就是Monster对象
Monster o = (Monster) aClass.newInstance();
//给o对象通过反射来赋值 => 这里先简化
o.setMonsterId(monsterId);
o.setName(name);
o.setSkill(skill);
//8.将创建好的对象放入到singletonObjects
singletonObjects.put(id, o);
}
public Object getBean(String id) {
//这里可以再处理一下
return singletonObjects.get(id);
}
}
3.测试 ZzwApplicationContextTest
package ***.zzw.spring.zzwapplicationcontext;
public class ZzwApplicationContextTest {
public static void main(String[] args) throws Exception {
ZzwApplicationContext ioc = new ZzwApplicationContext("beans.xml");
Monster monster01 = (Monster) ioc.getBean("monster01");
System.out.println("monster01=" + monster01);
System.out.println("monster01.name=" + monster01.getName());
System.out.println("ok~");
}
}
🍊Spring原生容器结构梳理
🍊作业布置
在beans.xml中, 注入两个Monster对象, 但是不指定id, 运行会不会报错?
如果不会报错, 如果知道id, 并获取Monster对象.
- 不会报错, 会正常运行
- 系统会默认分配id. 分配id的规则是: 全类名#0, 全类名#1 这样的规则来分配id的. 例如 ***.zzw.spring.bean.Monster#0, ***.zzw.spring.bean.Monster#1
- 通过debug方式来查看
public class homework01 {
@Test
public void getMonster() {
//1.创建容器, 习惯用接口的形式接收
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster1 = ioc.getBean("***.zzw.spring.bean.Monster#0", Monster.class);
System.out.println("monster1=" + monster1);
Monster monster2 = ioc.getBean("***.zzw.spring.bean.Monster#1", Monster.class);
System.out.println("monster2=" + monster2);
System.out.println("ok~");
}
}
创建一个Car类, 要求
- 创建ioc容器文件(配置文件), 并配置一个Car对象(bean).
- 通过java程序到ioc容器获取该bean对象, 并输出
public class Car {
private Integer id;
private String name;
private Double price;
public Car() {
System.out.println("car对象 无参构造器被执行");
}
//有参构造器, setter, getter, toString()
beans1.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">
<!--配置carbean-->
<bean class="***.zzw.spring.bean.Car" id="car01">
<property name="id" value="100"/>
<property name="name" value="奔驰"/>
<property name="price" value="120000.00"/>
</bean>
</beans>
public class homework02 {
public static void main(String[] args) {
//1.创建容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans1.xml");
Car car01 = ioc.getBean("car01", Car.class);
System.out.println("car01=" + car01);
System.out.println("car01.name=" + car01.getName());
System.out.println("ok~");
}
}
💞基于XML配置Bean
💖通过类型获取bean
案例: 通过spring的ioc容器, 获取一个bean对象, 获取方式: 按类型.
<!--配置Monster, 通过类型获取-->
<bean class="***.zzw.spring.bean.Monster">
<!--
解读
1.当我们给某个bean对象设置属性的时候
2.底层是使用对应的setter方法完成的, 比如setName()
3.如果没有这个方法, 就会报错
-->
<property name="monsterId" value="100"/>
<property name="name" value="孙悟空"/>
<property name="skill" value="火眼金睛"/>
</bean>
演示通过bean的类型获取对象
@Test
public void getBeanByType() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
//直接传入class对象/类型
Monster bean = ioc.getBean(Monster.class);
System.out.println("bean=" + bean);
}
细节
按照类型获取bean, 要求ioc容器中的同一个类的bean只能有一个, 否则会抛出异常 NoUniqueBeanDefinationException
这种方式的应用场景: 比如XxxAction / Servlet / Controller, 或XxxService在一个线程中只需要一个对象实例(单例)的情况
在容器配置文件(比如beans.xml)中给属性赋值. 底层是通过setter方法完成的. 所以需要提供setter方法.
💖通过指定构造器配置bean
<!--配置Monster对象, 并且指定构造器-->
<!--
解读
1.constructor-arg标签可以指定使用构造器的参数
2.index表示构造器的第几个参数, 从0开始计算的
3.除了可以通过index, 还可以通过name / type来指定参数方式
4.解除大家的疑惑: 类的构造器, 不能有完全相同类型和顺序的构造器, 所以可以通过type来指定
一个类中的两个构造器, 参数的类型和顺序不能完全相同
可以类型相同, 但顺序不同
-->
<bean class="***.zzw.spring.bean.Monster" id="monster03">
<constructor-arg value="100" index="0"/>
<constructor-arg value="齐天大圣" index="1"/>
<constructor-arg value="如意金箍棒" index="2"/>
</bean>
<bean class="***.zzw.spring.bean.Monster" id="monster04">
<constructor-arg value="200" name="monsterId"/>
<constructor-arg value="斗战胜佛" name="name"/>
<constructor-arg value="无法无天" name="skill"/>
</bean>
<bean class="***.zzw.spring.bean.Monster" name="monster05">
<constructor-arg value="300" type="java.lang.Integer"/>
<constructor-arg value="猪悟能" type="java.lang.String"/>
<constructor-arg value="追嫦娥~" type="java.lang.String"/>
</bean>
演示通过构造器来设置属性
@Test
public void setBeanByConstructor() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster03 = ioc.getBean("monster03", Monster.class);
System.out.println("monster03=" + monster03);
}
通过index属性来区分是第几个参数;
通过type属性来区分是什么类型(按照顺序, 这是可以的)
💖通过p名称空间配置bean
xmlns:p=“http://www.springframework.org/schema/p”
<!--通过p名称空间来配置bean
将光标放在p这个位置, 输入alt+enter, 就会自动的添加xmlns.
有时需要多来几次, 有个识别的过程
-->
<bean class="***.zzw.spring.bean.Monster" id="monster06"
p:monsterId="400"
p:name="天蓬元帅"
p:skill="掌管十万天军"
/>
演示通过p名称空间来设置属性
public class SpringBeanTest {
@Test
public void setBeanByP() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster06 = ioc.getBean("monster06", Monster.class);
System.out.println("monster06=" + monster06);
}
}
💖通过ref配置bean
引用注入其它bean对象
在spring的ioc容器, 可以通过ref来实现bean对象的相互引用[ref全称: reference]
<!--配置MemberDaoImpl对象-->
<bean class="***.zzw.spring.dao.MemberDaoImpl" id="memberDao"/>
<!--
配置MemberServiceImpl对象
1.ref="memberDao"表示 MemberServiceImpl对象属性memberDao引用的对象是id="memberDao"的对象
2.这里就体现出spring容器的依赖注入
3.注意: 在spring容器中, 它是作为一个整体来执行的, 即如果引用到了一个bean对象, 对配置的顺序没有要求
4.建议还是按顺序. 好处是在阅读的时候, 比较方便
-->
<bean class="***.zzw.spring.service.MemberServiceImpl" id="memberService">
<property name="memberDao" ref="memberDao"/>
</bean>
package ***.zzw.spring.service;
public class MemberServiceImpl {
private MemberDaoImpl memberDao;
public MemberDaoImpl getMemberDao() {
return memberDao;
}
public void setMemberDao(MemberDaoImpl memberDao) {
this.memberDao = memberDao;
}
public void add() {
System.out.println("MemberServiceImpl add方法被调用...");
memberDao.add();
}
}
package ***.zzw.spring.dao;
public class MemberDaoImpl {
public MemberDaoImpl() {
System.out.println("MemberDaoImpl 构造器...");
}
public void add() {
System.out.println("MemberDaoImpl add方法被执行");
}
}
通过ref来设置bean属性
public class SpringBeanTest {
@Test
public void setBeanByRef() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
MemberServiceImpl memberService = ioc.getBean("memberService", MemberServiceImpl.class);
memberService.add();
}
}
💖通过内部bean配置属性
引用/注入内部bean对象
在spring的ioc容器, 可以直接配置内部bean对象
<!--配置MemberServiceImpl对象-使用内部bean-->
<bean class="***.zzw.spring.service.MemberServiceImpl" id="memberService2">
<!--自己配置一个内部bean-->
<property name="memberDao">
<bean class="***.zzw.spring.dao.MemberDaoImpl"/>
</property>
</bean>
通过内部bean, 设置属性
public class SpringBeanTest {
@Test
public void setBeanByPro() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
MemberServiceImpl memberService2 = ioc.getBean("memberService2", MemberServiceImpl.class);
memberService2.add();
}
}
下乘: Spring系列二:基于XML配置bean