spring中常见面试题

目录

1:spring中bean是单例吗,是线程安全的吗,如果不是安全的,请举一个例子,并告诉我要怎么做才能安全

2:什么是aop,有哪些使用场景

3:Java的动态代理和反射

4:spring中事务失效的场景有哪些,怎么处理

5:spring的bean生命周期


1:spring中bean是单例吗,是线程安全的吗,如果不是安全的,请举一个例子,并告诉我要怎么做才能安全

如果Bean包含可修改的成员变量(状态),多个线程同时操作这个Bean时,可能导致数据不一致,例如

@Service
public class UnsafeCounterService {
    private int count = 0; // 可修改的成员变量

    public void increment() {
        count++; // 非原子操作,线程不安全
    }

    public int getCount() {
        return count;
    }
}

当多个线程同时调用increment()时,count++(实际是读取-修改-写入三个操作)可能导致值覆盖,最终结果不符合预期。

那么,如何保证线程安全?

1. 无状态设计(推荐)

避免使用成员变量,改为局部变量或方法参数。无状态Bean天然线程安全。

2. 使用线程安全类

对于必须共享的状态,使用AtomicIntegerConcurrentHashMap等线程安全的容器。

3. 同步控制

通过synchronizedReentrantLock限制并发访问。

2:什么是aop,有哪些使用场景

AOP(面向切面编程,Aspect-Oriented Programming) 是一种编程范式,核心思想是将横切关注点(Cross-Cutting Concerns)从业务逻辑中分离,通过动态代理或字节码增强技术,将通用功能(如日志、事务、权限校验等)以“切面”的方式织入到目标代码中,从而实现代码的模块化和解耦。

AOP 的核心概念

  1. 切面(Aspect):封装横切关注点的模块(例如日志切面、事务切面)。

  2. 连接点(Join Point):程序执行过程中的某个点(如方法调用、异常抛出)。

  3. 通知(Advice):切面在连接点执行的具体逻辑(如@Before@After@Around)。

  4. 切点(Pointcut):通过表达式匹配需要织入通知的连接点(例如execution(* com.example.service.*.*(..)))。

  5. 织入(Weaving):将切面逻辑插入目标代码的过程(编译期、类加载期或运行期)。

AOP 的典型使用场景

1. 日志记录

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("调用方法: " + methodName);
    }
}

2. 事务管理,

@Transactional
public void transferMoney(Account from, Account to, double amount) {
    // 业务逻辑
}

3. 权限校验,

@Aspect
@Component
public class SecurityAspect {
    @Before("@annotation(RequiresAdmin)")
    public void checkAdminPermission(JoinPoint joinPoint) {
        if (!currentUser.isAdmin()) {
            throw new SecurityException("无权限操作!");
        }
    }
}

4. 性能监控,

@Aspect
@Component
public class PerformanceAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        System.out.println("方法执行耗时: " + duration + "ms");
        return result;
    }
}

5. 异常处理,

@Aspect
@Component
public class ExceptionAspect {
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void handleException(Exception ex) {
        // 发送邮件或记录错误日志
        System.err.println("发生异常: " + ex.getMessage());
    }
}

6. 缓存管理,

@Cacheable(value = "users", key = "#userId")
public User getUserById(Long userId) {
    // 查询数据库
}

参数校验

@Aspect
@Component
public class ValidationAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void validateArgs(JoinPoint joinPoint) {
        for (Object arg : joinPoint.getArgs()) {
            if (arg == null) {
                throw new IllegalArgumentException("参数不能为null!");
            }
        }
    }
}

3:Java的动态代理和反射

动态代理是一种在运行时动态生成代理类对象的技术,无需手动编写代理类代码。代理对象可以拦截对目标方法的调用,并在调用前后添加额外逻辑(如日志、事务等)。

反射是Java在运行时动态获取类信息并操作类属性、方法、构造器的能力。通过反射可以绕过编译时的类型检查,直接操作对象。

4:spring中事务失效的场景有哪些,怎么处理

1. 自调用问题(方法内部调用)

场景:在同一个类中,一个非事务方法调用另一个带有@Transactional注解的方法,事务不生效。
原因:Spring事务基于动态代理实现,自调用不走代理对象,导致事务拦截失败。

2. 异常未正确抛出或被捕获

场景:事务方法抛出异常但未触发回滚。
原因

  • 默认只有未检查异常RuntimeExceptionError)会触发回滚。

  • 若捕获异常未重新抛出,或抛出了检查异常(如IOException),事务不会回滚。

5:spring的bean生命周期

  • Bean定义解析 → BeanFactoryPostProcessor处理 → 实例化 → 属性注入 → Aware接口回调 → 
    BeanPostProcessor前置处理 → 初始化方法 → BeanPostProcessor后置处理 → Bean就绪 → 
    容器关闭 → 销毁方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值