1 简介
1.1 核心功能
- IOC/DI(控制反转、依赖注入)
- AOP(面向切面编程)
- 声明式事务
1.2 运行时环境
1.3 重要概念
1.3.1 容器
Spring是一个大的容器,新版本中ApplicationContext接口,是一个大容器,可以取到Spring中所有的东西
从Spring3开始,把Spring的功能拆分成多个jar包,用什么导入什么jar包
1.3.2 test
Spring提供的单元测试功能
1.3.3 核心容器
Beans:Spring负责管理类对象
Core:核心类
Context:上下文参数,获取外部资源或管理参数
expression:
1.3.4 AOP
AOP:实现aop功能
aspects:aop依赖的包
1.3.5 data access/integration
spring封装数据访问层代码
jdbc:spring对jdbc封装后的代码
orm:封装了一些持久层代码(没有mybaits)
Transactions:声明式事务使用
1.3.6 Web
完成web相关功能时,比如使用tomcat加载配置文件时
2 IOC
控制反转
2.1 定义
原来由程序员由new实例化的任务转交给spring执行
控制:控制类对象
反转:任务转给spring处理
作用:解耦
程序员不需要管理对象了,解除了程序员与对象之间的耦合
2.2 环境搭建
1.导入jar包,包括核心包和日志包
2.编写applicationContext.xml
文件名称与路径自定义,知识为了记住spring中的容器applicationContext,xml中配置的信息最终存储到了这个容器中
3.配置文件是基于schema的
文件扩展名是.xsd,把schema理解成dtd的升级版,比DTD具备更好的扩展性,每次引入一个xsd文件时
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
4.默认配置文件被加载时创建对象
2.3 实战
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
//返回Spring容器管理的所有的对象
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
People people = ac.getBean("peo",People.class);
2.4 Spring创建对象
2.4.1 无参构造
2.4.2 有参构造
<bean id="peo" class="com.zy.pojo.People">
<constructor-arg name="name" value="zy"></constructor-arg>
</bean>
2.4.3 实例工厂
单例设计模式:整个工程只能创建某个类的一个对象
工厂是用来创造对象的,一个工厂可以用来生产多个对象
实例工厂:需要先创建工厂,才能生产对象
2.4.4 静态工厂
不需要创建工厂,可以快速创建对象
把方法变成静态的
2.5 给Bean的属性赋值(注入)
1、通过构造方法赋值
2、通过set方法赋值
如果属性是基本数据类型或者String等简单数据类型
<bean id="peo2" class="com.zy.pojo.People">
<property name="name" value="zy2" ></property>
</bean>
如果属性是set类型等
list类型
但是只会赋给一个值
数组
Map
properties
2.6 DI
依赖注入
DI和IoC是一样的
当一个类中需要依赖另一个类对象时,为依赖注入
3 整合mybaits
3.1 步骤
1、导入jar包
2、编写spring配置文件,applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 数据源封装类 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=false&serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
</bean>
<!-- 创建sqlsessionFactory -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 扫描后会给相应的接口创建对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 要扫描哪个包 -->
<property name="basePackage" value="com.zy.mapper"></property>
<!-- 与factory创建关系 -->
<property name="sqlSessionFactory" ref="factory" ></property>
</bean>
<bean id="airportService" class="com.zy.service.impl.AirportServiceImpl">
<property name="airportMapper" ref="airportMapper"></property>
</bean>
</beans>
3、编写代码
正常编写pojo
mapper中必须有接口
正常编写service接口与service实现类,要在实现类总声明mapper接口对象
public class AirportServiceImpl implements AirportService{
private AirportMapper airportMapper;
public AirportMapper getAirportMapper() {
return airportMapper;
}
public void setAirportMapper(AirportMapper airportMapper) {
this.airportMapper = airportMapper;
}
@Override
public List<Airport> selAll() {
return airportMapper.selAll();
}
}
servlet无法被spring管理
4、整合实例
web.xml中配置监听配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
servlet
package com.zy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.zy.service.AirportService;
import com.zy.service.impl.AirportServiceImpl;
/**
* Servlet implementation class AirportServlet
*/
@WebServlet("/AirportServlet")
public class AirportServlet extends HttpServlet {
AirportService airportService;
@Override
public void init() throws ServletException {
WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
airportService=ac.getBean("airportService",AirportServiceImpl.class);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("list",airportService.selAll() );
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
4 AOP
4.1 基本概念
面向切面编程
正常程序执行流程都是纵向执行流程
在原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面的过程叫做面向切面编程。
原有功能:切点
前置通知:在切点之前执行的功能(beforeAdvice)
后置通知:在切点之后执行的功能(afterAdvice)
如果切点执行过程中出现异常,会触发异常通知,throws advice
形成切面的过程叫做织入
4.3 schema-based实现aop
每个通知都需要实现接口或类
<bean id="mybefore" class="com.zy.advice.MyBeforeAdvice"></bean>
<bean id="myafter" class="com.zy.advice.MyAfterAdvice"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.zy.test.Demo.demo2())" id="mypoint"/>
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
步骤
1、导入jar包
2、前置通知
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("前置通知");
}
}
3、后置通知
public class MyAfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("后置通知");
}
}
4、配置文件
<bean id="mybefore" class="com.zy.advice.MyBeforeAdvice"></bean>
<bean id="myafter" class="com.zy.advice.MyAfterAdvice"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.zy.test.Demo.demo2())" id="mypoint"/>
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
*是通配符,可以匹配任意方法名、任意类名、任意一级包名
如果匹配任意类型参数,(…)
4.3.2 参数意义
before
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("切点方法对象:"+arg0+"方法名"+arg0.getName());
System.out.println("切点方法参数"+arg1);
System.out.println("对象"+arg2);
System.out.println("前置通知");
}
}
after
public class MyAfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("返回值"+arg0);
System.out.println("切点方法对象"+arg1);
System.out.println("参数"+arg2);
System.out.println("切点方法所在类的对象"+arg3);
System.out.println("后置通知");
}
}
4.3.3 异常通知
按照特定的要求编写异常通知
4.3.4 环绕通知
1、新建一个类实现MethodInterceptor
package com.zy.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAround implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕前置");
Object re = arg0.proceed();
System.out.println("环绕后置");
return re;
}
}
2、配置文件
<aop:config>
<aop:pointcut expression="execution(* com.zy.test.Demo.demo3(..))" id="mypoint"/>
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myaround" pointcut-ref="mypoint"/>
</aop:config>
4.4 AspectJ
4.4.1 配置异常通知的步骤
只有当切点报异常才能触发异常通知
<bean id="mythrow" class="com.zy.advice.MyThrowAdvice"></bean>
<aop:config>
<aop:aspect ref="mythrow">
<aop:after-throwing method="myExecption" pointcut-ref="mypoint"/>
</aop:aspect>
</aop:config>
package com.zy.advice;
public class MyThrowAdvice {
public void myExecption() {
System.out.println("异常通知");
}
}
spring aop 都是拦service异常,一般service都不去try catch
因为无法捕获异常
不需要每个通知实现接口和类
输出异常信息
<aop:after-throwing method="myExecption" pointcut-ref="mypoint" throwing="e"/>
package com.zy.advice;
public class MyThrowAdvice {
public void myExecption(Exception e) {
System.out.println("异常通知"+e.getMessage());
}
}
4.4.2 常用配置
package com.zy.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
public void mybefore() {
System.out.println("AspectJ前置");
}
public void myafter()
{
System.out.println("AspectJ后置");
}
public void myaftering()
{
System.out.println("AspectJ后置2");
}
public void mythrow()
{
System.out.println("AspectJ异常");
}
public void myaround(ProceedingJoinPoint p) throws Throwable
{
System.out.println("aspectJ环绕");
System.out.println("环绕前置");
Object proceed = p.proceed();
System.out.println("环绕后置");
}
}
<aop:config>
<aop:aspect ref="mythrow">
<aop:after-throwing method="myExecption" pointcut-ref="mypoint" throwing="e"/>
</aop:aspect>
</aop:config>
<bean id="myaround" class="com.zy.advice.MyAround"></bean>
<bean id="myadvice" class="com.zy.advice.MyAdvice"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.zy.test.Demo.demo3(..))" id="mypoint"/>
<aop:aspect ref="myadvice">
<aop:before method="mybefore" pointcut-ref="mypoint"/>
<aop:after method="myafter" pointcut-ref="mypoint"/>
<aop:after-returning method="myaftering" pointcut-ref="mypoint"/>
<aop:after-throwing method="mythrow" pointcut-ref="mypoint"/>
<aop:around method="myaround" pointcut-ref="mypoint"/>
</aop:aspect>
</aop:config>
4.4.3 参数获取
<aop:config>
<aop:pointcut expression="execution(* com.zy.test.Demo.demo3(String)) and args(s)" id="mypoint"/>
<aop:aspect ref="myadvice">
<!-- <aop:before method="mybefore" pointcut-ref="mypoint"/> -->
<aop:after method="myafter" pointcut-ref="mypoint" arg-names="s"/>
<!-- <aop:after-returning method="myaftering" pointcut-ref="mypoint"/> -->
<!-- <aop:after-throwing method="mythrow" pointcut-ref="mypoint"/> -->
<!-- <aop:around method="myaround" pointcut-ref="mypoint"/> -->
</aop:aspect>
</aop:config>
4.4.4注解方式配置
1、Spring不会自动去找注解,必须告诉spring哪些包下有注解
<context:component-scan base-package="com.zy.advice"></context:component-scan>
引入
<?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"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zy.advice">
2、@Component
相当于bean
默认将首字母变小写
@Component
public class Demo {
public void demo1(){
System.out.println("demo1");
}
3、在方法上添加pointcut
@Pointcut("execution(* com.zy.test.Demo.demo2())")
public void demo2() {
System.out.println("demo2");
}
定义切点
4、在通知类中配置
@Component
@Aspect
public class MyAdvice {
@Before("com.zy.test.Demo.demo2()")
public void mybefore() {
System.out.println("前置");
}
}
@Component被spring管理
@Aspect表示是一个切面
4.5 静态代理
设计模式:之前总结的一套解决特定问题的代码
代理设计模式
真实对象:老总
代理对象:秘书
抽象对象(抽象功能):面试
优点:
1、保护真实对象
2、让真实对象职责更加明确
3、扩展
静态代理:
自己编写代理类
每一个代理的功能需要自己编写
缺点:
当代理功能比较多时,代理类中方法也需要写很多
4.6 动态代理
为了解决静态代理需要频繁编写代理功能的缺点
JDK
cglib动态代理
4.6.1 JDK
优点:自带,不需要额外导入第三方jar
缺点:真实对象必须实现接口;利用的反射机制,效率不高
4.6.2 cglib
基于字节码机制,生成真实对象的子类
运行效率很高
不需要实现接口
AOP拦截的是被spring管理的方法,fliter拦截的是请求
5 自动注入
在spring配置文件中,对象名和ref的id名相同,这种情况下可以使用自动注入,不用配置property
6 加载外部属性
1、在src下新建一个properties文件
在spring配置文件中引入
2、
<context:property-placeholder location="classpath:db.properties,classpath:te.properties"/>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zy.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>
4、
@Value("${te}")
private String te;
7 Scope属性
控制对象的有效范围(单例、多例)
bean标签对应的对象默认是单例的
8 单例设计模式
提升运行效率
实现数据共享。案例:application对象
在应用程序中最多有一个实例
1、提高运行效率
2、实现数据共享
8.1 懒汉式
对象被调用时才被创建
package com.zy.pojo;
public class SingleTon {
//必须是private,禁止可以直接访问
private static SingleTon singleton;
private SingleTon() {
}
/**
* 为了让类可以直接调用
* 防止多线程重复访问
* 采用双重验证
* @return
*/
public static SingleTon getSingleTon() {
if(singleton==null) {
synchronized (SingleTon.class) {
if(singleton==null) {
singleton=new SingleTon();
}
}
}
return singleton;
}
}
8.2 饿汉式
由于添加了锁,导致低效率
解决了懒汉式中多线程访问可能出现同一个对象效率低的问题
9 声明式事务
9.1 编程式事务
由程序员来编写事务控制代码
OpenSessionInView
9.2 声明式事务
事务控制代码
Spring已经写好代码,程序员只需要声明哪些方法需要进行事务控制和如何进行事务控制。
声明式事务都是针对ServiceImpl类下方法的
事务管理器是基于通知(advice)的
配置文件
<?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"
xmlns:context="http://www.springframework.org/schema/context"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="peo" class="com.zy.pojo.People" scope="singleton"></bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="ins*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.zy.service.impl.*.*(..))" id="mypoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"/>
</aop:config>
<context:property-placeholder location="classpath:db.properties,classpath:te.properties"/>
<context:component-scan base-package="com.zy.service.impl.UsersServiceImpl"></context:component-scan>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=false&serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
</bean>
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="com.zy.pojo"></property>
</bean>
</beans>
9.3 属性解释
name=“”表示哪些方法需要有事务控制
9.3.1readonly
=“true”是否为只读事务
如果为true,为只读事务。是数据库优化,会对性能有一定提升
所以只要是查询方法,建议使用此属性
如果为false(默认),比如增删改等
9.3.2 propagation
控制事务传播行为
当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务(新建事务,在事务中执行,把事务挂起,报异常)
9.4 事务隔离级别
在并发访问下,如何保证访问到的数据具有完整性
9.4.1 脏读
一个事务读取到另一个事务中未提交的数据,另一个事务的数据可能进行了改变,此时A事务读取的数据可能和数据库中数据时不一致的,此时认为数据时脏数据,这个过程叫做脏读
9.4.2 不可重复读
主要针对的是某行数据(或行中某列)
主要针对的操作是修改操作
两次读取在同一个事务内
当事务A在第一次读取事务后,事务B对事务A读取的数据进行修改,事务A再次读取的数据和之前读取的数据不一致,叫做不可重复读。
锁行
9.4.3 幻读
针对的操作是新增或者删除
两次事务的结果
事务A按照特定条件查询出了结果,事务B新增了一条符合条件的数据,事务A查询出的数据与数据库中的数据不一致,这种情况为幻读
锁表
9.4.4 isotation
1、default
默认值,由底层数据库自动判断隔离级别
2、READ_UNCOMMITED
可以读取未提交数据,出现脏读、、、但效率最高
3、READ_COMMITED
只能读取其它事务中已提交数据,可以防止脏读,可能出现幻读、不可重复读
4、REPEATABLE——READ
读取的数据枷锁,防止其它事务修改次数据,可以防止不可重复读
5、SERIALIZABLE
排队操作,锁表,一个数据操作时,另一个事务等待操作完成后才能操作此表,最安全的,效率最低的
9.4.5 事务回滚
当出现什么异常时,需要进行回滚,建议给定该属性值
当我们手动抛异常时,一定给该属性值
10 常用注解
@Compent
创建类对象,,相当于配置
@Service
与@Compent功能相同,写在ServiceImpl上
Repository
与@Compent功能相同
写在数据访问层上
@Controller
与@Compent功能相同
写在控制器类上
@Resource
jdk的注解
默认按照byName进行注入
如果没有,按照byType注入
不需要写get、set方法
AutoWired
Spring的注解
默认按照byType注入
@Value
获取属性文件中的内容