今天我们来学习一个新的框架spring!!!
spring是什么呢?
spring是2003年兴起的,是一款轻量级、非侵入式的IOC和AOP的一站式的java开发框架,为简化企业即开发而生。
轻量级:spring核心功能的jar包不大
非侵入式:我们的业务代码不需要继承或实现spring中的任何类和接口
IOC:控制反转,以前项目都是在哪儿用到对象,在哪儿new,把生成对象的权利反转给spring框架
Aop:面向切面编程
一站式框架:
- 提供核心功能,主要是IOC,创建管理对象
- 提供面向切面编程,增强程序扩展
- 对数据访问层进行了封装(重点在事务管理)
- 对Web层进行封装,使得请求更加的便捷
Spring思想是什么?由框架统一对项目的类进行管理(创建对象,后期增强一些功能),在需要的地方注入即可。
Core Container(核心容器):
- Beans: 管理 Beans
- Core: Spring 核心
- Context: 配置文件
IOC(控制反转):就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来
管理。
在Spring Bean对象管理
- 基于xml配置管理
<bean id="adminservice" class="com.ffyc.ssm.service.AdminService">
<property name="adminDao" ref="adminDao"></property><!--注入-->
</bean>
让spring帮我们管理项目中的类 方式1:在xml中进行配置 配置需要让spring进行管理的类 id="唯一的标识" class="让spring管理的类名" bean? 由spring框架创建并管理的对象称为一个bean对象 Scope="singleton 默认值" 单例的 在整个应用程序中只创建一个对象,在spring框架启动时就创建好了 prototype 原型的 每次获取时创建一个对象,可以创建多个 spring框架创建对象时,需要为对象的属性进行赋值操作: 这个赋值操作称为依赖注入 1.属性赋值 getXXX,setXXX方法 2.构造方法赋值
- 注解方式实现
- 注解开发准备工作
- 注解需要的 jar 包
- 注解功能封装在 AOP 包中,导入 Spring aop jar 包即可
- 开启注解扫描
<context:component-scan base-package="包名"> </context:component-scan>
5. 注解创建对象
@Component(value=“user”)等于
<bean id=“user” class=“”></bean>
@Service
@Repository
以上注解都可以实现创建对象功能,只是为了后续扩展功能,在不同的层
使用不同的注解标记
@Scope(value=“prototype”)
原型
@Scope(value=“ singleton ”)
单例
注解方式注入属性
/* * Map("adminDao,AdminDao",对象) * @Autowired * spring框架中提供的一个自动注入注解标签 * 有两种方式去查找对象: * 1.byType 去spring容器中根据当前类型进行搜索 * 2.byName 通过名称查找 需要结合当前@Qualifier(value="adminDao") * @Autowired(required=true) 注入时,对象值不能为空,为空的话会进行报错 * *@Resource * 是jdk中提供的一个注解标签 * 有两种方式去查找对象: * 1.byType 去spring容器中根据当前类型进行搜索 * 2.byName 通过名称查找 @Resource(name="adminDao") * */ // @Autowired // @Qualifier(value="adminDao")
注解与XML的对比
- 注解优点: 方便,直观,高效(代码少,没有配置文件的书写那么复杂)。
- 注解缺点:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
- xml 优点是: 配置和代码是分离的,在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
- xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。
Spring JDBC
Spring 是一个一站式框架:Spring自身也提供了控制层的SpringMVC和持久层的Spring JdbcTemplate.
1.下载Spring JdbcTemplate的包
<!-- 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>
2.创建一个config.properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/数据库名?serverTimezone=Asia/Shanghai
uname=root//数据库账号
pwd=root//数据库密码
3.在spring配置文件中导入
<context:property-placeholder location="config.properties"/>
4.在db.xml中管理数据源对象
<!--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>
5.在db.xml中创建 JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
6.在Dao层获得 JdbcTemplate对象
execute:无返回值,可执行 ddl,增删改语句
update:执行新增、修改、删除语句;
queryForXXX:执行查询相关语句;
AOP开发思想:开发好一个版本后,后期需要添加新的功能,就需要改变原来的代码,加入调用新功能的代码,开发麻烦,会存在大量的冗余的代码
AOP是一种编程技巧,不是代替OOP,可以使业务逻辑和非业务逻辑进行隔离,使得各个部分之间的耦合度降低,提高程序的复用性,提高了开发效率
AOP
Aop:将程序中的一些非业务逻辑代码进行提取,在不需要修改原来代码情况,为程序添加额外的功能,非业务逻辑代码(验证权限 打印日志 提交事务 统一异常处理)
实现的方法:是通过一个代理对象,告诉代理对象,调用哪个方法时,让代理对象去帮助我们代用哪个方法(AdminDao->生成一个代理对象)底层:动态代理模式
AOP并不是Spring框架中特有的,只是Spring框架引入这一思想
Aop的基本概念:
- 连接点:类中可以被增强的方法,称为连接点
- 切入点:类中实际被增强的方法,称为切入点(横切面切入的方法)
- 通知:提取得非业务的功能称为通知
- 切面:把通知添加到切入点的过程
- 目标:代理的目标对象
- 代理:向目标对象应用通知创建的代理对象
AspectJ 是一个基于 Java 语言的 AOP 框架,它提供了强大的 AOP 功能,且其实
现方式更为简捷,使用更为方便, 而且还支持注解式开发。配置步骤如下:
1.下载相关的Jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
2.基于aspectj的xml配置方式实现
<bean id="aopdemo" class="com.ff.spring.aop.AopDemo"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(*
com.ff.spring.service.UserService.adduser(..))" id="adduser"/>
<aop:pointcut expression="execution(*
com.ff.spring.service.UserService.*(..))" id="allmethod"/>
<!-- 配置通知和切入点 -->
<aop:aspect ref="aopdemo">
<aop:before method="savelog" pointcut-ref="adduser"/>
<aop:after method="savelog" pointcut-ref="adduser"/>
<aop:around method="aroundAdvice" pointcut-ref="adduser"/>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="allmethod"
throwing="e" />
</aop:aspect>
</aop:config>
该通知有五种:
- 前置通知:方法执行前调用
- 后置调用:业务方法执行后调用,当方法出现异常后不执行
- 异常通知:业务方法出现异常时调用
- 最终通知:业务方法执行后调用,当方法出现异常时也会调用
- 环绕通知
<aop:config>
<!--配置切入点-->
<aop:pointcut id="saveAdmin" expression="execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))"/>
<!--将通知与切入点进行配置,生成的代理对象就知道如何调用-->
<aop:aspect ref="myutil">
<!-- <aop:before method="printLog" pointcut-ref="saveAdmin"></aop:before>-->
<!-- <aop:after-returning method="printLog" pointcut-ref="saveAdmin"></aop:after-returning>-->
<!-- <aop:after method="printLog" pointcut-ref="saveAdmin"></aop:after>-->
<!-- <aop:after-throwing method="exceptionAdvice" pointcut-ref="saveAdmin" throwing="e"></aop:after-throwing>-->
<aop:around method="aroundAdvice" pointcut-ref="saveAdmin"></aop:around>
</aop:aspect>
</aop:config>
基于注解方式实现
@Component//让spring管理生成对象
@Aspect//启动Aspect 开启自动代理 表面装有通知的类/切面
public class MyUtil {
// @Before("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")
// public void printLog(){
// System.out.println("打印日志");
// }
//
// public void commit(){
// System.out.println("事务提交");
// }
// @AfterThrowing(value="execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))",throwing = "e")
// public void exceptionAdvice(Throwable e){
// System.out.println("异常通知"+e.getMessage());
// }
//@Around("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")
public void aroundAdvice(ProceedingJoinPoint point){
try {
System.out.println("前置通知");
point.proceed();
System.out.println("后置通知");
} catch (Throwable throwable) {
// throwable.printStackTrace();
// System.out.println("异常通知");
}
System.out.println("最终通知");
}
}
Spring事务管理
什么是数据库事务?
spring框架里面提供了事务管理的功能,事务管理是数据库提供佛仍一种功能,为了保证了数据在过程在过程中的准确性,事务管理是管理一组中有多个执行单元,一个事务就是一个单元,一个单元可以包含很多个SQL语句。
关系型数据库的基本特征:
- 原子性特征(一次操作中的多条SQL语句,要么都执行,要么都不执行)
- 隔离性
- 持久性
- 一致性
Spring中事务管理有两种:
1.编程式事务管理-->在代码中自己写
2.声明式事务管理-->使用注解标签实现(底层思想是AOP思想,是方法级别的,在执行某个方法中,为方法添加额外的事务管理功能)
@Transactional标签的用法:
- 一般把@Transactional标签添加在service中
- @Transactional可以添加在service层中类上,类中的所有方法都会添加事务
- @Transactional如果值添加到方法上,表示此方法在事务管理进行
以下6种情况@Transactional不会生效:
- @Transactional应用在非public修饰的方法上(底层针对public修饰的方法)
- 方法中异常被catch捕获处理
- 编译器异常(事务不生效,默认情况下只对运行期异常进行捕获 rollbackFor=Expection.class 任何异常都能捕获)
- @Transactional事务传播行为设置错误
- 数据库引擎不支持事务(数据库引擎是mysql底层具体的一种数据处理实现的机制 常用的两个引擎 innodb(支持事务) myisam(不支持事务))
- 同一个类中,使用非代理对象调用增强方法,用this调方法表示自己没有被进行任何加强
spring事务传播行为:
事务传播行为(propagation behavior)(必须要有两个对象)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。事务传播行为是 Spring 框架独有
的事务增强特性,他不属于的事务实际提供方数据库行为.
例如:A方法中调用另一个方法,B方法事务该如何进行?B事务是一个独立是我事务呢?还是B事务合并到A事务中呢?
以下是事务传播行为的7种方法
下来我们简单的介绍3种比较常用的事务传播方式
1. PROPAGATION_REQUIRED
指定的方法必须在事务内执行,若当前存在事务,加入到当前事务中,若当前没有事务,则创建一个新事务,这种传播行为是最常见的,也是 spring 默认的传播行为.
2. PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
3. PROPAGATION_REQUIRES_NEW
总是新建一个事务,如果当前存在事务,把当前事务挂起,直到新建的事务结束。
BeanFactory和ApplicationContext
在Spring中BeanFactory接口是IOC中最基础的接口,定义了最基本的方法
BeanFactory有非常多的实现类,每个实现类都有不同的职责(单一职责)功能DefaultListableBeanFactory是生产bean的工厂
ApplicationContext也是间接继承了BeanFactory,BeanFactory相当于Spring的心脏,那么
ApplicationContext就是完整的身躯(功能的实现),Spring容器是生成Bean实例的工厂,管理容器中的bean对象
区别:
1.BeanFactory不支持AOP、Web等插件,ApplicationContext不仅包含BeanFactory的所有功能,还支持spring各种插件
2.BeanFactory采用的是延迟加载形式来注入Bean的,只有调用getBean()时,才会对Bean进行加载实例化,这样我们就无法发现Spring配置上的问题,而ApplicationContext相反,在容器创建的启动的时候一次性创建了所有的Bean,我们就可以及时发现Spring中的配置错误,但是占用内存空间
Spring的生命周期
1.实例化
2.属性赋值
3.初始化
4.销毁
Spring中的bean是线程安全的么?
Spring本身是不具备线程安全的特性,但是是否安全还是看scope中的Bean的情况
1.singleton:单例,默认作用域(存在着资源竞争)
2.prototype:原型,每次创建一个新对象(线程之间不存在Bean分享,没有线程安全问题)
3.request:请求,每次Http请求创建一个新对象
Bean的状态:
有状态:有数据存储功能
无状态:不会保存数据
Bean循环依赖
@Component
public class A {
@Autowired
public B b;
public A(){
System.out.println("init B");
}
}
@Component
public class B {
@Autowired
public A a;
public B(){
System.out.println("init A");
}
}
在非spring环境中,循环依赖不是一个问题,但是在Spring中循环依赖就成了一个问题
在Spring管理的bean时,在自动注入时,如果两个类相互之间关联,那么会出现注入时,另一个对象还没有完成初始化。
循环依赖解决方法:用过三级缓存(3个Map对象)
一级缓存(singletonObjects):主要存储初始化完成的对象
二级缓存(earlySingletonObjects):存储实例化完成的半成品bean对象,提前暴露给需要的地方
三级缓存(singletonFactories):放的是创建对象的工厂
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
代码如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);//从一级缓存去获取bean
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);//从二级缓存中查找,有没有提前暴露的半成品对象
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);//从三级缓存中取出对象的工厂对象
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();//通过工厂对象创建出单例对象
this.earlySingletonObjects.put(beanName, singletonObject);//把对象放入二级缓存
this.singletonFactories.remove(beanName);//删除创建对象的工厂
}
}
}
}
}
}
return singletonObject;
}
循环依赖的根本就是实例化和初始化是分开处理的
Servlet的过滤器与Spring拦截器区别
过滤器属于javaweb(servlet规范)中定义的,实现控制由服务器调度拦截范围更大,所有进入到web后端的请求,都可以被过滤器拦截。
Spring常用注解
bean的注解:
@Component : 泛指各种组件
@Controller、@Service、@Repository 都可以称为@Component
@Controller : 控制层
@Service : 业务层
@Repository : 数据层访问层
Bean的生命周期:
@Scope :
singleton:单例,一个spring容器中只有一个bean实例,默认模式
prototype:每次创建一个bean对象
request:web 项目中,给一个http request新建一个bean
session : web项目中,给每一个会话新建一个bean对象
globalSession : 给每一个globalSession新建一个bean对象
注入bean对象的注解:
@Autowired : spring提供
@Qualifier: 当有多个同一类型的bean时,可以用@Qualifier("name")来指定
@Resource : java提供
切面(AOP)相关注解:
@Aspect:声明一个切面
@After:在方法执行后执行(方法上)
@Before:在方法上执行前执行(方法上)
@Around:在方法执行前与执行后执行(方法上)
@PointCut:声明切点
@EnableAspectJAutoProxy:开启Spring对AspectJ代理的支持
SpringMVC常用注解:
@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody
@RequestMapping:用于映射web请求,包括访问路径和参数
@ResponseBody:支持request的参数在request体中,而不是在呢直接连接的地址后面
其他注解:
@JsonFormat
此注解用于属性或者方法上(最好是属性上),可以方便的把 Date 类型直接转
化为我们想要的模式.
@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事
务属性信息。
SpringBoot 注解:
@SpringBootApplication:
包含@Configuration、@EnableAutoConfiguration、@ComponentScan 通
常用在主类上;
@RestControllerAdvice,@ExceptionHandler 用于统一异常处理,捕获指定的
异常.
配置类相关注解:
@Configuration:声明当前类为配置类
@Bean:注解在方法上,声明当前方法的返回值为一个 bean,替代 xml 中的
方式
@ComponentScan:用于对 Component 类型注解进行扫描
SpringBoot自动装配原理
1.注解层面梳理
2.run()方法层面梳理