目录
(1)IoC(Inversion of Control)控制反转 和 DI(Dependency Injection)依赖注入
2.写配置文件ApplicationContext.xml (禁止更改)
1. 首先基于IoC管理Bean,这一步在上一级目录IoC容器(已完成)
方式1:调用无参构造方法(public或者private都行反射方式)
<1>我们用springConfig这个java类来取代配置文件
<2> 配置文件所有的头文件,由@Configuration取代
<3> 注解语句包扫描替换为@ConpoentSan("包路径")
<2>@PostConstruct 和@PreDestroy 的使用
<2>在sping的配置类中将这个配置类用@Import导入
1.创建mybatisConfig类作为mybatis的配置类
2.创建Mapper包和pojo包分别存放sql和数据源实体对象
3.创建sercvice包并且在里面封装Mapper包里接口的方法
<1>userService用@Component注解定义为bean
一、Spring Framework 系统架构
学习顺序
二、核心容器Core Container(本章主IoC)
(1)IoC(Inversion of Control)控制反转 和 DI(Dependency Injection)依赖注入
第一:IoC
在使用对象时,不在主动new一个对象,这个对象由外部提供 。在这个过程中,对象创建控制权由程序改为外部。 ——控制反转
????? Spring如何实现控制反转????
1.Spring提供一个容器,成为IoC容器,来充当IoC思想的外部
2.IOC容器负责对象的创建、初始化等工作,被创建的对象或者被管理的对象,被称为Bean
这里面的service和dao这两个对象统称为Bean
第二:DI
在容器中在、创建Bean和Bean之间依赖关系的整个过程称为依赖注入。
详细:
(2)IoC容器
1.导入Spring的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
2.写配置文件ApplicationContext.xml (禁止更改)
例如:本程序中的两个类为
dao01:(我改dao为dao01)
service:
在配置文件中这样配置:
文件名:
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">
<!--2.写入控制的Bean-->
<!-- id是名字,调用的对象就是这个名字-->
<!-- class定义属性,通俗的说是类的具体路径-->
<bean id="dao" class="com.wrx.Dao.dao"></bean>
<bean id="service" class="com.wrx.service.service"></bean>
</beans>
3、在主函数初始化IoC容器,并且调用Bean
这里面的创建容器对象通用。
//3.加载配置文件,获取容器的对象 这一行代码通用,不用记忆
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml") ;
//4.获取Bean getBean("Bean名")方法来获取Bean对象
dao01 dao = (dao01) ctx.getBean("dao");
service01 service = (service01) ctx.getBean("service");
String s = dao.dateDao();
String s1 = service.dateService();
System.out.println(s+s1);
此时,我们运行在其他类运行就不用创建对象了,而是直接在主方法里面来从IoC中获取。
(3)DI关系绑定
我们先看下两个方法里面的内容
dao01:
service:
这是上一目录IoC里面的两个方法,此时我们对,service进行改动,把 new dao01()去掉。
即:
此时如果在运行的主方法肯定是空指针异常,因为缺少了dao01的对象。
如何改动配置文件,来运行这个程序呢????
1. 首先基于IoC管理Bean,这一步在上一级目录IoC容器(已完成)
2.删除Dao对象(已完成)
3.提供依赖对象对应的setter方法(主要)
package com.wrx.service;
import com.wrx.Dao.dao01;
public class service01 {
// 删除这个类中的new的Dao的对象
private dao01 dao;
public String dateService(){
System.out.println("dao01....");
String s = dao.dateDao();
return s;
}
// 创建setter方法:容器在执行这个setter方法
public void setDao(dao01 dao) {
this.dao = dao;
}
}
4.service和dao01关系的描述(主要)
视频截图:(参照作用)
程序实例:
配置文件:(核心)
<?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">
<!--2.写入控制的Bean-->
<!-- id是名字,调用的对象就是这个名字-->
<!-- class定义属性,通俗的说是类的具体路径-->
<bean id="dao" class="com.wrx.Dao.dao01"></bean>
<bean id="service" class="com.wrx.service.service01">
<!-- property标签表示配置当前bean属性-->
<!-- name的dao对应是你在其他类中创建的对象名,表示配置哪一个具体的属性-->
<!-- ref的dao是<Bean>中的id,表示参照哪一个bean-->
<property name="dao" ref="dao"></property>
</bean>
</beans>
此时关系绑定好了,可以运行。
(4)Bean配置
1.Bean基本配置
2.Bean的别名
实例:
在配置文件中新增三个别名:
在tset类中使用:
结果:
3.Bean的作用范围
实际就是创建的对象数量
Spring默认创建的对象为单例的,每次调用的对象都是一个重复的对象,这大大减小了IoC容器的负担,对象的复用性也更高了。
但是有些对象不能用单例的,比如实体类的javaBean对象,单例导致里面每次都存放的有数据,还要清除数据比较麻烦。
实例:
单例: 在默认的单例配置下,我们获取的两个不同名的对象,本质是同一个对象,地址相同。
非单例:
我们配置的非单例的dao:
尝试获取两个Bean,看是否为同一个:
我们清晰的发现这是两个不同的对象
(5)Bean的实例化
方式1:调用无参构造方法(public或者private都行反射方式)
注意:若构造方法有参数,则出现BeanCreatException的错误
方式2:静态工厂(了解)
方式3:实例工厂(了解)
方式4:FactoryBean (实用——必会)
第一步:创建一个类的Factory、
要求:工厂名为 类名+FactoryBean
例如:dao01FactoryBean 意义为dao01的工厂,用来生产dao01的对象
首先创建类,然后继承FactoryBean<T>,泛型T写要创建的对象的类名。然后重写三个方法:
第一个方法:返回值写new + 对象
第二个方法:返回值写 类名.class
第三个方法:true 为单例,false为非单例
package com.wrx.Factory;
import com.wrx.Dao.dao01;
import org.springframework.beans.factory.FactoryBean;
//第一步创建工厂
public class dao01FactoryBean implements FactoryBean<dao01> {
// 创建对象的方法:返回需要创建的Bean,return返回值为 要创建的对象
public dao01 getObject() throws Exception {
return new dao01();
}
//确定创建的对象的类型
public Class<?> getObjectType() {
return dao01.class;
}
//true为单例,false为非单例
public boolean isSingleton() {
return true;
}
}
第二步:在配置文件里管理工厂Bean
此时,就完成了bean的创建。
视频截图:
(6)bean的生命周期
执行Bean的初始化方法的时机在,set方法之后。
执行Bean的销毁方法在关闭或者销毁容器之后。
那么我们究竟如何控制Bean的生命周期呢?????
1.提供初始化方法init()和销毁方法destroy()
2.在配置文件中添加Bean的配置信息init-method和deotroy-method
3.创建对象,执行构造方法,set注入
4.执行初始化方法init()
5.在主类中提供关闭容器的方法。
方式1:(温和方式)
//3.加载配置文件,获取容器的对象
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml") ;
// 注册关闭钩子,在虚拟机关闭之前,先关闭容器再推出虚拟机
ctx.registerShutdownHook();
方式2:关闭容器
//3.加载配置文件,获取容器的对象
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml") ;
/*。
。
。
。中间程序
。
。*/
ctx.close();
6.执行销毁的方法destroy()
过程:
三、DI(依赖注入)篇
(1)依赖注入
学习计划:
setter注入的引用类型已经学过。
1.setter注入引用类型。见上一章
2.setter注入——简单类型
<1>在bean定义简单类型属性,并提供set方法
<2>在对应的bean中的property标签value属性注入简单类型的值
结果显示:
3.构造器注入
第一:为需要注入的属性添加构造方法。
第二:在配置文件里,配置bean的属性
无论是引用类型还是简单类型,第一步都是为属性新增构造方法。
例:把dao类和简单类型依赖注入service类(前提:容器已经管理了两个类的bean)
1.添加构造方法引用类型和简单类型放在一起的构造方法。
service类:
2.配置bean
引用类型:把标签<property>换为<constructor-arg>
注意:name的属性是构造方法的形参名
完整配置:
简单类型:把标签<property>换为<constructor-arg>
注意:name的属性是构造方法的形参名
value为属性赋值。
完整配置:
代码:
<?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">
<!--2.写入控制的Bean-->
<!-- id是名字,调用的对象就是这个名字-->
<!-- class定义属性,通俗的说是类的具体路径-->
<bean id="dao" class="com.wrx.Dao.dao01"></bean>
<!-- 构造器注入方法:1.使用构造方法。2.标签换为<constructor-arg></constructor-arg>-->
<!--其他的内容和setter注入的方式相同 -->
<!--注意:name的属性是构造方法的形参名,这种方法耦合性较高,建议使用index来选择形参的序号 -->
<bean id="service" class="com.wrx.service.service01">
<!-- property标签表示配置当前bean属性-->
<!-- name的dao对应是你在其他类中创建的对象名,表示配置哪一个具体的属性-->
<!-- ref的dao是<Bean>中的id,表示参照哪一个bean-->
<constructor-arg name="dao" ref="dao"></constructor-arg>
<constructor-arg name="sexTimes" value="12"></constructor-arg>
</bean>
</beans>
降低解耦度:不使用name属性,使用index属性,用形参的顺序替代名字,这样即使形参发生变动配置文件也不用更改。
4.自动装配
(dao装配到service)
步骤:1、提供set方法
2、容器已经管理bean
3、配置bean的属性autowire
autowire常用byType和byName
如果使用byType:class类型相同的bean唯一
byName要求的和set注入的要求相同,耦合性较高,不推荐使用。
注意事项:
5.集合注入
<1>
<2>
<3>
<4>
<5>
(2)第三方数据源对象管理
以下以Druid德鲁伊数据库连接池为例(了解即可)
<1>写配置文件,数据库配置
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///shop?useSSL=false&useServerPrepStmts=true #useServerPrepStmts=true 预编译,高效率
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
#最大等待时间
maxWait=3000
<2>导入druid的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<3>配置数据源对象作为spring管理的bean
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///shop?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
运行结果:
观察下以上代码 ,我们很容易地发现耦合性极高,所以这里只是演示怎么操作,了解即可。
(3)加载properties配置文件
详细如图:
以上是方法,下面进行实操。
<1>导入对应的坐标就不说了.
<2>开启context命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context ="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<3>使用context命名空间加载配置文件(可复用)
<!-- 使用命名空间加载配置文件-->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
这里面的location的属性的格式为最全可复用的格式,后面的不加载系统环境变量,防止一些命名和系统重复。
<4>管理bean
添加value属性使用${}
<!-- 管理bean-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
此后就可以在类中获取这个bean了。
小结:
bean相关
依赖注入相关
(四)注解开发
1.注解开发定义bean
不过多赘述,详细见纯注解开发
使用@Component("id名") 来定义bean
除此之外为了分门别类还可以使用三个注解定义bean:
1.@Controller
2.@Service
3.@Repository
2.纯注解开发
纯注解阶段,我们不再需要配置文件,取而代之的是java类和注解语句。
<1>我们用springConfig这个java类来取代配置文件
<2> 配置文件所有的头文件,由@Configuration取代
<3> 注解语句包扫描替换为@ConpoentSan("包路径")
如果需要扫描多个包名,@ConpoentSan({"包路径1","包路径2"})括号里加大括号,逗号隔开。
<4>在主方法里,不再加载配置文件,加载上面的java类
这种加载配置文件的语句不再使用
取代的是如下语句,加载Java配置类
//3.加载配置类,获取容器的对象
ApplicationContext ctx = new AnnotationConfigApplicationContext(springConfig.class);
详细见 视频截图:
3.bean的生命周期和作用范围
<1>@Scope的使用
作用范围相关的就是bean是单例还是非单例,在配置文件中时我们使用的是<bean>
的一个属性scope来设定这个bean是单例、还是非单例。在注解开发模式,我们取而代之使用同名的注解。
*******在bean类中使用 @Scope("singleton")设bean为单例
@Scope("prototype")设bean为非单例
<2>@PostConstruct 和@PreDestroy 的使用
我们知道初始化方法在构造方法之后运行,而销毁方法在容器彻底摧毁前运行。
所以两个注解见名知意。
@PostConstruct 放在初始化方法之上代表这个方法是初始化方法。
@PreDestroy放在销毁方法之上代表这个方法是销毁方法
注: 使用销毁方法前要关闭容器。
4.依赖注入
<1>@AutoWired自动装配
将dao01的依赖导入到service
如果没有相同类型的bean只用@AutoWired,也不用set方法,即可完成自动装配。
<2>@Qualifier({"id名"})
但是如果存在相同类型的bean
此时我们要使用另一个注解@Qualifier({"id名"}),来完成特定的依赖注入
比如我们注入dao01,如图即可:
<3>@Value("属性值")
那么前面说了引用类型的依赖注入,那么如何完成简单类型的依赖注入呢?
这里我们使用@Value注解。
如此一来name=wrx了。
<4>@PropertySuorce的使用读取配置文件
第一步,我们添加配置文件jdbc.properties
第二步,在java配置类中添加@PropertySuorce的注解
这里需要注意:不允许使用*通配符。
第三步,我们可以使用${}的形式来获取配置文件的内容
如图:
视频截图详解:
5.管理第三方bean
先导入坐标
<1>新建配置类,使用@bean配置第三方bean
<2>在sping的配置类中将这个配置类用@Import导入
@Import注解在一个类中只能出现一次,如果需要导入多个配置类,格式是数组格式:
@Import{配置类1,配置类2,配置类3}
<3>第三方bean简单类型依赖注入,使用@Value
<4>第三方bean引用类型依赖注入,在函数添加参数即可
详细如图:
小结:
五.spring整合Mybatis
数据源:JdbcConfig类
package com.wrx.Config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
//第1步配置获取bean的方法,类似工厂
public class JdbcConfig {
//通过Value注解把简单类型注入
@Value("${jdbc.driverClassName}")
private String Driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("jdbc")//通过参数把引用类型注入
public DataSource dataSourse(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(Driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
配置文件:jdbcProperties.xml
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis?useSSL=false&useServerPrepStmts=true #useServerPrepStmts=true 预编译,高效率
jdbc.username=root
jdbc.password=root
# 初始化连接数量
jdbc.initialSize=5
# 最大连接数
jdbc.maxActive=10
#最大等待时间
jdbc.maxWait=3000
思想是使用java类作为配置类取代mybatis的配置文件。
1.创建mybatisConfig类作为mybatis的配置类
里面只有两个路径需要变动。可复用.
package com.wrx.Config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb =new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.wrx.pojo");//扫描pojo包
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.wrx.Mapper");//扫描Mapper接口
return msc;
}
}
2.创建Mapper包和pojo包分别存放sql和数据源实体对象
这里我们的userMapper里面的书写格式和之前的一样。
3.创建sercvice包并且在里面封装Mapper包里接口的方法
<1>userService用@Component注解定义为bean
<2>使用@AutoWires自动装配Mapper接口
<3>封装方法
<4>controller里面调用service
六.sping整合Junit测试类
1.导入Spring-test和junit的坐标
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
2.新建测试类![](https://i-blog.csdnimg.cn/blog_migrate/2cbdb0dd77a743b3cc8f4693c962a061.png)
七.AOP面向切面编程
1.Aop
无侵入式功能增强
2.AOP核心概念
3.入门案例
1.导入AOP相关依赖坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2.制作连接点方法(原始代码)
3.制作共性功能(写通知类和通知)
4.定义切入点
5.绑定切入点与通知关系(切面)
此处表示通知方法在切入点之前执行
6.开启spring的注解识别管理切面类
在通知类写这两个注解
在spring配置类写如下注解:
4.AOP的工作流程
5.切入点表达式
如图,我们看这个切入点的形式:
execution(进行)(访问修饰符 返回值类型 包名.类名/接口.方法名(参数)异常名 )
访问修饰符一般为公共类型public,省略的话默认public。
<1>通配符 * 表示一个任意的符号,不表示空。
放在返回值类型处,表示任意类型的返回值,不包括空。
!!!!!!不包括空
<2> .. :多个连续的符号
6.通知类型
通知类型共五种:前置通知,后置通知,环绕通知,返回后通知,抛出异常后通知
其中环绕通知最为重要,也最为常用
<1>前置通知@Before
<2> 后置通知@After
<3>环绕通知@Around
此图重点记忆:
环绕通知要点1:必须要由返回值通常为Object
2:使用ProcedingJoinPoint调用切入点方法
3:抛出异常
注意事项:
<4>返回后通知
<5>抛出异常后通知
7.获取通知数据
<1>获取切入点方法的参数信息
<2>获取切入点方法的返回值信息
<3>获取切入点方法的异常信息(了解即可)
八.Spring事务
1.spring事务简介(银行转账案例)
首先如何开启spring事务?spring事务有啥用?
开启事务有三步。
<1>在业务层开启spring事务注解
<2>在jdbc的配置类开启spring的配置管理器
<3>在sping配置类里添加事务功能的注解
下面是案例:
1.首先我们在业务层接口添加spring事务注解
接口功能是给定id值使指定的id密码加2,用来模拟银行转账。
即当账户1增加50元,账户2也会增50元(不要疑惑了哈哈哈,我不想写两个sql,大家理解就行)
数据层接口:
实现类:
2.在jdbc的配置类里添加事务管理器
3.在spring配置类里添加事务注解
就此事务就算开启成功了。
那么问题来了,他有啥用呢?
答案:在添加事务注解的业务层出现异常时,会回滚事务,使得操作失效,防止操作异常的作用。
假如不开启事务:
我们在实现类添加一个异常
然后我们去执行操作
操作的结果是:账户1密码加了2,账户2密码没有加2
在银行操作时,就是账户1得到了钱,账户21钱数不变,显然不对
看出问题出在哪里了吧。
如果开启事务结果是这样的。
出现异常,直接回滚事务。
2.spring事务角色
在进行增删改的时候数据层开了上面右面两个事务,当spring开启业务层事务时候一共是三个事务。
但是这是其实就一个事务,数据层的两个事务已经加入到了spring的业务层事务。
新增概念:
3.spring事务属性(配置)+案例追加日志功能
新功能要求:无论是否转账成功,都要记录日志在数据库里
如何实现呢?
首先我们先看示例代码:
1.先模拟日志功能,即在表中新建用户来表示日志
2.写日志接口和实现类
3.在原始的原始转账功能里新加入写日志
代码解释:
try{
userMapper.updateById(i);
int c =2;
c=c/0;
userMapper.updateById(a);
}finally {
logService.log();//打印日志
}
//这一段表示:无论try里面是否出现异常,打印日志代码段都会执行
4.执行代码
注意:原始代码里我们设置了异常
我们期待的结果是:
转账效果不会生效,但是会打印日志。
结果是:并没有打印日志
问题出在哪里了呢?????
原因是 :事务传播行为导致的
我们在logService接口里开启了事务,两个转账行为两个事务,最后在transfer转账实现类里面再次开启一个事务,出现了4个事务,但是上图右边的三个事务在默认情况下的设置都是加入左边的这个事务,但是在这个事务管理员里的代码有异常,它就直接回滚了,由于右边的事务协调者已经加入了,所以也会跟着回滚。
解决办法:
让写日志的事务不加入转账事务。
进行如下操作:
执行结果是:
没有转账成功,但是写了日志
spring事务的相关属性如下: