前言:工作的原因,最近没有及时更新相关系列。很抱歉!在复习Spring框架的同时也别忘了j2ee的相关知识理论wo~
大家好!我是JAVA 中的Spring框架,我是一个开源的容器性质的轻量级框架。
我有三大特点: 容器、IOC(控制反转)、AOP(切面方程
)
我有三大优点:
1、容器: 我就是个容器,管理项目中所有的对象(后宫?)
2、IOC(控制反转): 我将创建对象的方式反转了,从程序员自己创建反转给了程序。(嗯,听不懂)
3、AOP(面向切面):面向切面编程,简而言之,就是将纵向重复的代码横向抽取出来。(这是啥啊?)
Spring框架应用了面向切面的思想,
主要体现在为容器中管理的对象生成动态代理对象
关于什么是IOC
控制反转,指的是将对象的创建权反转给Spring。
作用是:实现程序的解耦
这正转:没有IOC的时候,我们自己的对象中都是主动去创建被依赖的对象。
这反转:有IOC,所依赖的对象直接由IOC容器创建后注入到 被注入的对象中。依赖的对象由原来主动获取
变为被动接受。
关于什么是Di
:依赖注入,需要有IOC环境,在Spring创建Bean对象时,动态的将依赖对象注入带Bean对象去。
依赖注入最大的好处就是解耦合
。
通常是一个概念,他有多种解释,这概念是说你不要创建对象,而只需描述它是如果被创建的。你不在代码里直接组装你的组件和服务
但是要在配置文件里描述哪些组件需要哪些服务,之后IOC(容器)就负责把他们组装起来
5.你知道我(spring)身上的 BeanFactory接口和 ApplicationContext接口吗?
有什么区别呢
BeanFactory接口是Spring框架的顶层接口,是最原始
的接口,
通过new(BeanFactory的实现类)来启动Spring容器时,并不会创建Spring容器里面的对象。
只有
在每一次通过getBean获取对象时才会创建。
ApplicationContext接口是用来替代
BeanFactory接口的,通过new(ApplicationContext接口的实现类)
ClassPathXmlApplicationContext来启动容器时,就会创建容器中配置的所有对象
6.Spring中的工厂容器有两个哦,
BeanFactory和ApplicationContext。
BeanFactory接口是Spring框架的顶层接口,是最原始的接口。
ApplicationContext是对BeanFactory扩展
。
BeanFactory在第一次getBean是才会初始化Bean。
ApplicationContext在加载配置文件时初始化Bean
ApplicationContext继承BeanFactory接口,也继承了MessageSource,因此支持国际化,统一资源文件访问方式。
提供在监听器注册bean的事件,同时加载多个配置文件
Bean Factory 与Factory Bean 有什么区别
相同点:都是用来创建bean对象的
不同点:使用BeanFactory创建对象的时候必须要遵循严格的生命周期流程,太复杂。
如果想要简单的自定义某个对象的创建。同时创建完成的对象想交给spring来管理,那么就要
实现Factory Bean 接口
Spring容器中Bean标签你是怎么理解?
Bean标签用来描述Spring容器管理的对象
例如,有个User对象,需要交给Spring容器来管理,
这样就需要在Spring容器的主配置文件中通过Bean标签
来描述该对象,Bean标签常见的属性有:
name属性:给被管理的对象起给名称,获得对象时要根据该名称来获得。
class属性:被管理对象的完整类名。
scope属性:scope属性常见的有两个属性值:singleton和prototype。
这两个属性值用来指定创建对象时是单例还是多例。默认是单例
。
8.Spring通过配置<bean>标签来生成Bean对象有三种方式
一般只会用空参构造方式
<bean id="bean1" class="cn.itcast.spring.b_instance.Bean1"></bean>
9.Spring框架中属性注入有哪几种方式:
.什么是bean的自动装配?
在spring框架,使用autowire类
.配置自动装载模式,无需自己找出或者创建与其他关联的其他对象。
由容器负责把需要相互协作的对象引用赋予给各个对象。
在spring框架xml配置共有5种自动装配:
在基于注解
的自动装配方式:
在Spring框架中,配置文件中设定bean的依赖关系是个很好的机制,Spring容器能够自动装配相互合作的bean,
这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。
这意味着Spring可以通过Bean 工厂中注入方式自动搞定bean之间的依赖关系。
自动装配可以设置在每个bean上,也可以设定在特定的bean上。
Spring容器的启动流程
spring的启动流程可以归纳为三个步骤:
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
谈一下spring IOC 的底层实现原理(重点)
首先,spring中的bean都是通过反射的方式生成的
1、先通过createBeanFactory创建出一个Bean工厂
(DefautListableBeabFactory)
2、开始循环创建对象
,因为容器中的bean默认都是单例的,所以优先通过getBean,doGetBean
从容器中查找。如果找不到
3、通过createBean,doCreateBean方法,一反射的方式
创建对象,一般情况下使用的是无参
的构造方法
(getDeclaredConstructor,newInstance)
4、进行对象的属性填充(populateBean方法)
5、进行其他的初始化操作(initializingBean方法)
(不可能像背书一样的,面试官也不记得。重点是一些关键词要描述到)
spring容器中Bean创建的过程 (重点)
实例化Bean
(实际上就是通过构造方法区得到一个对象)调用createBean
进行实例化。BeanDefinition对象保存。奎BeanDefinition对象
中的信息,实例化所有的bean依赖注入
):实例化后的对象被封装在BeanWrapper对象中。接着,spring根据BeanDefinition中的信息,通过spring会检测该对象是否实现了XXXAware接口
,通过Aware类型的接口,可以让我们拿到Spring容器的资源BeanPostProcessor接口
,那将会调用IntiaizingBean接口
,执行afeterPropertiesSet方法intit-method
属性,则会调用其配置的初始化方法实现了BeanPostProcessor接口
,将会调用postProcessAfterInitialization(Object由于这个方法是在Bean初始化结束时调用的,所以可以被运用于缓存技术或内存结束;
上述 7 步完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了
Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?
Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身是不具备线程安全特性的,
但具体情况还是要结合Bean 的作用域来讨论。
1、对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在共享,因此不会出现线程安全的问题
2、对应singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此线程安全问题就出现,
如果单例Bean
是一个无状态的Bean,也就是线程中地 操作不会对Bean的成员执行查询以外的操作,那么这个线程是安全的。
例如:Controller类。service类,DAO类,这些Bean大多都是无状态的,只关注与方法的本身
有状态Bean(Stateful Bean):就是有实例变量的对象
,可以保存数据
,非线程安全
无状态Bean(Stateless Bean):就是没有实例变量
的对象,不能保存数据,是线程安全
对于有状态的bean,(Model 和View),就需要自行保证线程安全,
办法就是将有状态的bean的作用域由 singleton 改为prototype
Spring如何解决循环依赖问题:
循环依赖问题在Spring中主要有三种
情况
在spring中,只有第3中方式的循环依赖问题被解决
了,其他两种产生的循环依赖问题时,都会产生异常
。
这是因为,
new对象的时候
就会塞住,也就是先有鸡还是先有蛋。无数个Bean
。导致OOM在spring单例模式下的setter方法依赖注入引起的循环依赖的问题,主要通过二级缓存
和三级缓存
来解决的,
三级缓存是主要功臣。解决原理:
在对象实例化之后,依赖注入之前,Spring提前暴露bean实例引起三级缓存进行存储
说说什么是一级缓存,二级缓存、以及三级缓存
一级缓存:singletonObjects是用来放就绪状态
的Bean。保存在该缓存中的Bean所
实现Aware子接口的方法已经是回调完毕,
自定义初始化已经执行完毕,也经过BeanPostProcessor实现类的postProcessorBeforeInitialization、
postProcessorAfterInitiallization方法处理;
二级缓存:earlySingletonObject是用来存放早期曝光的Bean
,一般只有处于循环引用状态
的Bean才会被保存在该缓存中。保存在该缓存中
的bean所实现Aware子接口的方法还未回调
,
自定义初始化方法未执行,
也未经过BeanPostProcessor实现类的
postProcessorBeforeInitalization、postProcessorAfterInitialization方法处理,
如果启用Spring
AOP,并处于切点表达式处理 范围之内,那么会被增强,即创建其代理。
额外提的一点,普通Bean被增强
(JDK动态代理或者CGLIB)的时机是在AbstractAutoProxyCreate实现的BeanPostProcessor的
postProcessorAfterInitialization方法中,
而处于循环引用状态的Bean
被增强的时机是在AbstractAutoProxyCreator实现的
SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法中。
三级缓存:singletonFactories是用来存放 创建用于获取Bean的工厂类
ObjectFactory实例。在IOC容器中,所有刚被创建出来的Bean
, 默认都会保存到该缓存中。
Bean在三个缓存之间的流传顺序为(存在循环引用):
1、通过反射创建Bean实例,是单例Bean,并且IOC容器允许Bean之间循环引用,保存到三级缓存中
2、当发生了循环引用时,从三级缓存中取出Bean对应的ObejctFactory实例,调用其getObject方法,来获取早期曝光Bean,
三级缓存中移除,保存到二级缓存中。
3,、Bean初始化完成,生命周期的相关方法执行完毕,保存到一级缓存中,从二级缓存以及三级缓存中移除。
Bean在这三个缓存之间的流传顺序为(没有循环引用):
1、通过反射创建Bean实例。是单例Bean,并且IOC容器允许Bean之前循环引用,保存到三级缓存中。
2、Bean初始化完成,生命周期的相关方法执行完毕,保存到一级缓存中,从二级缓存以及三级缓存中移除。
总结:
通过以上分析,我们可以得知Bean在一级缓存、二级缓存、三级缓存中的流传顺序为:
三级缓存---》二级缓存-----》一级缓存。
但是并不是所有Bean都会经历这个过程,
例如对应原型Bean(Prototype),
IOC容器不会将其保存到任何一个缓存中,另外即便是单例
Bean(Singleton),
如果没有循环引用关系,也不会被保存到二级缓存中的
11.简述bean的作用域
bean有5种作用域
,分别是singleton(单例、默认)prototype(默认)、request、session、globalSession
singleton
:当bean的作用域为singleton,那么Spring IOC容器只会存在一个bean实例,并且所有对bean的请求
只要该ID与该bean定义相匹配,则会返回bean的同一实例。(一般都是)
prototype
: prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用 /ˈproʊtətaɪp/
容器的getBean方法),时都会创建一个新的bean实例,根据经验,对所有有状态的bean应该使用prototype
作用域。而对无状态的bean则应该使用singleton作用域
request
:一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求都会有各自bean实例,他们根据
某个bean定义创建而成。该作用域仅在web的Spring ApplicationContext情型下有效。(回话)
session
:在一个HTTP Session中,同一个session共享一个Bean实例。不同session使用不同。
global Session
:在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context时候
有效,该作用域仅限于web的Spring ApplicationContext情形下有效。
Spring容器中注册一个对象,需要在Spring的主配置文件中用Bean标签来描述该对象,在实际开发中,
一个项目会有特别多的对象,如果都用Bean标签来配置,怎么可能?
所有Spring容器为我们提供了注解概念,用注解来替代配置。
1.什么是基于Java的Spring注解配置? 给一些注解的例子
基于java的配置,允许你在少量的Java注解的帮助下,
进行大部分Spring配置 而非通过XML文件
12.如何使用Spring中的注解。
在使用注解之前,要先在spring的主配置文件中通过
Context:component-scan
标签来开启注解开关。
<context:component-scan base-package="com.ljh.service"/>
用注解讲对象注册到Spring容器中,有几种注解方式,它们有什么区别。
1、@Component()
2、@Service()、
3、@Controller()、
4、@Respository()
声明bean的注解Spring框架最早出现
的只有@Component()注解,
到如果所有的对象都使用同一个注解,很难区分
对象究竟所属哪层架构。
所有spring有推出@Service()、@Controller()、@Respository()注解。 用于区分对象属于哪一层架构
4种注解的方式从功能上来讲没什么区别
13.Spring框架中,什么注解 可以用来指定对象的作用范围。
@Scope(scopeName="singleton")注解
@Configuration
:声明当前类为配置类,相当于xml形式上的spring配置
@Bean
:注解在方法上,声明当前方法返回为一个bean。
@ComponentScan
:用于对Componen进行扫描,相当于xml(写在类上):
@Aspect
声明一个切面(写在类上)
@After、@Before、@Around:都是写在方法上的。
@Scope:设置容器如何新建Bean实例(在方法上)
@PostConstruct:在构造函数执行完成之后执行,相当于配置文件中bean的init-Method
@Conditional :此注解定义条件注解,通过实现Condition/kənˈdɪʃn/接口,并重写matches/'mætʃɪz/方法,从而决定该bean是否被实例化
@ConditionalOnBean:仅仅当前上下文中存在某个对象,才实例化一个Bean
@ConditionalOnClass:存在某个一个class类路径,才实例化Bean
@ConditionalOnMissingBean:仅仅当前上下文不存在某个对象时,才实例化一个Bean
@ConditionalOnMissingClass:某个类路径不存在时,才实例化一个Bean
@ConditionalOnBean:当前容器中有指定Bean的条件下进行实例化
@ConditionalOnMissingBean:当前容器没有指定Bean的条件下进行实例化
@EnableAsync 开启异步方法的支持
@EnableWebMvc 开启Web MVC
@EnableTransactionManagement 开启注解式事务的支持
@EnableCaching:开启注解式缓存支持
消息
14.如何用注解的方式来完成属性注入
按类型分可以分为值类型
注入和引用类型
注入
值类型注入:
可以通过@Value()注解来完成,该注解既可以声明在属性上,也可以声明在方法上。建议声明在方法上,
但是更多人声明在属性上,因为方便。
引用类型注入:
可以通过三种注解方式来完成,分别为:@Autowired 、@Autowired和@Qualifier二者结合,
@Resource()建议使用,但经常用的是@Autowired
Spring支持的事务管理类型, spring 事务实现方式有哪些?
spring支持两种类型的事务管理
编程式事务管理:
这意味着可以通过编程的方式管理事务,带来极大的灵活性,但是难维护。
声明式事务管理:这意味着可以将业务代码和事务管理分离,只需用注解和XML的配置来管理事务,通过@Transactional注解来实现/trænˈzækʃənəl/
Spring事务的实现方式和实现原理
spring事务的本质就是数据库对事务的支持,没有数据库的事务支持,
spring 是无法提供事务功能的。
真正的事务提交和回滚是通过binlog
或者redo log
实现的
其实事务的操作本来应该是由数据库来进行控制的,但是为了方便用户进行业务逻辑操作,spring对事务功能进行了扩展。
spring事务操作是AOP的一个核心体现,当一个方法添加@Transactional
注解之后,
spring会基于这个类生成一个代理对象
会将这个代理对象作为bean。当使用这个代理对象的方法时,
如果有事务,那会先把事务的自动提交给关系,然后去执行具体的业务逻辑
如果执行逻辑没有出现异常,那么代理逻辑就会直接提交
如果出现任何异常,那么直接进行回滚操作,当然用户可以控制对哪些异常进行回滚
Spring事务什么时候会失效?
1、bean对象没有被spring容器管理
2、方法的访问修饰符不是public
3、自身调用的问题
4、数据源没有配置事务管理器
5、数据库不支持
6、异常被捕获
7、异常类型错误或者配置错误
Spring事务的传播机制
首先要知道事务的传播是一个方法调用另一个方法并将事务传递给它,
而传播机制是针对被调用者。控制它是否被传播或者被怎么样传播。
REQUIRED
(required,有事务则加入,没有则创建):REQUIRED是spring事务的默认方式。
REQUIRES_NEW
(requires_new,新事务,有事务就创建新事务)
NESTED
(nested ,嵌套事务,如果当前方法存在事务,则创建一个子事务,等父事务提交以后,子事务在提交;
如果当前没有事务,则新建事务。(子事务出异常,父事务不一定回滚。父事务出异常,子事务一定会回滚)
MANDATORY
(mandatory,必须要有事务,没有就抛异常)
SUPPORTS
(支持事务,有没有都可以)
NOT_SUPPORTED
(不支持事务,有事务也是以非事务的方式执行)
NEVER
(不可能有事务,有就出异常)
Spring中的AOP
aop名词学习
Joinpoint(连接点):目标对象中,所有可以增强的方法
Pointcut(切入点) :目标对象,已经增强的方法。
Advice(通知/增强):增强的代码
T arget(目标对象):被代理对象
Weaving(织入):将通知应用到切入点的过程
Proxy(代理):将通知织入目标对象之后,形成代理对象
AOP。面向切面编程,简单来说就是讲纵向重复的代码,横向抽取出来。
很明显的一个体现就是在Filter过滤器
中,在没有Filter之前,解决servlet的乱码问题是很复杂的。每次接收请求之前,都要
写句解决乱码问题的代码,request.setCharacterEncoding(“UTF-8")
,只要写个servlet,就可以写一句代码来解决乱码的问题。
直到Filter的出现,我们把解决乱码的那句代码放到Filter中去,从此servlet中,就再也不用重复写解决乱码的代码,从架构来
说Filter解决乱码的事,是架在了所有的servlet上,
这样一来切面就形成了,面向切面编程的思想,
还有一种直接的体现就是在拦截器中。
Spring中的AOP思想靠什么来体现
Spring中的AOP思想体现在能够为容器中管理的对象生成动态代理对象。
Spring实现AOP的原理
JDK动态代理和cglib代理
JDK动态代理是有缺陷,就是被代理对象必须实现接口
才能产生代理对象,如果没有接口,就不能使用动态代理技术。使用spring容器
实现动态代理,假如要管理的对象没有实现接口,那么就不能产生代理对象了。为了让所有的对象都能够产生动态代理对象。
spring又融入了第三方代理技术cglib代理,cglib可以对任何类生成代理对象,它的原理是对目标对象进行继承代理
,如果目标对象被final修饰
那么该类无法被cglib代理。
那么Spring到底使用JDK代理,还是cglib代理?
混合使用,如果被代理对象实现了接口,就使用JDK代理
如果没有就使用cglib代理
cglib代理
就是Code Generator Library,它是一个强大的、高性能的代码生产库。被广泛应用于AOP框架,
用以提供方法拦截操作。
CGLIB代理主要通过对字节码的操作,为对象引入间接级别。以控制对象的访问
CGLIB动态代理相对于JDK动态代理局限性就小很多,目标对象不需要实现接口,底层
是通过继承目标对象产生代理子对象
Spring切面可以应用5中类型通知
前置通知(Before)、
后置通知(After,在方法完成之后通知,无论方法是否成功)
后置通知(After-returning,在方法执行成功后通知)
异常通知(After-throw,在方法抛出异常通知)
环绕通知(Around,在目标方法执行前后都调用)
Spring中应用AOP,步骤
1、导包
2、准备目标对象
3、准备通知
4、将通知织入目标对象中, (spring配置主要是1、xml配置方式。2、注解配置方式)
xml配置方式
<context:component-scan base-package="com.gx.spring"></context:component-scan>
2、注解配置方式
@Component
@Aspect
public class MyAspect {
public MyAspect() {
System.out.println("初始化切面");
}
@Pointcut(value = "execution(* com.gx.spring.po..*.*(..))")
private void myPoincut() {
}
@Before(value = "myPoincut()") //它的效果相当于:
private void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知,目标:");
System.out.print(joinPoint.getTarget() + "方法名称:");
System.out.println(joinPoint.getSignature().getName());
System.out.println();
}
AOP机制、实现、具体怎么使用?具体到标签?
AOP,是面向切面编程,是对OOP(面向对象编程)的补充和完善,简单来说,就是纵向重复的代码,横向抽取出来。最明显的体现:
过滤器
和拦截器
的使用。
Spring实现AOP的本质是动态代理Spring采用两种动态代理方式,分别是JDK动态代理和cglib动态代理。
JDK动态代理:被代理对象必须实现接口,
cglib动态代理:被代理对象原则上可以是任何类,cglib实现动态代理的原理是被代理对象进行继承,重写被代理对象的所有方法。
所有被代理对象不能被final修饰。
Spring操作AOP,具体可分为4步,1、导包。2、准备目标对象。 3、准备通知。4、将通知织入对象。其中第2、第3都是由java代码
实现,第4(将通知织入目标对象)有两中实现方式
1、xml配置。
其中所用到的标签有aop:config \ aop:pointcut
aop:after-throwing
2、注解配置
@Aspect , @Pointcut @Before @AfterReturning @Around @AfterThrowing @After
Spring切面可以应用5中类型的通知,哪5种?
前置通知(Before)、后置通知(After-returning,方法执行成功后调用通知)、
环绕通知(Around,在目标方法前后都通知) 异常通知(After-throwing,在方法抛出异常后通知)
后置通知(After,在方法完成之后,无论是否执行成功)
简单介绍一下Spring中的事务管理。
事务就是对一系列的数据库操作,(插入多条数据)进行统一的提交或回滚操作。如果成功那就一起成功。如果中间有一条数据
出异常,那么回滚之前的所有操作,这样可以防止出现脏数据, 防止数据库出现问题。
现实中常见的例子:转账
Spring整合SSM
spring整合ssm框架,需要的配置?
Spring整合Dao:要配置数据库驱动,配置SQLSessionFactory。配置Mapper动态代理。
spring整个service:要配置包扫描器(开启注解驱动),配置事务管理(事务管理器、事务通知、事务切面)
spring整合web:要配置包扫描器(扫描Controller),配置处理器、映射器、处理适配器(采用注解驱动的方法),配置视图解析器
在web.xml中:要加载spring容器,并且要配置一个springMVC的前端控制器
spring的配置有哪些?
spring整合Dao:要配置数据库驱动,配置SQLSessionFactory,配置Mapper动态代理
spring整合Service:要配置包扫描器(开启注解驱动),要配置事务管理(事务管理器、事务通知、事务切面)
spring整合web:要配置包扫描器(扫描controller)。配置处理器、映射器、和处理器适配器(采用注解驱动的方法),配置视图解析器
web.xml中,要加载spring容器,并且要配置一个springMVC的前端控制器
spring平时主要用到的东西?
控制反转(IOC)、依赖注入(DI)、属性注入、事务管理、面向切面编程(AOP)等;
spring框架中使用了哪些设计模式以及应用场景
1、工厂模式:在各种BeanFactory以及ApplicationContext创建中都用到
2、模版模式:在各种BeanFactory以及ApplicationContext实现中也用到
3、代理模式:springAOP利用了Aspect AOP实现
4、策略模式:加载资源文件的方式,使用了不同的方法。如:ClassPathResourece
FileSystemResource,ServletContextResource,UrlResource但他们都是有共同的接口Resourec;
在AOP的实现中,采用了两种不同的方式。JDK代理、CGLIB代理
5、单例模式:创建bean的时候
6、观察者模式:spring中的ApplicationEvent,ApplicationListener,ApplicationEventPublisher
7、适配器模式:MethodBeforeAdviceAdapter,ThrowAdviceAdapter,AfterReturningAdapter
8、装饰着模式:源码中类型带Wrapper或者Decorator的 都是