Spring
1. 概述
1.1 概念
Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核提供了展现层 SpringMVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业 应用开源框架。
1.2 特点
- 方便解耦,简化开发
通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制, 避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底 层的需求编写代码,可以更专注于上层的应用。
- AOP 编程的支持
通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现 的功能可以通过 AOP 轻松应付。
- 声明式事务的支持
- 方便程序的测试
- 方便集成各种优秀框架
- 降低 JavaEE API 的使用难度
- Java 源码是经典学习范例
1.3 体系结构

2. 解耦
2.1 使用properties文件+工厂类
使用
new方法耦合性太强,现在使用properties和factory来替代原来的方法
- 创建
properties文件
accountDao=com.qf.dao.impl.AccountDaoImpl
accountService=com.qf.service.impl.AccountServiceImpl
- 创建工厂类, 加载配置文件 , 每次使用再通过配置信息反射得到需要的对象
public class BeanFactory {
static Properties properties;
static{
InputStream inputStream =
BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties = new Properties();
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String name){
String value = properties.getProperty(name);
try {
Object o = Class.forName(value).newInstance();
return o;
} catch (Exception e) {
throw new RuntimeException();
}
}
}
- 但是每次多次使用反射时, 获得的对象都是不同的, 为此我们可以使用
Map来存储该对象
/**
* 工厂类 专门生产bean的
*/
public class BeanFactory {
static Properties properties;
static Map<String,Object> maps; //装载bean
//加载配置文件,将数据封装到Properties对象中
static{
try {
properties = new Properties();
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
properties.load(in);
maps = new HashMap();
//获取所有properties文件里的key的集合
Enumeration<Object> keys = properties.keys();
while(keys.hasMoreElements()){
String key = (String) keys.nextElement();
String value = properties.getProperty(key);
Class clazz = Class.forName(value);
Object object = clazz.newInstance();
maps.put(key,object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//根据bean的名称获取bean对象
public static Object getBean(String name){
Object o = maps.get(name);
return o;
}
}
3. IOC
3.1 概念
org.springframework.context.ApplicationContext接口代表 Spring IoC 容器,并负责实例化,配置和组装 Bean。容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令。配置元数据以 XML,Java 注解或 Java 代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。
3.2 使用
3.2.1 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
3.2.2 创建配置文件bean.xml
bean标签 : 专门用来管理bean就是将bean交给IOC器管理id: bean的名称 必须要唯一class: bean的全限定名路径 spring的底层就是获取这个全限定名路径,通过反射的机制获取这个全限定名的字节码文件对象ref: 当此类需要引用其他对象时, 需要使用该标记来获取引用对象的idscope: 作用域singleton默认的(单例bean) 每次获取的bean都是同一个beanprototype多例的 每次获取的bean都不一样
<bean id="accountDao" class="com.qf.dao.impl.AccountDaoImpl" scope="prototype" init-method="init" destroy-method="destroy"></bean>
<bean id="accountService" class="com.qf.service.impl.AccountServiceImpl"></bean>
<!--管理bean(使用非静态化的工厂)-->
<!--
factory-bean: 引用的是工厂bean的id
factory-method:工厂里面生产bean的方法
-->
<bean id="demo" class="com.qf.factory.FactoryDemo"></bean>
<bean id="student" class="com.qf.pojo.Student" factory-bean="demo" factory-method="getStudent"></bean>
<!--管理bean(静态实例化工厂)-->
<bean id="demo2" class="com.qf.factory.FactoryDemo2" factory-method="getEmp"></bean>
3.2.3 获取bean对象
//使用applicationContext引用bean.xml文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//使用反射获取student对象
Student student = (Student) context.getBean("student");
3.3 Spring对Bean的实例化方式
- 无参数构造函数实例化方式是 Spring 默认的 bean 的实例化方式
- 使用工厂中的普通方法实例化对象
- 使用工厂中的静态方法实例化对象
// 方式一
//接口
public interface UserDao {
public void addUser();
}
//实现类
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("构造函数执行了.....");
}
public void addUser() {
System.out.println("新增用户实现了....");
}
}
//配置文件
<bean id="userDao" class="com.qf.dao.impl.UserDaoImpl"></bean>
//使用
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.addUser();
//方式二
//接口
public interface PersonDao {
public void addPerson();
}
//实现类
public class PersonDaoImpl implements PersonDao {
public void addPerson() {
System.out.println("新增 person 执行了....");
}
}
//工厂类 new一个实现类
public class PersonFactory {
//方法的返回值就是需要管理的 bean 的类型
public PersonDao getPerson(){
return new PersonDaoImpl();
}
}
//bean.xml
<bean id="personFactory" class="com.qf.factory.PersonFactory" ></bean>
<bean id="personDao" class="com.qf.dao.impl.PersonDaoImpl" factory-bean="PersonFactory" factory-method="getPerson"></bean>
//使用
context.getBean("personDao")
//方式三
//接口
public interface OrderDao {
public void addOrder();
}
//实现类
public class OrderDaoImpl implements OrderDao {
public void addOrder() {
System.out.println("addOrder 方法执行了.....");
}
}
//工厂类
public class OrderFactory {
public static OrderDao getOrder(){
return new OrderDaoImpl();
}
}
//配置信息
<bean id="orderFactory" class="com.qf.factory.OrderFactory" factory-method="getOrder"></bean>
//使用
context.getBean("orderFactory")
3.4 Bean的作用域
bean 标签的 scope 属性:
作用:用于指定 bean 的作用范围 取值:
常用的就是单例的和多例的
singleton:单例的(默认值)
prototype:多例的
request:作用于 web 应用的请求范围
session:作用于 web 应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环 境时,它就是 session。
4. 依赖注入DI
4.1 基本概念
Dependency Injection- 作用 : 依赖关系的管理,依赖都交给 spring 来维护 在当前类需要用到其他类的对象,由 spring 为我们提供,我们只需要在配置文件中说 明
- 能注入的数据类型
- 基本类型和
- String 其他 bean 类型(在配置文件中或者注解配置过的 bean)
- 复杂类型/集合类型
- 注入的三种方式 :
- 第一种:使用构造函数提供
- 第二种:使用 set 方法提供
- 第三种:使用注解提供
4.2 注入的方式以及其示例
set方法注入- 构造方法注入
- 注解注入
<!--
依赖注入: 依赖注入的前提是控制反转(将对象创建的权利交给spring容器管理)。
依赖注入的意思就是给spring管理的bean的属性赋值
第一种依赖注入的方式(bean所属的类必须提供set方法):
property: 给bean的属性赋值的标签
name: bean对应的属性名称
value:给bean的属性设置值
ref: 给引用类型的属性赋值
-->
<bean id="car" class="com.qf.pojo.Car">
<property name="id" value="1001"></property>
<property name="brand" value="BMW"></property>
</bean>
<bean id="emp" class="com.qf.pojo.Emp">
<property name="id" value="10010"></property>
<property name="name" value="James"></property>
<property name="salary" value="3000"></property>
<property name="car" ref="car"></property>
</bean>
<!--
使用构造函数的方式管理bean
-->
<bean id="person" class="com.qf.pojo.Person">
<constructor-arg name="name" value="eric"></constructor-arg>
<constructor-arg name="age" value="15"></constructor-arg>
<constructor-arg name="car" ref="car"></constructor-arg>
</bean>
<!--给复杂的数据类型设置值-->
<bean id="animal" class="com.qf.pojo.Animal">
<!--给数组类型的属性设置值-->
<property name="strs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<!--给list类型的属性设置值-->
<property name="list">
<list>
<value>LIST1</value>
<value>LIST2</value>
<value>LIST3</value>
</list>
</property>
<!--给set类型的属性设置值-->
<property name="set">
<set>
<value>SET1</value>
<value>SET2</value>
<value>SET3</value>
</set>
</property>
<!--给map类型的属性设置值-->
<property name="map">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
<entry key="k3" value="v3"></entry>
</map>
</property>
<!--给properties类型的属性赋值-->
<property name="pros">
<props>
<prop key="p1">pro1</prop>
<prop key="p2">pro2</prop>
<prop key="p3">pro3</prop>
</props>
</property>
</bean>
<!--
在spring容器里面管理第三方的bean
-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://192.168.10.137:3306/ssm"></property>
<property name="user" value="root"></property>
<property name="password" value="Admin123!"></property>
</bean>
<!--
管理dao service
-->
<bean id="accountDao" class="com.qf.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="com.qf.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
5. 注解
5.1 注解功能分类
- 用于创建对象的: 作用:他们的作用就和在 XML 配置文件中编写一个标签实现的功能是一样的。
- 用于注入数据的: 作用:他们的作用就和在 xml 配置文件中的 bean 标签中写一个标签的作用是 一样的。
- 用于改变作用范围的: 作用:他们的作用就和在 bean 标签中使用
scope属性实现的功能是一样的。 - 生命周期相关的: 作用:他们的作用就和在 bean 标签中使用
init-method和destroy-methode的作用是一 样的。
5.2 具体注解
- 在添加注解之前, 需要在
bean中添加扫描注解的功能
<context:component-scan base-package="com.qf"></context:component-scan>
- 第一种:描述bean的类型的注解
-
用法:定义在类名上。 作用:用于把当前类对象存入 spring 容器中。 属性:value:用于指定 bean 的 id。当我们不写时,它的默认值是当前类名,且首字 母改小写。
注释 用法 @Component 标识当前类所属的bean交给spring容器管理 @Controller 用来标识web层 @Service 用来标识Service层 @Repository 用来标识Dao层 @ComponentScan 作用:用于通过注解指定 spring 在创建容器时要扫描的包 属性:value:它和 basePackages 的作用是一样的,都是用于指定创建容器时要扫描 的包。 @Bean 作用:用于把当前方法的返回值作为 bean 对象存入 spring 的 ioc 容器中 属性: name:用于指定 bean 的 id。当不写时,默认值是当前方法的名称 细节:当我们使用注解配置方法时,如果方法有参数,spring 框架会去容器中查找有没有 可用的 bean 对象 @Configuration 指定当前类是配置类,用于替代 bean.xml
-
- 第二种:描述bean生命周期的注解
@Scope作用于类上面 @Scope(“prototype”)
- 第三种:描述bean的作用域的注解
@PostConstruct//修饰init方法
@PreDestroy//修饰destroy方法
5.3 自动装配Autowired
| 注解 | 用法 |
|---|---|
| @Autowired | 当一个类中需要引用其他类的对象时 , 可以声明该对象 , 并在声明上方添加, 按照类型自动注入 注意 : 当bean.xml中只有一个该类型的对象时 , 会自动匹配该对象 , 但是,如果有多个该类型的对象时 , 就需要在类型声明后面加上具体的对象名id |
| @Qualifier | 需要配合@Autowired使用, 用来具体定位对象的名称 , value:用于指定注入 bean 的 id |
| @Resource | 作用:直接按照 bean 的 id 注入。它可以独立使用。 属性:name:用于指定 bean 的 id。 |
| @Value | 作用:用于注入基本类型和 String 类型的数据。 属性:value:用于指定数据的值。它也可以使用 spring 中 SpEL(也就是 spring 的 el 表达式)。 SpEL 的写法:${表达式}。 |
以上三个注入都只能注入其他 bean 类型的数据,而基本类型和 String 类型无法使用 上述注解实现。另外,集合类型的注入只能通过 XML 来实现。
1、使用IOC注解来改造案例
spring有哪些IOC注解
第一种:描述bean的类型的注解
@Component 标识当前类所属的bean交给spring容器管理
@Controller
@Service
@Repository
这样使用衍生注解的好处是在不同的层下面使用不同的注解,标识三层开发的架构
dao层的实现类上使用@Repository注解
service层的实现类上使用@Service注解
controller层的实现类上使用@Controller注解
第二种:描述bean生命周期的注解
@Scope 作用于类上面 @Scope("prototype")
第三种:描述bean的作用域的注解
@PostConstruct //修饰init方法
@PreDestroy //修饰destroy方法
spring依赖注入的注解
@AutoWired
进行引用数据类型的自动注入。
@AutoWired
AccountDao accountDao;
首先会按照变量的数据类型在ioc容器里面寻找是否存在与之匹配的数据类型的bean。如果找到了,并且这个bean是唯一的,那么就注入成功。
如果在ioc容器中找到与之匹配的bean存在多个。那么再按照名称进行匹配,比较变量的名称是否和ioc容器中bean的名称是否一致。
如果一致,说明注入成功,如果不一致说明注入失败。
@Resource
@Resource
AccountDao accountDao;
注入引用数据类型,按照bean的名称进行注入。
@Value
给基本数据类型或者字符串注入值
@Value("hello")
String name;
当然我们也可以在@Value注解中使用springEL表达式
@Value("#{1+1}")
int age;
5.4 数据源配置示例
- 方式一
<!-- 配置 Dao 对象-->
<bean id="accountDao" class="com.qf.dao.impl.AccountDaoImpl">
<!--注入 QueryRunner 对象-->
<property name="runner" ref="runner"></property>
</bean>
<!--
配置 QueryRunner 对象
对于数据源对象创建多例的,因为可能是有多个用户都在用数据源,可能存在一个用户在用数据源的时候,另外一个
用户正在用的问题。
-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--注入数据源-->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://192.168.10.137:3306/ssm"></property>
<property name="user" value="root"></property>
<property name="password" value="Admin123!"></property>
</bean>
- 方式二
<!--开启包扫描-->
<context:component-scan base-package="com.qf"></context:component-scan>
<!--加载properties配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--管理Druid数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="username" value="${name}"></property>
<property name="password" value="${password}"></property>
<property name="url" value="${url}"></property>
</bean>
<!--管控QueryRunner核心对象-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
6. 整合Junit
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!---junit依赖可以被 在测试类上面定义 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(location={classpath:bean.xml})替代-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
7. 事务
7.1 事务的概念
通俗来讲事物就是一系列原子操作单元,从数据库角度来说,事物就是一个或好几个组成的sql执行单元,状态统一改变,或者统一不改变。再通俗点就是我们操作数据库的几条sql,要么全部执行成功,要不全不成功。
7.2 事务的性质
- 原子性:原子不可再分 , 一个操作不能分为更小的操作, 要么全都执行, 要么全不执行。
- 一致性:事务在完成时, 必须使得所有的数据保持一致的状态。
- 隔离性:事务查看数据时数据所处的状态,要么是另一个并发事务修改它之前的状态,要么 是另一并发事务修改它之后的状态,事务不会查看中间状态的数据。
- 持久性:事务完成之后,它对于系统的影响是永久性的
7.3 事务的隔离级别
- 未提交读(Read uncommitted)一个事物可以读取到另一个事物没有提交的数据, 基本没用
- 已提交读(Read committed)一个事务可以读取到另一个事务已经提交的数据, 还可以读取到另一个事务对已有记录的更新, 解决脏读
- 可重复读(Repeatable read)一个事务可以读取到另一个事务已经提交的数据, 但是不能读取到另一个事务对已有记录的更新, 解决脏读和重复读
- 可串行化(Serializable )一个事务在执行的过程中完全看不到其他事务对数据库所做的更新,可避免脏读、不可重复读、幻读的发生。
7.4 并发可能带来的错误问题
- 脏读:读取了未提交事务中的数据
- 不可重复读:对于数据库中的某个数据,一个事务范围内多次读取同一个数据,却有不同的值
- 幻读:一个事务读到另一个事务新增加并提交的数据(insert)。在同一个事务中,对于同一 组数据读取到的结果不一致
7.5 事务的传播行为
7.5.1 概念
指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
7.5.2 分类

8. 代理
8.1 概述
功能 : 代理可以降低代码的耦合性 , 不修改源码的情况下对方法进行增强
8.2 代理分类
8.2.1 静态代理
可以实现在不修改目标对象的基础上,对目标对象的功能进行扩展。
但是由于代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。
8.2.2 JDK动态代理
动态代理 : 代理对象在程序的运行过程中创建。
基于对接口进行代理
创建方法:使用 Proxy 类中的
newProxyInstance方法创建创建条件:被代理类最少实现一个接口,如果没有则不能使用
newProxyInstance方法的参数: ➢
ClassLoader:类加载器: 它是用于加载代理对象字节码的,和被代理对象使用相同的类加载器。是固定写法 (写被代理对象的类加载器) ➢
Class[]:字节码数组 它是用于让代理对象和被代理对象有相同的方法(只要两者都实现了同一个接口, 那么两者的方法必然相同,所以我们传接口的字节码文件即可)。固定写法(写接口字节码 文件) ➢
InvocationHandler:处理器
8.2.1 cglib代理
第三方代理
基于子类的代理
使用
Configuration,对应的类会被代理,原理是对目标对象进行继承代理
9. AOP
9.1 概述
AOP:面向切面编程,AOP 是 OOP 的扩展和延伸,用来解决 OOP 开发中遇到的问题。
利用的是一种横切技术,解剖开封装的对象内部,并将那些影响多个类的
公共行为封装到一个可重用模块,这就是所谓的 Aspect 方面/切面。所谓的切面,简单点所说,就 是将哪些与业务无关,却为业务模块所共同调用的行为(方法)提取封装,减少系统的重复 代码,以达到逻辑处理过程中各部分之间低耦合的隔离效果。
9.2 SpringAOP
SpringAop 的底层采用的是
动态代理技术,但是动态代理(jdk 动态代理 cglib 字节 码代理)过于繁琐。于是 spring 引入了第三方的AspectJ框架。
9.2.1 相关术语
- 连接点:可能被增强的方法
- 切入点:真实被增强的方法
- 增强:方法层面的增强,权限校验的方法
- 引介:类层面的增强
- target:被增强的对象
- Weaving:织入,将通知应用到目标上的过程
- Aspect : 切面
9.2.2 使用
-
引入依赖
aspectjweaver -
编写目标类
-
编写切面类,
checkPrivilege()、前置通知,开启权限校验printLog(Object result)、后置通知,在目标方法执行之后进行的操作。around(ProceedingJoinPoint joinPoint)、在目标方法执行前和执行后都要执行afterThrowing(Throwable tx)、在目标方法执行出现异常的时候,进行增强after()、无论代码是否有异常,总会执行(了解)。
-
配置切面
10. JdbcTemplate
它是 spring 框架中提供的一个对象。是对原生 jdbc 的简单封装,Spring 框架为我们 提供了很多操作的模板类。
11. Spring 的事务管理的 API
PlatformTransactionManager:平台事务管理器
接口,是 Spring 用于管理事务的真正的对象。
DataSourceTransactionManager:底层使用 JDBC 管理事务。
TransactionDefinition:事务定义信息
事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读。
11.1 编程式事务
- 搭建案例环境
- 配置平台事务管理器和事务管理模板
<!--配置平台事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!--注入平台事务管理器-->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
- 在业务类中注入事务管理模板
- 在业务类里面进行事务控制
11.2 声明式事务(xml方式)
- 配置事务增强
- 配置 aop,将事务增强和切点连接在一起
11.3 注解
- 配置开启注解对事务的支持
<tx:annotation-driven></tx:annotation-driven>
-
在业务类上添加注解
@Transactional -
TransactionDefinition:事务定义信息
事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读。
11.1 编程式事务
- 搭建案例环境
- 配置平台事务管理器和事务管理模板
<!--配置平台事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!--注入平台事务管理器-->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
- 在业务类中注入事务管理模板
- 在业务类里面进行事务控制
11.2 声明式事务(xml方式)
- 配置事务增强
- 配置 aop,将事务增强和切点连接在一起
11.3 注解
- 配置开启注解对事务的支持
<tx:annotation-driven></tx:annotation-driven>
- 在业务类上添加注解
@Transactional


2650

被折叠的 条评论
为什么被折叠?



