Spring是什么?
Spring是一个轻量级的非侵入式的IOC和AOP的一站式框架
轻量级:
Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需的 jar 总共在 3M 左右。 Spring 框架运行占用的资源少,运行效率高。
非侵入式:
在编写一些业务类的时候不需要继承spring特定的类,通过配置完成依赖注入后就可以使用,此时spring没有侵入到我们所编写的业务类的代码里。
IOC:
控制反转,把生成的对象的控制权反转给Spring框架,由Spring容器管理统一管理bean的生命周期。
AOP:
面向切面编程,是把一些项目业务不相关的公共的功能进行提取,使用动态代理方式为业务代码动态添加功能,不会在业务代码显示编码。
Spring体系结构
Core Container(核心容器):
- Beans:管理Beans
- Core:Spring核心
- Context:配置文件
- ExpressionLanguage:SpEL 表达式
Data Access(数据库整合):
JDBC,ORM,OXM,JMS,Transaction
Web(MVC Web 开发):
Web, Servlet, Portlet, Struts
Spring Hello World 搭建
Maven 导入 spring 核心基础 jar
编写 spring 配置文件
编写一个User实体类
public class User {
private int no;
private String name;
public User(){
System.out.println("User 无参构造方法");
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试Spring
public class Test1 {
public static void main(String[] args) {
/*以前使用类创建对象是这样的,在哪使用在哪new
User user = new User();*/
/*
* 使用spring框架来管理生成对象
* 使用spring框架中的一个实现类,读取spring的配置文件,生成并管理这些对象
* 在程序中需要对象时,直接从spring框架中获取即可
* */
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
User user1 = app.getBean("user",User.class);
System.out.println(user1);
}
}
IOC(控制反转)
IOC是一种设计思想,是将把生成的对象的控制权反转给Spring框架,由Spring容器管理统一管理bean的生命周期。
IOC容器具有依赖注入功能的容器,负责对象的实例化、对象的初始化,对象和对象之间依赖关系配置、对象的销毁、对外提供对象的查找等操作。
正控:若要使用某个对象,需要自己去负责对象的创建。
反控:若要使用某个对象,只需要从Spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架。
好处:解耦 、功能增强
底层的实现方式:解析xml/扫描注解标签+工厂模式+反射机制
Spring Bean管理
基于xml配置方式
bean 配置需要spring管理的类
id:“对象名99”
class:“类的地址”(全类名)
scope: 指导spring框架如何生成对象,
-singleton(默认值):在 Spring 中只存在一个 bean 实例, 单例模式,对象在spring容器启动时创建。
-prototype:原型 getBean()的时候都会 new Bean()
-request:每次 http 请求都会创建一个 bean, 仅用于 WebApplicationContext 环境。
-session:同一个 http session 共享一个 Bean, 不同 Session 使用不同的 Bean, 使用环境同上。
Xml 配置方式依赖注入【DI:Dependency Injection】
在IOC的同时,会发生一个动作称为依赖注入,就是在spring创建对象的同时,为对象中的属性注入值。
注入方式有两种:
1.通过属性的set方法注入:
<property name="no" value="100"></property>
2.通过构造方法注入(有弊端不建议使用)
注解方式实现
注解功能封装在AOP包中。导入Spring aop jar包即可
开启注解扫描
<context:component-scan base-package="包名"> </context:component-scan>
@Repository(value=" ") :表示的是数据访问层。
@Component(value=“ user(对象类)”)等于
@Service:注解在service层。
以上注解都可以实现创建对象功能,只是为了后续扩展功能,在不同的层使用不同的注解标记。
@Scope(value=“prototype”) 原型
@Scope(value=“ singleton ”) 单例
可以使用注解标签为属性注入值
@Autowired 属于spring框架提供的注入的注解标签,一般写在属性上面或者属性的set方法上面
可以通过类型注入: 不需要对象名,可以根据属性的类型,在spring容器中查找
也可通过对象名注入:通过@Qualifier指定需要注入的对象名
@Autowired
@Qualifier(value = "userDao")
UserDao userDao;
默认情况下注入的属性值不能为空
JDK 注解@Resource 自动注入
@Resource 是jdk中提供的注解标签:
-既可以按照类型注入:@Resource
-也可以按照名称注入:@Resource(name = "userDao")
注解与XML的对比
注解的优点: 方便、直观、高效(代码少,没有配置文件的书写复杂)。
注解的缺点: 是以硬编码的方式写入到Java代码中,修改是需要重新编译代码的。
XML的优点: 配置和代码是分离的,在XML中做修改,无需编译代码,只需要重启服务器即可将新的配置加载。
XML的缺点: 编写麻烦、效率低、大型项目过于复杂。
Spring JDBC
Spring自身也提供了控制层的SpringMVC和持久层的Spring jdbcTemplate
开发步骤:
1.下载Spring JdbcTemplate 的 jar 包
<!-- 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.导入属性文件
<context:property-placeholder location="config.properties"/>
3.管理数据源对象
spring管理与数据库链接(数据源)
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<propertyname="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${uname}"></property>
<property name="password" value="${pwd}"></property>
<property name="initialSize" value="10"></property>
<property name="minIdle" value="5"></property>
<property name="maxActive" value="20"></property>
</bean>
4.在配置文件中创建JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
在类中获得JdbcTemplate对象,就可以直接用
@Autowired
JdbcTemplate jdbcTemplate;
JdbcTemplate 中常用的方法 execute:
无返回值,可执行 ddl,增删改语句
update:执行新增、修改、删除语句;
jdbcTemplate.update("", "");
queryForXXX:执行查询相关语句;
public void save(){
jdbcTemplate.update("", "");
User user = jdbcTemplate.queryForObject("selcet * from user where id = ?",
new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setName(resultSet.getString("account"));
user.setNo(resultSet.getInt("id"));
return user;
}}, 1);
}
AOP
AOP:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,但是面向不同领域的两种设计思想。是Spring中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间得耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP为程序带来的好处: 可以将一些公共的,重复出现的非业务代码进行提取(减少重复,专注业务)。如何使用动态代理的方式,为业务代码横切添加额外提取的功能,而且不需要显示的在业务代码中调用。
可以通过一个代理对象,来帮助我们调用这些抽取出来的方法。
核心原理: 使用动态代理的方式在执行方法前后或者出现异常的时候做加入相关的逻辑.
使用案例:
-事务处理:开启事务,关闭事务,出现异常后回滚事务
-权限判断:在执行方法前,判断是否具有权限
-日志:在执行前进行日志处理
AOP 的基本概念
连接点(Joinpoint): 类中可以被增强的方法,这个方法就被称为连接点。
切入点(pointcut): 类中有很多方法可以被增强,但实际中只有 add 和 update 被增了,那么 add 和 update 方法就被称为切入点(实际实现的连接点)。
通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为方法执行前通知,方法执行后通知,环绕通知等。
切面(Aspect): 把通知添加到切入点的过程叫切面.。
目标(Target): 代理的目标对象(要增强的类) 。
代理(Proxy): 向目标对象应用通知之后创建的代理对象。
springAOP 实现
AOP底层是Java设计模式中的动态代理模式。
AOP不是Spring框架特有的技术,Spring框架应用这一技术思想进行封装。
Spring中是集成AspectJ这样的一个AOP框架。
AspectJ 中常用的通知有五种类型: 前置通知,后置通知,环绕通知,异常通知,最终通知.
下载AOP相关jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
基于 aspectj 的 xml 配置方式实现
<bean id="aopdemo" class="com.ffyc.spring.aop.AopDemo"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.ffyc.spring.service.UserService.adduser(..))" id="adduser"/>
<aop:pointcut expression="execution(* com.ffyc.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>
基于注解方式的实现
启动 AspectJ 支持:<aop:aspectj-autoproxy />
定义通知:
@Component
@Aspect
public class AOPDemo {
@Before("execution(* com.ffyc.springpro.dao.UserDao.*(..))")
public void before(){
System.out.println("before");
}
@Around("execution(* com.ffyc.springpro.dao.UserDao.*(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("start"); point.proceed();
System.out.println("end");
}
@AfterThrowing(value = "execution(* com.ffyc.springpro.dao.UserDao.*(..))",throwing = "e")
public void afterthrow(Throwable){
System.out.println("afterthrow");
}
@AfterReturning("execution(* com.ffyc.springpro.dao.UserDao.*(..))")
public void afterreturn(){
System.out.println("afterreturn");
}
}
Spring 事物管理
事务属于数据库功能,事务可以看做是由对数据库若干操作组成的一个单元(一个整体)。
事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态,这些操作 要么都完成,要么都取消,从而保证数据满足一致性的要求。
数据库事务管理: 就是在将这若干操作执行完成后,需要对事物进行提交,数据库对数据进行操作,最终达到持久性。
数据库事务重要性,增、删、改操作必须在事务管理(没有问题提交,有问题不能提交)中进行.
事务特性: 原子性、隔离性、持久性、一致性
Spring 中的事务管理分为两种形式,一种是 编程式事务 ,一种是 声明式事务 。
–编程式事务: 在项目中很少使用 ,这种方式需要注入一个事务管理对象TransactionTemplate,在我们代码中需要提交事务或回滚事务时自己写代码实现。
–声明式事务: 管理建立在AOP基础上,本质是对方法前后进行拦截,所以声明式事务是方法级别的。
Spring声明式事物管理方式有两种:
1.基于xml配置: 需要大量的配置,为需要在事务管理中的方法增强能。
2.基于注解实现
在 service 中控制事务
@Service(value="userservice")
@Transactional(propagation=Propagation.REQUIRED)
声明式事务不生效的场景:
1.同一个类中方法调用,导致@Transactional失效;
2.@Transactional应用在非public修饰的方法上;
3.注解@Transactional所在类非Spring容器管理的bean;
4.@Transactional注解属性propagation设置错误;
5.异常被catch捕获导致@Transactional失效;
6.数据库引擎不支持事务。
Spring 事务传播行为
什么是事务传播行为?
事务传播行为指的就是当一个事务方法(当前事务)被另一个事务方法(调用者)调用时,这个事务方法(当前事务)对另一个事务方法(调用者)的态度。
事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。
例如:A事务方法调用B事务方法时,B需要如何应对(是继续在调用者A的事务中运行呢,还是为自己开启一个新事务运行),这就是由B的事务传播行为决定的。
Spring的七种事务传播行为
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 表示当前方法必须在事务中运行。如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 表示当前方法不必在事务中运行。支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 表示当前方法必须在调用者事务中运行。使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 表示当前方法必须在事务中运行。新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 表示当前方法不支持在事务中运行。以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 表示调用者必须以非事务形式运行。以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 表示当前方法必须在事务中运行。如果当前存在事务,则在嵌套事务内执行。如果当前没有事 务,则执行与 PROPAGATION_REQUIRED 类似的操作。 |