Spring面试题

对Spring的理解?

spring是一个框架,一个容器,还是一个生态。

控制反转(IOC)

Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。
aop是ioc整体流程中的一个扩展点。

1,先通过createBeanFactory创建出一个bean工厂(DefaultListableBeanFactory)

BeanDefinition保存着bean的定义信息
BeanFactoryPostProcessor中将比如xml中数据源的${username}信息替换为真正的值

2,开始循环创建对象,因为容器中的bean默认都是单例的,所以优先通过getBean从容器中查找
3,如果找不到,再通过createBean方法,通过反射的方式创建对象,一般情况下使用的是无参的构造方法(getDeclaredConstructor)

//获取Class对象
Class clazz = Class.forName("包名+类名");
//获取构造器
Constructor constructor = clazz.getDeclaredConstructor();
//创建对象
Object obj = constructor.newInstance();

4,进行对象的属性填充populateBean
5,进行其他的初始化操作(InitializingBean)

控制反转:

1,之前是需要什么对象,程序员自己来控制new出对象,而有了ioc容器之后,就变成由ioc容器来控制
2,有了ioc之后,依赖的对象直接由ioc容器创建后注入到对象中,由主动创建变成被动接受,这就是控制反转。

容器:

存放对象,使用map结构来存储,在spring中一般存在三级缓存,singletonObjects存放完整的bean对象,整个bean的生命周期,从创建到使用到销毁的过程全部都是由容器来管理。

依赖注入(DI)

Spring使用Java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。

面向切面编程(AOP)

在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事务管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码冗余太多,因此需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去,aop正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原有的业务逻辑基础之上做一些增强功能即可。
bean的创建过程中在BeanPostProcessor的后置处理方法中进行aop实现,aop本身就是一个扩展功能。

Pointcut(切点):被增强的方法
Advice(通知/ 增强):封装增强业务逻辑的方法
Aspect(切面):切点+通知
Weaving(织入):将切点与通知结合的过程

spring中aop的底层是怎么实现的

Spring中AOP底层的实现是基于动态代理进行实现的。
常见的动态代理技术有两种:JDK的动态代理和CGLIB。
两者的区别如下所示:
1,JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2,Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法进行增强,但是因为采用的是继承,所以该类或方法最好不要声明为final,对于final类或方法,是无法继承的。

Spring如何选择是用JDK还是cglib?

1,当bean实现接口时,会用JDK代理模式
2,当bean没有实现接口,会用cglib实现
3,可以强制使用cglib

JDK的动态代理

基于接口的动态代理技术

public interface TargeInterface {
    public void save();
}

public class Targe implements TargeInterface{
    public void save() {
        System.out.println("save...");
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        final Targe targe = new Targe();
        Enhance enhance = new Enhance();
        enhance.beforeEnhance();
        //返回值 就是动态生成的代理对象
        TargeInterface proxy = (TargeInterface)Proxy.newProxyInstance(
                targe.getClass().getClassLoader(),//目标对象类加载器
                targe.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
                new InvocationHandler() {
                    //调用代理对象的任何方法,实质执行的都是invoke方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object invoke = method.invoke(targe, args);
                        return invoke;
                    }
                }
        );
        //调用代理对象的方法
        proxy.save();
        enhance.afterEnhance();
    }
}

执行结果
beforeEnhance
save...
afterEnhance
cglib 的动态代理

基于父类的动态代理技术

public class ProxyTest {

    public static void main(String[] args) {

        //目标对象
        final Target target = new Target();

        //增强对象
        final Advice advice = new Advice();

        //返回值 就是动态生成的代理对象  基于cglib
        //1、创建增强器
        Enhancer enhancer = new Enhancer();
        //2、设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                advice.before(); //执行前置
                Object invoke = method.invoke(target, args);//执行目标
                advice.afterReturning(); //执行后置
                return invoke;
            }
        });
        //4、创建代理对象
        Target proxy = (Target) enhancer.create();

        proxy.save();
    }
}

执行结果:
前置增强....
save running.....
后置增强....

Spring事务的传播机制?

Spring的事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法对事务的态度。
举例:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

spring事务传播特性?

required需要:默认事务传播特性

如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。适用于增删改查
解释:A调用B,B发现A有事务,就加入A的事务;B发现A没有事务就新建事务

-- 要挂内外都挂
begin;
	update table1 set score=100 where id=1;
	融入外界事务
		update table1 set score=100 where id=3;
	update table1 set score=100 where id=2;
commit;
requires_new:

新建事务,如果当前在事务中,把当前事务挂起。适用于日志。

-- 内外互不影响
连接1,begin;
	update table1 set score=100 where id=1;
	连接1挂起
		连接2,update table1 set score=100 where id=3;
		连接2释放
	连接1恢复
	update table1 set score=100 where id=2;
commit;
nested嵌套:

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,就创建一个新事务。

-- 内部受外部影响:外部出错,内部也挂
-- 外部独立:内部出错,外部回滚到保存点,外部不受影响
begin;
	update table1 set score=100 where id=1;
	savepoint a;  -- 保存点
		update table1 set score=100 where id=3;
	rollback to a;
	//以上事务如果存储问题回滚到保存点
	update table1 set score=100 where id=2;
commit;
supports支持:

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。适用于查询

not_supported:

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

mandatory强制性的:

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

never:

以非事务方式执行,如果当前存在事务,则抛出异常。

nested与requires_new的区别

REQUIRES_NEW是内部新建一个事务与外部事务无关,内部或外部事务回滚,不会影响另一个事务
NESTED在外部事务基础上,内部嵌套一个事务,也就是使用savepoint,外部事务回滚,内部也就回滚了;内部事务回滚,会回滚到savepoint所以外部事务没有影响。

nested与required的区别

REQUIRED情况下,内部事务会融入外部事务,无论内外出现异常都会回滚。
NESTED在外部事务基础上,内部嵌套一个事务,也就是使用savepoint,外部事务回滚,内部也就回滚了;内部事务回滚,会回滚到savepoint所以外部事务没有影响。

事务隔离级别

spring事务隔离级别就是数据库的隔离级别,当数据库和spring代码中隔离级别不同时,以spring的配置为主。

事务隔离级别中文名产生的问题解析默认级别
ISOLATION_READ_UNCOMMITTED读未提交脏读、不可重复读、幻读事务a,转账了,但是没有提交;事务b查询到的是转账后的数据Oracle默认
ISOLATION_READ_COMMITTED读已提交不可重复读、幻读事务a,转账了,但是没有提交;事务b查询到的是转账前的数据;事务a提交后,事务b查到的转账后数据事务a提交了事务,事务b读取到事务a提交前后的不同数据
ISOLATION_REPEATABLE_READ可重复读幻读事务a读取范围数据比如age>18读取到一条数据,事务b新增数据,事务a再次读取范围数据,读取到两条数据MySQL默认
ISOLATION_SERIALIZABLE串行化解决所有的问题事务a读取过程中,事务b不能读写;所有的事务排队执行

Spring事务实现方式原理

在使用spring框架时,可以有两种事务的实现方式,一种是编程式事务,通过代码来控制事务的处理逻辑,一种是声明式事务,通过@Transactional注解来实现。
其实事务操作本来应该由数据库来控制,为了方便用户进行业务逻辑操作,spring对事务功能进行了扩展实现,一般我们很少用编程式事务,更多的是添加@Transactional注解来进行实现,当添加此注解之后事务的自动功能就会关闭,由spring框架来帮助进行控制。
事务操作是AOP的一个核心体现,当方法添加@Transactional注解之后,spring会基于这个类生成一个代理对象,会将这个代码对象作为bean,当使用这个代理对象的方法的时候,如果有事务处理,那么会先把事务自动提交给关闭,然后去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会直接提交,如果出现任何异常情况,那么直接进行回滚操作,当然用户可以控制对哪些异常进行回滚操作。

Spring事务什么情况下会失效

1,数据库引擎不支持事务:这里以
MySQL为例,其MyISAM引擎是不支持事务操作的,InnoDB才是支持事务的引擎,一般要支持事务都会使用InnoDB。
2,bean没有被Spring管理。如果此时把 @Service注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被Spring 管理了,事务自然就失效了。
3,方法不是public的:@Transactional只能用于public的方法上,否则事务不会失效。
4,自身调用问题

@Service
public class MyService {
    @Autowired
    private MyRepository repository;
   
    @Transactional
    public void doTransactionalWork() {
        // 这个方法会被事务管理
        repository.save(someEntity);
        // 内部调用,事务不生效
        doNonTransactionalWork();
    }
}

5,数据源没有配置事务管理器

<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
    <property name="username" value="username"/>
    <property name="password" value="password"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- 启用注解驱动事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

6,异常在方法内部通过try…catch处理掉了

@Service
public class MyService {
    @Autowired
    private MyRepository repository;
    
    @Transactional
    public void doTransactionalWork() throws Exception {
        // 这个方法会被事务管理
        repository.save(someEntity);
        // 抛出受检查异常,事务会回滚
        throw new Exception("Some checked exception");
    }
    
    @Transactional
    public void doAnotherTransactionalWork() {
        try {
            // 这个方法会被事务管理
            repository.save(anotherEntity);
            // 抛出运行时异常,事务会回滚
            throw new RuntimeException("Some runtime exception");
        } catch (RuntimeException e) {
            // 在这里捕获了异常,事务不会回滚
            // 可能导致事务失效
            // 可以在这里记录日志或者进行其他处理
        }
    }
}

7,异常类型错误:事务默认回滚的是:RuntimeException

spring事务是如何回滚的?

1,启动事务获取数据库连接,关闭自动提交功能,开启事务
2,执行具体的sql逻辑操作
3,在操作过程中如果执行失败了,通过completeTransacationAfterThrowing来完成事务回滚操作,回滚具体逻辑通过doRollBack方法实现的,实现的时候也会先获取数据库连接,通过连接对象来回滚
4,在执行成功后,通过completeTransacationAfterReturning来完成事务的提交操作,提交的具体逻辑通过doCommit方法来实现,实现的时候也是先获取数据库连接,通过连接对象来提交
5,当事务执行完毕之后,需要清理相关事务信息cleanupTransacationInfo

Spring框架中的单例bean是线程安全的吗?

Spring中的bean对象默认是单例的,Spring框架并没有对单例bean进行任何多线程的封装处理。
如果bean是有状态的,就需要开发者来保证线程安全,最浅显的解决办法就是将bean的作用域由“singleton”变更为“prototype”,这样每次请求bean对象就相当于是创建新的对象来保证线程安全。
有状态就是有数据存储的功能,无状态就是不会存储数据,我们的controller、service、dao本身并不是线程安全的,只是调用里面的方法,那么多线程调用一个实例方法,会在内存中复制遍历,这时自己线程的工作内存,才是最安全的。
因此在进行使用时,不要在bean中声明任何有状态的实例变量或类变量,如果必须如此,也推荐使用ThreadLocal把变量变成线程私有,如果bean的实例变量或类变量需要多个线程之间共享,就只能使用synchronized、lock、cas等实现线程安全。

//简单的服务类 UserService,它包含一个计数器属性 counter 和一个方法 incrementCounter(),每次调用该方法都会将计数器加一
public class UserService {
    private int counter = 0;
    public void incrementCounter() {
        counter++;
    }
    public int getCounter() {
        return counter;
    }
}
//Spring 的配置文件中声明 UserService 为单例 bean
<bean id="userService" class="com.example.UserService" scope="singleton"/>
//测试类 Main,并启动多个线程来并发调用 incrementCounter() 方法
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    userService.incrementCounter();
                }
            });
            thread.start();
        }
        // 等待所有线程执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Counter value: " + userService.getCounter());
    }
}

Spring中所使用的设计模式

1,工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象
2,单例模式:Bean默认为单例模式
3,策略模式:XmlBeanDefinitionReader、PropertiesBeanDefinitionReader
4,代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
5,模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。RestTemplate,JmsTemplate,JpaTemplate
6,适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,MethodBeforeAdviceAdapter
7,观察者模式:Spring事件驱动模型、listener
8,桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库
9,装饰者模式:依赖注入时,实例化后的对象被封装在BeanWrapper对象中

Spring中所使用的设计模式

Spring中Bean的五大作用域

可以通过@Scope注解指定

@Scope(scopeName="prototype")
public interface UserDao{}

Singleton(单例):默认的作用域

在整个应用程序中只创建一个Bean实例。
所有对该Bean的请求都将返回同一个实例。
Bean是全局共享的,适用于无状态的Bean或者需要在多个组件之间共享数据的情况

@Component
public class CalculatorService {
    
    public int add(int a, int b) {
        return a + b;
    }
    public int subtract(int a, int b) {
        return a - b;
    }
    // 其他方法...
}

Prototype(原型)

每次对Bean的请求都会创建一个新的实例。
没有共享状态,适用于有状态的Bean或者需要频繁创建新实例的情况

@Component
public class ShoppingCart {
    private List<String> items = new ArrayList<>();
    public void addItem(String item) {
        items.add(item);
    }
    public void removeItem(String item) {
        items.remove(item);
    }
    public List<String> getItems() {
        return items;
    }
    // 其他方法...
}

Request(请求)

在每个HTTP请求中创建一个新的Bean实例。
每个请求的Bean实例对于该请求是唯一的。
仅在Web应用程序的上下文中有效,适用于处理HTTP请求的控制器或服务

Session(会话)

在每个用户会话(Session)中创建一个新的Bean实例。
对于同一用户的所有请求,都将使用相同的Bean实例。
仅在Web应用程序的上下文中有效,适用于保存用户特定的数据或状态。

Global Session(全局会话)

在整个应用程序的全局会话中创建一个新的Bean实例。所有的session共享一个bean实例。

spring bean的生命周期

Spring Bean的生命周期如下图所示:
在这里插入图片描述
--------------------------------1,创建bean--------------------------------
1,实例化Bean
createBeanInstance(),通过反射的方式进行对象的创建。此时的创建只是在堆空间中申请空间,属性都是默认值

public class Dog{
    public Dog() {
        System.out.println("1,实例化");
    }
 }

2,设置对象自定义属性(依赖注入)
populateBean()填充bean方法、循环依赖
实例化后的对象被封装在BeanWrapper对象中,紧接着Spring根据BeanDefinition中的信息以及通过BeanWrapper提供的设置属性的接口完成依赖注入。

public class Dog{
    public void setName(String name) {
        System.out.println("2,依赖注入");
        this.name = name;
    }
 }

3,处理Aware相关接口,容器对象属性赋值
实现BeanNameAware,BeanFactoryAware,BeanClassLoaderAware接口,invokeAwareMethod()完成BeanName、BeanFactory、BeanClassLoader容器对象属性设置
如果对象中需要引用容器内部的对象,那么需要调用aware接口的子类方法来进行统一的设置,可以认为是依赖注入的拓展

public class Dog implements BeanNameAware,BeanFactoryAware {
	//实现 BeanNameAware 接口的 bean 可以感知到自己在容器中的名称
	@Override
    public void setBeanName(String s) {
        System.out.println("3,Aware接口执行了,BeanName "+s);
    }
    
    //实现 BeanFactoryAware 接口,bean 可以获取到当前所属的 BeanFactory 对象,从而可以访问容器的其他 bean 以及容器本身的功能
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("3,Aware接口执行了,beanFactory "+beanFactory);
    }
 }

4,bean的增强器(BeanPostProcessor)的前置处理方法
bean依赖注入之后,初始化之前进行前置的处理工作,对bean进行自定义处理的扩展点
比如ApplicationContextPostProcessor设置ApplicationContext、Environment、ResourceLoader等对象

public class DogProcessor implements BeanPostProcessor {
	//以用于实现各种预处理、扩展等功能
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if("dog".equals(beanName)){
            System.out.println("4,前置处理器BeanPostProcessor before方法执行");
        }
        return bean;
    }
}

5,检查是否实现InitializingBean接口,重写afterPropertiesSet方法
提供了一种与 Spring 容器无关的初始化方式,同时也能与其他初始化方式init-method或@PostConstruct兼容

public class Dog implements InitializingBean {
	@Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5,InitializingBean 接口执行了,检查是否是InitializingBean的子类");
    }
 }

6,检查是否配置有自定义的init-method方法
如果当前Bean对象定义了初始化方法,那么在此处调用初始化方法。

public class Dog{
	@PostConstruct
    private void init(){
        System.out.println("6,初始化");
    }
 }

7,bean的增强器(BeanPostProcessor)的后置处理方法
对生成的Bean对象进行后置的处理工作。
在 bean 初始化完成后对 bean 进行自定义处理的扩展点
spring的aop就是在此处实现的,AbstractAutoProxyCreator

public class DogProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if("dog".equals(beanName)){
            System.out.println("7,前置处理器BeanPostProcessor after方法执行 AOP操作");
        }
        return bean;
    }
}

-------------------------------2,使用bean--------------------------------
8,使用bean

public class DogUse {
    @Autowired
    private Dog dog;
	@PostConstruct
    public void use() {
        dog.getName();
        System.out.println("8,使用bean");
    }
}

-------------------------------3,销毁bean--------------------------------
9,实现DisposableBean接口
为了方便对象的销毁,在此处调用销毁的回调接口,方便对象进行销毁操作。

public class Dog implements DisposableBean {
	@Override
    public void destroy() throws Exception {
        System.out.println("9,DisposableBean接口执行了,销毁前可做回收资源等操作");
    }
}

10,销毁bean

public class Dog{
    @PreDestroy
    private void destoryMethod(){
        System.out.println("10,销毁bean");
    }
 }   

嵌入式服务器?

在springboot框架中,内嵌了Tomcat,在之前的开发中,每次写好代码之后必须要将项目部署到一个额外的web服务器中,只有这样才能运行,这样比较麻烦,而在springboot框架中内置了tomcat.jar,来通过main方法启动容器,达到一键开发部署的方式,不需要额外任何其他操作。

spring的优势?

1,spring通过DI,AOP来消除样板式代码来简化Java开发
2,spring框架之外还存在一个构建在核心框架之上的生态圈,它将spring扩展到不同领域,web服务、rest、nosql等
3,低侵入式设计,代码污染极低
4,spring的ioc容器降低了业务对象替换复杂性,由容器来创建、销毁,提高了组件之间的解耦
5,spring的面向切面aop支持将一些通用任务:安全、事务、日志等进行集中式处理,从而更好的复用
6,spring的orm和dao提供了与第三方持久层框架的良好整合,简化了底层数据库访问
7,spring的高度开放性,并不强制依赖于spring,开发者可以自由选用spring框架部分或全部

spring bean循环依赖?

Spring解决循环依赖是通过Singleton(单例)三级缓存,对应的三级缓存如下所示:
1,当依赖注入通过属性注入、setter方法注入spring可以解决循环依赖
2,通过构造方法注入spring也不能解决循环依赖,因为解决循环依赖的思路是实例化与初始化分开执行,提前暴露不完整的bean对象
3,Prototype(原型)多例循环依赖spring也不能解决循环依赖,因为每次调用生成新的bean不走缓存

Singleton(单例)三级缓存

缓存map名称作用
一级缓存singletonObjects单例池; 缓存已经经历了完整声明周期, 已经初始化完成的bean对象
二级缓存earlySingletonObjects缓存早期的bean对象(生命周期还没有走完,未依赖注入的代理对象或普通对象)
三级缓存singletonFactories缓存的是ObjectFactory, 表示对象工厂,存放获取对象的lambda表达式
三级缓存解决循环依赖问题:
1,a对象b属性,b对象属性a来实例化+初始化
2,a对象实例化:a三级缓存lambda表达式
3,a对象b属性实例化:b三级缓存lambda表达式
4,b对象属性a依赖注入: 根据三级缓存lambda表达式,生成a二级缓存未依赖注入的bean,
	也就是提前暴露不完整的bean,如果有aop返回代理bean,如果没aop就返回普通bean
5,b对象和a属性都有了:b一级缓存完整的bean
6,a对象b属性都有了:a一级缓存完整的bean
7,一旦循环依赖被解除,Spring会清理二级缓存、三级缓存中的对象

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

两级缓存,能否解决循环依赖?

没有代理对象的时候,当不使用aop的时候,两个缓存map就可以解决循环依赖问题。
对象在什么时候被暴露出去或者被其他对象引用时没办法提前确定好的,所以只有在被调用的那一刻才可以进行原始对象还是代理对象的判断,使用lambda表达式类似于一种回调机制,不暴露的时候不需要调用执行,当需要被调用的时候,才真正执行lambda表达式,来判断返回的到底是代理对象还是原始对象。

BeanFactory与FactoryBean有什么区别?

相同点:BeanFactory与FactoryBean都是用来创建bean对象的

BeanFactory

使用BeanFactory创建对象的时候,必须严格遵循bean的生命周期流程,BeanFactory通常通过XML配置文件或Java注解进行配置,定义和管理Bean对象。

FactoryBean接口

如果想要简单的自定义某个对象的创建,同时创建完成的对象想交给spring来管理,那么就需要实现FactoryBean接口了。
1,实现FactoryBean接口

@Component
public class MyBeanFactory implements FactoryBean<MyBean> {
    @Override
    public MyBean getObject() throws Exception {
        // 在这里执行创建和配置 MyBean 对象的逻辑
        MyBean myBean = new MyBean();
        myBean.setName("Example");
        myBean.setValue(123);
        return myBean;
    }
    @Override
    public Class<?> getObjectType() {
        return MyBean.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

2,@Autowired注解将MyBean注入到其他类

@Component
public class MyBeanConsumer {
    private MyBean myBean;
    @Autowired
    public MyBeanConsumer(MyBean myBean) {
        this.myBean = myBean;
    }
    // 使用 myBean 对象进行其他操作
}

BeanFactory和ApplicationContext有什么区别?

1,BeanFactory:BeanFactory在启动的时候不会去实例化Bean,当从容器中拿Bean的时候才会去实例化;
2,ApplicationContext:ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;

在Spring中有几种配置Bean的方式?

1,基于XML的配置
2,基于注解的配置
3,基于Java的配置

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值