目录
1: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. 使用线程安全类
对于必须共享的状态,使用AtomicInteger
、ConcurrentHashMap
等线程安全的容器。
3. 同步控制
通过synchronized
或ReentrantLock
限制并发访问。
2:什么是aop,有哪些使用场景
AOP(面向切面编程,Aspect-Oriented Programming) 是一种编程范式,核心思想是将横切关注点(Cross-Cutting Concerns)从业务逻辑中分离,通过动态代理或字节码增强技术,将通用功能(如日志、事务、权限校验等)以“切面”的方式织入到目标代码中,从而实现代码的模块化和解耦。
AOP 的核心概念
-
切面(Aspect):封装横切关注点的模块(例如日志切面、事务切面)。
-
连接点(Join Point):程序执行过程中的某个点(如方法调用、异常抛出)。
-
通知(Advice):切面在连接点执行的具体逻辑(如
@Before
、@After
、@Around
)。 -
切点(Pointcut):通过表达式匹配需要织入通知的连接点(例如
execution(* com.example.service.*.*(..))
)。 -
织入(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) {
// 查询数据库
}
7 参数校验
@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. 异常未正确抛出或被捕获
场景:事务方法抛出异常但未触发回滚。
原因:
-
默认只有未检查异常(
RuntimeException
和Error
)会触发回滚。 -
若捕获异常未重新抛出,或抛出了检查异常(如
IOException
),事务不会回滚。
5:spring的bean生命周期
-
Bean定义解析 → BeanFactoryPostProcessor处理 → 实例化 → 属性注入 → Aware接口回调 →
BeanPostProcessor前置处理 → 初始化方法 → BeanPostProcessor后置处理 → Bean就绪 →
容器关闭 → 销毁方法