什么是Spring?Spring的核心是什么?
1.Spring的核心
控制反转(IoC)和面向切面(AOP)
2.IOC
也叫做:控制反转:
- Inversion Of Control 简称IOC
- IOC 就表示控制反转
- 控制反转: 把对象的创建交给外部的容器,程序中只需要接收获取对象即可,不需要关注对象由谁创建创建的细节。
- 控制反转,不是spring所特有的,是一个通用的概念。只是Spring提供了IOC控制反转容器。
3.SpringIOC容器(理解)
IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法。
Spring的IOC有三种注入方式 :构造器注入、setter方法注入、接口注入。
1.SpringIOC容器,创建容器几种方式
由以下三种方式
|— ClassPathXmlApplicationContext 加载类路径下的配置文件
|— FileSystemXmlApplicationContext 加载外部的配置文件
|— AnnotationConfigApplicationContext 加载注解类的方式创建容器
PS:
|— BeanFactroy SpringIOC容器的顶层接口. 创建容器默认不创建单例的对象,默认采用延迟加载创建对象的机制。
|— ApplicationContext 子接口。 默认创建容器时候就创建单例的对象。
面试题
BeanFactory和ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
(1)BeanFactory:是Spring里面最顶层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
①继承MessageSource,因此支持国际化。
②统一的资源文件访问方式。
③提供在监听器中注册bean的事件。
④同时加载多个配置文件。
⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
(2)①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
(3)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
(4)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
4.Spring创建对象的三种方式
方式1:默认无参数构造函数创建对象(常用)
PS:其实还有有参数构造函数创建,但一般只有在依赖注入构造器参数时才创建,在bean里面子标签写constructor-arg标签:通过有参数构造器创建对象;
方式2:工厂类的静态方法创建对象
方式3:工厂类的实例方法创建对象
1.默认无参数构造函数创建对象
直接在bean.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
1. 创建对象,入门程序
User user = new User();
User user2 = user;
2. 创建对象的细节
bean 表示一个需要创建的对象。不能为接口(通常这里都需要我们new)
class 要创建对象的类型全名
id 表示对象的名称; 一次只能定义一个对象;
name 表示对象的名称; 一次可以定义多个对象,用逗号或空格隔开
小结:
2.1 推荐使用id。
2.2 默认创建的是单例的对象
2.3 在创建容器时候,默认就创建单例的对象。
scope 指定对象的范围(单例、多例)
singleton 默认值,表示单例. 创建IOC容器,默认创建单例的对象。
prototype 多例,每次从容器中获取对象,都会创建一个新的对象
创建容器默认不创建多例的对象,总是在用到时候才创建。
request:web项目中,将对象存入request域中【了解】
session:web项目中,将对象存入session域中【了解】
globalsession:web项目中,应用在集群环境,
lazy-init="true"
初始化IOC容器不会去创建懒加载的对象 而是使用对象是才创建 默认值是false
1.只对单例对象有效;
2. 对单例的对象,配置延迟初始化,表示在第一次使用时候才创建。
3. 小结
dao、service: 默认即可,就是单例,且在创建ioc容器时候就创建单例的对象。
init-method="init"
对应User对象的init()方法,在创建对象之后始终执行
destroy-method="preDestroy"
对应User对象的preDestroy()方法,在销毁容器之前执行。
销毁容器:调用容器的close()方法。
注意:只对单例有效。
-->
<bean
id="user"
name="user2,user3 user4"
class="com.itheima.entity.User"
scope="prototype"
lazy-init="true"
init-method="init"
destroy-method="preDestroy"
/>
</beans>
2.工厂类的静态方法创建对象
在bean里面添加factory-method就可以调用静态方法了,
调用某个类的静态方法创建对象,使用: class + factory-method
3.工厂类的实例方法创建对象
5.依赖注入的方式
方式一:带参数构造函数
方式二:set方法【最常用】
方式三:p名称空间
概念
依赖注入,DI,Dependency Injection , 就是IOC容器提供的另外一块功能:给对象属性赋值
1.带参数构造函数
2.set方法
3.p名称空间
6.注解实现创建对象(注解实现要在xml配置扫描注解)
基于注解的 IOC 配置,用于创建对象的注解
创建对象的注解有下面四个:
@Component: 创建对象,加入ioc容器。举例:工具类
@Controller: 同@Component 一般用于表现层的注解。
@Service: 同@Component 一般用于业务层的注解。
@Repository: 同@Component 一般用于持久层的注解。
7.注解实现注入数据(依赖注入)(注解实现要在xml配置扫描注解)
基于注解的 IOC 配置,注入数据 A @Autowired修饰字段
@Autowired注解说明
ioc容器中,给对象的属性注入数据时用@Autowired注解
- @Autowired注入数据,可以根据名称或类型注入。
- @Autowired注解,可以定义在字段上,也可以定义在方法上。
1.定义在字段上,根据名称或类型注入
2.定义在方法上,根据名称或类型注入
PS:
dao接口已经用注解形式创建对象了,所以不用在bean里面配置。
8.其他注解
1.@Value:
可以直接给简单类型的字段赋值。
可以获取配置文件值。(纯注解讲解)
PS:@Value获取配置文件,但前提是
使用@PropertySource 加载类路径下的配置文件
2.@Resource
相当于@Autowired注解,也可以根据名称或类型注入。也可以指定名称或类型注入。
简单来说 @Resource = @Autowired + @Qualifier,但在jdk1.8以后版本,不提供此注解支持
3.@Qualifier
@Autowired是根据指定的名称,但不能去容器中查找对应的对象注入,这时需要用到@Qualifier
4.对象范围与生命周期相关注解
1.@Scope
@PostConstruct
@PreDestroy
@Lazy
/** * @Scope("singleton") * 指定对象为单例或多例。 * @Lazy * 延迟创建单例的对象 * @PostConstruct * 此注解修饰的方法,会在创建对象之后执行。始终执行。 * @PreDestroy * 此注解修饰的方法,会在调用容器的close()方法,销毁容器时候执行。只对单例有效。 */
xml方式
<bean id="" class=""
scope="" 使用@Scope注解取代
init-method="" 使用@PostConstruct注解取代
destroy-method="" 使用@PreDestroy注解取代
lazy-init="" 使用@Lazy注解取代
/>
9.Spring的AOP理解:
AOP主要用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于日志、事务等处理。
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。
静态代理的代表为AspectJ;
动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)植入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
10.SpringAOP
1.AOP
AOP(Aspect Oriented Programming),即面向切面编程。
什么是切面?切面就是重复执行的代码形成的类,叫做工具类,也叫做切面类。
什么是面向切面编程?面向重复的代码编程,重复的代码只要写一次,不需要自己调用,自动调用自动运行。统一维护。
举例:面向切面编程,有哪些切面类?
1. 日志切面类
2. 事务的切面类
3. 权限切面
面向切面编程好处?
重复的代码只要写一次,不需要自己调用,自动调用自动运行。
解耦。举例: 日志记录代码与业务代码解耦。事务控制代码与业务代码解耦。
2.SpringAop怎样做日志?
有两种方式,注解方式和xml方式
实现方式为
1.做一个日志切面类
<!--2. 创建切面类-->
<bean id="logAspect" class="com.itheima.utils.LogAspect"/>
2.做一个切入点
<!--3. Aop配置-->
<aop:config>
<!--3.0 配置切入点表达式(AspectJ表达式)-->
<aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.UserServiceImpl.*())"/>
<!--3.1 配置切面-->
<!--ref 指定引用的切面类-->
<aop:aspect ref="logAspect">
<aop:after method="insertLog" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
3.把日志植入到目标对象中
<aop:aspect ref="logAspect">
<aop:after method="insertLog" pointcut-ref="pt"/>
</aop:aspect>
aop的通知类型:
可以在记录日志的切面类进行通知
【前置通知】 在执行目标对象方法之前执行
【后置通知】 在执行目标对象方法之后执行, 出现异常不执行。
【异常通知】 在执行目标对象方法出现异常时候才执行
【最终通知】 在执行目标对象方法之后执行,始终执行
【环绕通知】 环绕目标方法执行(相当于前面四种通知的结合体)
在xml配置或者注解实现
<!--3. Aop配置-->
<aop:config>
<!--3.0 配置切入点表达式-->
<aop:pointcut id="pt" expression="bean(*Service)"/>
<!--3.1 配置切面-->
<aop:aspect ref="logAspect">
<!--注意:后置通知,一定要放到最终通知的前面。执行结果与配置顺序有关系。-->
<aop:before method="before" pointcut-ref="pt"/>
<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
<aop:after method="after" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
11.Spring事务
spring事务包括声明式事务和编程式事务
12.事务的传播行为 (PROPAGATION),
作用: 控制事务的边界。 说白了,就是通过传播行为指定如何管理事务。
REQUIRED(required)
- 默认值。如果一个方法指定事务的传播行为是REQUIRED,就表示当前运行方法必须有事务环境,如果当前方法没有事务,就创建一个新的事务。如果当前执行的方法有事务环境,就把当前方法加入当前事务环境中。“智能 人性化 合理”
- 应用: 增删改
SUPPORTS(supports)
- 如果一个方法指定事务的传播行为是SUPPORTS, 表示当前方法支持事务环境的运行。对事务的要求是:可有可无。
- 应用:查询
REQUIRES_NEW(requires_new)
- 不管当前运行方法是否有事务环境,都会创建一个全新的事务。
- 应用:
13.怎样做spring声明式事务
可以通过xml方式或者注解方式实现
实现:
1.配置事务管理器
xml方式
<!--5. Spring声明式事务控制的配置-->
<!--5.1 配置事务管理器,也叫做事务切面类。Spring已经提供了针对连接池的事务控制的实现。-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.配置事务通知规则
xml方式
<!--5.2 配置事务通知规则。作用:拦截到方法后,如何管理事务,在这里配置。-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--find/get等开头的方法,事务可有可无,只能进行查询操作。-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="search*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="load*" propagation="SUPPORTS" read-only="true"/>
<!--其他所有方法,必须有事务环境,支持读写操作。-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
3.Aop配置(Aop配置 = 切入点表达式 + 事务通知规则)
xml方式
<!--5.3 Aop 配置-->
<aop:config>
<!--5.3.1 配置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com..*ServiceImpl.*(..))"/>
<!--5.3.2 建立切入点表达式与通知规则的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
注解形式
在bean.xml配置
<!--5. Spring声明式事务控制的配置-->
<!--5.1 配置事务管理器,也叫做事务切面类。Spring已经提供了针对连接池的事务控制的实现。-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
事务注解支持
<tx:annotation-driven transaction-manager="transactionManager"/>
就可以使用注解:@Transactional
类上,方法上使用@Transactional
/** * @Transactional * 1. Spring声明式事务的注解支持。 * 2. 可用范围 * 定义在类上:表示当前类的所有方法都应用事务。 最常用。 * 定义在类的方法上: 表示当前方法应用事务。 * 定义在接口或父类上,表示所有实现了接口或继承父类的子类的所有方法都应用事务。 * 定义在接口方法或父类方法上,表示所有重写了该方法的地方都就应用事务。 * 3. 事务属性 * propagation * REQUIRED 默认值,表示必须有事务环境. * isolation * DEFAULT 使用数据库默认的隔离级别 * timeout * -1 默认值,表示不指定事务的超时时间。由数据库底层决定事务超时时间。 * readOnly * false 默认值,支持读写操作 * * noRollbackFor 指定遇到指定的异常不回滚。 * */
@Service
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = -1,
readOnly = false,
noRollbackFor = { ArithmeticException.class,NullPointerException.class}
)
public class类{ }
Spring声明式事务原理:
原理:Aop
Aop原理: 动态代理
动态代理: 方法
Spring声明式事务的优缺点:
优点:
1. 解耦: 事务代码与业务代码完全解耦。
2. 每一个项目都要连接数据库,那就必须由事务控制,spring已经提供了。
我们只需要写业务,不需要关注事务。
缺点:
Spring声明式事务,是粗粒度的事务控制。
是方法级别的事务控制,只能对方法进行事务控制。
不能对方法的某几行进行事务控制。
细粒度的事务控制:
可以对方法任意行进行事务控制。
细粒度的事务控制,只能硬编码的方式、自己写事务控制代码完成。
还没有评论,来说两句吧...