Spring基础须知_系面试知识点

spring

本文主要应对自己在面试前的一些准备,若有错误之处,可及时在评论区指出,多多交流,天下IT不分彼此。

Spring定义

轻量级的IOC与AOP容器框架,解决应用程序的业务层与其它层的耦合问题,java应用程序的基础框架,简化应用程序的开发。


七大模块

Spring Context 	 ------->		提供框架式的Bean访问方式
Spring Core   	 -------> 	 	核心类库
Spring AOP    	 ------->  		 AOP服务(面向切面)
Spring web       ------->       提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持,Spring能够管理这些框架,
                                将Spring的资源注入给框架,也能在这些框架的前后插入拦截器;

Spring MVC       ------->      提供面向Web应用的Model-View-Controller,即MVC实现
Spring DAO       ------->       对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务
Spring ORM       ------->       对现有的ORM框架的支持

Spring优点

(1)、spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性
(2)、Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用
(3)、方便集成各种优秀框架(mybatis、hibernate、structs2等)

IOC与AOP

IOC(控制反转,两个概念:IOC与DI)

IOC

指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。

对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,
而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。


DI :

依赖注入
应用程序在运行时依赖 IoC 容器来动态注入对象所需要的外部依赖


依赖注入方式:

1、构造函数注入

例如:

 public User(String username) {
        this.username = username;
    }

此时对应的xml添加的配置:

<bean id="user" class="com.hanfan.domain.User">
        <constructor-arg name="username" value="张三"/>
</bean>

其中 标签就是构造函数的标签 。

测试情况:

@Test
public void test4() {
       ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");
        System.out.println(user);
    }

结果

User{username='张三'}

自行验证;


2、Setter方法注入

User类加一个set方法

public void setUsername(String username) {
        this.username = username;
    }

xml配置:

<bean id="user" class="com.hanfan.domain.User">
   <property name="username" value="李四"/>      // 该类的username命名为李四 
</bean>

测试结果:

User{username='李四'}

3、属性注入

注入方式非常简单:加上@Autowired注解,加入要注入的字段,即可完成。

在介绍注解注入的方式前,先简单了解bean的一个属性autowire,autowire主要有三个属性值:constructor,byName,byType。

constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,
如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,
那么spring会优先将bean注入到多参数的构造方法中。

byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。

byType:查找所有的set方法,将符合参数类型的bean注入。

在以前的开发中,我们主要使用四种注解注册bean,每种注解可以任意使用,只是语义上有所差异:

@Component:可以用于注册所有bean
@Repository:主要用于注册dao层的bean
@Controller:主要用于注册控制层的bean
@Service:主要用于注册服务层的bean

随着springboot的流行,@Bean注解也逐渐的被我们使用起来。
Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。
产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

注解方式注入依赖(主要有两种):

@Resource :java的注解,默认以byName的方式去匹配与属性名相同的bean的id,如果没有找到就会以byType的方式查找,
如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。

@Autowired :spring注解,默认是以byType的方式去匹配类型相同的bean,可以结合@Qualifier 注解根据byName方式匹配。

UserService类:

package com.hanfan.service;

import com.hanfan.dao.UserDao;

public class UserService {

	UserDao userDao;

	// UserDao userDao1; 

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void addUser() {
		userDao.addUser();
	}

}


其中
byname注入:

byName注入可以有两个类型一样的bean,根据类中属性名来匹配bean.xml的id值。

如下:

<?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">

    <!-- 因为属性名叫userDao,所以会注入这个 -->
	<bean id="userDao" class="com.hanfan.dao.UserDao"></bean>
	
    <!-- 因为属性名叫userDao,所以不会注入这个 -->
	<bean id="userDao1" class="com.hanfan.dao.UserDao"></bean>
	
	<bean id="userService" autowire="byName" class="com.hanfan.service.UserService"></bean>

</beans>


byType注入:

byType注入不能有两个类型一样的bean,否则会注入失败。

如下:

<?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">

	<bean id="userDao" class="com.hanfan.dao.UserDao"></bean>
	
    <!-- 不能有两个一样类型的bean -->
	<!-- <bean id="userDao1" class="com.hanfan.dao.UserDao"></bean> -->
	
	<bean id="userService" autowire="byType" class="com.hanfan.service.UserService"></bean>

</beans>


AOP (面向切面编程)

接口只需要关心具体的业务,而不需要关注其他非该接口关注的逻辑或处理。
以下理解,来自一位同行的文章,文章的链接如下(写的很好,我是搬运工,不知是否可以加深诸位的理解)
Spring AOP概念理解 (通俗易懂)


为什么用aop ?

1、就是为了方便,看一个国外很有名的大师说,编程的人都是“懒人”,因为他把自己做的事情都让程序做了。用了aop能让你少写很多代码,这点就够充分了吧

2、就是为了更清晰的逻辑,可以让你的业务逻辑去关注自己本身的业务,而不去想一些其他的事情,这些其他的事情包括:安全,事物,日志等。


1、Advice(通知)

Advice 定义了在 Pointcut (切点)里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point (连接点)之前、之后还是代替执行的代码。
切面也有目标-他必须要完成的工作。在AOP中,切面的工作被称为通知。

通俗来说,就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。
Advice 类型:

1、before advice(前置通知): @Before
在 joint point 前被执行的 advice. 虽然 before advice 是在 joint point 前被执行, 但是它并不能够阻止 joint point 的执行,
除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 joint point 中的代码)

2、after return advice(返回之后通知): @AfterReturning
在一个 joint point 正常返回后执行的 advice

3、after throwing advice(抛异常后通知):@AfterThrowing
当一个 joint point 抛出异常后执行的 advice

4、after advice(后置通知):@After
无论一个 joint point 是正常退出还是发生了异常, 都会被执行的 advice.

5、around advice(环绕通知):@Around
在 joint point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.


2、Joint point(连接点)

表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point (连接点)。
说白了,连接点就是正在执行的方法

这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),
或抛出异常时都可以是连接点,
spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,
只要记住,和方法有关的前前后后(抛出异常),都是连接点。

3、Pointcut(切点)

表示一组 joint point(连接点),这些 joint point (连接点)或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice(通知) 将要发生的地方。
提供一组规则 来匹配Joint Point (连接点), 给满足规则的Joint Point (连接点)添加Advice(通知).

上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,
但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,
之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

4、Aspect(切面)

切面(Aspect )声明类似于 Java 中的类声明,在切面(Aspect )中会包含着一些 切点(Pointcut) 以及 相应的 通知(Advice)。

切面是通知和切入点的结合。
现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。
advice(通知)说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),
而Pointcut(切入点)说明了在哪干(指定到底是哪个方法),
这就是一个完整的切面定义。

5、introduction(引入)
允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

6、target(目标对象)
引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。
而自己专注于业务本身的逻辑。

7、proxy(代理)
怎么实现整套aop机制的,都是通过代理,这个一会给细说。

8、weaving(织入)

将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时,为什么是运行时,后面解释。

总结

AOP使用来对某一类事情进行集中处理的,
那么处理事情,这个整体就是一个切面(Aspect ),
处理事情的范围就是切点(Pointcut ),
范围中具体的事物就是连接点(joint point),
怎么处理就是通知(Advice )。

关键就是:切点定义了哪些连接点会得到通知

	spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,
	让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。

  现在可以自己想一想,怎么搞出来这个伪装类,才不会被调用者发现(过JVM的检查,JAVA是强类型检查,哪里都要检查类型)。

  1.实现和目标类相同的接口,我也实现和你一样的接口,反正上层都是接口级别的调用,
  这样我就伪装成了和目标类一样的类(实现了同一接口,咱是兄弟了),也就逃过了类型检查,到java运行期的时候,
  利用多态的后期绑定(所以spring采用运行时),伪装类(代理类)就变成了接口的真正实现,而他里面包裹了真实的那个目标类,
  最后实现具体功能的还是目标类,只不过伪装类在之前干了点事情(写日志,安全检查,事物等)。

  这就好比,一个人让你办件事,每次这个时候,你弟弟就会先出来,当然他分不出来了,以为是你,你这个弟弟虽然办不了这事,
  但是他知道你能办,所以就答应下来了,并且收了点礼物(写日志),收完礼物了,给把事给人家办了啊,
  所以你弟弟又找你这个哥哥来了,最后把这是办了的还是你自己。但是你自己并不知道你弟弟已经收礼物了,
  你只是专心把这件事情做好。

  顺着这个思路想,要是本身这个类就没实现一个接口呢,你怎么伪装我,我就压根没有机会让你搞出这个双胞胎的弟弟,
  那么就用第2种代理方式,创建一个目标类的子类,生个儿子,让儿子伪装我

  2.生成子类调用,这次用子类来做为伪装类,当然这样也能逃过JVM的强类型检查,我继承的吗,当然查不出来了,
  子类重写了目标类的所有方法,当然在这些重写的方法中,不仅实现了目标类的功能,还在这些功能之前,
  实现了一些其他的(写日志,安全检查,事物等)。

  这次的对比就是,儿子先从爸爸那把本事都学会了,所有人都找儿子办事情,但是儿子每次办和爸爸同样的事之前,
  都要收点小礼物(写日志),然后才去办真正的事。当然爸爸是不知道儿子这么干的了。这里就有件事情要说,
  某些本事是爸爸独有的(final的),儿子学不了,学不了就办不了这件事,办不了这个事情,自然就不能收人家礼了。

  前一种兄弟模式,spring会使用JDK的java.lang.reflect.Proxy类,它允许Spring动态生成一个新类来实现必要的接口,
  织入通知,并且把对这些接口的任何调用都转发到目标类。

  后一种父子模式,spring使用CGLIB库生成目标类的一个子类,在创建这个子类的时候,spring织入通知,
  并且把对这个子类的调用委托到目标类。

  相比之下,还是兄弟模式好些,他能更好的实现松耦合,尤其在今天都高喊着面向接口编程的情况下,
  父子模式只是在没有实现接口的时候,也能织入通知,应当做一种例外。

AOP的两种代理方式

①: CGLIB代理 (基于父类的动态代理技术)

②: JDK动态代理(基于接口的动态代理技术)
JDK本身只提供接口的代理,而不支持类的代理。


Spring的注解

声明bean的注解:

@Component: 普通的注解,各种组件
@Service :业务逻辑层以及Service层
@Controller: 外部层
@Repository :dao层即持久层,或者也可以叫数据访问层


注入bean的注解:

@Autowired (根据属性类型自动装配)
@Resource (可以根据类型注入,也可以根据名称注入)
@Qualifier (根据属性名称进行注入)
@Value 注入普通类型的注入


Java配置类相关注解:

@Configuration 声明当前类为配置类;
@Bean注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式;
@ComponentScan用于对Component进行扫描


切面(AOP)相关注解 Spring支持AspectJ的注解式切面编程:

@Aspect 声明一个切面
@After 在方法执行之后执行(方法上)
@Before 在方法执行之前执行(方法上)
@Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点


Spring事务注解:

@Transactional


SpringMVC常用注解:

@RequestMapping用于映射web请求,包括访问路径和参数。
@ResponseBody 支持将返回值放到response内,而不是一个页面,通常用户返回json数据。 @RequestBody允许request的参数在request体中,而不是在直接连接的地址后面。(放在参数前)
@PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)声明的路径,将注解放在参数前,即可获取该值,通常作为Restful的接口实现方法。
@ModelAttribute


Spring 中的设计模式

单例模式、工厂模式、代理模式、观察者模式、模板方法模式、适配器模式、装饰器模式


Spring中Bean相关

因为 Spring 中的单例 Bean 是在应用程序启动时创建的,并且在整个应用程序的生命周期中只有一个实例存在,
因此我们认为它是线程安全的

这是因为所有线程都共享相同的实例,所以不会出现多个线程尝试同时访问或修改不同的实例的情况。
这使得单例 Bean 在多线程环境中非常适合使用。
但是,如果单例 Bean 中包含了可变状态,
例如实例变量,那么在多线程环境下使用时,仍然需要考虑线程安全问题。

可以使用 synchronized 等同步机制来保证单例 Bean 内部的可变状态的线程安全性。

Bean 是什么?

在 Spring 中,Bean 是一个由 Spring 容器管理的对象。Bean 是 Spring 中最基本的组件之一,它可以是任何 Java 对象,
包括 POJO(Plain Old Java Object)、服务、数据源等。

在 Spring 中,可以通过 XML 配置文件或 Java 注解来定义 Bean。以 XML 配置文件为例,可以使用 元素来定义一个 Bean,
需要指定 Bean 的 id 和 class 属性
例 : <bean id="userService" class="com.hanfan.service.UserService"/>

Bean 的作用域有哪些?如何在 Spring 中创建 Bean?

Spring 定义了以下五种作用域:
1、singleton:单例模式,一个 Bean 容器中只存在一个实例。
2、prototype:每次请求都会创建一个新的实例。
3、request:每个 HTTP 请求都会创建一个新的实例。
4、session:每个 HTTP Session 都会创建一个新的实例。
5、global-session:全局的 HTTP Session 中只会创建一个实例

在 Spring 中,有三种方式可以创建 Bean:
1、使用构造函数创建 Bean。
2、使用静态工厂方法创建 Bean。
3、使用实例工厂方法创建 Bean。

Spring 的 Bean 生命周期是什么?它有哪些常用的回调方法?

1、Spring对bean进行实例化;
①实例化Bean

2、Spring将值和bean的引用注入到bean对应的属性中;
②设置属性值

3、如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
③调用BeanNameAware 的setBean-Name()方法

4、如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
④调用BeanFactoryAware 的setBeanFactory()方法

5、如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,
将bean所在的应用上下文的引用传入进来;
⑤调用ApplicationContextAware的setApplicationContext()方法

6、如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
⑥调用BeanPostProcessor的预初始化方法

7、如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,
如果bean使用initmethod声明了初始化方法,该方法也会被调用;
⑦调用InitializingBean的after-PropertiesSet()方法

8、如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
⑧调用定制的初始化方法

9、此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
⑨调用BeanPostProcessor的初始化方法

10、如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,
该方法也会被调用。

在 Spring 中,常用的回调方法包括:

1、InitializingBean 接口的 afterPropertiesSet() 方法:在 Bean 的属性赋值完成后,Spring 容器会自动调用该方法,
可以在该方法中进行一些初始化操作。

2、DisposableBean 接口的 destroy() 方法:在容器销毁 Bean 实例时调用该方法,可以在该方法中进行一些资源释放操作。

3、自定义初始化方法:可以在 Bean 配置文件或 Bean 类上使用 init-method 属性指定初始化方法。

4、自定义销毁方法:可以在 Bean 配置文件或 Bean 类上使用 destroy-method 属性指定销毁方法。

Spring 中的循环依赖是什么?如何解决它?

循环依赖是指两个或多个Bean彼此依赖对方,形成闭环,导致无法确定Bean初始化的顺序。在Spring中,尤其是当使用构造器注入时,循环依赖可能会引发问题,因为每个Bean的创建都要求依赖的Bean先被创建。

循环依赖的类型:
1、构造器循环依赖:当两个或更多的Bean互相在构造器中注入对方时,会发生构造器循环依赖,这是无法解决的,
因为在构造器调用之前,没有Bean实例可用来注入。

2、字段注入循环依赖:当Bean通过字段(属性)注入依赖对方时,这种依赖可以被Spring解决,因为Bean可以在设置属性之前被实例化。

3、方法注入循环依赖:当Bean通过设置器方法(setter方法)注入依赖时,Spring也能够处理这种依赖。

Spring如何解决循环依赖:
Spring容器解决单例作用域Bean的循环依赖主要通过三级缓存:

1、一级缓存(singletonObjects):包含所有已经初始化完成的Bean。
2、二级缓存(earlySingletonObjects):包含早期暴露的Bean对象,即尚未完全初始化的Bean。
3、三级缓存(singletonFactories):包含所有Bean的ObjectFactory,用于解决循环依赖。

Springmvc

工作原理

1.用户发送请求至前端控制器DispatcherServlet。

2.DispatcherServlet收到请求调用处理器映射器HandlerMapping。

3.处理器映射器根据请求url找到具体的处理器,
生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。

4.DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,
如:参数封装,数据格式转换,数据验证等操作。

5.执行处理器Handler(Controller,也叫页面控制器)。

6.Handler执行完成返回ModelAndView。

7.HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet。

8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9.ViewReslover解析后返回具体View。

10.DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。

11.DispatcherServlet响应用户。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值