title: 2 spring
- 传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
- UserDao userdao = new UserDaoImpl();
- 部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤 ,Servlet的开发步骤)。
- 侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。
Spring是一个项目管理框架,同时也是一套Java EE解决方案。
Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
官方网站:https://spring.io/
下载地址:http://repo.spring.io/release/org/springframework/spring/
Spring架构由诸多模块组成,可分类为
- 核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP。
- 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
- 数据访问:事务,DAO支持,JDBC,ORM,封送XML。
- Spring MVC和 Spring WebFlux Web框架。
- 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
- 语言:Kotlin,Groovy,动态语言。
Spring架构组成 |
---|
GroupId | ArtifactId | 说明 |
---|---|---|
org.springframework | spring-beans | Beans 支持,包含 Groovy |
org.springframework | spring-aop | 基于代理的AOP支持 |
org.springframework | spring-aspects | 基于AspectJ 的切面 |
org.springframework | spring-context | 应用上下文运行时,包括调度和远程抽象 |
org.springframework | spring-context-support | 支持将常见的第三方类库集成到 Spring 应用上下文 |
org.springframework | spring-core | 其他模块所依赖的核心模块 |
org.springframework | spring-expression | Spring 表达式语言,SpEL |
org.springframework | spring-instrument | JVM 引导的仪表(监测器)代理 |
org.springframework | spring-instrument-tomcat | Tomcat 的仪表(监测器)代理 |
org.springframework | spring-jdbc | 支持包括数据源设置和 JDBC 访问支持 |
org.springframework | spring-jms | 支持包括发送/接收JMS消息的助手类 |
org.springframework | spring-messaging | 对消息架构和协议的支持 |
org.springframework | spring-orm | 对象/关系映射,包括对 JPA 和 Hibernate 的支持 |
org.springframework | spring-oxm | 对象/XML 映射(Object/XML Mapping,OXM) |
org.springframework | spring-test | 单元测试和集成测试支持组件 |
org.springframework | spring-tx | 事务基础组件,包括对 DAO 的支持及 JCA 的集成 |
org.springframework | spring-web | web支持包,包括客户端及web远程调用 |
org.springframework | spring-webmvc | REST web 服务及 web 应用的 MVC 实现 |
org.springframework | spring-webmvc-portlet | 用于 Portlet 环境的MVC实现 |
org.springframework | spring-websocket | WebSocket 和 SockJS 实现,包括对 STOMP 的支持 |
org.springframework | spring-jcl | Jakarta Commons Logging 日志系统 |
创建容器 ,从容器得到对象 ,而不是程序员自己new 对象,从而实现 对象间的解耦
UserService
public interface UserService {
public void addUser();
}
UserServiceImpl
@Override
public void addUser() {
//使用 spring容器 之前的做法 是 在这里 new 一个 dao 对象 程序员自己new
//UserDao userDao = new UserJdbcDaoImpl();
//现在 需求变了 要使用mybatis 操作数据库 硬编码 耦合度太高 我们希望 不改代码 来改变底层的实现
userDao = new UserMybatisDaoImpl();
userDao.addUser();
}
UserDao
public interface UserDao {
void addUser();
}
UserJdbcDaoImpl
public class UserJdbcDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("使用jdbc来添加用户");
}
}
UserMybatisDaoImpl
public class UserMybatisDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("使用mybatis来添加用户");
}
}
原来咱们的做法 没有spring容器的时候
@Test
public void test1(){
//使用 spring 容器之前的 做法 自己 new
UserService userService = new UserServiceImpl();
userService.addUser();
}
引入spring 容器, 从容器中获取对象
MyClassPathXmlApplicationContext
package com.glls.java2301.framework;
import com.glls.java2301.dao.UserDao;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* @date 2023/4/19
* @desc 山寨版IOC容器 来模拟 真实的 基于XML的 spring 容器
*/
public class MyClassPathXmlApplicationContext {
//用于 存放 解析完 xml文件之后的 bean 的 信息, 以 beanId 为key 以BeanDefinition 为值
private Map<String,BeanDefinition> beansMap = new HashMap<>();
//这个map 用于存放 spring 容器 创建出来的对象 在容器里面创建出来的对象 叫 bean
// 这个map 以 beanId 为 key 以反射创建出来的对象 为值
private Map<String,Object> objectMap = new HashMap<>();
public MyClassPathXmlApplicationContext() {
}
public MyClassPathXmlApplicationContext(String xmlPath) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
//这个构造方法 创建 容器对象 , 创建容器对象的时候 要把容器管理的bean 对象 也创建好
//咱们就得告诉 Spring 容器 它所管理的bean对象 有哪些 ,通常 咱们会通过 xml , 注解, java 配置类
//这些方式 告诉 spring 容器 管理的 bean 有哪些 ,这里 咱们 采用的方式 是 xml 的方式 告知spring 管理
// 哪些bean 所以 现在 咱们要做的事情 就是 解析 xml 文件 ,得到 要创建的 bean 的 信息
//1. 解析 xml 文件 得到 bean的信息 把bean 的信息 封装到 BeanDefinition ,每个bean 都会有自己的
//beanDefinition , 咱们把 很多 bean 的 beanDefinition 放入一个 map 中
parseXml(xmlPath);
//2.从 beansMap 中 获取bean 的信息 通过反射 创建对象 将创建出来的对象 放入 objectMap
createObject();
//3. di 依赖注入 第二部创建的对象的属性时没有值的
di(); //dependency injection 属性注入
}
//依赖注入 通过反射 调用对象的 set 方法 给对象的属性赋值
private void di() throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {
Set<Map.Entry<String, BeanDefinition>> entrySet = beansMap.entrySet();
Iterator<Map.Entry<String, BeanDefinition>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<String, BeanDefinition> beanDefinitionEntry = iterator.next();
String beanId = beanDefinitionEntry.getKey();
BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
//得到的bean 的property 集合
Map<String, PropertyDefinition> propsMap = beanDefinition.getPropsMap();
Set<Map.Entry<String, PropertyDefinition>> proSet = propsMap.entrySet();
Iterator<Map.Entry<String, PropertyDefinition>> proIter = proSet.iterator();
while (proIter.hasNext()){
Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = proIter.next();
//得到 property 的name
String proName = propertyDefinitionEntry.getKey();
PropertyDefinition propertyDefinition = propertyDefinitionEntry.getValue();
//ref 指向 属性对应的beanId
String ref = propertyDefinition.getRef();
//根据属性名 得到set方法 通过set方法给对象的属性赋值
//setUserDao
String setMethodName = "set" + proName.substring(0,1).toUpperCase()+proName.substring(1);
//通过反射 得到方法对象
String beanClass = beanDefinition.getBeanClass();
Class<?> aClass = Class.forName(beanClass);
Method[] declaredMethods = aClass.getDeclaredMethods();
for(Method m:declaredMethods){
//判断对象中 有没有这个 set 方法
if(m.getName().equals(setMethodName)){
//表示 有这个 set 方法
//得到对象 给这个对象的属性赋值 要反射调用这个对象的 set 方法
Object o = objectMap.get(beanId);
//得到 set 方法的参数
Object para = objectMap.get(ref);
//反射调用set 方法 给 对象o 的属性赋值
// userService.setUserDao(para);
m.invoke(o,para);
}
}
}
}
}
//创建对象
private void createObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//通过反射创建对象 要拿到类的全路径信息
Set<Map.Entry<String, BeanDefinition>> entrySet = beansMap.entrySet();
Iterator<Map.Entry<String, BeanDefinition>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<String, BeanDefinition> bean = iterator.next();
String beanId = bean.getKey();
BeanDefinition beanDefinition = bean.getValue();
//得到类的全路径名
String beanClass = beanDefinition.getBeanClass();
//通过反射创建对象
Object o = Class.forName(beanClass).newInstance();
//将创建好的对象 存入 obejctMap
objectMap.put(beanId,o);
}
}
private void parseXml(String xmlPath) throws DocumentException {
//读取配置文件 得到流对象
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(xmlPath);
//使用dom4j的api
SAXReader saxReader = new SAXReader();
//得到文档对象
Document doc = saxReader.read(resourceAsStream);
//得到根节点
Element rootElement = doc.getRootElement();
//遍历根节点
Iterator<Element> iterator = rootElement.elementIterator();
while (iterator.hasNext()){
Element bean = iterator.next();
String beanId = bean.attributeValue("id");
String beanClass = bean.attributeValue("class");
//System.out.println(beanId+"---"+beanClass);
//把解析出来的信息 存入 BeanDefinition 中
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanId(beanId);
beanDefinition.setBeanClass(beanClass);
//遍历bean 标签的子标签
Iterator<Element> proIterator = bean.elementIterator();
while (proIterator.hasNext()){
Element pro = proIterator.next();
String proName = pro.attributeValue("name");
String proRef = pro.attributeValue("ref");
//将bean 的 子节点 属性信息 封装到 PropertyDefinition 中
PropertyDefinition propertyDefinition = new PropertyDefinition();
propertyDefinition.setName(proName);
propertyDefinition.setRef(proRef);
//将属性信息 封装到 BeanDefinition 中的 propsMap
Map<String, PropertyDefinition> propsMap = beanDefinition.getPropsMap();
propsMap.put(proName,propertyDefinition);
}
//将这个 bean 的定义信息 封装到 beansMap
beansMap.put(beanId,beanDefinition);
}
}
//从容器拿对象的方法
public Object getBean(String beanName) {
return objectMap.get(beanName);
}
}
BeanDefinition
package com.glls.java2301.framework;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.HashMap;
import java.util.Map;
/**
* @date 2023/4/19
* @desc bean 的定义信息 来封装bean 的 信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BeanDefinition {
//解析xml 解析出来的数据 封装到这个类的对象中
private String beanId; // bean 的 名字 bean 在容器中的标识
private String beanClass; // 封装bean 的 全路径
private Map<String,PropertyDefinition> propsMap = new HashMap<>();
}
PropertyDefinition
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PropertyDefinition {
//property 节点的 name 属性值
private String name;
//property 节点的 ref 属性值
private String ref;
}
@Test
public void test2() throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
//山寨版的IOC容器
// 由容器 来 创建 管理 对象
// 创建咱们 的IOC 容器, 从容器中 获取 对象 ,这里 咱们模拟的情况是
//容器 创建好了 里面的bean 也创建好了 , 可以直接用了
String config= "applicationContext.xml";
//创建容器对象 告知容器对象扫描的 xml 配置文件 ,xml 文件中 记录了 spring 容器要管理的 bean的信息
MyClassPathXmlApplicationContext applicationContext = new MyClassPathXmlApplicationContext(config);
//UserDao userDao = (UserDao) applicationContext.getBean("userJdbcDao");
//UserDao userDao2 = (UserDao) applicationContext.getBean("userMybatisDao");
UserService userService = (UserService) applicationContext.getBean("userService");
//userDao.addUser();
//userDao2.addUser();
userService.addUser();
}
手写 ioc 容器 – 基于 xml 版本的 目的 理解 ioc 容器
详见 springday1 案例
后面有时间 再带大家写一个 基于注解版本的 理解 bean的生命周期
在工程下创建module |
---|
注意GAV坐标 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HY3THQDD-1683019927703)(Pictures/image-20230420091510936.png)] |
<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.0modelVersion>
<groupId>com.qfgroupId>
<artifactId>hello-springartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.9.RELEASEversion>
dependency>
dependencies>
project>
命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml
这里 咱们创建beans1.xml
<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类型
注意:spring容器知识帮助咱们创建对象 管理对象 ,所以 这个对象对应的类 还是需要咱们自己创建好
public class MyClass{
public void show(){
System.out.println("HelloWorld");
}
}
beans1.xml中的< beans >内部配置bean标签
<bean id="myClass" class="com.glls.java2301.pojo.MyClass">bean>
调用Spring工厂API(ApplicationContext接口)
public class TestSpringIocFactory {
@Test
public void test1(){
//1.创建spring 容器,通过配置文件 beans1.xml 告知容器 要管理的 bean 有哪些
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans1.xml");
//2. 通过id获取bean对象
//MyClass myClass = (MyClass) applicationContext.getBean("myClass");
MyClass myClass = applicationContext.getBean("myClass", MyClass.class);
//3. 使用对象
myClass.show();
}
}
Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖Jar包实现功能。
Spring常用功能的Jar包依赖关系 |
---|
配置文件中的顶级标签中包含了语义化标签的相关信息
- xmlns:语义化标签所在的命名空间。
- xmlns:xsi:XMLSchema-instance 标签遵循Schema标签标准。
- xsi:schemaLocation:xsd文件位置,用以描述标签语义、属性、取值范围等。
重点
】Inverse Of Controll:控制反转
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
补充: 什么是IOC
1.控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
2.使用 IOC 目的:为了耦合度降低
3.入门案例就是 IOC 实现
IOC 底层原理
xml 解析、工厂模式、反射
public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService {
// !!!强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健!!
private UserDAO userDAO= new UserDAOImpl();
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {
// !!!不再耦合任何DAO实现!!!,消除不稳健因素!!
private UserDAO userDAO;
// 为userDAO定义set/get,允许userDAO属性接收spring赋值
//Getters And Setters
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
<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="myClass" class="com.glls.java2301.pojo.MyClass">bean>
<bean id="userJdbcDao" class="com.glls.java2301.dao.impl.UserJdbcDaoImpl" >bean>
<bean id="userMybatisDao" class="com.glls.java2301.dao.impl.UserMybatisDaoImpl">bean>
<bean id="userService" class="com.glls.java2301.service.impl.UserServiceImpl">
<property name="userDao" ref="userJdbcDao">property>
bean>
beans>
此时,如果需要更换其他UserDAO实现类,则UserServiceImpl不用任何改动!
则此时的UserServiceImpl组件变得更加稳健!
BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人 员进行使用
下面这个图 是找到ApplicationContext 类 , 按 F4 可以展示这个接口的 实现类信息
Bean的管理指的是两个操作:1.Spring创建对象 2.Spring注入属性
Bean的常见的管理操作有三种方式:1.基于 xml 配置文件方式实现 2.基于注解方式实现 3.基于配置类
重点
】在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
创建对象时,Spring工厂会通过Set方法为对象的属性赋值。
public class User {
private Integer id;
private String password;
private String sex;
private Integer age;
private Date bornDate;
private String[] hobbys;
private Set<String> phones;
private List<String> names;
private Map<String,String> countries;
private Properties files;
//Getters And Setters
}
<bean id="user" class="com.glls.java2301.pojo.User">
<property name="id" value="1001" />
<property name="password" value="123456" />
<property name="sex" value="male" />
<property name="age" value="20" />
<property name="bornDate" value="1990/1/1" />
bean>
<bean id="u1" class="com.qf.spring.part1.injection.User">
<property name="hobbys">
<array>
<value>Runvalue>
<value>Swimvalue>
<value>Climbvalue>
array>
property>
<property name="phones">
<set>
<value>13777777777value>
<value>13888888888value>
<value>13999999999value>
set>
property>
<property name="names">
<list>
<value>tomvalue>
<value>jackvalue>
<value>marryvalue>
list>
property>
<property name="countries">
<map>
<entry key="CN" value="China" />
<entry key="US" value="America" />
<entry key="KR" value="Korea" />
map>
property>
<property name="files">
<props>
<prop key="first">Oneprop>
<prop key="second">Twoprop>
<prop key="third">Threeprop>
props>
property>
bean>
<bean id="address" class="com.glls.java2301.pojo.Address">
<property name="city" value="郑州">property>
<property name="province" value="河南">property>
bean>
<bean id="user" class="com.glls.java2301.pojo.User">
<property name="address" ref="address">property>
bean>
创建对象时,Spring工厂会通过构造方法为对象的属性赋值。
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
//Constructors
public Student(Integer id , String name , String sex , Integer age){
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
}
<bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student">
<constructor-arg name="id" value="1234" />
<constructor-arg name="name" value="tom" />
<constructor-arg name="age" value="20" />
<constructor-arg name="sex" value="male" />
bean>
不用在配置中 指定为哪个属性赋值,及赋什么值.
由spring自动根据某个 “原则” ,在工厂中查找一个bean,为属性注入属性值
也叫自动装配 根据装配规则 (属性名和属性类型) spring 自动将匹配的属性值进行注入
演示过程
1.根据属性名称自动注入
在 bean 标签 使用 autowire 配置自动装配,autowire 属性 常用两个值:
byName 根据属性名称注入 注入值的 bean 的 id 和 类的属性名一样
byType 根据属性类型注入 如果 容器内 有多个这个类型的bean 那么 容器如果不知道注入哪一个 就会报错
public class UserServiceImpl implements UserService {
private UserDao userDao;
......
}
<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="userService" class="com.glls.java2301.service.impl.UserServiceImpl" autowire="byType">
bean>
<bean id="userJdbcDao" class="com.glls.java2301.dao.impl.UserJdbcDaoImpl">bean>
beans>
<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="userService" class="com.glls.java2301.service.impl.UserServiceImpl" autowire="byName">
bean>
<bean id="userDao" class="com.glls.java2301.dao.impl.UserMybatisDaoImpl">bean>
beans>
在spring 容器里 设置 bean 实例 是 单例 还是 多例 , 默认情况下 bean 是 单例的
配置< bean scope=“singleton | prototype” />
<bean id="userService" class="com.glls.java2301.service.impl.UserServiceImpl" autowire="byName" scope="prototype">
注意 和 前面的 BeanFactory 的区别
补充:spring有两种类型的bean ,一种是 普通bean ,另外一种是 工厂bean (FactoryBean)
普通的bean : 在配置文件中定义的bean的类型 就是返回类型
工厂bean 在配置文件中定义的bean类型可以和返回的bean类型不一样
作用:让Spring可以创建复杂对象、或者无法直接通过反射创建的对象。
FactoryBean解决复杂对象创建 |
---|
接口方法描述 |
---|
package com.glls.java2301.factorybean;
import org.springframework.beans.factory.FactoryBean;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* @date 2023/4/20
* @desc 一种特殊的 bean ,用来创建其他 bean 的 bean
*
* 这种 bean 称为 工厂bean , 一般是用来 创建 复杂对象的 bean
*/
public class MyFactoryBean implements FactoryBean<Connection> {
@Override
public Connection getObject() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true", "root", "123456");
return connection;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
配置与获取方式 |
---|
<?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
spring有两种类型的bean ,一种是 普通bean ,另外一种是 工厂bean (FactoryBean)
普通的bean : 在配置文件中定义的bean的类型 就是返回类型
工厂bean 在配置文件中定义的bean类型可以和返回的bean类型不一样
-->
<bean id="myFactoryBean" class="com.glls.java2301.factorybean.MyFactoryBean"></bean>
</beans>
获取FactoryBean接口的实现类对象,而非getObject()所生产的对象。 |
---|
工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。
提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)
如果默认是多例bean 首先创建多例bean 会消耗资源 还会占用堆空间
什么生命周期?
bean 对象从创建到销毁的过程
步骤
要考虑 bean 的后置处理器 BeanPostProcessor
创建一个类实现BeanPostProcessor 重写 他的两个方法
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在bean 初始化之前执行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在bean初始化之后执行");
if(bean instanceof User){
User user = (User) bean;
user.setName("貂蝉");
return user;
}
return bean;
}
}
在配置文件 配置这个 bean
<bean id="myPostProcessor" class="com.qf.postprocessor.MyBeanPost"></bean>
总结: 面试答案
自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
自定义销毁方法:添加“destroy-method”属性,Spring则会在销毁对象之前,调用此方法。
销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。
初始化注解、销毁注解
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@PostConstruct //初始化
public void init(){
System.out.println("init method executed");
}
@PreDestroy //销毁
public void destroy(){
System.out.println("destroy method executed");
}
**单例bean:**singleton
随工厂启动创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁
**多例bean:**prototype
被使用时创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
功能分离 |
---|
通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。
静态代理 |
---|
House
public interface House {
//出租
public void rent();
}
HouseOwner
/**
* @date 2023/4/21
* @desc 房东 真实对象
*/
public class HouseOwner implements House{
@Override
public void rent() {
System.out.println("签合同");
System.out.println("收房租");
}
}
Intermediary
/**
* @date 2023/4/21
* @desc 中介 是 代理对象
*/
public class Intermediary implements House{
// 代理哪个真实对象
private HouseOwner houseOwner;
public Intermediary() {
}
public Intermediary(HouseOwner houseOwner) {
this.houseOwner = houseOwner;
}
@Override
public void rent() {
System.out.println("发布租房信息");
System.out.println("带租户看房");
//调真实对象的方法 也就是被代理的对象
houseOwner.rent();
System.out.println("维修");
System.out.println("转租");
System.out.println("收中介费");
System.out.println("收违约金");
}
}
Tenant
public class Tenant {
public static void main(String[] args) {
Tenant tenant = new Tenant();
tenant.rentHouse();
}
private void rentHouse() {
//房子的主人 房子的拥有者
HouseOwner houseOwner = new HouseOwner();
Intermediary intermediary = new Intermediary(houseOwner);
intermediary.rent();
}
}
动态创建代理类的对象,为原始类的对象添加辅助功能。
代理对象和真实对象的关系 像是兄弟 代理对象 对真实对象进行增强
个人代码
接口
public interface Marry {
public void marry();
public int money();
}
真实对象
public class You implements Marry {
@Override
public void marry() {
System.out.println("终身大事");
}
@Override
public int money() {
int num = 10;
System.out.println("花了"+num+"块钱");
return num;
}
}
封装的一个工具类
public class JdkProxy implements InvocationHandler {
private Object target; //真实对象 被代理的对象
//把真实对象传进来 返回代理对象
public Object getProxy(Object target){
this.target = target;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//对真实对象的功能增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备工作");
Object result = method.invoke(target, args);
System.out.println("收尾工作");
if(target instanceof Marry){
return 100;
}
return result;
}
}
测试
public class TestJdkProxy {
public static void main(String[] args) {
//真实对象
You you = new You();
JdkProxy jdkProxy = new JdkProxy();
//得到代理对象
Marry proxy = (Marry) jdkProxy.getProxy(you);
//调用代理对象的方法
proxy.marry();
int money = proxy.money();
System.out.println(money);
}
}
代理对象和真实对象的关系 就像是 父子
final OrderService os = new OrderServiceImpl();
Enhancer cnh = new Enhancer();//1.创建字节码曾强对象
enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口)
enh.setCallback(new InvocationHandler(){//3.设置回调函数(额外功能代码)
@Override
public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{
System.out.println("start...");
Object ret = method.invoke(os,args);
System.out.println("end...");
return ret;
}
});
OrderService proxy = (OrderService)enh.create();//4.创建动态代理类
proxy,createOrder();
个人代码
真实对象
public class You {
public void marry() {
System.out.println("终身大事");
}
public int money() {
int num = 10;
System.out.println("花了"+num+"块钱");
return num;
}
}
工具类 封装得到代理对象的方法 和 回调方法
package com.qf.proxy.cglibproxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class CglibProxy implements InvocationHandler {
//真实对象
private Object target;
//传入真实对象 得到代理对象
public Object getProxy(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();//1.创建字节码曾强对象
enhancer.setSuperclass(target.getClass());//2.设置父类(等价于实现原始类接口)
//设置回调函数
enhancer.setCallback(this);
//返回代理对象
return enhancer.create();
}
//真实对象方法增强
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("准备工作");
Object result = method.invoke(target, objects);
System.out.println("收尾工作");
return result;
}
}
测试
package com.qf.proxy.cglibproxy;
public class TestCglibProxy {
public static void main(String[] args) {
//真实对象
You you = new You();
//工具类对象
CglibProxy cglibProxy = new CglibProxy();
//得到代理对象
You proxy = (You) cglibProxy.getProxy(you);
proxy.marry();
}
}
重点
】AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
什么是AOP ?
1.面向切面编程 利用AOP 可以对业务逻辑的各个部分进行隔离 从而使业务逻辑的各部分之间 耦合度降低,提高程序的可重用性,提好了开发效率,通俗的讲 可以实现不修改源代码的方式,在核心业务里面 添加新的功能
AOP 底层的原理 就是 动态代理 ,真正干活的 bean 是 代理bean , 代理bean 对真实bean 功能增强
- 连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
- 说白了 类中的哪些方法 可以被增强 这些方法 就称为是 连接点
- 切入点(Pointcut):被Spring切入连接点。
- 真正被增强的方法 称为 切入点
- 通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知,最终通知等。
- 实际增强的逻辑部分 称为通知(增强)
- 目标对象(Target):代理的目标对象 真实对象
- 引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。
- 织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
- 代理(Proxy):被AOP织入通知后,产生的结果类。
- 切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。是一个动作 把通知 应用到 切入点的过程
切点: 真正要被增强的方法
通知:增强的业务代码
切面:切点 + 通知
Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。
引入AOP相关依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.2.9.RELEASEversion>
dependency>
spring-context.xml引入AOP命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
beans>
定义原始类
package com.glls.java2301.service;
/**
* @date 2023/4/21
* @desc
*/
public interface UserService {
public void save();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
//增删改 的代码 执行之前 需要开启事务
System.out.println("save user");
//增删改 的代码 执行之后 需要提交 或者 回滚事务
}
}
下面是 基于 Schema-based 的 aop实现方式 得认识
四种通知
MyBeforeAdvice 前置通知
AfterReturningAdvice 后置返回通知
注意 Schema-based 没有最终通知
MethodInterceptor 环绕通知
ThrowsAdvice 异常通知
定义通知类(添加额外功能)
package com.glls.java2301.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @date 2023/4/21
* @desc 基于 Schema-based 的 前置通知
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
/**
* @param method 切点的方法对象
* @param args 切点的方法参数
* @param target 被代理的对象
*
* */
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知");
System.out.println("开启事务");
}
}
定义bean标签
<bean id="userService" class="com.glls.java2301.service.impl.UserServiceImpl">bean>
<bean id="myBeforeAdvice" class="com.glls.java2301.advice.MyBeforeAdvice">bean>
定义切入点(PointCut)
形成切面(Aspect)
<aop:config>
<aop:pointcut id="point1" expression="execution(* com.glls.java2301.service.impl.UserServiceImpl.save(..))"/>
aop:config>
<aop:config>
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="point1" >aop:advisor>
aop:config>
下面是基于 AspectJ 的 AOP 实现方式
方式1 基于 xml 的配置
创建 通知类
package com.qf.aspectjadvice;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @ClassName : MyAspectJAdvice
* @Description : AspectJ 的 通知类 无需实现接口
*/
public class MyAspectJAdvice {
public void mybefore(){
System.out.println("aspectj的前置通知");
}
public void myafter(){
System.out.println("aspectj的后置通知");
}
public Object myaround(ProceedingJoinPoint p) throws Throwable {
System.out.println("环绕前置");
Object result = p.proceed();
System.out.println("环绕后置");
return result;
}
}
xml 配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.qf">context:component-scan>
<bean id="myaspectj" class="com.qf.aspectjadvice.MyAspectJAdvice">bean>
<aop:config>
<aop:aspect ref="myaspectj">
<aop:pointcut id="aspectjpoint1" expression="execution(* save(..))"/>
aop:aspect>
<aop:aspect ref="myaspectj">
<aop:pointcut id="aspectjpoint2" expression="execution(* com.qf.service.impl.OrderServiceImpl.save(..))"/>
aop:aspect>
<aop:aspect ref="myaspectj">
<aop:before method="mybefore" pointcut-ref="aspectjpoint1">aop:before>
<aop:after method="myafter" pointcut-ref="aspectjpoint1">aop:after>
<aop:around method="myaround" pointcut-ref="aspectjpoint2">aop:around>
aop:aspect>
aop:config>
beans>
测试:
@Test
public void test2(){
// 基于 Aspectj 的 xml 的 AOP 了解 看得懂
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans2.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
userService.save();
//userService.delete(1);
Order order = new Order(1, "订单描述");
//orderService.save(order);
}
方式2 aspectj 基于 注解的aop 实现 要求掌握
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.qf">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
package com.glls.java2301.aspectjannoadvice;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @date 2023/4/23
* @desc 基于AspectJ的 注解形式的aop实现 要求 熟练掌握
*
* 这种方式 很简洁 ,只需要在当前类 写通知 和 切点元素 ,无需在 配置文件中 进行繁琐的配置
*
*/
@Aspect // 当前类 是一个 切面类
@Component // 这个注解 表明 当前类的对象 是spring 管理的bean
public class AspectJAnnoAdvice {
// @Before 表示前置通知
//@Before("execution(* save(..))")
//public void before(){
// System.out.println("基于aspectj注解形式的前置通知,要求大家掌握");
//}
@Pointcut(value = "execution(* save(..))")
public void point1(){
System.out.println("切点1");
}
@Pointcut(value = "execution(* delete(..))")
public void point2(){
System.out.println("切点2");
}
@Before("point1()")
public void before(){
System.out.println("基于aspectj注解形式的前置通知,要求大家掌握");
}
@After("point2()")
public void after(){
System.out.println("基于aspectj注解形式的后置通知,要求大家掌握");
}
@Around("point2()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前置");
Object result = joinPoint.proceed();
System.out.println("环绕后置");
System.out.println(result);
return result;
}
@AfterReturning("point2()")
public void afterreturning(){
//切点正常执行 没有异常 才会执行 最终通知
System.out.println("最终通知");
}
@AfterThrowing(value = "point2()",throwing = "ex")
public void afterthrowing(Exception ex){
System.out.println("异常通知");
System.out.println("异常信息:"+ ex.getMessage());
}
}
通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。
进而彻底解决了辅助功能冗余的问题;
业务类中职责单一性得到更好保障;
辅助功能也有很好的复用性。
定义通知类,达到通知效果
前置通知:MethodBeforeAdvice
后置通知:AfterAdvice
后置通知:AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值
异常通知:ThrowsAdvice
环绕通知:MethodInterceptor
没有必要把通知的执行顺序 记得非常精确 因为 spring 新版本 5 和 之前的旧版本 通知的执行顺序 不一样
根据表达式通配切入点
<aop:pointcut id="myPointCut" expression="execution(* *(com.qf.aaron.aop.basic.User))" />
<aop:pointcut id="myPointCut" expression="execution(* save())" />
<aop:pointcut id="myPointCut" expression="execution(* save(..))" />
<aop:pointcut id="myPointCut" expression="execution(com.qf.aaron.aop.basic.User *(..))" />
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.UserServiceImpl.*(..))" />
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.*.*(..))" />
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop..*.*(..))" />
切入点表达式:expression 知道对哪个类的那个方法进行增强
语法结构: execution([权限修饰符] [返回值类型] [类全路径] [方法名称] ([参数列表])
spring底层,包含了jdk代理和cglib代理两种动态代理生成机制
基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
class DefaultAopProxyFactory{
// 该方法中明确定义了 JDK代理和CGLib代理的选取规则
// 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
public AopProxy createAopProxy(){...}
}
spring中定义了很多后处理器;
每个bean在创建完成之前 ,都会有一个后处理过程,即再加工,对bean做出相关改变和调整;
spring-AOP中,就有一个专门的后处理器,负责通过原始业务组件(Service),再加工得到一个代理组件。
常用后处理器 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flEGgsYu-1683019927709)(https://gllspictures.oss-cn-beijing.aliyuncs.com/img/系统后处理器.jpg)] |
/**
* 定义bean后处理器
* 作用:在bean的创建之后,进行再加工
*/
public class MyBeanPostProcessor implements BeanPostProcessor{
/**
* 在bean的init方法之前执行
* @param bean 原始的bean对象
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器 在init之前执行~~~"+bean.getClass());
return bean;
}
/**
* 在bean的init方法之后执行
* @param bean postProcessBeforeInitialization返回的bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器 在init之后执行~~~"+bean.getClass());
return bean;// 此处的返回是 getBean() 最终的返回值
}
}
<bean class="com.qianfeng.beanpostprocessor.MyBeanPostProcessor">bean>
构造 》 注入属性 满足依赖 》 后处理器前置过程 》 初始化 》后处理器后置过程 》 返回 》 销毁
记住 记住 记住 记住 记住 记住
1.如果是单例 bean , 随着容器的创建而创建 即 实例化,多例bean 是 获取的时候 实例化
2.属性注入
3.后处理器前置过程 即在初始化方法之前执行的 方法 postProcessBeforeInitialization
4.初始化方法
5.后处理器后置过程 即在初始化方法之后执行的 方法 postProcessAfterInitialization aop动态代理就在这一步
6.得到最终的 bean
7.销毁
// AbstractAutoProxyCreator是 AspectJAwareAdvisorAutoProxyCreator的父类
// 该后处理器类中的 wrapIfNecessary方法即动态代理生成过程
AbstractAutoProxyCreator#postProcessAfterInitialization(Object bean, String beanName){
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 开始动态定制代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
重点
】1.添加依赖
4.0.0
com.glls.java2301
sm
1.0-SNAPSHOT
war
sm Maven Webapp
http://www.example.com
UTF-8
1.8
1.8
org.springframework
spring-context
5.2.9.RELEASE
org.springframework
spring-aspects
5.2.9.RELEASE
junit
junit
4.12
org.projectlombok
lombok
1.18.12
mysql
mysql-connector-java
8.0.18
org.mybatis
mybatis
3.4.6
org.springframework
spring-jdbc
5.2.9.RELEASE
org.mybatis
mybatis-spring
1.3.2
com.alibaba
druid
1.1.16
org.springframework
spring-web
5.2.9.RELEASE
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet
jsp-api
2.0
provided
javax.servlet
jstl
1.2
org.slf4j
slf4j-api
1.7.25
ch.qos.logback
logback-core
1.2.3
ch.qos.logback
logback-classic
1.2.3
log4j
log4j
1.2.17
org.logback-extensions
logback-ext-spring
0.1.4
sm
maven-clean-plugin
3.1.0
maven-resources-plugin
3.0.2
maven-compiler-plugin
3.8.0
maven-surefire-plugin
2.22.1
maven-war-plugin
3.2.2
maven-install-plugin
2.5.2
maven-deploy-plugin
2.8.2
src/main/java
**/*.xml
src/main/resources
**/*.xml
**/*.properties
**/*.ini
准备数据源配置文件 db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=123456
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.glls.java2301">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
<context:property-placeholder location="classpath:db.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxWait" value="60000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
<property name="configLocation" value="classpath:mybatis-config.xml">property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.glls.java2301.mapper">property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">property>
bean>
beans>
mybatis的配置文件
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.glls.java2301.pojo" />
typeAliases>
<mappers>
<package name="com.glls.java2301.mapper"/>
mappers>
configuration>
添加依赖
org.slf4j
slf4j-api
1.7.25
ch.qos.logback
logback-core
1.2.3
ch.qos.logback
logback-classic
1.2.3
log4j
log4j
1.2.17
org.logback-extensions
logback-ext-spring
0.1.4
添加日志的配置文件 logback.xml
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<Target>System.outTarget>
<Encoding>UTF-8Encoding>
<encoder>
<pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} %5p [%t] [%c{1}]:%L - %m%n
pattern>
encoder>
appender>
<appender name="logfile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<Encoding>UTF-8Encoding>
<encoder>
<pattern>%d %p [%t] [%c]:%L - %m%npattern>
encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUGlevel>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/%d{yyyy-MM-dd-HH'.log'}fileNamePattern>
rollingPolicy>
appender>
<logger name="org.springframework" level="WARN" />
<logger name="org.springframework.remoting" level="WARN" />
<logger name="org.springframework.scheduling.quartz" level="WARN" />
<logger name="org.springframework.data.jpa" level="DEBUG" />
<logger name="org.cometd" level="WARN" />
<logger name="ch.qos.logback" level="WARN" />
<logger name="com.springapp.mvc" level="DEBUG" />
<logger name="com.glls.java2301.mapper" level="DEBUG">logger>
<root level="ERROR">
<appender-ref ref="stdout" />
<appender-ref ref="logfile" />
root>
configuration>
配置到 web.xml 中 通过监听器 读取日志的配置文件
<context-param>
<param-name>logbackConfigLocationparam-name>
<param-value>classpath:logback.xmlparam-value>
context-param>
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListenerlistener-class>
listener>
整合 servlet ,后期 换成 springmvc ,当前先用servlet
1.添加依赖
org.springframework
spring-web
5.2.9.RELEASE
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet
jsp-api
2.0
provided
javax.servlet
jstl
1.2
2.在web.xml 中 添加一个监听器 用于读取spring 的配置文件 创建 容器
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
package com.qf.web;
import com.qf.pojo.User;
import com.qf.service.UserService;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName : LoginServlet
* @Author : glls
* @Date: 2021/8/10 14:43
* @Description :
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private UserService userService;
@Override
public void init() throws ServletException {
WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
userService = applicationContext.getBean("userService", UserService.class);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
//有了spring 管理 bean ,就不要再自己new 对象了 ,所以
//从容器 把 userService 取出来
User user = new User();
user.setName(username);
user.setPassword(password);
User result = userService.login(user);
System.out.println(result);
}
}
重点
】什么是事务?
事务是数据库操作的基本单元,逻辑上的一组操作,要么都成功,要么都失败,也就是说只有一组操作中 有一个失败了,那么这组操作都失败
举个例子
wsc转账500给wjl
update t_account set money = money-500 where accountid = 1 wsc 的账户 减500
update t_account set money = money+500 where accountid=2 wjl的账户 加 500
事务的特性 ACID
A atomicity 原子性 一组操作 整体不可拆分要么都成功 要么都失败
C consistency 一致性 数据在事务的前后,业务整体一致
I isolation 隔离性 事务之间 互相隔离
D durability 持久性 一旦事务执行成功 数据一定会落盘在数据库
3.准备事务的环境
账户表
service 定义转账方法
//转账操作
@Override
public void transferMoney(int from, int to, double money) {
//from 给 to 转了 money
//一个账户 扣钱
accountMapper.reduceMoney(from,money);
//模拟一个异常
int i = 5/0;
//一个账户加钱
accountMapper.addMOney(to,money);
}
dao
@Update("update t_account set money= money - #{money} where userid= #{from} ")
void reduceMoney(@Param("from") int from,@Param("money") double money);
@Update("update t_account set money = money + #{money} where userid = #{to} ")
void addMOney(@Param("to")int to, @Param("money") double money);
转账过程中 模拟异常,出现一个账户扣钱了,另一个账户却没有加钱 数据不一致问题 怎么解决?
使用事务 进行解决
spring提供了 事务的解决方案 底层原理是 AOP
第一步 配置DataSourceTransactionManager
事务管理器,其中持有DataSource,可以控制事务功能(commit,rollback等)。
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource的Bean,否则事务控制失败!!!
第二步 配置事务通知
基于事务管理器,进一步定制,生成一个额外功能:Advice。
此Advice可以切入任何需要事务的方法,通过事务管理器为方法控制事务。
<tx:advice id="txadvice" transaction-manager="tx">
<tx:attributes>
<tx:method name="*User" rollback-for="Exception"/>
<tx:method name="query*" propagation="SUPPORTS"/>
<tx:method name="*"/>
tx:attributes>
tx:advice>
第三步 配置事务切面
<aop:config>
<aop:pointcut id="txpoint" expression="execution(* com.glls.java2301.service.impl.AccountServiceImpl.*(..))"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="txpoint">aop:advisor>
aop:config>
第四步 进行测试
在 applicationContext2.xml 中 进行 基于注解的事务配置
第一步 还是配置事务管理器
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
第二步 开启事务的注解
<!-- 2. 开启事务的注解-->
<tx:annotation-driven transaction-manager="tx"/>
第三步 在需要事务的方法上 使用注解
@Transactional
public void transferMoney(int from, int to, double money) {
......
第四步 测试
isolation
隔离级别
名称 | 描述 |
---|---|
default | (默认值)(采用数据库的默认的设置) (建议) |
read-uncommited | 读未提交 |
read-commited | 读已提交 (Oracle数据库默认的隔离级别) |
repeatable-read | 可重复读 (MySQL数据库默认的隔离级别) |
serialized-read | 序列化读 |
隔离级别由低到高为:read-uncommited < read-commited < repeatable-read < serialized-read
安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。
并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。
事务并发时的安全问题
问题 | 描述 |
---|---|
脏读 | 一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止 |
不可重复读 | 一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止 |
幻影读 | 幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。mysql 幻读的详解、实例及解决办法 - SegmentFault 思否 |
SELECT @@tx_isolation; -- 查看 当前 隔离级别
SHOW VARIABLES LIKE 'autocommit' -- 查看自动提交 是否开启
SET autocommit=0; -- 关闭自动提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 设置 读未提交 隔离级别 会出现脏读 不可重复度 幻读
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 设置 读已提交 隔离级别 可出现 不可重复度 幻读
SET SESSION TRANSACTION ISOLATION LEVEL READ REPEATABLE READ; -- 设置 可重复度 隔离级别 可出现幻读
#步骤1
START TRANSACTION; -- 开启事务
UPDATE t_order SET productNum = 3 WHERE userId = 10;
#步骤3
-- 提交 或者 回滚
COMMIT; -- 提交事务
ROLLBACK; -- 回滚事务
//@Transactional(isolation = Isolation.SERIALIZABLE) // 序列化读的隔离级别 可以避免脏读 不可重复读 幻读
@Transactional(isolation = Isolation.REPEATABLE_READ) // 可重复读的隔离级别 可以避免脏读 不可重复读 但是会出现 幻读
//@Transactional(isolation = Isolation.READ_COMMITTED) // 读已提交的隔离级别 可以避免脏读 但是会出现 不可重复读 幻读
//@Transactional(isolation = Isolation.READ_UNCOMMITTED) // 读未提交的隔离级别 会出现脏读 不可重复读 幻读
public List<Order> findOrderById(Integer id) {
List<Order> list1 = orderMapper.findOrderByUserId(id);
System.out.println(list1);
System.out.println("----------------------");
List<Order> list2 = orderMapper.findOrderByUserId(id);
return list1;
}
propagation
传播行为
当涉及到事务嵌套(Service调用Service)时,可以设置:
SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)
REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改)
参考资料: 事务隔离级别和传播行为_Spring事务的传播行为和隔离级别_weixin_39604685的博客-CSDN博客
orderService
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public Integer addOrder(Order order) throws ClassNotFoundException {
//添加订单
int result = orderMapper.addOrder(order);
//int i = 5/0;
//Class.forName("java.lang.String2"); // 默认 对检查时异常 不回滚
// 扣减库存
//int result2 = updateStock(order);
//stockMapper.updateStock(order);
int result2 = stockService.updateStock(order);
// 扣减账户
//int result3 = updateAccount(order);
//accountMapper.updateAccount(order);
int result3 = accountService.updateAccount(order);
return result3;
}
stockService
@Override
//@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public int updateStock(Order order) {
return stockMapper.updateStock(order);
}
accountService
@Override
//@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public int updateAccount(Order order) {
return accountMapper.updateAccount(order);
}
readonly
读写性
true:只读,可提高查询效率。(适合查询)
false:可读可写。 (默认值)(适合增删改)
timeout
事务超时时间
当前事务所需操作的数据被其他事务占用,则等待。
- 100:自定义等待时间100(秒)。
- -1:由数据库指定等待时间,默认值。(建议)
rollback-for
回滚属性
如果事务中抛出 RuntimeException,则自动回滚
如果事务中抛出 CheckException(非运行时异常 Exception),不会自动回滚,而是默认提交事务
处理方案 : 将CheckException转换成RuntimException上抛,或 设置 rollback-for=“Exception.class”
1.什么是注解
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置
2.spring针对Bean管理创建对象提供的注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
3.基于注解方式 实现对象创建
1.需要aop 依赖的支持 spring-aop
2.开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.glls.java2301">context:component-scan>
beans>
3.创建类 在类上添加创建bean 的注解
//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService – userService
4.开启组件扫描细节配置
<context:component-scan base-package="com.glls" >
context:component-scan>
5.基于注解的方式 实现属性注入
(1)@Autowired:先根据类型注入 再根据名字注入
第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
(2)@Qualifier:根据名称进行注入
这个@Qualifier 注解的使用,和上面@Autowired 一起使用
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1")
//根据名称进行注入
private UserDao userDao;
(3)@Resource:先根据名字注入 再根据类型注入
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
(4)@Value:注入普通类型属性
@Value(value = "abc")
private String name;
6、完全注解开发
(1)创建配置类,替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.glls"})
public class SpringConfig {
}
(2)编写测试类
@Test
public void testService2() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
用于替换自建类型组件的
标签;可以更快速的声明bean
@Service 业务类专用
@Repository dao实现类专用
@Controller web层专用@Component 通用
@Scope 用户控制bean的创建模式
// @Service说明 此类是一个业务类,需要将此类纳入工厂 等价替换掉
// @Service默认beanId == 首字母小写的类名"userServiceImpl"
// @Service("userService") 自定义beanId为"userService"
@Service //声明bean,且id="userServiceImpl"
@Scope("singleton") //声明创建模式,默认为单例模式 ;@Scope("prototype")即可设置为多例模式
public class UserServiceImpl implements UserService {
...
}
用于完成bean中属性值的注入
- @Autowired 基于类型自动注入 先根据类型注入 如果找到多个 再根据名称注入
- @Resource 基于名称自动注入 先根据名称注入 如果根据名称没找到 则根据类型找 根据类型 找到多个 则报错 找到一个则注入
- @Qualifier(“userDAO”) 限定要自动注入的bean的id,一般和@Autowired联用
- @Value 注入简单类型数据 (jdk8种+String)
@Service
public class UserServiceImpl implements UserService {
@Autowired //注入类型为UserDAO的bean
@Qualifier("userDAO2") //如果有多个类型为UserDAO的bean,可以用此注解从中挑选一个
private UserDAO userDAO;
}
@Service
public class UserServiceImpl implements UserService {
@Resource("userDAO3") //注入id=“userDAO3”的bean
private UserDAO userDAO;
/*
@Resource //注入id=“userDAO”的bean
private UserDAO userDAO;
*/
}
public class XX{
@Value("100") //注入数字
private Integer id;
@Value("shine") //注入String
private String name;
}
小结:
四个创建bean的注解: @Controller @Service @Repository @Component
注入依赖bean的注解: (@Autowired 搭配 @Qualifier 指定名字) @Resource
注入普通属性的注解:@Value
标识配置类的注解:@Configuration
指定扫描包的注解 : @ComponentScan(basePackages = {“com.glls.java2301”})
指定优先级的注解:@Primary
指定初始化方法的注解:@PostConstruct
指定销毁bean 的时候调用的方法:@PreDestroy
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {
// 定义切入点
@Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")
public void pc(){}
@Before("pc()") // 前置通知
public void mybefore(JoinPoint a) {
System.out.println("target:"+a.getTarget());
System.out.println("args:"+a.getArgs());
System.out.println("method's name:"+a.getSignature().getName());
System.out.println("before~~~~");
}
@AfterReturning(value="pc()",returning="ret") // 后置通知
public void myAfterReturning(JoinPoint a,Object ret){
System.out.println("after~~~~:"+ret);
}
@Around("pc()") // 环绕通知
public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
System.out.println("interceptor1~~~~");
Object ret = p.proceed();
System.out.println("interceptor2~~~~");
return ret;
}
@AfterThrowing(value="pc()",throwing="ex") // 异常通知
public void myThrows(JoinPoint jp,Exception ex){
System.out.println("throws");
System.out.println("===="+ex.getMessage());
}
}
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>4.3.6.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
可以免去工厂的创建过程;
可以直接将要测试的组件注入到测试类。
集成Junit 5
org.springframework
spring-test
5.2.9.RELEASE
org.junit.jupiter
junit-jupiter-api
5.5.2
test
测试类
@SpringJUnitConfig(locations = "classpath:applicationContext2.xml")
class OrderServiceImplTest {
@Autowired
private OrderService orderService;
@Test
void findOrderByUserId() {
List<Order> orderByUserId = orderService.findOrderByUserId(10);
}
}
总结:
spring5 新的模块 webflux
1、SpringWebflux 介绍
(1)是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 使用 当前一种比较流程响应式编程出现的框架。
(2)使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,Webflux 是一种异步非阻 塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相关 API 实现 的。
(3)解释什么是异步非阻塞
(4)Webflux 特点:
第一 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以 Reactor 为基础实现响应式编程
第二 函数式编程:Spring5 框架基于 java8,Webflux 使用 Java8 函数式编程方式实现路由请求
(5)比较 SpringMVC
第一 两个框架都可以使用注解方式,都运行在 Tomet 等容器中
第二 SpringMVC 采用命令式编程,Webflux 采用异步响应式编程
2、响应式编程(Java 实现)
(1)什么是响应式编程 响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便 地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。 电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公 式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
3.手写spring 伪源码
在spring包下创建容器类 GllsApplicationContext
个人学习
源码分析
ClassPathXmlApplicationContext:
super(parent); 此时 传进来的super 为空, 在这个方法中 调父类AbstractApplicationContext 构造方法 创建了资源解析器
/**
Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
setConfigLocations(configLocations);
调父类构造
这个方法由子类重写
总结: resolvePath(String path) 方法做了什么事情?
1.实例化 Environment
2.解析传进来的 path
refresh()方法
AbstractApplicationContext 类中
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
cationContext with no parent.
>*/
>public AbstractApplicationContext() {
> this.resourcePatternResolver = getResourcePatternResolver();
>}
> setConfigLocations(configLocations);
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170316872.png" alt="image-20210731170316872" style="zoom:80%;" />
>
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170329424.png" alt="image-20210731170329424" style="zoom:80%;" />
>
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170341320.png" alt="image-20210731170341320" style="zoom:80%;" />
>
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170621745.png" alt="image-20210731170621745" style="zoom:80%;" />
[外链图片转存中...(img-18iiTbpW-1683019925567)]
> 调父类构造
[外链图片转存中...(img-ffaW8PFC-1683019925567)]
>
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170751050.png" alt="image-20210731170751050" style="zoom:80%;" />
> 这个方法由子类重写
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170819152.png" alt="image-20210731170819152" style="zoom:80%;" />
>
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170858551.png" alt="image-20210731170858551" style="zoom:80%;" />
>
<img src="https://gllspictures.oss-cn-beijing.aliyuncs.com/img/image-20210731170927146.png" alt="image-20210731170927146" style="zoom:80%;" />
总结: resolvePath(String path) 方法做了什么事情?
1.实例化 Environment
2.解析传进来的 path
------------------------------------
refresh()方法
```java
AbstractApplicationContext 类中
```
~~~java
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}