IOC与快速入门、Bean的创建、IOC注解、AOP简介与注解、事务控制
- IOC原理 , AOP原理 ?
- IOC容器的初始化过程
- BeanFactory如何创建bean?
- Spring Bean的创建过程
- Spring的事务有了解吗?
- Spring里是如何管理事务的?
- Spring里的声明式事务的做法和优点?
- CodeSheep–Spring 经典面试题汇总
- SSM面试总结
IOC是思想,DI是实现 AOP(主要实现事务)切面 动态代理:JDK和cglib
1.Spring
1、在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化。
– 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用。
2、Spring 提供了两种类型的 IOC 容器实现。
– BeanFactory: IOC 容器的基本实现。
– ApplicationContext: 提供了更多的高级特性。
是 BeanFactory 的子接口( getBean() 方法所在位置)。
– ClassPathXmlApplicationContext 是ApplicationContext 实现类,从类路径下加载配置文件。
Spring的核心模块如下所示:
Spring Core:是核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能 (JNDI、定时任务等);
Spring AOP:提供AOP服务;
Spring DAO:对JDBC进行了抽象,简化了数据访问异常等处理;
Spring ORM:对现有的ORM持久层框架进行了支持;
Spring Web:提供了基本的面向Web的综合特性;
Spring MVC:提供面向Web应用的Model-View-Controller实现。
轻量级Spring的优点有哪些呢?
Spring的依赖注入将对象之间的依赖关系交给了框架来处理,减小了各个组件之间的耦合性;
AOP面向切面编程,可以将通用的任务抽取出来,复用性更高;
Spring对于其余主流框架都提供了很好的支持,代码的侵入性很低。
1.1搭建 Spring 运行时环境
- 加入 JAR 包
- 在 Spring Tool Suite 工具中通过如下步骤创建 Spring 的配置文件
① File->New->Spring Bean Configuration File
② 为文件取名字 例如:applicationContext.xml
1.2HelloWorld
- 目标: 使用 Spring 创建对象, 为属性赋值
- 创建person类
public class Person {
//javaBean对象
private String name;
//get/set,tostring,有参无参
- 创建 Spring 配置文件
<!-- 使用bean元素定义一个由IOC容器创建的对象 -->
<!-- class属性指定用于创建bean的全类名 -->
<!-- id属性指定用于引用bean实例的标识 -->
<bean id="p1" class="com.spring.beans.Person">
<!-- 使用property子元素为bean的属性赋值 -->
<property name="name" value="Spring我来了!!!"></property>
</bean>
- 测试: 通过 Spring 的 IOC 容器创建 person 类实例
/* 传统方式
//1.创建Person 对象
Person person = new Person();
//2.为name 赋值
person.setName("Spring我来了!!!");
//调用hello 方法
person.hello();
*/
//创建对象和为对象的属性赋值,可以由spring来负责
//1.创建Spring 的IOC 容器(Bean被创建)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//2.从IOC 容器中获取bean实例
Person person = (Person) applicationContext.getBean("p1");
System. out . println(person);//Spring我来了!!!
2.spring IOC&DI
1). IOC也叫控制反转,将对象间的依赖关系交给Spring容器,使用配置文件来创建所依赖的对象,由主动创建对象改为了被动方式,实现解耦合。可以通过注解@Autowired和@Resource来注入对象,被注入的对象必须被下边的四个注解之一标注:
@Controller
@Service
@Repository
@Component2). 在Spring配置文件中配置<context:annotation-config/>元素开启注解。
DI(依赖注入),IOC的另一种表述方式:即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源(对象等)。
即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。
3).IOC容器在Spring中的实现
ApplicationContext:BeanFactory的子接口
4).ApplicationContext的主要实现类
[1]ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
[2]FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
[3]在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。
2.2 bean的高级配置
2.2.1 通过类型获取bean
- 从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。
如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。
HelloWorld helloWorld = cxt.getBean(HelloWorld. class);
2.2.2 给bean的属性赋值
2.1 赋值的途经
2.1.1通过bean的setXxx()方法赋值(HelloWorld中使用的方式)
<bean id="p1" class="com.spring.beans.Person">
<property name="name" value="Spring我来了!!!"></property>
</bean>
2.1.2.p名称空间
<bean
id="p2"
class="com.helloworld.bean.Student"
p:studentId="2002" p:stuName="Jerry2020" p:age="18" />
2.2.3 bean的作用域
在Spring中,可以在元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。
默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
2.2.4 请别再问Spring Bean的生命周期了!
https://huge0612.gitbooks.io/tour-of-rpc/content/rpc/3.1_%E4%B8%8Espring%E6%95%B4%E5%90%88.html
①Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。
②Spring IOC容器对bean的生命周期进行管理的过程:
[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]调用bean的初始化方法
[4]bean可以使用了
[5]当容器关闭时,调用bean的销毁方法
③在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法
④bean的后置处理器
[1]bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
[2]bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
[3] bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
●postProcessBeforeInitialization(Object, String)
●postProcessAfterInitialization(Object, String)
⑤添加bean后置处理器后bean的生命周期
[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
[4]调用bean的初始化方法
[5]将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
[6]bean可以使用了
[7]当容器关闭时调用bean的销毁方法
- 总结
Spring Bean的生命周期分为四个阶段和多个扩展点。扩展点又可以分为影响多个Bean和影响单个Bean。整理如下:
请别再问Spring Bean的生命周期了!
四个阶段
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
至此,Spring Bean的生命周期介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。
2.2.5 引用外部属性文件
①直接配置
②创建properties属性文件
prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver
③引入context名称空间
④指定properties属性文件的位置
<context:property-placeholder location=“classpath:jdbc.properties”/>
⑤从properties属性文件中引入属性值
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="jdbcUrl" value="${prop.url}"/>
<property name="driverClass" value="${prop.driverClass}"/>
</bean>
2.2.6 通过注解配置bean
①普通组件:@Component
标识一个受Spring IOC容器管理的组件
②持久化层组件:@Respository
标识一个受Spring IOC容器管理的持久化层组件
③业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件
④表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件
⑤组件命名规则
[1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
[2]使用组件注解的value属性指定bean的id
扫描组件
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
//①指定被扫描的package
<context:component-scan base-package="com.component"/>
//使用resource-pattern属性过滤特定的类
<context:component-scan
base-package="com.component"
resource-pattern="autowire/*.class"/>
@Autowired注解
[1]根据类型实现自动装配。
[2]构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解
@Resource
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
@Inject
@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。
配置(创建)bean
在Spring的世界中, 我们通常会利用bean config file 或者 annotation注解方式来配置bean.
在第一种利用bean config file(spring xml)方式中, 还包括如下三小类
反射模式
工厂方法模式
Factory Bean模式
3.Spring AOP
AOP,面向切面编程是指当需要在某一个方法之前或者之后做一些额外的操作,比如说日志记录,权限判断,异常统计等,可以利用AOP将功能代码从业务逻辑代码中分离出来。
Spring中的AOP主要有两种实现方式:
1.使用JDK动态代理实现,使用java.lang.reflection.Proxy类来处理
2.使用cglib来实现
两种实现方式的不同之处:
JDK动态代理
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上,实现步骤大概如下:
- 定义一个实现接口InvocationHandler的类
- 通过构造函数,注入被代(dai)理类
- 实现invoke( Object proxy, Method method, Object[ ] args)方法
- 在主函数中获得被代(dai)理类的类加载器
- 使用Proxy.newProxyInstance( )产生一个代(dai)理对象
- 通过代(dai)理对象调用各种方法
cglib主要针对类实现代(dai)理,对是否实现接口无要求。原理是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以被代(dai)理的类或方法不可以声明为final类型。实现步骤大概如下:
1.定义一个实现了MethodInterceptor接口的类
2.实现其 intercept()方法,在其中调用proxy.invokeSuper( )
<!-- aop配置 -->
<aop:config>
<!--配置切入点
* com.bean.person.*(..):person类中的所有方法
* com.bean.*.*(..) bean包里的所有类的所有犯法
* com.bean.person.add*(int,int) :person 类中以add开头的方法,并且参数是两个int值
-->
<aop:pointcut expression="execution(* com.bean2.MyMath.*(..))" id="pc"/>
<!-- 切面 -->
<aop:aspect ref="mylog">
<!-- 通知
<aop:before method="beforelog" pointcut-ref="pc"/>
<aop:after method="afterlog" pointcut-ref="pc" />
<aop:after-returning method="returnLog" pointcut-ref="pc" returning="o"/>
<aop:after-throwing method="throwLog" pointcut-ref="pc" throwing="e"/>
-->
<aop:around method="aroundLog" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
4.Spring事务
4.1 事务
为了保证数据的完整性和一致性,必须引入数据库事务的概念
事务的四个关键属性(ACID)
atomicity
consistency
isolation
durability
4.2 Spring事务管理
1 编程式事务管理
使用原生的JDBC API实现事务管理是所有事务管理方式的基石,相对于核心业务而言,事务管理的代码显然属于非核心业务,如果多个模块都使用同样模式的代码进行事务管理,显然会造成较大程度的代码冗余。
4.2.1 声明式事务管理
事务管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事务管理。
Spring在不同的事务管理API之上定义了一个抽象层,通过配置的方式使其生效,从而让应用程序开发人员不必了解事务管理API的底层实现细节,就可以使用Spring的事务管理机制。
4.2.2 事务管理器的主要实现
①DataSourceTransactionManager:在应用程序中只需要处理一个数据源,而且通过JDBC存取。
②JtaTransactionManager:在JavaEE应用服务器上用JTA(Java Transaction API)进行事务管理
③HibernateTransactionManager:用Hibernate框架存取数据库
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
2 在需要进行事务控制的方法上加注解
@Transactional
4.2.3 事务的隔离级别
1 数据库事务并发问题
假设现在有两个事务:Transaction01和Transaction02并发执行。
①脏读
[1]Transaction01将某条记录的AGE值从20修改为30。
[2]Transaction02读取了Transaction01更新后的值:30。
[3]Transaction01回滚,AGE值恢复到了20。 [4]Transaction02读取到的30就是一个无效的值。
②不可重复读
[1]Transaction01读取了AGE值为20。 [2]Transaction02将AGE值修改为30。
[3]Transaction01再次读取AGE值为30,和第一次读取不一致。
③幻读
[1]Transaction01读取了STUDENT表中的一部分数据。 [2]Transaction02向STUDENT表中插入了新的行。
[3]Transaction01读取了STUDENT表时,多出了一些行。
2 隔离级别
一个事务与其他事务隔离的程度称为隔离级别
①读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
②读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
③可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
④串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
在Spring中指定事务隔离级别
①注解
用@Transactional注解声明式地管理事务时可以在@Transactional的isolation属性中设置隔离级别
②XMl
在Spring 2.x事务通知中,可以在tx:method元素中指定隔离级别
事务的超时和只读属性
Spring 经典面试题汇总
三歪红着眼睛总结了Spring知识点
未完待续~
知识补充
IOC操作Bean管理(xml和注解两种方式)
IOC架构演变
为什么要用工厂模式?
IOC原理
Bean对象的管理
1.通过XML文件set注入属性
2.通过有参构造器注入属性
FactoryBean(可返回不同类型的bean)
- Bean的作用域(设置创建bean实例是单例还是多例)
- 如何设置单实例还是多实例? scope
bean的生命周期(7步)
就是定义一个bean
第一步 执行无参构造器创建bean实例
第二步 调用set方法设置属性值
bean的后置处理器 实现BeanPostProcessor--before方法
第三步 执行init-Method方法(xml还要在bean标签中配置 初始化和销毁的方法名)
bean的后置处理器 实现BeanPostProcessor--after方法
第四步 获取bean对象使用
第五步 销毁 (需要手动调用close方法)
xml的自动装配
类似@autowire,通过在Bean标签中配置autowire
byName(可以定义多个不同名)/byType(只能有一个相同类型)
引入外部属性文件(jdbc.properties)
引入context名称空间
${等号左边}
IOC操作Bean管理-注解
需要引入aop依赖
- 组件扫描配置(完全注解开发 - 配置类)
实现注入属性
@Autowired 类型注入
@Qualifier(value=“”) 名称注入 需要和@Autowired一起使用
@Resource
@Value 普通属性赋值注入
AOP
不修改源代码添加新功能
底层原理-动态代理
1.有接口 JDK
2.没有 cglib
- 实现JDK动态
1.使用Proxy类创建接口代理对象
-可以使用内部类
-也可以创建一个类实现InvocationHandler
- JDK动态代理类