侧边栏壁纸
博主头像
lmg博主等级

  • 累计撰写 55 篇文章
  • 累计创建 6 个标签
  • 累计收到 2 条评论
标签搜索

Spring常见面试题

lmg
lmg
2020-05-20 / 0 评论 / 0 点赞 / 338 阅读 / 15,339 字
温馨提示:
本文最后更新于 2022-04-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

RESTful API介绍

REST,即 REpresentational State Transfer 的缩写。这个词组的翻译过来就是"表现层状态转化"。这样理解起来甚是晦涩,实际上 REST 的全称是 Resource Representational State Transfe ,直白地翻译过来就是 “资源”在网络传输中以某种“表现形式”进行“状态转移”

  • 资源(Resource) :我们可以把真实的对象数据称为资源。一个资源既可以是一个集合,也可以是单个个体。比如我们的班级 classes 是代表一个集合形式的资源,而特定的 class 代表单个个体资源。每一种资源都有特定的 URI(统一资源定位符)与之对应,如果我们需要获取这个资源,访问这个 URI 就可以了,比如获取特定的班级:/class/12。另外,资源也可以包含子资源,比如 /classes/classId/teachers:列出某个指定班级的所有老师的信息
  • 表现形式(Representational):"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式比如 json,xml,image,txt 等等叫做它的"表现层/表现形式"。
  • 状态转移(State Transfer) :大家第一眼看到这个词语一定会很懵逼?内心 BB:这尼玛是啥啊? 大白话来说 REST 中的状态转移更多地描述的服务器端资源的状态,比如你通过增删改查(通过 HTTP 动词实现)引起资源状态的改变。ps:互联网通信协议 HTTP 协议,是一个无状态协议,所有的资源状态都保存在服务器端。

综合上面的解释,我们总结一下什么是 RESTful 架构:

  1. 每一个 URI 代表一种资源;
  2. 客户端和服务器之间,传递这种资源的某种表现形式比如 json,xml,image,txt 等等;
  3. 客户端通过特定的 HTTP 动词,对服务器端资源进行操作,实现"表现层状态转化"。

REST 接口规范

  • GET :请求从服务器获取特定资源。举个例子:GET /classes(获取所有班级)

  • POST :在服务器上创建一个新的资源。举个例子:POST /classes(创建班级)

  • PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /classes/12(更新编号为 12 的班级)

  • DELETE :从服务器删除特定的资源。举个例子:DELETE /classes/12(删除编号为 12 的班级)

get和post的区别

  • GET 用于获取信息,是无副作用的,是幂等的,且可缓存。数据在URL中对全部人可见。
  • POST 用于修改服务器上的数据,有副作用,非幂等,不可缓存。数据不显示在URL中。比get更安全。

状态码范围:

2xx:成功3xx:重定向4xx:客户端错误5xx:服务器错误
200 成功301 永久重定向400 错误请求500 服务器错误
201 创建304 资源未修改401 未授权502 网关错误
403 禁止访问504 网关超时
404 未找到
405 请求方法不对

介绍一下IOC

IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。

将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。

推荐阅读:https://www.zhihu.com/question/23277575/answer/169698662

Bean的作用域

单实例:singleton

1.默认的,容器启动完成之前就已经创建好对象了,保存在容器中了。

2.任何时候获取,都是获取之前创建好的那个对象。

prototype

1.容器启动时,默认不会去创建多实例bean,获取的时候创建bean。

2.每次获取都会创建一个新的对象。

spring中的单例bean的线程安全问题了解吗?

大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程安全问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

常见的两种解决方法:

  1. 在Bean对象中尽量避免定义可变的成员变量(不太现实)。

  2. 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)。

@Bean和@Component的区别是什么?

相同点:两者的结果都是为spring容器注册Bean.

作用对象:

@component注解作用于类,可以通过类路径来自动扫描并注册到spring容器中,也可以使用@ComponentScan定义需要扫描的路径。

@Bean注解作用于方法,告诉spring这个方法会返回一个对象,要将其注入到容器中。

@Bean相对来说就更加灵活了,而且如果你要用到第三方类库里面某个类或者方法的时候,你就只能用@Bean把这个类或者方法注册到spring容器,因为用@Component你需要配置组件扫描到这个第三方类路径而且还要在别人源代码加上这个注解,很明显是不现实的。

@Bean注解使用示例:

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
	}
}

上面的代码相当于下面的xml配置

  • @Component : 通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪个层,可以使用这个注解。
  • @Repository : 对应持久层即Dao层,主要用于数据库相关操作。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到Dao层。
  • @Controller : 对应Spring MVC控制层,主要用于接受用户请求并调用Service层返回数据给前端页面。

Spring中的Bean生命周期

https://www.cnblogs.com/zrtqsk/p/3735273.html

Spirng依赖注入的两种注入方式(IOC容器创建对象的方式,底层都是通过反射):

Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。

1.构造函数注入,使用构造器对对象的初始化注入对应的值

<?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-3.2.xsd">
	<bean name="teacher" class="com.xxx.spring.ioc.bean.Teacher">
		<!--  1.按照属性名赋值 ,调用有参数的构造器,顺序是参数顺序-->
		<constructor-arg name="id" value="1"/> <!-- person(int id,String name, String gender) -->
		<constructor-arg name="name" value="tom"/>
		<constructor-arg name="gender" value="male"/>
		<!-- 2.index从0开始,按照属性在构造器中出现的顺序赋值 索引值是构造器中的属性顺序 -->
		<!-- <constructor-arg index="0" value="2"/>
		<constructor-arg index="1" value="jack"/>
		<constructor-arg index="2" value="male"/> -->
		<!-- 3.按照类型进行赋值,如果出现相同的类型,按照属性在构造器中出现的顺序进行复制 -->
	<!-- 	<constructor-arg type="int" value="3"/>
		<constructor-arg type="String" value="rose"/>
		<constructor-arg type="String" value="female"/> -->
	</bean>
</beans>

Teacher.java


public class Teacher implements Serializable{
	private static final long serialVersionUID = 1L;
	private int id;
	private String name;
	private String gender;
 
	public Teacher(int id, String name, String gender) {
		super();
		this.id = id;
		this.name = name;
		this.gender = gender;
	}
 
	@Override
	public String toString() {
		return "Teacher [id=" + id + ", name=" + name + ", gender=" + gender
				+ "]";
	}

测试


	@Test
	public void test3() throws Exception {
		ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
		Teacher teacher = (Teacher) ac.getBean("teacher");
		System.out.println(teacher);//Teacher [id=1, name=tom, gender=male]
	}

2.set方法注入,就是在类中提供需要注入成员的set方法

beans.xml

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <property name="name" value="test"></property> 
    <property name="age" value="21"></property>
    <property name="birthday" ref="now"></property>
</bean> 
<!--Spring就会通过反射调用没有参数的构造方法生成对象,同时通过反射对应的setter注入配置的值-->
<bean id="now" class="java.util.Date"></bean>

AccountServiceImpl.java

public class AccountServiceImpl implements IAccountService { 
    private String name; 
    private Integer age; 
    private Date birthday;
    
    public void setName(String name) { 
        this.name = name; 
    } 
    public void setAge(Integer age) {
        this.age = age; 
    }
    public void setBirthday(Date birthday) { 
        this.birthday = birthday; 
    }

常用注解

用于创建对象的

相当于:

@Component

作用: 把资源让spring来管理。相当于在xml中配置一个bean。

属性: value:指定bean的id。如果不指定value属性,默认bean的id是当前类的类名。首字母小写。

@Controller @Service @Repository

他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。 他们只不过是提供了更加明确的语义化。

@Controller:一般用于表现层的注解。

@Service:一般用于业务层的注解。

@Repository:一般用于持久层的注解。

用于注入数据的

@Autowired

作用: 自动按照类型注入。当使用注解注入属性时,在spring容器查找,找到了也可以注入成功。找不到就报错。自动装配值。找到多个的话,可以用qulifier指定id

@Autowired

private UserService userService;

如果没有Autowired注解,userService=null

@Autowired 和 @Resource的区别

@Autowired更强大,spring框架,离开spring没法用(谁会舍得离开呢)

@Resource是java自带的

@Qualifier

作用: 在自动按照类型注入的基础之上,再按照Bean的id注入。它在给字段注入时不能独立使用,必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。

属性: value:指定bean的id。

1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

`@Autowired``()``@Qualifier``(``"baseDao"``)``private``BaseDao baseDao;`

3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

`@Resource``(name=``"baseDao"``)``private``BaseDao baseDao;`

AOP

区别OOP(Object Oriented Programming)

思想:在程序运行期间,讲某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,

@Before:前置通知,在方法执行之前执行

@AfterRunning:返回通知,在方法返回正常结果之后执行

@AfterThrowing:异常通知,在方法抛出异常之后执行

@After:后置通知,在方法执行之后执行

@Around:环绕通知,围绕着方法执行 4合1

通知顺序:前置--返回--后置 前置--异常--后置

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
  • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
  • Target(目标对象):织入 Advice 的目标对象.。
  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
//切面如何编写
1.导入切面场景 spring-boot-starter-aop
2.编写切面
    1> @Aspect
    2> 切入点表达式
    3> 通知,前置,后置,环绕

动态代理

可以在运行期动态创建某个interface的实例。实际上是jdk自动帮你inplements了这个接口类。XXXDynamicProxicy.

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)

构造实现指定接口的代理类的一个新实例。所有方法会调用给定处理器对象handler的invoke方法

public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
    //输出如下
    /*
    public abstract void Hello.morning(java.lang.String)
    Good morning, Bob
    */
}

静态代理和动态代理廖雪峰

静态代理(看廖雪峰比较容易理解)

首先, 定义接口和接口的实现类, 然后定义接口的代理对象, 将接口的实例注入到代理对象中, 然后通过代理对象去调用真正的实现类。

缺点:为现有的每一个类都编写一个对应的代理类,并且让它实现和目标类相同的接口(假设都有)

public interface Movie {//接口
     void play();
}

public class RealMovie implements Movie {//接口的实现类
    @Override
    public void play() {
        // TODO Auto-generated method stub
        System.out.println("您正在观看电影 《肖申克的救赎》");
    }
}

public class Cinema implements Movie {//代理对象
    RealMovie movie;
 
    public Cinema(RealMovie movie) {
        super();
        this.movie = movie;
    }

    @Override
    public void play() {
        guanggao(true);
        movie.play();
        guanggao(false);
    }

    public void guanggao(boolean isStart){
        if ( isStart ) {
            System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
        } else {
            System.out.println("电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!");
        }
    }
 
 }

JDK和CGLIB动态

1、JDK动态代理

利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

2、CGLIB动态代理

利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

3、何时使用JDK还是CGLIB?

1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。

2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。

3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

4、如何强制使用CGLIB实现AOP?

1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

5、JDK动态代理和CGLIB字节码生成的区别?

1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。代理对象必须实现一个接口,否则会报异常,因为人家原理就是根据接口来生成代理对象的。

2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

public static interface Hello {
    void hi(String msg);
}
public static class HelloImpl implements Hello {
    @Override
    public void hi(String msg) {
        System.out.println("hello " + msg);
    }
}

/**
 * 代理类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class HelloProxy implements InvocationHandler {
    private Object proxied = null;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("hello proxy");
        return method.invoke(proxied, args);
    }
}

public static void main(String[] args) {
    Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class[]{Hello.class}, new HelloProxy(new HelloImpl()));

    System.out.println(hello.getClass()); // class com.sun.proxy.$Proxy0
    hello.hi("world");
}

介绍一下MVC及其优缺点

  • 模型(Model)封装了应用程序数据,通常它们将由POJO类组成。
  • 视图(View)负责渲染模型数据,一般来说它生成客户端浏览器可以解释HTML输出。
  • 控制器(Controller)负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。

Spring MVC 的简单原理图如下:

请求流程:

具体步骤:

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求HandlerMapping查找 Handler (就是Cotroller对象和请求方式组合的一个Object对象)(可以根据xml配置、注解进行查找)

第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)

第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)

第十一步:前端控制器向用户响应结果

MVC的优点:

1、低耦合性:

视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码。同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可,因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。

2、高重用性和可适用性

MVC模式允许你使用各种不同样式的视图来访问同一个服务器端的代码。它 包括任何WEB(HTTP)浏览器或者无线浏览器(wap),例如:例如,很多数 据可能用HTML来表示,但是也有可能用WAP来表示,而这些表示所需要的仅令是改变视图层的实现方式,而控制层和模型层无需做任何改变。

3、较低的生命周期成本MVC使降低开发和维护用户接口的技术含量成为可能。

4、快速的部署 使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员) 集中 精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上

5、可维护性

优点:分层,结构清晰,耦合性低,大型项目代码的复用性得到极大的提高,开发人员分工明确,提高了开发的效率,维护方便,降低了维护成本。

MVC的缺点:

1、增加了系统结构和实现的复杂性

2、视图与控制器间的过于紧密的连接

3、视图对模型数据的低效率访问

缺点:简单的小型项目,使用MVC设计反而会降低开发效率,层和层虽然相互分离,但是之间关联性太强,没有做到独立的重用。(视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。【例如,不可能总是在jsp页面中直接访问模型,一般放在逻辑控制层进行处理,servlet】)

Spring设计模式

常见面试题

1.Spring 框架中用到了哪些设计模式?

工厂设计模式 : Spring使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。

代理设计模式 : Spring AOP 功能的实现。

单例设计模式 : Spring 中的 Bean 默认都是单例的。

模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。

观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

适配器模式 :当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类BB,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用BB中的合适方法,这样就完成了一个简单的类适配器。

Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配。除此之外,Java IO中由于InputStream是字节流不能享受到字符流读取字符那么便捷的功能,因此借助InputStreamReader将其转为Reader子类,因此可以拥有便捷操作文本文件方法。OutputStream同理。

装饰者设计模式 : 对装饰器模式来说,装饰者(Decorator)和被装饰者(Decoratee)都实现一个接口。

对代理模式来说,代理类(Proxy Class)和真实处理的类(Real Class)都实现同一个接口。

区别:装饰器模式偏重对原对象功能的扩展,扩展后的对象仍是是对象本身;然而代理模式偏重因自己无法完成或无需关心,需要他人干涉事件流程,更多的是对对象的控制(代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可,即将客户端与实现类解耦)

换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器

eg1.我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

eg2.将InputStream字节流包装为BufferedReader过程就装饰的过程。一开始InputStream只有read一个字节的方法,包装为Reader之后拥有read一个字符的功能,在包装成BufferedReader之后就拥有read一行字符串功能。OutputStream同理。

Spring 框架中用到了哪些设计模式

2.介绍一下单例模式,都怎么实现

保证一个类仅有一个实例,并提供一个访问它的全局访问点。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

懒汉式,线程安全

//懒汉式,用到才创建,线程安全
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

饿汉式

//线程安全,类加载时就初始化,浪费内存
public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

双重校验锁

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

3.工厂模式都有哪几种?怎么实现?

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

1.简单工厂模式

该模式对对象创建管理方式最为简单,因为其仅仅简单的对不同类对象的创建进行了一层薄薄的封装。该模式通过向工厂传递类型来指定要创建的对象

代码:点击下载

2.工厂方法模式

和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂

3.抽象工厂模式

4.spring如何解决循环依赖

①:构造器(函数)的循环依赖。【这个Spring解决不了】

②【setter循环依赖】【可以解决】

Spring是先将Bean对象实例化【依赖无参构造函数】--->再设置对象属性的

【setter方式 单例,默认方式-->通过递归方法找出当前Bean所依赖的Bean,然后提前缓存【会放入Cach中】起来。通过提前暴露 -->暴露一个exposedObject用于返回提前暴露的Bean。】递归实例化所有的Bean对象后,再反递归设置对象属性。

5.设计模式六大原则

1.单一原则:一个类或者一个方法只负责一项职责

2.里氏替换原则:子类可以扩展父类的功能,但不能改变原有父类的功能

3.依赖倒置原则:面向接口编程;上层模块不应该依赖下层模块,两者应依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;

通俗点就是说变量或者传参数,尽量使用抽象类,或者接口;

4.接口隔离:建立单一接口

简单理解:复杂的接口,根据业务拆分成多个简单接口;(对于有些业务的拆分多看看适配器的应用)

5.迪米特原则:最少知道原则,尽量降低类与类之间的耦合;

6.开闭原则:用抽象构建架构,用实现扩展原则;(总纲)

6.Spring,SpringMVC,SprinBoot有什么区别?

  • Spring

Spring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就是控制反转(IOC),和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。

  • SpringMVC

SpringMVC是基于Spring功能之上添加的Web框架,想用SpringMVC必须先依赖Spring。

  • SpringBoot

实现自动配置,去除大量xml配置文件。

Maven快速整合第三方框架,如spring-boot-starter-web

自带tomcat服务器

关系

Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的一个 MVC 框架,而Spring Boot 是基于Spring的一套快速开发整合包

0

评论区