目录
1.Spring是什么?
Spring 是一款轻量级的,IOC 和 AOP 的Java 开发框架,它是为了简化企业级应用开发而生的。
轻量级的:Spring框架的核心功能jar包并不大,3M左右,运行时占用内存小,运行效率高。
2.搭建Spring(Hello World版本)
2.1导入spring的核心jar包(给出坐标)
<!-- spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.2.RELEASE</version> </dependency>
2.2创建实体类(举例Admin)
public class Admin {
private int id;
private String account;
private String password;
public Admin() {
System.out.println("Admin无参构造方法");
}
public Admin(int id, String account, String password) {
System.out.println("Admin带参构造方法");
this.id = id;
this.account = account;
this.password = password;
}
public int getId() {
System.out.println("调用了get");
return id;
}
public void setId(int id) {
System.out.println("调用了set");
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
'}';
}
}
2.2编写spring配置文件+配置bean
在resources目录下创建spring.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="admin" class="com.ffyc.ssm.model.Admin</bean>
</beans>
2.3测试(创建Test类)
public class Test01 {
public static void main(String[] args) {
//读取配置文件,ClassPathXmlApplicationContext就是spring中的实现者
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
//Object admin = app.getBean("admin"); 它也不知道返回的是啥类型,只是根据传来的键去找值对象,返回即可
Admin admin = app.getBean("admin", Admin.class);//指定类型返回
System.out.println(admin);
}
}
输出结果:
Admin无参构造方法
Admin{id=0, account=null, password=null}
3.控制反转IOC
IOC:可理解为控制反转,一种设计思想,以前我们获取对象都是我们自己new对象,自己实现(及正控),IOC的思想(反控)就是我们把创建对象的控制权交给spring框架,让框架去帮助我们创建管理对象,如果我们需要直接在在IOC容器当中获取即可。
IOC容器里面存储的格式可理解为:Map("id/类型/name",所创建的对象)
底层实现方式: 解析 xml/扫描注解标签 + 工厂模式 + 反射机制
4.Spring对Bean的管理
4.1xml配置方式对Bean管理
语句 | 含义 |
---|---|
<bean> | 配置需要spring框架管理的类 |
id | 唯一的标识,可利用id来获取spring框架创建的代理对象 |
class | 所管理类的地址,地址要写全 |
name | 与id作用基本一致,也可通过name来获取对象 |
<alias> | 起别名,例如:<alias name="admin2" alias="admin3,admin4"></alias> |
scope | singleton(默认值):在 Spring 中只存在一个 bean 实例, 单例模式,读取spring.xml配置文件时创建。 |
prototype:原型的,getBean()的时候都会 new Bean(),获取对象时创建,可创建多个。 |
4.2xml配置方式的依赖注入(DI)
在spring创建对象时可以给对象依赖的属性赋值,我们称之为依赖注入。
4.2.1set方法注入
<bean id="admin" class="com.ffyc.ssm.model.Admin" scope="prototype"> <property name="id" value="1"></property> <property name="account" value="zhangsan"></property> <property name="password" value="123"></property>--> </bean>
测试结果:
Admin无参构造方法
调用了set
Admin{id=1, account='zhangsan', password='123'}
4.2.2构造方法注入
<bean id="admin" class="com.ffyc.ssm.model.Admin" scope="prototype"> <constructor-arg name="id" value="2"></constructor-arg> <constructor-arg name="account" value="lisi"></constructor-arg> <constructor-arg name="password" value="456"></constructor-arg> </bean>
测试结果:
Admin带参构造方法
Admin{id=2, account='lisi', password='456'}
4.3利用注解方式对Bean管理
4.3.1开启注解配置扫描
在spring.xml配置文件中开启注解配置 ,运行时,会扫描所配置的包里面,然后将带有注解的类并且scope必须为"singleton"的进行创建。
<context:component-scan base-package="com.ffyc.ssm"> </context:component-scan>
4.3.2注解标签讲解(7种)
作用在类上的注解标签 | 作用 |
---|---|
@Component(value=" ") | 一般作用在model |
@Service(value=" ") | 一般作用在service层 |
@Repository(value=" ") | 一般作用在dao层 |
@Scope(value="prototype/singleton") | 原型/单例 |
以上前三种注解都可以实现创建对象功能,只是为了后续扩展功能,在不同的层使用不同的注解标记 。
作用在属性上(依赖注入) | 功能及作用 |
---|---|
@Autowired(spring提供) | byType(默认使用) 自动注入,该注解默认使用按类型自动装配Bean的方式。 例如: @Autowired AdminDao adminDao; |
byName,结合@Qualifier注解,value 属性用于指定要匹配的 Bean 的 id 值。 例如: @Autowired @Qualifier(value = "adminDao") AdminDao adminDao; | |
@Resources(JDK提供) | byType(默认使用) 利用属性类型去检索 例如: @Resource AdminDao adminDao; |
byName,其name属性用于指定要匹配的 Bean 的 id 值。 @Resource(name="adminDao") AdminDao adminDao; |
注意:Autowired(required=true) 默认情况下它要求依赖对象必须存在,不允许为null,如果要使可以允许为null值,可以设置它的required属性为false。
4.4xml配置与注解方式的区别
xml:
优点:配置和代码分离,修改后,只需要重启服务器即可。
缺点:编写麻烦,效率低。
注解方式:
优点:代码少,直观简洁。
缺点:以硬编码的方式写入java代码中,修改需要重新编译项目。
5.Spring管理数据源+JdbcTemplate
Spring是一站式框架,spring自身也提供了持久层的JdbcTemplate。我们在resources目录下创建db.xml,里面写关于jdbc的一些配置信息,以及创建congig.properties配置文件,里面配置数据库连接的一些信息,以后方便修改。
5.1导入jar包(jdbc+阿里数据源+mysql驱动)
<!-- spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.2.RELEASE</version> </dependency> <!-- 阿里数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency>
5.2 配置数据源和JdbcTemplate信息
config.properties:
db.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--jdbc配置文件-->
<!--加载config.properties文件-->
<context:property-placeholder location="classpath:config.properties"></context:property-placeholder>
<!--spring统一管理数据库连接对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${uname}"></property>
<property name="password" value="${pwd}"></property>
<property name="initialSize" value="5"></property>
<property name="maxActive" value="10"></property>
</bean>
<!--创建spring框架封装的jdbc实现类,以后引入mybatis框架,也是只需要注入数据源对象,不用jdbcTemplate
而且jdbcTemplate的事务采用的是以前jdbc的执行一条sql自动提交。
-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据源对象-->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
spring.xml:
<!--将配置jdbc的配置文件导入进来-->
<import resource="classpath:db.xml"></import>
5.3使用举例(非重点)
配置好以上相关信息后,就可以使用JdbcTemplate了,因为我们最终的话要集成mybatis框架,所以JdbcTemplate并不是重点。
以下只是将JdbcTemplate的一些用法语句展示一下————
@Repository(value = "adminDao") 暂时注释
public class AdminDao1 {
@Autowired
@Qualifier(value = "jdbcTemplate")
JdbcTemplate jdbcTemplate; //依赖注入
public void save(Admin admin){
jdbcTemplate.update("insert into admin(account,password) value(?,?)","wangwu","789");
jdbcTemplate.update("delete from admin where id = ?",5);
Admin admin1 = jdbcTemplate.queryForObject("select * from admin where id =?", new RowMapper<Admin>() {
@Override
public Admin mapRow(ResultSet resultSet, int i) throws SQLException {
Admin admin1 = new Admin();
admin1.setId(resultSet.getInt("id"));
admin1.setAccount(resultSet.getString("account"));
admin1.setPassword(resultSet.getString("password"));
return admin1;
}
}, 1);
System.out.println(admin1);
List<Admin> list = jdbcTemplate.query("select * from admin", new RowMapper<Admin>() {
@Override
public Admin mapRow(ResultSet resultSet, int i) throws SQLException {
Admin admin1 = new Admin();
admin1.setId(resultSet.getInt("id"));
admin1.setAccount(resultSet.getString("account"));
admin1.setPassword(resultSet.getString("password"));
return admin1;
}
});
System.out.println(list);
}
}
6.Aop(面向切面编程)
6.1AOP概述
AOP:全称Aspect Oriented Programming,意为:面向切面编程,AOP是OOP思想的延续(一种补充),它是一种编程技巧,并不是spring框架所特有的,而是spring框架使用了这种思想,aop思想是我们可以把代码分为业务代码和非业务代码(比如 打印日志、提交事务、统一验证),之后可以把非业务代码抽取到一个类当中,在业务代码中不需要展现,而在执行业务代码前后或出现异常时,利用切面的思想,通过spring框架创建的特有代理对象来调用非业务代码,这样的话,减少重复,注重业务,即降低了代码的耦合度,又提高了程序的可重用性,就大大提高了开发效率。
代理模式更关注于为单个对象提供代理和扩展功能,而AOP则更侧重于在整个应用程序中横切关注点的模块化和管理。
6.2AOP的基本概念
连接点(JoinPoint) | 类中可以被增强的方法,我们称之为连接点 |
切入点(PointCut) | 实际被增强的方法,我们称之为切入点 |
通知(Advice) | 抽取出来的非业务代码的功能,指要做的事情,称之为通知 |
目标(Target) | 增强的方法所处的类,称为目标类 |
切面(Aspect) | 把通知添加到切入点的整个过程我们称为切面 |
代理(Proxy) | 调用通知的代理对象,由spirng框架创建 |
通知分为5种:
* 前置通知——before 业务执行前执行
* 后置通知——after-returning 业务方法执行后执行,当出现异常不执行
* 异常通知——after-throwing 业务出现异常时调用
* 最终通知——after 业务方法执行后执行,当出现异常也会执行
* 环绕通知——包含概括了前四种通知
6.3SpringAOP的实现
6.3.1下载Aop-jar包
<!--aop jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.2.RELEASE</version> </dependency>
6.3.2测试环境搭建
为了测试我们Aop是否成功实现,我们搭建一个案例,通过service层调用dao层的save()方法来实现新增管理员。
Test类:
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
AdminService adminService = app.getBean("adminService", AdminService.class);
adminService.save();
}
}
AdminService类:
@Service(value = "adminService")
public class AdminService{
@Autowired
@Qualifier(value = "adminDao")
AdminDao adminDao; //依赖注入
public void save(){
adminDao.save();
}
}
AdminDao类:
@Repository(value = "adminDao")
public class AdminDao {
@Autowired
@Qualifier(value = "jdbcTemplate")
JdbcTemplate jdbcTemplate; //依赖注入
public void save(Admin admin){
jdbcTemplate.update("insert into admin(account,password) value(?,?)","wangwu","789");
int i = 10/0;
System.out.println("保存成功");
}
}
非业务代码(通知类):
public class MyUtil {
public void printLog(){
System.out.println("打印日志");
}
public void commit() {
System.out.println("提交事务");
}
public void throwable(Throwable e){
System.out.println("异常:"+e.getMessage());
}
/*环绕通知 ProceedingJoinPoint 连接点指向的是切入点方法*/
public void around(ProceedingJoinPoint joinPoint){
printLog();//前置通知
try {
joinPoint.proceed(); //调用自己的业务代码
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知:"+throwable.getMessage());
}finally {
commit(); //最终通知
}
}
}
6.3.2xml方式实现动态代理
为了方便起见,我们在resources目录下创建aopdemo.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
把非业务代码(通知交给spring框架来管理)
-->
<bean id="myutil" class="com.ffyc.ssm.util.MyUtil"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="saveAdmin" expression="execution(* com.ffyc.ssm.dao.AdminDao.save(..))"/>
<!--将通知与切入点配置,生成的代理对象就知道该怎么调用-->
<aop:aspect ref="myutil">
<!--如果在xml中配置环绕通知,在执行切入点方法前,会先执行抽取的around方法,在around里再调用执行切入点方法-->
<aop:around method="around" pointcut-ref="saveAdmin"></aop:around>
</aop:aspect>
</aop:config>
</beans>
将此xml导入到spring.xml配置文件当中:
<!--将配置aop的xml文件导入,利用xml方式配置Aop-->
<import resource="classpath:aopdemo.xml"></import>
运行Test,测试结果:
打印日志
//异常信息
异常通知:/ by zero
提交事务
但是我们会发现,即使save()方法中我们出现异常,但是所添加的数据在数据库中仍然会添加,所以spring中JdbcTemplate的事务采用的和之前JDBC的一样,执行完一条sql后直接自动提交。
6.3.3注解方式实现动态代理
注释刚才的导入的aopdemo.xml,在spring.xml文件中启动AspectJ支持:
<!--启动AspectJ支持(自动代理) 注解方式配置Aop-->
<aop:aspectj-autoproxy />
在Myutil中做配置:
//基于注解方式配置Aop 所抽取的通知类
@Component //让spring管理生成对象,让spring框架检索到
@Aspect //表明装有通知的类 /切面,就可以在方法上使用通知注解了
public class Myutil1 {
//前置通知
//@Before("execution(* com.ffyc.ssm.dao.AdminDao.save(..))")
public void printLog(){
System.out.println("打印日志");
}
//后置通知
//@AfterReturning("execution(* com.ffyc.ssm.dao.AdminDao.save(..))")
public void commit() {
System.out.println("提交事务");
}
//异常通知
//@AfterThrowing(value = "execution(* com.ffyc.ssm.dao.AdminDao.save(..))",throwing = "e")
public void throwable(Throwable e){
System.out.println("异常:"+e.getMessage());
}
//环绕通知
@Around("execution(* com.ffyc.ssm.dao.AdminDao.save(..))")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("前置通知");
try {
joinPoint.proceed(); //调用自己的业务代码
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知:"+throwable.getMessage());
}finally {
System.out.println("最终通知");
}
}
}
运行Test,测试结果:
前置通知
//异常信息
异常通知:/ by zero
最终通知
和xml配置方式所达到的效果一致,但是注解方式更加的方便、简洁。
注意:用来调用通知的代理对象并不是spring创建的Myutil对象,而是spring框架中自己创建的特有代理对象,让spring管理创建Myutil,是因为需要让spring框架检索到这个通知类。
7.spring事务管理(使用了Aop思想)
事务:事务是数据库为了保障数据一致性的功能,把对数据库的多条操作,可以看作为一个执行单元,这个单元可称为事务。事务的基本特征就是原子性,要么多条sql同时执行成功,要么一条都不执行。spring框架把提交事务和管理事务的功能都封装好了,底层使用了Aop的思想,代理我们来对事务进行管理。
spring中事务管理分为两种形式,一种是编程式事务,还有一种是声明式事务。编程式事务很少用到,要自己写代码实现,声明式事务建立在AOP基础上,是方法级别的,分为基于xml配置和注解方式实现,这里我们采用的是注解方式实现。
7.1在db.xml文件中配置事务管理类,并注入数据源
<!-- 配置spring事务管理类, 并注入数据源 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
7.2在spring.xml文件中开启注解事务管理
<!-- 开启注解事务管理 --> <tx:annotation-driven transaction-manager="transactionManager"/>
7.3Service层中添加事务
对于经典案例转账,对于service层来讲,转账是一个单元操作,所以得把事务管理的注解标签加在service层上 ,不要加在dao层里,因为集成了mybatis之后,dao里一个方法对应一条sql,不能把事务管理加到多条dao方法上,起不到原子性的作用 。 一个业务逻辑中,可能要执行dao中的多条sql操作,所以得把多个sql放在同一个事务下管理。
@Transactional 添加到类上表示类中的所有方法都会添加事务管理功能
@Transactional 只添加到某个方法,表示此方法在事务管理中进行
7.4六种情况下@Transactional不生效(重点)
1.@Transactional所修饰的方法访问权限不是public |
2.修饰的方法出现的异常被catch所捕获 |
3.方法出现运行时异常事务生效,如果出现编译器异常,事务不生效,所以可设置为———— @Transactional(rollbackFor = Exception.class) |
4.事务传播行为设置错误 |
5.数据库引擎不支持,innodb支持事务(多用于增,删,改操作的表),myisam不支持事务(多用于查询操作的表) |
6.在同一个类当中使用spring非代理对象调用事务方法,方法不生效。 在同一个类当中,如果此方法没有被事务管理,然后在此方法内还调用了事务管理的方法,就例如: zhuanzhang() == this.zhuanzhang(), this相当于又自己new对象来调用,这样的话调用的事务方法就不被事务管理了。 必须用spring所管理创建的对象(不是new创建的代理对象)来调用事务方法!!! |
8.spring事务传播行为(代理机制+AOP)
事务传播行为是spring框架独有的事务增强特性,不属于事务提供方数据库的行为。
事务传播行为主要研究的地方在methodA方法中调用methodB方法,那么methodB方法是整合于methodA中的事务一起被管理,还是methodB独自又新建一个事务,它们之间独立运行,互不干涉。这就是又methodB的事务传播行为来决定的!
spring中定义了7种事务传播行为,这里我们举例三种:
propagation = Propagation.REQUIRED
如果A有事务,那么B就整合在A事务中,一起被管理(合并)。
如果A不存在事务,B存在事务,那么B就在自己的事务中运行,于A无关,各干各的。
propagation = Propagation.REQUIRES_NEW
如果A有事务,那么B就自己单开一个事务独自运行(挂起),于A无关,各干各的。
如果A不存在事务,B会自己新建属于自己的事务,独立运行。
propagation = Propagation.NEVER
如果可以执行以非事务方式执行。
如果A存在事务,调用B,会抛出异常。
spring框架是一站式框架在于它对持久层和web层进行了封装,并且可以很好的管理其他框架。