【面试】浅学Spring

目录

说下Spring框架的组成

Spring的优点

什么是Spring

SpringIOC

SpringIOC底层原理

什么是ConcurrentHashMap?_GuGuBirdXXXX的博客-CSDN博客

Spring如何解决Bean的循环依赖?

到底什么是循环依赖?

Spring中有哪些循环依赖?

1构造方法注入循环依赖

2setter 注入循环依赖

3.单例Bean之间循环依赖

Spring为什么解决不了多例和构造器的循环依赖?

Spring解决循环依赖使用的三级缓存

Spring如何解决Bean的循环依赖的执行流程

从ConcurrentHashMap缓存中获取Bean的流程

SpringAOP

SpringAOP实现原理

AOP的设计模式

AOP案例【事务】

Spring AOP里面的几个名词的概念:

说一下定义切面相关的注解

Spring框架用到了那些设计模式?

单例bean(singleton)与原型bean(prototype)【多例】的区别

单例模式-懒汉模式【双重校验锁-解决线程安全问题和效率低】

什么是反射?【SpringAOP】

为什么需要使用反射?

Spring AOP 和 AspectJ的区别

什么是Spring Bean?

Spring的Bean懒加载和非懒加载有什么区别

实例化和初始化的区别

Spring Bean的生命周期?

把对象交给Spring容器进行管理的注解有那些?

Spring的依赖注入bean的方式有哪些

@Autowired和 @Resource的区别?

Bean的四种注册【创建】方式

Spring常用注解

Spring的Bean的作用域有那些?

BeanFactory和ApplicationContext有什么区别

Spring是如何管理事务的?

Spring事务传播(机制)有哪些?

Spring事务失效的场景

事务+分布式事务【2023面试题】_seata面试题_MXin5的博客-CSDN博客


说下Spring框架的组成

  1. CoreContain核心容器模块:

    1. spring-core:提供框架的基本组成部分,包括 IoC 和依赖注入功能

    2. spring-beans:提供 BeanFactory,工厂模式

    3. context:提供国际化,事件传播,资源加载等功能

    4. spring-ExpressionLanguage:提供表达式语言

  2. Web模块

    1. Web:提供面向web的基本功能和面向web的应用上下文

    2. Web-MVC:为web应用提供模型视图控制(MVC)

    3. Web-Socket:在 web 应用程序中提供客户端和服务器端之间通信的方式

    4. Web-Portlet:模块提供了用于Portlet环境的MVC实现

  3. 数据/集成模块

    1. JDBC:包含了Spring对JDBC数据访问进行封装的所有类

    2. ORM:为对象-关系映射提供交互层

    3. OXM:提供对Object/XML映射实现的抽象层

    4. JMS:主要包含了一些制造、消费和消息的功能

    5. Transaction:为实现特殊接口类以及所有的 POJO 支持编程式和声明式的事务管理

  4. 其他模块

    1. AOP:提供了面向切面编程相关实现

    2. Aspects:模块提供了与AspectJ的集成,是一个功能强大的AOP框架

    3. Instrumentation:提供了class instrumentation 的支持和类加载器classloader的实现

    4. Messaging:为 STOMP 提供支持

    5. Test:支持使用JUnit和TestNG对Spring组件进行测试

Spring的优点

1,方便解耦,简化开发【将创建对象和依赖关系交给Spring容器进行管理】:

通过spring提供的IOC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免原编码所造成过度程序耦合;有了spring,用户不再为了单列模式类,属性文件解析等这些很底层的需求编写代码,可以专注于上层的应用的

2,AOP编程的支持【增加新的功能-事务、异常的统一处理、日志】:

通过spring提供的AOP功能方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付.spring的AOP支持允许将一些通用任务如安全,事务,日志等进行集中式管理,从而提供了更好的复用

3,声明事务的支持【直接使用注解@Transactional】:

在spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明方式灵活地进行事务的管理,提高开发效率和质量

4,方便程序的测试【支持Junit4】:

可以用非容器依赖编程方式进行几乎所有的测试工作,在spring里,测试不在是昂贵的操作,而是随手课做的事情没列入spring对junit4支持,可以通过注解方便的测试spring程序

5,方便集成各种框架:

spring不排斥各种优秀的开源框架,相反,spring可以降低各种框架的使用难度,spring提供了对各种哟毓秀框架(如Strust,Hibernate,Hessian,Quartz)等的直接支持.

6,降低javaEE API的使用难度:

spring对很多难用的java EE API(如JDBC,javaMail,远程调用等)提供了一个薄薄的封装层,通过spring的简易封装,这些java EE API的使用难度大大降低.

什么是Spring

        Spring是一个开源的轻量级控制反转【IOC-Inversion Of Control--思想,DI依赖注入是实现】面向切面编程【Aop-Aspect-oriented programming】容器框架轻量级是说它开发使用简单功能强大

SpringIOC

        控制反转【Inversion of Control将对象的创建和依赖关系交给Spring容器进行管理,而不是由程序员进行完成。IOC的核心就是依赖注入(Dependency Injection,简称DI),通过DI可以将某个Bean所依赖的其他Bean实例注入到该Bean中,这样的好处就是降低了Bean之间的耦合性,使程序更加灵活、可维护性高。--比如UserController类中注解注入UserSerive对象。

SpringIOC底层原理

1在配置文件中定义一个Bean

2加载Spring的配置,并对xml进行解析。

3针对Bean的定义属性,会创建一个对象BeanDefination(注意不是Bean实例)

4存储多个BeanDefination(Bean的定义对象)

要用ConcurrentHashMap<String,BeanDefination>装起来,Bean的注册。

【因为基本上使用在多线程的环境中,读取的性能就会更高--JDK1.8过后采用的数据+链表+红黑树存储】环境中,使用ConcurrentHashMap依赖于他的特性,查询效率就会更高

5如果是单例的,【默认是饿汉,在Bean注册完成之后(4步)就会去concurrentHashMap拿到BeanDefination,根据BeanDefination

使用反射或代理的方式进行创建真正的实例对象。】第一次使用的时候进行创建

6接下来就是对Bean进行初始化,以及属性注入。

7将创建好的Bean存储在另外一个ConcurrentHashMap<String,BeanWrapper>

8使用的时候直接去ConcurrentHashMap中取,根据Id(name)类型获取Bean。【@Autowired通过类型注入,类型没有就会采用名字注入,@Resources通过名字进行注入】。

什么是ConcurrentHashMap?_GuGuBirdXXXX的博客-CSDN博客

当Spring启动时,IOC容器会加载Spring的配置文件,包括XML配置或者注解,然后解析这些Bean并把相关定义信息封装成BeanDefinition对象,通过Bean注册器BeanDefinitionRegistry注册到IOC容器,也就是一个ConcurrentHashMap中

此时会找出所有的单例且非懒加载的bean,根据其BeanDefinition进行Bean的实例化,它会判断如果bean中有方法覆盖,就使用JDK反射创建Bean,否则使用CGLIB动态代理方式生成代理。然后把实例化好的Bean缓存到一个ConcurrentHashMap中

==========================底层比较重要的类=================================

BeanFactory:创建Bean的工厂,提供了基本的获取Bean的方法

ApplicationContext:是BeanFactory的子接口,功能增强了如读取Resources,对消息的支持,如国际化支持等

ClasspathXmlApplicationContext:是ApplicationContext的子实现,可以自动从ClassPath类路径下加载xml配置,然后完成IOC的启动【getBean,getType】

AnnotationConfigApplicationContext:是针对注解配置的IOC容器在SpringBoot中使用

XmlWebApplicationContext:是Spring整合了SpringMVC,在Web环境中使用的IOC工厂

Spring如何解决Bean的循环依赖?

到底什么是循环依赖?

循环依赖指的是两个或多个对象之间相互持有对象作为赖,形成一个循环依赖的关系。

举例:比如A类中有B属性(成员变量),B类中有A属性,

这样子就造成了spring在实例化其中一个类A的时候,A中所依赖的B还没有实例化,这个时候就要去实例化类B,而实例化类B的时候又发现B依赖了A类,导致循环引用,无限创建

Spring中有哪些循环依赖?

1构造方法注入循环依赖

2setter 注入循环依赖

3单例Bean之间的循环依赖

1构造方法注入循环依赖

构造方法循环依赖是指两个或多个Bean在构造方法中相互依赖。例如:

public class A {
    public A(B b) {
        ...
    }
}

public class B {
    public B(A a) {
        ...
    }
}

在这个例子中,A和B之间存在构造方法循环依赖。当Spring容器在初始化A时,需要初始化B,而初始化B时又需要初始化A,因此会导致循环依赖。

解决方法:使用setter方法注入,或者使用构造方法中依赖注入时使用延迟加载(@Lazy注解)。

  1. 构造方法循环依赖的解决方法示例:
public class A {

    private B b;

    public A() {
    }

    // 使用setter方法注入B
    public void setB(B b) {
        this.b = b;
    }
}

public class B {

    private A a;

    public B() {
    }

    // 使用setter方法注入A
    public void setA(A a) {
        this.a = a;
    }
}

// 在配置文件中进行Bean的声明和注入
@Bean
public A a() {
    A a = new A();
    a.setB(b());
    return a;
}

@Bean
public B b() {
    B b = new B();
    b.setA(a());
    return b;
}

2setter 注入循环依赖

属性的setter方法循环依赖是指两个或多个Bean之间通过setter方法进行相互引用。例如:

public class A {
    private B b;

    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public void setA(A a) {
        this.a = a;
    }
}

在这个例子中,A和B之间存在setter方法循环依赖。当Spring容器在初始化A时,需要初始化B,而初始化B时又需要初始化A,因此会导致循环依赖。

解决方法:使用构造方法注入,或者使用属性注入时使用延迟加载。

  1. 属性的setter方法循环依赖的解决方法示例:
public class A {

    private B b;

    public A() {
    }

    // 使用构造方法注入B
    public A(B b) {
        this.b = b;
    }

    // 使用setter方法注入B
    public void setB(B b) {
        this.b = b;
    }
}

public class B {

    private A a;

    public B() {
    }

    // 使用构造方法注入A
    public B(A a) {
        this.a = a;
    }

    // 使用setter方法注入A
    public void setA(A a) {
        this.a = a;
    }
}

// 在配置文件中进行Bean的声明和注入
@Bean
public A a() {
    return new A(b());
}

@Bean
public B b() {
    B b = new B();
    b.setA(a());
    return b;
}

3.单例Bean之间循环依赖

单例Bean之间循环依赖是指两个或多个单例Bean之间存在相互依赖。例如:

@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a;
}

在这个例子中,A和B之间存在单例Bean循环依赖。当Spring容器在初始化A时,需要初始化B,而初始化B时又需要初始化A,因此会导致循环依赖。

解决方法:使用setter方法注入,或者使用单例Bean之间依赖时使用延迟加载。

@Service
public class A {

    @Autowired
    private B b;

    public void methodA() {
        b.methodB();
    }
}

@Service
public class B {

    @Autowired
    private A a;

    public void methodB() {
        a.methodA();
    }
}

// 在配置文件中进行Bean的声明和注入
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public A a() {
    A a = new A();
    a.setB(b());
    return a;
}

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public B b() {
    B b = new B();
    b.setA(a());
    return b;
}

Spring为什么解决不了多例和构造器的循环依赖?

Spring中利用缓存机制解决循环依赖问题,核心是利用一个map。来解决这个问题,这个map就是缓存。

为什么可以这么做,因为我们的Spring创建的bean是默认单例的,而且是字段注入(setter注入)的,单例意味着只需要创建一次对象,然后将创建的对象存储到缓存中,后面就可以从缓存中取出来。

如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存; 如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。

Spring解决循环依赖使用的三级缓存

  • 一级缓存(singletonObjects):用来存放已经(创建)实例化好的单例bean对象,即单例Bean缓存池

  • 二级缓存(earlySingletonObjects):用于存放早期暴露出来的单例对象,这些对象是不完整的,通过提前暴露用来解决循环依赖的问题。用来缓存正在创建的bean对象。

  • 三级缓存(SingletonFactories):用来存放创建Bean的工厂

Spring如何解决Bean的循环依赖的执行流程

假设有两个bean,A依赖B,B依赖A

  1. Spring容器启动时,先创建Bean对象A

  2. 根据Bean对象A的依赖关系,在singletonObjects缓存【一级缓存】中对Bean对象A进行初始化,如果当前需要注入的Bean对象B在singletonObjects【一级缓存】中已经存在,则直接返回该对象,不需要再次创建。

  3. 如果Bean对象B对象不在singletonObjects【一级缓存】中,会将Bean对象A存放在二级缓存中,然后检查earlySingletonObjects【二级缓存】中是否包含Bean对象B,如果包含则直接返回该对象,否则会去三级缓存进行创建一个新的Bean对象B并放入earlySingletonObjects【二级缓存】缓存中,这时候就可以对Bean对象A注入Bean对象B了。

  4. 创建过程中,如果出现循环依赖情况【就是创建Bean对象B的时候发现依赖了Bean对象A】,则直接去一级缓存或二级缓存进行查找,查找到了就直接返回该对象实现注入,否则使用三级缓存进行创建Bean对象并存放在二级缓存中。

  5. Bean对象A创建完成后,放入singletonObjects【一级缓存】中,并从earlySingletonObjects【二级缓存】和singletonFactories缓存【三级缓存】中删除该对象。

从ConcurrentHashMap缓存中获取Bean的流程

        Spring在获取Bean实例时首先会尝试从一级缓存中获取。就会走二级缓存尝试获取,如果也没有,就会走三级缓存singletonFactories查找该Bean,如果找到则通过创建工厂来创建Bean实例对象。如果在走三级缓存singletonFactories中没有找到对应的Bean,则返回null,表示当前Bean没有被创建过。

SpringAOP

        面向切面编程【AOP-Aspect-Oriented Programming】是一种编程规范,他的目的是将与业务无关的代码从业务代码中分离出来,使代码更加的简洁,清晰。比如事务日志,异常处理等。在Spring中,AOP的主要作用就是在不改变原生类原有业务代码的情况下,给原生类进行功能的增强就是严格遵守了OCP原则(面向扩展开放,面向修改封闭)。

下面是一个简单的Spring AOP的Java代码例子:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
    
    @Before("execution(* com.example.service.impl.UserService.*(..))")
    public void beforeMethod() {
        System.out.println("beforeMethod:");
    }

}

        在这个例子中,我们定义了一个切面(LogAspect),使用@Aspect标注该类为切面,使用@Before注解标注切面执行的位置。在这里,@Before注解的参数表达式表示:对于com.example.service.impl包中的UserService的任何方法执行前,都会执行LogAspect类中的beforeMethod()方法。

        当UserService被调用时,Spring AOP框架将检查其是否匹配该切面(LogAspect),如果匹配,则会执行该切面方法beforeMethod()。在执行方法之前,可以做一些日志记录、安全检查、权限控制等操作,以提高应用系统的健壮性和安全性。

SpringAOP实现原理

        AOP的实现原理是基于动态代理,动态代理就是在运行时期,动态的为原生类生成一个代理类,该代理类中将原生类当作自己的成员变量,然后调用原生类的方法,然后在方法之前和方法之后加上需要增加的功能,从而实现对原生类的一个功能增强

        动态代理分为JDK动态代理和CGLIB动态代理,CGLIB代理需要导入相关的jar包,两者的区别是JDK动态代理要求目标类需要实现至少一个接口。而CGLIB则是基于继承进行代理,不需要实现任何接口。

        Spring中默认采用JDK动态代理(代理类和目标对象(原生类)实现了相同的接口),如果目标对象没有实现任何接口,Spring会选择CGLIB动态代理(代理类通过继承原生类),或者你可以在配置文件中强制指定使用CGLIB代理。

常用设计模式+代理模式【Design Pattern】【我终于懂设计模式了】_GuGuBirdXXXX的博客-CSDN博客

AOP的设计模式

        SpringAOP就是采用的是代理模式中的动态代理模式。 ​ 代理模式:动态的为原生类生成一个代理类,该代理类中将原生类当作自己的成员变量,然后调用原生类的方法,然后在方法之前和方法之后加上需要增加的功能,从而实现对原生类的一个功能增强。【遵守OCP原则

代理模式分为静态代理和动态代理。

静态代理:手写的增强代码可以理解为静态代理。

动态代理:JDK动态代理和cglib动态代理

JDK动态代理必须要实现接口】(给你生成一个兄弟,你的功能你兄弟也有,并且你兄弟还可以有其他的功能。)

必须需要实现接口--为什么要实现接口?

代理类和原生类需要实现相同的接口从而具有相同的功能,JDK动态代理就是通过接口创建一个代理类,然后具有相同的接口功能,然后进行增强。

cglib动态代理--采用继承的方式【给你生成一个儿子】

代理对象(类)去继承目标对象(需要代理的类),而且可以进行重写和功能增强。

AOP案例【事务】

下面我们使用AOP来做一个事务管理案例:在每个Service方法执行前后添加事务的代码

1.创建一个普通类,这个类的方法需要有事务

@Service
public class UserServiceImpl implements IUserService {
​
    public void insert() {
        System.out.println("UserServiceImpl.insert:保存User...");
    }
​
    public void delete() {
        System.out.println("UserServiceImpl.delete:删除User");
    }
}

2.创建一个切面类(加上注解@Aspect表示这是一个切面类,@Component表示将这个类交给Spring容器进行管理),这个类里面提供公共的业务代码,即:事务代码

@Component
@Aspect
public class TranscationManager {
​
    //定义切点,表达式作用于所有到service的所有的方法
    @Pointcut("execution(* cn.xxx.*.service.*.*(..))")
    public void pointcut(){}
​
    //前置通知 , 方法执行前执行,用来开启事务
    @Before("pointcut()")
    public void begin(JoinPoint joinPoint){
        System.out.println("TranscationManager.begin:开启事务...:");
    }
    //后置返回通知 ,方法正常返回后执行, 用来提交事务
    @AfterReturning("pointcut()")
    public void commit(){
        System.out.println("TranscationManager.commit:提交事物...");
    }
​
    //后置异常通知,方法出现异常调用,用来回滚事务
    @AfterThrowing(value = "pointcut()",throwing="e")
    public void rollback(JoinPoint joinPoint,Throwable e){
        System.out.println("TranscationManager.rollback:回滚事物咯...:"+e.getMessage());
    }
​
    //后置通知,不管方法会不会出现异常,都会执行,用来关闭资源
    @After("pointcut()")
    public void close(){
        System.out.println("TranscationManager.close:关闭连接...");
    }
​
​
    //环绕通知,拦截原始类的类的方法,可以通过 joinPoint调用原始的类
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){   
        return null;
    }
​
}

3.配置Spring支持AOP注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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-4.0.xsd
       http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!--开启IOC注解支持-->
    <context:component-scan base-package="cn.xx" />
    <!--开启AOP注解支持,默认使用jdk动态代理,如果指定 proxy-target-class=true 则实例CGLIB代理-->
    <aop:aspectj-autoproxy />
</beans>

4.测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application_aop.xml")
public class AopTest {
​
    @Autowired
    private IUserService userService ;
​
    @Test
    public void testAop(){
        userService.insert();
        System.out.println("=====================================");
        userService.delete();
    }
}

控制台效果

TranscationManager.begin:开启事务…: UserServiceImpl.insert:保存User… TranscationManager.commit:提交事物…

TranscationManager.close:关闭连接…

TranscationManager.begin:开启事务…: UserServiceImpl.delete:删除User… TranscationManager.commit:提交事物… TranscationManager.close:关闭连接…

Spring AOP里面的几个名词的概念:

(1)连接点(Join point):指程序运行过程中所执行的方法。在Spring AOP中,一个连接点总代表一个方法的执行。

(2)切面(Aspect):被抽取出来的公共模块,可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合,一个切面可以由多个切点和通知组成。

在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现。

(3)切点(Pointcut):切点用于定义 要对哪些Join point进行拦截。

切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add、search。annotation方式可以指定被哪些注解修饰的代码进行拦截。

(4)通知(Advice):指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。

(5)目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。

(6)织入(Weaving):通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。

(7)引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

说一下定义切面相关的注解

@Aspect切面:用来类上,代表这个类是一个切面

@Pointcut切点:定义切点 = cn.xx.service.*,在那个业务上运行

@Before:前置通知,在目标方法运行之前运行

@After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)

@AfterReturning:返回通知,在目标方法正常执行返回之后运行

@AfterThrowing:异常通知,在目标方法出现异常以后运行

@Around:环绕通知

Spring框架用到了那些设计模式?

1.单例模式:Spring中的Bean默认是单例模式,只创建一次实例且在整个应用中共享。

2工厂模式:Spring中使用BeanFactory或者ApplicationContext作为工厂,生成指定Bean的实例,并交给Spring容器进行管理。

3代理模式:AOP底层实现就是用的代理模式,Spring通过代理模式实现对目标方法的增强。

等多种模式

单例bean(singleton)与原型bean(prototype)【多例】的区别

        Bean默认是单例模式(singleton)【Spring为什么将bean默认设计成单例模式?--为了提高性能,减少bean对象的创建,能够快速从缓存中获取到bean,减少jvm垃圾回收所带来的新性能损耗】,在处理多次请求的时候Spring容器只会实例化一个bean对象,后续的请求都会公用这个bean对象。这个对象会保存在一个map里面,当有请求来的时候会先去缓存(map)中查找是否存在,如果有就直接使用,如果没有就实例化一个对象。所以这是单例的,对于原型bean(prototype多例)来说,每次请求都会创建一个新的实例化对象bean

img

img

单例模式-懒汉模式【双重校验锁-解决线程安全问题和效率低】

img

单例模式-饿汉模式

img

测试类

img

什么是反射?【SpringAOP】

        反射就是在程序运行的时候,通过获取类的字节码文件动态地去获取类中的信息(类的信息,方法信息,构造器信息,字段等信息),并映射成一个个对象。

以下是Java代码举例:

// 通过全限定路径名获取类的Class对象
Class<?> clazz = Class.forName("java.lang.String");

// 创建一个对象
Object obj = clazz.newInstance();

// 获取方法并调用
Method method = clazz.getMethod("equals", Object.class);
boolean result = (boolean) method.invoke(obj, "test");

// 获取字段并设置值
Field field = clazz.getDeclaredField("value");
field.setAccessible(true);
char[] newValue = {'t', 'e', 's', 't'};
field.set(obj, newValue);

// 获取构造函数并创建实例
Constructor<?> constructor = clazz.getConstructor(String.class);
Object newObj = constructor.newInstance("test");

        这段代码通过反射机制获取String类的Class对象,然后通过newInstance方法创建一个字符串对象。接着获取equals方法并调用,设置字段value的值,以及获取构造函数并创建实例。这些操作都是在程序运行时进行的,可以根据不同的需求进行动态地操作对象。

为什么需要使用反射?

        反射是框架的灵魂,增加了程序的灵活性和减少了代码的重复性,避免将程序写死在代码里(解决硬编码问题)

举一个具体的例子:

假设我们需要设计一个通用的日志模块,可以记录调用类的名称、方法名称和参数,我们可以使用反射机制来实现这个功能。

如果不使用反射,我们需要在每个类中手动的记录方法的调用信息,而这将非常麻烦。比如下面的例子:

public class MyService {
    public void doSomething(Object arg) {
        System.out.println("MyService.doSomething(" + arg + ")");
        // ...
    }
}

如果我们同时存在多个类似的方法,将会极大地增加维护的复杂度。使用反射,我们只需要在日志模块中预定义好调用方法的信息,具体的调用过程可以通过反射机制来完成,代码如下所示:

public class LogUtil {
    public static void log(Class<?> clazz, Method method, Object... args) {
        String msg = clazz.getName() + "." + method.getName() + "(";
        for (int i = 0; i < args.length; i++) {
            msg += args[i];
            if (i < args.length - 1) {
                msg += ", ";
            }
        }
        msg += ")";
        System.out.println(msg);
    }
}

public class MyService {
    public void doSomething(Object arg) {
        LogUtil.log(MyService.class, getClass().getMethod("doSomething", Object.class), arg);
        // ...
    }
}

通过使用反射,我们可以减少代码的重复性和维护性,并且提高了程序的灵活性。同时,这种方式也增加了程序的复杂度,所以需要根据具体的场景来权衡使用反射的好处和风险。

Spring AOP 和 AspectJ的区别

springAOP 是spring支持的面向切面AOP 编程

AspectJ是一个面向切面的框架,语法简单。它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

然后SpringAOP就借助了AspectJ的语法风格开发出了属于自己的aspectJ。

什么是Spring Bean?

        Spring Bean是Spring中最基本的组成单元(也是非常重要的部分),官方文档中的解释:在Spring中,构成应用程序主干【我们的程序是由一个个Bean构成的】,由Spring容器进行管理的对象称为Bean

Spring的Bean懒加载和非懒加载有什么区别

Spring默认是单例的然后是迫切加载

懒加载:Spring容器启动时不会进行创建,需要使用bean对象的时候才创建,执行效率降低了,但节省资源,但不利于提前发现错误。

非懒加载,也叫迫切加载,Spring容器启动时就创建bean对象,执行效率提高了,但消耗资源,但有利于提前发现错误。

spring中默认是迫切加载,即在项目启动时,spring会扫描注解下的所有bean对象注入到IOC容器中进行管理

如果需要懒加载,可以使用@Lazy注释或者xml中配置属性default-lazy-init="true"

实例化和初始化的区别

类的实例化:是指创建一个对象的过程,可以通过new和通过反射机制等方式实现。这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。

类的初始化:指的是在对象实例化后,进行一些必要的设置和处理的过程在Spring中,Bean的初始化阶段是在实例化和属性赋值之后进行的。在这个阶段,静态的(变量,方法,代码块)会被执行。同时在会开辟一块存储空间用来存放静态的数据。初始化只在类加载的时候执行一次

Spring Bean的生命周期?

从宏观的角度来说,Bean的什么周期可以分为五个阶段:

  1. Bean的实例化(实例化对象)

  2. Bean的属性注入(对象的属性注入)

  3. Bean的初始化

  4. Bean的就绪

  5. Bean的销毁(Spring容器关闭时,容器中的Bean对象会被销毁,释放所占有的资源)

把对象交给Spring容器进行管理的注解有那些?

@Controller @Service @Component @Repository(持久化层注解) @Bean @RestController=@Controller+@ResponseBody

@ResponseBody的作用:一般是方法上获取或者类上,将方法的返回值(java对象)转换成json格式的数据

json格式的好处就是可以直观的看到返回的java对象中的数据

不用@responseBody的话返回的就是一个Object对象

Spring的依赖注入bean的方式有哪些

1.注解注入:@Autowired @Resources

@Autowired注解和@Resource(通过名字byName进行注入)注解的作用相同,

@Autowired按照byType(通过类型)注入,如果@Autowired想使用名称可以结合@Qualifier注解进行使用

2.构造方法注入

3.通过setter方法注入

Spring 注入 Bean 的七种方式code小生的博客-CSDN博客

@Autowired和 @Resource的区别?

@Autowired和@Resource都是在Spring框架下,用于实现依赖注入的注解。

Autowired默认是以类型进行注入的(byType)。

如果需要按照名字(byName,比如将名称为UserMapper01的bean注入匹配的话,可以使用@Qualifier注解与@Autowired结合使用。

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;

    // 业务方法
    // ...
}

@Resource默认是按照名字自动注入(byName)。

@Component
public class User {

    @Resource
    private UserService userService; 

    //其他属性和方法
    // ...
}

Bean的四种注册【创建】方式

Spring创建Bean的四种方式_快敲代码去的博客-CSDN博客

创建Bean其实就是创建对象。

方式一:普通注册方式,直接通过class注册

方式二:简单静态工厂方式注册

方式三:简单实例工厂方式注册

方式四:FactoryBean方式注册

Spring常用注解

创建对象(注入对象)的注解:@Controller(控制层的注解) @Service(业务逻辑层的注解) @Component(普通类的注解) @Repository(持久层的注解) - 要求:扫描包路径

注解注入的注解:@Autowired(先以类型进行匹配,类型相同再匹配变量名) @Resource(以名字进行匹匹配)

匹配请求的注解:@RequestMapping - 要求:开启Spring对Mvc的注解支持

响应json格式的注解:@ResponseBody -将当前方法的返回值转成json格式的字符串进行返回,而且不会经过视图解析器

@ResponseBody // 加了这个才不会进入视图解析器(前端配置文件springmvc.xml) 才不会加上前缀和后缀

// 作用:将当前方法返回值转换成json格式的字符串,并响应给调用方【如果是浏览器调用,就响应给浏览器】

// 如果是ajax异步调用,就会响应回调函数

要求:需要导入jackson的三个jar包 - 不导包会出现406

注解的注解,称为[元注解]

@Target(ElementType.TYPE)注解可以声明在哪些目标元素之前【类上,方法上】 @Retention(RetentionPolicy.RUNTIME)注解类的生命周期

Spring的Bean的作用域有那些?

Spring中为Bean定义了5种作用域,分别为Singleton(单例),prototype(多例),request(请求),session(会话)和global session(全局会话)。

singleton:单例模式,SpringIOC容器只会创建一个bean对象。

prototype:多例模式,每次请求访问spring容器时都会创建一个新的bean实例对象。

request:每次HTTP请求都会创建一个新的Bean,作用域绑定到HTTP请求的生命周期

session:在一个HTTP Session中,为每个会话创建一个bean实例。与请求作用域的范围不同,会话作用域在整个会话的生命周期中持续存在,直到会话销毁

globalSession: 同一个全局session共享一个Bean,一般用于portlet应用环境,该作用域仅适用于webApplicationContext环境.

BeanFactory和ApplicationContext有什么区别

BeanFactory【Bean工厂】

IOC容器的核心接口,是一个Bean的工厂采用的是延迟加载,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)进行生产并且管理的。

ApplicationContext接口是BeanFactory接口的子接口采用的是迫切加载

除了继承BeanFactory中所有管理bean的方法,还拥有环境、国际化、资源、事件传播等服务相关的接口

BeanFactory是延迟加载,ApplicationContext【应用上下文】是迫切加载

BeanFactory延迟加载:

使用时才进行加载。

延迟加载的好处:当为了追求传输效率就会需要什么就再去创建什么时,就会体现出延迟加载的好处

ApplicationContext【应用上下文】迫切加载

在启动项目创建上下文环境的时候就创建了Bean对象

配置一:让所有Bean都变成懒加载,配置default-lazy-init="true"

配置二:让其中一个指定的Bean变成懒加载,配置lazy-init="true"

Spring是如何管理事务的?

Spring事务管理详解程序员老石的博客-CSDN博客spring事务管理

Spring管理事务的两种方式---1编程式事务管理,2声明式事务管理

1编程式事务管理

        指的是直接使用事务管理的API手动写代码来控制事务的启动、提交或回滚等操作,代码的耦合度比较高,使用的很少。

2声明式事务管理

        声明式事务是通过注解@Transactional实现,无需任何配置。原理就是利用Spring框架的AOP的方式自动完成开启事务提交事务,回滚事务,回滚的异常默认是运行时异常,可以通过rollbackFor的属性指定回滚的异常类型

Spring事务传播(机制)有哪些?

Spring事务失效的场景

事务+分布式事务【2023面试题】_seata面试题_MXin5的博客-CSDN博客

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mxin5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值