IOC接口
BeanFactory和ApplicationContext的区别:
- BeanFactory 加载配置文件时不会创建对象
- ApplicationConttxt 加载配置文件时,就会把配置文件中的对象进行创建
Spring 针对 Bean 管理中创建对象提供注解
(1)@Component
一般用了创建服务对象和代理类、增强类
(2)@Service
创建各类Service
(3)@Controller
(4)@Repository
创建DAO接口的impl类,写成@Repository(value = "userDao"),相当于给bean命名
- 上面四个注解功能是一样的,都可以用来创建 bean 实例
xml注入
利用类的set方法注入:
- 首先在类中设置set方法
然后在xml中配置property 标签:
<bean id="book" class="com.atguigu.spring5.Book"> <!--使用 property 完成属性注入 name:类里面属性名称 value:向属性注入的值 --> <property name="bname" value="易筋经"></property> <property name="bauthor" value="达摩老祖"></property> </bean>
利用构造器注入:
- 首先在类中设置对应的构造器,里面有对应的要注入的属性
在xml中设置constructor-arg
<bean id="orders" class="com.atguigu.spring5.Orders"> <constructor-arg name="oname" value="电脑"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
- 特殊字符转义:
<value><![CDATA[<<南京>>]]></value>
- 要给类的属性注入值(使用value=),务必要生成对应的set方法
利用scope设置单例和多例:
- 默认值,singleton,表示是单实例对象
- 第二个值 prototype,表示是多实例对象
利用autowrite设置自动装配:
- “byName” 指按照属性名注入,即被注入对象中 的属性名 应该和 要注入的 对象的 bean名一致。
- “byType”根据属性进行注入,bean名字可以和属性名不一致,但是bean的声明类型只能有一个
- @Autowired 注解注入不需要set方法
名称空间
<beans
xmlns:p="http://www.springframework.org/schema/p
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
- p 在bean标签中利用 p:key=value给属性注入
util 提取 list 集合类型属性注入
<util:list id="bookList"> <value>易筋经</value> <value>九阴真经</value> <value>九阳神功</value> </util:list> <bean id="book" class="com.atguigu.spring5.collectiontype.Book"> <property name="list" ref="bookList"></property> </bean>
contest 引入外部类
<beans xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--引入外部属性文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean>
aspectj 注解方式配置AOP
<beans xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=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>
tx 开启事务管理器
<beans xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 创建事务管理器 --> <bean id = "transactionManager" class="org.springframework.jdbc.DataSourceTransactionManager"> <!--注入数据源--> <property name=“dataSource ref="dataSource"”></property> </beans> <!--开启事务注解--> <tx:annotation-driven transactionmanager="transactionManager"></tx:annotation-driven>
事务中的问题
脏读:
一个未提交事务读取到另一个未提交事务的数据
不可重复读(算现象不算问题)
一个要多次读取的事务,在每次读取的时候读出来的都不一样(被别的事务改了)
幻读
一个事务用一样的 SQL 多次查询,结果每次查询都会发现查到一些之前没看到过的数据。(被别的事务增删改了)
注意,幻读特指的是你查询到了之前查询没看到过的数据。此时说明你是幻读了
- ps 不可重复读和幻读很像,不可重复读是读到被修改的,幻读是读到被增删的。
p53 webflux
注解和其他
- 切入点表达式
execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
- @Value 给注释的属性赋值
@Autowired 给注释的对象进行自动的注入,默认byTypr,找不到就byName
- @Qualifier(value = "userDaoImpl1")
- @Resource(name = "userDaoImpl1") 配合@Autowired使用,根据名称注入
- 在 service 类上面添加注解@Transactional,声明式事务管理参数配置
- @EnableTransactionManagement //开启事务
@Bean 和 @Configuration
- @Configuration申明此类为配置类
- @Bean用在@Configuration或@Component注解的类中,用来声明bean的关系
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
等价于
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
- @Scope 指定作用域
- @Transactional 声明该类下所有方法或该方法为事务类
- 在catch中记得要throw错误才能让事务生效
复习
Spring 框架中用到了哪些设计模式?
工厂设计模式 : Spring使用工厂模式通过 BeanFactory
、ApplicationContext
创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
。
- ServletContext:servlet创建的全局共享对象
- ApplicationContext:ApplicationContext 是 IOC 容器的承载体,ApplicationContext创建时会把bean也创建了https://juejin.cn/post/6844903927071113230
- ContextLoaderListener:servlet用于监听 ServletContext 对象的创建与销毁过程,用来自动装配ApplicationContext的配置信息
spring初始化
tomcat在启动web容器的时候会启动一个叫ServletContextListener的监听器,每当在web容器中有ServletContextListener这个接口被实例化的时候,web容器会通知ServletContextListener被实例的对象去执行其contextInitialized()的方法进行相应的业务处理;
而spring框架在设计的过程中ContextLoadListener这个类实现了ServletContextListener这个接口,因此每当有ContextLoadListener这个类被实例化的时候,web容器会通知Spring执行contextInitialized()这个方法,从而进行spring容器的启动与创建的过程中;
IOC是什么
spring IOC容器负责创建对象,或者给对象注入变量,管理这些对象的生命周期
POJO 就是各种基类,MetaData就是注入的数据
BeanFactory 和 ApplicationContext?
BeanFactory懒加载,是Spring的内部接口,不提供给开发人员使用
ApplicationContext:是即时加载的、BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人 员进行使用
Spring配置方式?
xml和注解,注解需要在spring中开启:
<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
ContextLoaderListener的作用是什么?
ContextLoaderListener的作用就是启动Web容器时,读取在contextConfigLocation中定义的xml文件,自动装配ApplicationContext的配置信息,并产生WebApplicationContext对象,然后将这个对象放置在ServletContext的属性里,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。
简单来说,就是上面这段配置为项目提供了spring支持,初始化了Ioc容器。
bean标签中 ,scope可以写:
Spring bean 支持 5 种 scope:
- Singleton - 每个 Spring IoC 容器仅有一个单实例。
- Prototype - 每次请求都会产生一个新的实例。
- Request - 每一次 HTTP 请求都会产生一个新的实例,并且该 bean 仅在当前 HTTP 请求内有效。
- Session - 每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效。
- Global-session - 类似于标准的 HTTP Session 作用域,不过它仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范围内。如果你在 web 中使用 global session 作用域来标识 bean,那么 web 会自动当成 session 类型来使用。
仅当用户使用支持 Web 的 ApplicationContext 时,最后三个才可用。
IOC中有两种bean:
普通 bean和另外一种工厂 bean(FactoryBean),工厂类可以定义返回类型
bean 的生命周期
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3).如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。(和下面的一条均属于检查Aware接口)
(4)如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身
(3、4可以统一即为实现Aware接口的东西)
---(5)这里会把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(6)调用 bean 的初始化的方法(需要进行配置初始化的方法) (调用spring xml中的init 方法。)
---(7)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(8)bean 可以使用了(对象获取到了)
(9)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
postProcessBeforeInitialization和 postProcessAfterInitialization就是实现接口 BeanPostProcessor,它就是BeanPostProcessor后置处理器
@Autowired自动注入
默认是根据属性进行自动装配,可以配合以下注解进行别的方式注入:
1.@Qualifier(value = "userDaoImpl")
根据bean名称注入
2.@Resource(name = "userDaoImpl1")
根据类型、名称进行注入
3.@Value(value = "abc")
注入普通类型属性,比如private String name;
什么是 AOP
面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
AOP的代理模式
动态代理
Spring AOP,利用反射和代理的接口实现,在运行时才增强对象。
- 有接口情况,使用 JDK 动态代理(直接创建新类实现该接口)
- 没有接口情况,使用 CGLIB 动态代理(直接使用继承创建该类的子类)
使用代理类:
使用 java.lang.reflect.Proxy中的newProxyInstance方法,传入下面三个变量
类加载器ClassLoader
增强方法所在的类(这个类实现的接口,支持多个接口) interfaces
代理对象,写增强的部分InvocationHandler(也就是实现InvocationHandle接口的类)(这里会要重写前置、后置通知)
静态代理
AspectJ,在编译时就进行增强,最后生成的对象也就是被增强过的了
利用AspectJ包实现增强效果
AspectJ也可以使用注解开发或者xml配置
使用方法:
先在spring里开启注释扫描,使用
@Component
@Aspect
来修饰增强类,创建代理对象,
使用@Before、@AfterReturning这些来修饰前置、后置通知
AOP的术语
1.连接点
指可增强的类
2.切入点
指可增强的方法
3.通知(增强)
指增强的部分
4.切面
是动作,指增强这一过程
事务
一组逻辑操作单元,使数据从一种状态变换到另一种状态。
事务四大特性
(1)原子性
指事务包含的所有操作要么全部成功,要么全部回滚
(2)一致性
数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
(3)隔离性
隔离性是当多个用户并发访问数据库时,比如操作同一张时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离.
(4)持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作.
使用:
要使用事务如果要使用注释法,也先得在xml中开启注释扫描
在bean类上(被@Controller、@Service修饰的这些类上),接着添加解@Transactional
可在@Transactional中加入以下属性:
事务的传播行为propagation:
PROPAGATION_REQUIRED:
默认的Spring事物传播级别,若当前存在事务,则加入该事务,若不存在事务,则新建一个事务
PROPAGATION_REQUIRES_NEW:
若当前没有事务,则新建一个事务。若当前存在事务,则新建 一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交
PROPAGATION_SUPPORTS:
如果当前有事务在运行,就在其中运行、否则不可以运行在其他事务中
PROPAGATION_NOT_SUPPORTED:
以非事务的方式执行,若当前存在事务,则把当前事务挂起
PROPAGATION_MANDATORY:
强制事务执行,若当前不存在事务,则抛出异常
PROPAGATION_NEVER:
以非事务的方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED:
如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务, 则新建一个事务,类似于REQUIRE_NEW。
事务隔离级别ioslation:
- ISOLATION_READ_UNCOMMITTED
- ISOLATION_READ_COMMITTED
- ISOLATION_REPEATABLE_READ
- ISOLATION_SERIALIZABLE
脏读(Dirty reads)
一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能所有的操作都被回滚。
幻读(Phantom Reads)
事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。
不可重复读(Non-repeatable Reads)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。比如事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。又叫虚读。
在jdbc中,为了将多件事做成一个事务,往往需要将Connection.comit提交才会执行sql语句,但是在这其中可能会执行错误,导致执行到一半又进行数据的回滚,这就会造成以上错误,mysql默认 REPEATABLE READ
- mySql的驱动: com.mysql.jdbc.Driver
spring启动流程
Spring的启动过程其实就是ioc容器的初始化过程,本质就是beanFactory实例化并且装载各个ioc容器
其启动过程主要包含三个类,ContextLoaderListener,ContextLoader和XmlWebApplicationContext。
其中ContextLoaderListener会在配置在Servlet的监听器中,它的加载会加载ContextLoader和XmlWebApplicationContext
ContextLoader类中主要完成三件事:
1)创建WebApplicationContext;
2)加载对应的Spring配置文件中的bean;(refresh方法,完成bean的加载)
3)将WebApplicationContext放入servletContext中。
以上初始化完成后,才会读取web.xml配置中的如DispatcherServlet这些组件