如何学习spring
spring理论(能听懂技术介绍,并且可以对其他人说出去)
spring基础(IoC、DI、AOP)---很重要(spring boot、spring cloud 、spring data)
spring源码(重点是帮助深入理解spring应用)
spring应用(JDBC支持、事务支持、和mybatis整合)
可以解决spring框架中暴露出的问题(调bug)
交接(spring信息同步)
Spring介绍
我们现在学习的spring,spring framework(包含了20多个模块)其中里面有web mvc模块(springmvc)
springboot 、springcloud 与 spring framework不存在包含关系。
spring提供了一站式企业应用解决方案(企业开发的一条龙服务、邮件、消息服务、开发框架)。
spring容器其实指的就是IoC容器,Ioc容器指的就是BeanFactory工厂(DefaultListableBeanFactory)。BeanFactory有一个子接口叫ApplicationContext(应用上下文接口)。
我们需要了解spring容器的初始化过程(BeanFactory是如何管理Bean的实例的,需要源码专题去解决)
IoC:控制反转,创建Bean对象的角色发送了反转,由程序员创建,反转为Spring容器创建。
DI:基于IoC的,在Bean对象创建的过程中,需要注入属性(基本属性、对象属性、集合数组属性)
-
AOP:
面向切面的编程,它只是一种aop联盟提出来的编程思想,根据这种编程思想,有很多的实现:AspectJ、Spring AOP、Spring 整合了AspectJ。
-
AOP的主要作用:横向抽取重复代码,实现代码的重用(事务、日志监控等)、AOP是为了弥补OOP的一些不足。
- 纵向抽取(继承)
- 横向抽取(AOP)
Spring 入口
所谓的spring入口,指的就是如何启动spring容器。
-
基于XML
-
java应用
ApplicationContext ctx = new ClasspathXmlApplicationContext("spring.xml");
-
web应用
web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </context-param> <listener> <listener-class> ContextLoaderListener </listener-class> </listener>
ContextLoaderListener监听器中,会去调用getWebApplicationContext()—>AbstractApplicationContext()
-
-
基于注解
-
java应用
ApplicationContext ctx = new AnnotationConfigApplicationContext(@Configuration配置类);
-
web应用
web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>@Configuration配置类</param-value> </context-param> <listener> <listener-class> ContextLoaderListener </listener-class> </listener>
ContextLoaderListener监听器中,会去调用getWebApplicationContext()—>AbstractApplicationContext()
getWebApplicationContext得到的默认实现类AnnotationConfigWebApplicationContext
-
IoC和DI的三种实现方式
基于XML
java代码---面向接口开发(正常编写)
-
spring xml配置文件
- IoC---bean标签 ,表示该bean交给spring容器管理
- id
- init-method 初始化方法(源码分析部分,看到该属性啥时候执行)
-
destroy-method 它的值就是一个方法名称
- 数据库连接池配置的时候,一定要配置它,具体配置看bean的class类中定义的销毁方法是什么
- DI —bean标签的property子标签和constractor�-arg子标签
- ref
- value
- IoC---bean标签 ,表示该bean交给spring容器管理
基于注解和XML混合
-
java代码
- IoC注解:@Component、@Controller、@Service、@Repository
- DI注解:第一步:在IoC容器中查找指定的依赖;第二步:属性注入
- @Value(注入基本类型和String类型)
- ${xxx}
-
context:property-placeholder:第一个去加载指定的properties文件、第二个就是将读取到的key/value数据,去替换spring上下文中出现的属性占位符
${xxx}
- @Autowired(byType---class类型)---spring提供的注解。
- byName—需要配合@Qualifier注解
- @Resource(默认先byName[bean的id或者name])、再byType),建议使用。它是由java提供的注解。
- @Inject(默认是byType)
- byName — @Name
- @Value(注入基本类型和String类型)
-
spring xml配置
dataSource这种第三方的Bean对应的Bean标签
-
context:component-scan:
- 开启@Autowired等几个注解的功能(BeanFactoryPostProcessor)。
- 专门开启@Autowired注解的配置 context:annotation-driven
- 扫描该应用上下文中指定包下面的IoC注解,将这些扫描到的Bean,交给spring容器进行管理。
- 开启@Autowired等几个注解的功能(BeanFactoryPostProcessor)。
基于纯注解
- 零配置:是说的没有spring xml配置文件了,有没有可能包含mybatis(映射文件)
- java代码:
- @Configuration:替代XML配置文件
- @ComponentScan:替代context:component-scan标签
- @Bean:替代bean标签的。
- @ProperySource:主要是替代context:property-placeholder标签的
- @Import:替代import标签,可以将另一个@Configuration类引入到当前配置类中
基于XML和注解方式的优缺点
注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
l XML 的优势:
修改时,不用改源码。不涉及重新编译和部署。
AOP的三种实现方式
AOP原理
AOP的作用:在不修改代码的情况下,对功能进行增强(开闭原则)
预编译方式和运行时代理方式
- AspectJ是采取的预编译方式(静态织入)
- Spring AOP是采取的运行时代理方式(动态代理)
- JDK动态代理---基于接口
- CGLib动态代理---基于继承(任何一个非final类都可以被继承)
AOP术语
- 切入点:待增强的方法
- 通知(增强):日志、事务等功能
- 织入
- 切面:切入点和通知的组合
- 目标对象:表现层、业务层、持久层的相关代码。目标对象无须被修改。
- 代理:最终生成的对象。
一句话概括AOP就是:在【目标对象】中定位【切入点】,【织入】对应的【通知】,就变成了【代理对象】
基于XML(spring整合aspectJ)
目标对象(Service实现类)
编写通知类(单独的类,无须继承任何类和实现任何接口)
-
spring配置文件
<bean class="目标类的全路径"></bean> <bean class="MyAdvice的全路径"></bean> <aop:config> <!-- 这是使用的Spring AOP实现 --> <!-- <aop:advisor advice-ref="" pointcut=""/> --> <!-- 这是使用的Spring + AspectJ整合方式实现 --> <!-- 配置切面:切面由通知和切入点组成 --> <aop:aspect ref="myAdvice"> <!-- method属性值:指的是通知类的方法名称 --> <!-- pointcut属性值:切入点表达式 --> <aop:before method="before" pointcut="execution(* *..*.*ServiceImpl.*(..))" /> </aop:aspect> </aop:config>
-
通知类型(五种)
-
* 通知类型(五种):前置通知、后置通知、最终通知、环绕通知、异常抛出通知。
* 前置通知:
* 执行时机:目标对象方法之前执行通知
* 配置文件:<aop:before method="before" pointcut-ref="myPointcut"/>
* 应用场景:方法开始时可以进行校验
* 后置通知:
* 执行时机:目标对象方法之后执行通知,有异常则不执行了
* 配置文件:<aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
* 应用场景:可以修改方法的返回值
* 最终通知:
* 执行时机:目标对象方法之后执行通知,有没有异常都会执行
* 配置文件:<aop:after method="after" pointcut-ref="myPointcut"/>
* 应用场景:例如像释放资源
* 环绕通知:
* 执行时机:目标对象方法之前和之后都会执行。
* 配置文件:<aop:around method="around" pointcut-ref="myPointcut"/>
* 应用场景:事务、统计代码执行时机
* 异常抛出通知:
* 执行时机:在抛出异常后通知
* 配置文件:<aop:after-throwing method=" afterThrowing " pointcut- ref="myPointcut"/>
* 应用场景:包装异常
-
-
切入点表达式
execution(返回值 包名.类名.方法名(参数类型))
-
基于注解和XML混合(spring整合aspectJ)
目标对象(Service实现类)
-
编写切面类
类上必须加上@Aspect(标记该类是一个AOP 切面类)、@Component(标记该类可以被组件扫描器扫描到spring容器中)
方法上需要加上@Before、@AfterReturning等五个注解,分别对应五种通知类型。
可以在一些方法上加上@PointCut注解(为了去声明一个切入点表达式),该方法可以被@Before、@AfterReturning注解使用。
-
示例:
@Component("myAspect") @Aspect public class MyAspect { private static final String pcut="execution(* *..*.*ServiceImpl.*(..))"; @Before(value="MyAspect.fn()") public void before() { System.out.println("这是注解方式的前置通知"); } @After(value="execution(* *..*.*ServiceImpl.*(..))") public void after() { System.out.println("这是注解方式的最终通知"); } @AfterReturning(pcut) public void after() { System.out.println("这是注解方式的后置通知"); } @Pointcut("execution(* *..*.*ServiceImpl.*(..))") public void fn() {} }
-
spring配置文件
<!-- 扫描切面类和目标类 --> <context:component-scan base-package="com.kkb.spring.aop"></context:component-scan> <!-- 开启aspectj的自动代理,用于AOP的注解方式 --> <aop:aspectj-autoproxy/>
基于纯注解(spring整合aspectJ)
以下代码,主要替换的就是spring配置文件
@Configuration
@ComponentScan("com.kkb.spring.aop")
@EnableAspectJAutoProxy
public class SpringConfiguration{
}
Spring整合Junit
为Springmvc的MockMVC做铺垫
-
junit是专门实现单元测试的
单元测试,主要测试的是业务逻辑。
-
编写spring单元测试代码遇到的问题
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); UserService service1 = context.getBean(UserService.class);
以上代码每一个单元测试代码都要编写,但是它又和我们要测试的业务逻辑,没有关系。
解决思路:Junit是通过@RunWith注解,让我们制定一个自定义的运行器(spring已经实现)去运行单元测试代码。
-
示例
@RunWith(SpringJunit4ClassRunner.class) //加载XML配置文件的写法 @ContextConfiguration(locations="classpath:spring.xml") //加载Java配置类的写法 //@ContextConfiguration(classes=SpringConfiguration.class) public class TestSpring{ @Resource private UserService service; @Test public void testAop(){ service.saveUser(); } }
Spring应用之JDBC实现
模板模式:将模板化的代码,抽象为一个抽象类,然后定义一个或多个abstract方法待子类去实现。
持久层的相关框架(封装了JDBC):
- mybatis:和spring整合需要第三方中间整合包(mybatis)
- ibatis
- hibernate
- spring JDBC:本来就是spring的模块,无需和spring整合。
- JdbcTemplate:模板类,主要就是通过该类实现增删改查。
- DBUtils
示例代码:
@Test
public void run1(){
// 创建连接池,先使用Spring框架内置的连接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 创建模板类
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 完成数据的添加
jdbcTemplate.update("insert into account values (null,?,?)", "测试",10000);
}
- JdbcDaoSupport
- 封装了JdbcTemplate
- 继承了该类,则不需要在spring配置文件中,注入JdbcTemplate对象了。
spring应用之事务支持
(看源码--分析事务是如何实现的并且深刻理解AOP的功能)
事务
-
Spring事务:只是做了事务管理。
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 Spring事务管理器的接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
jdbc事务
-
MySQL事务:讲解MySQL的时候会去详细介绍(redo日志、undo日志)
总结:事务最终都是由数据库本身去实现的。
事务的四大特性
ACID的理解:
-
A:原子性
操作不可分割,要么都成功、要么都失败。
-
C:一致性
账户A(600元)和账户B(400元)总共有1000块钱。
账户A(600元)给账户B(400元)转账200元,最终的结果不管是成功还是失败,A和B的总额还是1000元。
-
I:隔离性(由锁机制实现的。会引起并发访问问题)
为了保证A事务和B事务之间操作是互不影响。
- D:持久性:将结果保存到数据库文件中。
事务并发问题
隔离性不好会引起事务并发问题。
- 更新丢失
- 脏读:A事务读到了B事务未提交的数据。
- 不可重复读:A事务两次读取同一行记录,显示的结果不一致。原因是两次读取期间,B事务对该记录进行了更新操作。
- 幻读:A事务两次读取同一张表,显示的结果条数不一致。原因是两次读取期间,B事务对该表进行了增加和删除操作。
SQL92标准提出了四种隔离级别:
① Read uncommitted (读未提交):最低级别,任何情况都无法保证。
② Read committed (读已提交):可避免脏读的发生。
③ Repeatable read (可重复读):可避免脏读、不可重复读的发生。(注意事项:MySQL在该级别的时候,就可以将幻读给解决掉)
④ Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
MySQL数据库的默认隔离级别是Repeatable Read。
注意事项:
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
spring两种事务实现
spring有两种事务实现方式:编程式事务(不推荐使用)、声明式事务。
spring声明式事务
XML方式
-
spring配置文件
<bean id="dataSource"></bean> <!-- spring提供的事务管理器 --> <bean id="transactionManager" class="DataSourceTransactionManager全路径"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置通知 spring提供的增强类,但是该类需要指定事务管理器去完成事务管理--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 配置事务属性 --> <!-- 常见事务属性: 事务隔离级别、事务传播特性 --> <!-- 事务传播特性:指定的两个都拥有事务的方法,发生调用,那么此时事务该如何处理--> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="transfer*" propagation="REQUIRED" /> <tx:method name="query*" read-only="true" /> </tx:attributes> </tx:advice> <!-- 类似于以下配置 --> <!-- <bean id="" class="MyTransactionAdvice"></bean> --> <!-- 配置AOP切面 --> <aop:config> <!-- 这使用的是Spring AOP的实现 --> <!-- advice-ref:指定advice增强类 --> <aop:advisor advice-ref="txAdvice" pointcut="execution(* *..*.*ServiceImpl.*(..))" /> </aop:config>
混合方式
由同学们自己去实现
纯注解方式
由同学们自己去实现
Spring和Mybatis整合
需求
查询account表的记录
整合思路
需要整合的,就是项目中的对象(这些对象都要被spring管理)
-
分析有哪些对象需要被spring管理(确定是XML方式还是纯注解方式来管理)
- 业务层
- 实现类(多个)
- 事务相关的对象
- 事务管理器
- 通知类
- 切面类
- 持久层(mybatis)
- 数据源(一个)
- SqlSessionFactory对象(一个)
- Mapper代理对象(多个)
- 业务层
-
配置spring文件(分模块配置思想---便于维护)
-
持久层---一个spring配置文件
<!-- org.mybatis.spring.SqlSessionFactoryBean --> <!-- 可以通过该标签配置mybatis的别名、配置mybatis的插件功能等 --> <!-- 只要mybatis的全局配置文件能做的事情,SqlSessionFactory都可以做 --> <bean id="sqlSessionFactory" class="SqlSessionFactoryBean的全路径"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 该类可以动态生成mapper代理对象 --> <bean class="MapperScannerConfigurer的全路径"> <property name="basePackage" value="mapper映射文件所在的包名"></property> </bean>
-
业务层
- 业务类---一个spring配置文件
- 事务类---一个spring配置文件
将所有spring配置文件整合到一起(方式有多种,具体有哪些??????)
-
-
问题:
- 不知道加哪些依赖?只需要添加最基本的依赖,保证程序不报错即可。