Spring总结
1.什么是Spring
-
Spring是一个Java框架,用来操作JavaBean。
-
是一个开源框架。
2.Spring的作用
-
简化开发
-
降低程序耦合性
-
整合市面上主流框架,
-
SpringMvc:Web框架,是一个基于MVC设计模式的轻量级Web开发框架。本质相当于是Servlet。和Spring框架无缝继承,性能好,是市面上主流的Web框架。
-
Mybatis:持久层(Dao层)框架,用来操作数据库。本质上是JDBC,隐藏了JDBC的繁琐操作。
-
SpringBoot:基于Spring4.0设计,简化开发,内置tomcat,Spring+SpringMVC+Mybatis总和。
3.为什么要学Spring
JavaWeb项目操作复杂,不易维护。Spring能帮助我们管理JavaWeb项目。
4.Spring核心技术
IOC和DI:
我们通常会在不同的类new相同的对象,操作复杂,繁琐。不易维护,如果一个对象改变了,代码都需要改变。
-
IOC(控制反转):
将JavaBean的创建与销毁交给Spring容器统一管理。
IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean
能够提高效率,建议单例对象放在容器中,多例对象自己new。
-
DI注入:
容器中Bean可能会存在关联关系,如果我们要使用就需要关联起来。给一个Bean对象注入另一个Bean对象的这个过程就叫做DI注入。
AOP:
-
面向切面编程,一种编程范式,指导开发者如何组织程序结构
-
与面向对象一个概念。
-
-
Spring理念:无入侵式|无侵入式
-
在不改变方法本身的情况下,进行方法增强。
-
连接点:方法。
-
切入点:需要增强的方法。
-
通知:增强的内容。
-
通知类:统一管理增强内容的类。
-
切面:将通知放入需要增强的方法的的地方。
核心概念:
-
目标对象(Target):被代理的对象,也叫原始对象,该对象中的方法没有任何功能增强。
-
代理对象(Proxy):代理后生成的对象,由Spring帮我们创建代理对象。类似于Mybatis中的getMapper(BookDao.class);
3.使用
1.IOC:
-
开启注解扫描:
配置文件开启:
<!--扫描com.itheima包及其子包下的类中注解--> <context:component-scan base-package="com.itheima"/>
注解开启:
@Configuration @ComponentScan("com.itheima") public calss SprinConig{}
容器启动的时候,加载此包下面的所有类,将加了注解的类添加到容器中。
2.定义Bean:
添加内部Bean:
Spring提供@Component
注解的三个衍生注解
-
@Controller
:用于表现层bean定义 -
@Service
:用于业务层bean定义 -
@Repository
:用于数据层bean定义
容器启动的时候会扫描指定包下面,如果加上如上注解,就会自动创建Bean对象注入Spring容器中,管理Bean的初始化与销毁。
配置第三方Bean:
@Bean
表示当前方法的返回值是一个bean对象,添加到IOC容器中。
使用场景:datasource注入,SqlSessionFactory注入...
public class JdbcConfig { //@Bean:表示当前方法的返回值是一个bean对象,添加到IOC容器中 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }
3.设置依赖注入:
@Autowired
:默认按照类型从容器装配。如果容器中同类的Bean有多个,那么默认按照变量名与Bean的名称匹配。
@Autowired private BookDao bookDao1;
@Qualifier
:从容器中取的时候,按照指定名称取。必须配合@Autowired使用。
@Qualifier("bookDao1") @Autowired private BookDao bookDao;
@Value
:可以对简单类型的注入,可以自己定义,也可从配置文件中读取。
需要在SrpingConfig添加@PropertySource
注解,指定从那个配置文件中读取。可以指定多个,建议添加classpath
@Configuration @PropertySource({"classpath:jdbc.properties"}) public calss SprinConig{}
从配置文件中读取name
属性的值赋给变量name。
//@Value:注入简单类型(无需提供set方法) @Value("${name}") private String name;
作用范围
@Scope
:配置Bean的作用范围,单例或者多例。默认是单例模式。
@Scope("singleton") public class BookDaoImpl implements BookDao { }
生命周期
@PostConstruct
:修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
@PreDestroy
:在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。
注意:@PostConstruct和@PreDestroy注解是jdk中提供的注解,从jdk9开始,jdk中的javax.annotation包被移除了,也就是说这两个注解就用不了了,可以额外导入一下依赖解决这个问题。
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
4.需要配置SpringConfig
配置文件,加载Bean对象到容器中。
@Configuration
指定当前类是个配置类
@Import
导入外部Bean的配置,可以导入多个。
@Configuration //@Import:导入配置信息 @Import({JdbcConfig.class}) public calss SprinConig{ }
2.AOP
工作流程:
-
Spring容器启动
-
读取所有切面配置中的切入点
-
初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
-
匹配失败,创建原始对象
-
匹配成功,创建原始对象(目标对象)的代理对象
-
-
获取bean执行方法
-
获取的bean是原始对象时,调用方法并执行,完成操作
-
获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
-
1.切入点表达式,用于指定要增强的方法的路径。
-
切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
-
execution(* com.itheima.service.Service.(..))
-
-
切入点表达式描述通配符:
-
作用:用于快速描述,范围描述
-
*:匹配任意符号(常用)
-
.. :匹配多个连续的任意符号(常用)
-
+:匹配子类类型
-
-
切入点表达式书写技巧
1.按标准规范开发 2.查询操作的返回值建议使用*匹配 3.减少使用..的形式描述包 4.对接口进行描述,使用*表示模块名,例如UserService的匹配描述为*Service 5.方法名书写保留动词,例如get,使用*表示名词,例如getById匹配描述为getBy* 6.参数根据实际情况灵活调整
下面表达的是:在cn.hmc.testaop包下面的TestAop类中返回值为void,方法名为update没有入参的方法。
反过来阅读:倒数第一个是方法名,倒数第二个是类名,除了第一个剩下的都是包名
//设置切入点 @Pointcut("execution(void cn.hmc.testaop.TestAop.update()))") private void pt(){}
简化版本:
匹配cn.hmc.testaop包下面任意以AOP结尾的类,返回值任意,方法名任意,方法参数任意。
@Pointcut("execution(* cn.hmc.testaop.*Aop.*(..))") private void test(){}
极度简化:
匹配当前项目下任意包,任意类,任意返回值,任意方法。
@Pointcut("execution(* ...*.*(..))") private void test2(){}
2.AOP通知
-
AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
-
AOP通知共分为5种类型
-
前置通知:在切入点方法执行之前执行
-
后置通知:在切入点方法执行之后执行,无论切入点方法内部是否出现异常,后置通知都会执行。
-
环绕通知(重点):手动调用切入点方法并对其进行增强的通知方式。
-
返回后通知(了解):在切入点方法执行之后执行,如果切入点方法内部出现异常将不会执行。
-
抛出异常后通知(了解):在切入点方法执行之后执行,只有当切入点方法内部出现异常之后才执行。
-
1.前置通知:在切入点方法执行之前执行
@Before("pt()") public void before() { System.out.println("before advice ..."); }
2.后置通知:在切入点方法执行之后执行
@After("pt()") public void after() { System.out.println("after advice ..."); }
3.返回后通知:在切入点方法正常执行完毕后运行
@AfterReturning("pt()") public void afterReturning() { System.out.println("afterReturning advice ..."); }
4.抛出异常后通知:在切入点方法运行抛出异常后执行
@AfterThrowing("pt()") public void afterThrowing() { System.out.println("afterThrowing advice ..."); }
5.环绕通知:在切入点方法前后运行。
@Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around before advice ..."); Object ret = pjp.proceed(); System.out.println("around after advice ..."); return ret; }
ProceedingJoinPoint
常用方法:
# 返回目标对象,即被代理的对象 Object getTarget(); # 返回切入点的参数 Object[] getArgs(); # 返回切入点的Signature Signature getSignature(); # 返回切入的类型,比如method-call,field-get等等,感觉不重要 String getKind();
JointPoint
是ProceedingJoinPoint
的父类 , 在其他通知中可以获取到 . ProceedingJoinPoint
只能在环绕通知中获取.
环绕通知注意事项
-
环绕通知方法形参必须是ProceedingJoinPoint,表示正在执行的连接点,使用该对象的proceed()方法表示对原始对象方法进行调用,返回值为原始对象方法的返回值。
-
环绕通知方法的返回值建议写成Object类型,用于将原始对象方法的返回值进行返回,哪里使用代理对象就返回到哪里
开启AOP注解支持
@Configuration//声明当前类是一个Spring配置类 @ComponentScan("com.itheima")//扫描指定包下面的类是否能够添加进容器.根据注解判断 @PropertySource("classpath:jdbc.properties")//@Value读取的配置文件 @Import({JdbcConfig.class,MybatisConfig.class})//外部bean的定义类 @EnableAspectJAutoProxy //开启AOP注解功能 public class SpringConfig { }
3.Spring事务管理
事务是对数据库的一组操作,要么同时成功要么同时失败。
-
原子性:事务是不可分割的,要么同时成功,要么同时失败
-
一致性:一致性事务操作前后数据总量不变
-
隔离性:事物之间互补影响
-
持久性:事务提交或回滚数据将会永久存在磁盘中
不考虑隔离性产生的问题:
-
脏读:一个事务读取到另一个事务未提交的数据
-
不可重复读:一个事务开启后多次读取数据不一致
-
幻读:一个事务进行整表操作后,结果与预期不一致
解决:
隔离级别:
-
串行化:事务一个一个排队
-
可重复读:能解决不可重复度以及脏读问题 mysql默认
-
读已提交:能解决脏读问题 oracle默认
-
读未提交:不能解决任何问题
Spring事务作用:
事务作用:在数据层保障一系列的数据库操作同成功同失败
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
例子: 转账问题:
A到银行给B转账,首先银行会将A的钱扣除,然后给B的钱增加.
正常执行没有问题,如果在扣除A的钱之后出现了异常,业务就会失败,A扣了钱,B没有加钱,不符合业务需求.
这个时候需要给执行这个流程的业务添加一个事务,保证在出现异常之后回滚到执行业务之前的状态.
开启事务注解支持:
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) //开启注解式事务驱动 @EnableTransactionManagement public class SpringConfig { }
在处理转账的接口上添加事务注解:
public interface AccountService { //配置当前接口方法具有事务 @Transactional public void transfer(String out,String in ,Double money) ; }
此方法运行的时候就会生成一个事务包裹里面的业务,出错自动回滚.
有时我们需要捕获一些错误信息,又需要进行事务回滚,这时我们就需要用到Spring提供的事务切面支持类TransactionAspectSupport。
int i = userDao.saveUser(); //如果用户添加失败,我们就不添加学生 if(i<1){ //手动强制回滚事务,这里一定要第一时间处理 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); throw new TestEception("添加用户失败!"); } i = studentDao.saveStudent();