Spring 是什么?
是一个开源的,轻量级的 Java 开发框架
Spring 的作用?
简化 java 应用程序的开发
使用了那些技术?
- 控制反转(IOC Inversion of Control)
- 面向切面编程(AOP Aspect Oriented Programming)
控制反转(IOC Inversion of Control)
IOC的底层就是对象工厂
什么是IOC(控制反转)
将对象的创建、依赖关系、调用交给 Spring 来管理
为什么使用 IOC
- 降低代码间的耦合度
- 减少代码量
IOC底层原理
使用xml解析、工厂模式、反射
- 配置xml文件
- 创建工厂类
String classPath = “xml解析”;
Class cl = Class.forName(classPath);
cl.newInstance();
Spring提供的IOC容器实现的两种方式(两个接口)
a)BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
b)ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!
Bean 管理
- 创建对象
- 注入属性
创建对象
基于 xml 配置文件创建对象
<!--1 配置User对象创建-->
<bean id="user" class="com.happy.User"></bean>
注入属性
基于 xml 方式注入属性
- set 方法进行注入
- 有参构造函数注入
实现
- 通过 set 方法进行注入
//(1)传统方式: 创建类,定义属性和对应的set方法
public class Book {
//创建属性
private String bname;
//创建属性对应的set方法
public void setBname(String bname) {
this.bname = bname;
}
}
<!--(2)spring方式: set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
<!--使用property完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="Hello"></property>
<property name="bauthor" value="World"></property>
</bean>
- 有参构造函数注入
//(1)传统方式:创建类,构建有参函数
public class Orders {
//属性
private String oname;
private String address;
//有参数构造
public Orders(String oname,String address) {
this.oname = oname;
this.address = address;
}
}
<!--(2)spring方式:有参数构造注入属性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-arg name="oname" value="Hello"></constructor-arg>
<constructor-arg name="address" value="China!"></constructor-arg>
</bean>
- 注入外部 bean
- 注入内部 bean
- 级联赋值
- 注入集合属性
<!--(2)在 spring 配置文件进行配置-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="courses">
<array>
<value>java课程</value>
<value>数据库课程</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
</list>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
</bean>
Bean 的类型 FactoryBean
Spring 中有两种类型的 Bean 一种是普通 Bean 一种是工厂 Bean
- 普通 Bean:在配置文件中定义 bean 类型就是返回的 bean 的类型
- 工厂 Bean:定义的类型和返回的类型不一样
- 创建类,让该类作为工厂 bean,实现 FactoryBean 接口
- 实现接口中的方法,定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean>
@Test
public void test3() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
System.out.println(course);
}
bean 的作用域
默认情况下 bean 是单实例对象
scope属性
通过设置 bean 标签中的 scope 属性来控制 bean 是为单实例(singleton)还是多实例(prototype)
bean 生命周期
正常 bean 生命周期
- 通过无参构造器创建 bean 实例
- 注入 bean 属性
- 调用 bean 的初始化方法(需要配置)
- 获取到 bean,可以使用了
- 当容器关闭时调用 bean 的销毁方法(需要配置)
配置后置处理器 bean 的生命周期
- 通过无参构造器创建 bean 实例
- 注入 bean 属性
- 将 bean 实例传递到后置处理器中的 postProcessBeforeInitialization方法
- 调用 bean 的初始化方法(需要配置)
- 将 bean 实例传递到后置处理器中的 postProcessAfterInitialization方法
- 获取到 bean,可以使用了
- 当容器关闭时调用 bean 的销毁方法(需要配置)
public class Orders {
//无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建 bean 实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用 set 方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
//创建执行的销毁的方法
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
}
public class MyBeanPost implements BeanPostProcessor {//创建后置处理器实现类
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
<!--配置文件的bean参数配置-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <!--配置初始化方法和销毁方法-->
<property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
@Test
public void testBean3() {
// ApplicationContext context =
// new ClassPathXmlApplicationContext("bean4.xml");
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步 获取创建 bean 实例对象");
System.out.println(orders);
//手动让 bean 实例销毁
context.close();
}
bean 管理外部属性文件
- 直接配置信息
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
- 引入外部属性文件
//创建配置文件后缀位 properties
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
Bean 的基于注解的管理方式
使用步骤
- 引入依赖 spring-aop jar 包
- 开启扫描组件
<!--开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
- 创建类,在类上方添加对象注解
//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService -- userService
@Component(value = "userService") //注解等同于XML配置文件:<bean id="userService" class=".."/>
public class UserService {
public void add() {
System.out.println("service add.......");
}
}
- 开启组件扫描的详细配置
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/><!--代表只扫描Controller注解的类-->
</context:component-scan>
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/><!--表示Controller注解的类之外一切都进行扫描-->
</context:component-scan>
- 基于注解的方式实现属性的注入
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
//Dao实现类
@Repository
//@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add.....");
}
}
注入属性的注解
- @Qualifier 根据名称注入
- @Autowired 根据类型注入
- @Resource 可以根据类型也可以根据名称注入(属于 javax 包下的注解,不推荐使用)
- @Value 注入普通类型的属性
使用配置类代替 xml 文件
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}
@Test
public void testService2() {
//加载配置类
ApplicationContext context
= new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService",
UserService.class);
System.out.println(userService);
userService.add();
}
自动装配
除了以上配置xml文件的方式,还以可以通过注解的方式来实现。 使用@Autowired、@Resource注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
@Autowired可用于:构造函数、成员变量、Setter方法
注:@Autowired和@Resource之间的区别:
(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
面向切面编程AOP
什么是 AOP
在不修改源代码的情况下,添加新的功能
AOP 如何实现
底层原理使用动态代理模式
AOP 中的术语
-
连接点:类里面哪些方法可以被增强,这些方法称为连接点
-
切入点:实际被真正增强的方法称为切入点
-
通知(增强):实际增强的逻辑部分称为通知,且分为以下五种类型:
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
-
切面:把通知应用到切入点过程
AOP实操
切入点表达式
用于指示那些地方需要被增强
(1)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
(2)例子如下:
例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))
例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))
基于 AspectJ框架实现 AOP 操作
- 基于 xml 配置文件
- 基于注解的方式实现
基于注解的方式
//1、创建类,在类里面定义方法
public class User {
public void add() {
System.out.println("add.......");
}
}
//2、创建增强类(编写增强逻辑)
//(1)在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类
public class UserProxy {
public void before() {//前置通知
System.out.println("before......");
}
}
<!--3、进行通知的配置-->
<?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"
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
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
<!-- 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {}
//被增强的类
@Component
public class User {}
//4、配置不同类型的通知
@Component
@Aspect //生成代理对象
public class UserProxy {
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before注解表示作为前置通知
@Before(value = "pointdemo()")//相同切入点抽取使用!
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
存在多个增强时设置优先级
//(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{ }
基于配置文件进行增强
<!--1、创建两个类,增强类和被增强类,创建方法(同上一样)-->
<!--2、在 spring 配置文件中创建两个类对象-->
<!--创建对象-->
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
<!--3、在 spring 配置文件中配置切入点-->
<!--配置 aop 增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
事务
事务数据库操作的基本单元,逻辑上一组操作,要么都成功,要么都失败
事务的特性
- 原子性
- 一致性
- 操作性
- 持久性
事务操作的基本过程
- 开启事务
- 进行业务操作
- 没有发生异常,提交事务
- 发生了异常,事务回滚
Spring 事务管理的 API
PlatformTransactionManager;这个接口针对不同的框架提供不同的实现类
在 Spring 中进行事务管理操作
一般添加到业务逻辑层 两种进行事务管理的方式
- 编程式事务管理
- 声明式事务管理
声明式事务管理
底层采用的是 AOP原理 两种实现方式
- 基于 xml 配置文件
- 在 spring 配置文件中配置事务管理器
选择对应的事务管理器,注入数据源
- 引入响应的名称空间tx
- 配置事务通知
- 配置切入点和切面
- 在 spring 配置文件中配置事务管理器
- 基于注解方式实现
- 在 spring 配置文件中配置事务管理器
选择对应的事务管理器,注入数据源
- 在 spring 配置文件中开启事务注解
(1) 引入响应的名称空间tx
(2) 开启事务注解
- 在需要使用事务管理的地方添加注解@Transactional
- 在 spring 配置文件中配置事务管理器
@Transactional 参数配置
- propagation 事务传播行为
2. isolation 事务隔离级别
事务有隔离性,多事务之间不会产生影响。如果不考虑隔离性将会产生很多问题。
产生是三个读的问题:赃读、不可重复度、虚(幻)读。
1. 赃读
一个未提交的事务读取到另一个未提交事务的数据
2. 不可重复读
一个未提交事务读取到另一个提交事务修改的数据
3. 虚读
一个未提交事务读取到另一个提交事务添加的数据
使用隔离级别来解决问题
3. timeout 超时时间
事务需要在规定时间内提交,如果不提交就进行回滚。
默认的时间是-1
4. readOnly 是否只读
默认为 false,也就是可以进行查询和修改,如果为 true 则只能进行查询操作
5. rollbackFor 回滚
设置出现哪些异常进行回滚
6. noRollbackFor 不会滚
设置出现哪些异常不进行回滚