时间:2025-09-27 11:10
人气:
作者:admin
@Component
public class UserService {
private final UserDao userDao;
// Spring 会自动注入 UserDao
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
@Component
public class UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
装配:把bean之间的依赖关系配置清楚
自动装配:让Spring容器自己根据规则把依赖对象诸如进去,而不是开发者手动写。这样可以减少配置,提升开发效率。
【注意】
在 Spring Boot 和注解驱动里,主要用:
@Autowired(默认 byType,可结合 @Qualifier 指定名字)
@Resource(JDK自带,默认 byName,找不到再 byType)
@Inject(JSR-330 标准注解,行为类似 @Autowired)
// 接口
public interface UserDao {
void save();
}
// 第一个实现
@Component("mysqlUserDao")
public class UserDaoMysql implements UserDao {
@Override
public void save() {
System.out.println("保存到 MySQL 数据库");
}
}
// 第二个实现
@Component("oracleUserDao")
public class UserDaoOracle implements UserDao {
@Override
public void save() {
System.out.println("保存到 Oracle 数据库");
}
}
getBean() 拿到的都是同一个对象。getBean(),都会新建一个对象。@Component
@Scope("prototype") // 或 "request" / "session"
public class UserService {}
Session = 会话
在 Web 里,Session 表示 用户从打开浏览器访问网站,到关闭浏览器/超时退出的这一段交互过程。
Spring的单例Bean不是天然线程安全的。是否有问题,取决于Bean是否有状态。
解决办法:
@Service
public class UserContextService {
private ThreadLocal<String> currentUser = new ThreadLocal<>();
public void setUser(String user) {
currentUser.set(user); // 每个线程有自己独立的副本
}
public String getUser() {
return currentUser.get(); // 取的就是当前线程的值
}
public void clear() {
currentUser.remove(); // 防止内存泄漏(重要!)
}
}
两个或多个Bean互相依赖,形成“死循环”,只在单例下会出现。如果是prototype的话会无限套娃。
那Spring能解决哪些情况?
singletonObjects存放 完全创建好的单例 Bean(成品)。
以后再来 getBean(),直接从这里拿。
earlySingletonObjects存放 提前曝光的单例 Bean(早期引用)。
这里的 Bean 已经实例化,但可能还没注入属性、没初始化。
如果别的 Bean 需要,可以先用它占个坑,后面再补全。
singletonFactories存放 ObjectFactory(对象工厂),可以生成早期 Bean。
为什么要三级?因为有些 Bean 可能需要 AOP 代理,必须等真正用到的时候才生成代理对象 → 所以放工厂。
???? 三级缓存的目的:保证 Bean 能被提前曝光,同时还能支持 代理增强(AOP)。
A是一个Bean,它需要被代理(比如加了@Transactional),如果只用二级缓存,把原始的A暴露出去,B会拿到“未代理的A”。后面再生成代理对象的时候,B里注入的还是旧的原始对象-->功能失效。
三级缓存的作用:三级缓存存放的是 ObjectFactory,不是 Bean 本身。当别的 Bean 需要 A 时,可以通过这个工厂去获取“真正的早期对象”:
如果 A 不需要代理,就返回原始对象;
如果 A 需要代理,就在这里生成代理对象返回。
所以,二级缓存只能解决循环依赖,但无法保证AOP代理生效。
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
field.setAccessible(true); field.set(userService, userDaoImpl);
面向切面编程,简单说就是把一些业务逻辑中的相同的代码抽取到一个独立的模块中,让业务逻辑更加清爽。AOP可以将遍布应用各处的功能分离出来形成可重用的组件。
AOP的核心就是动态代理,如果实现了接口就用JDK动态代理,否则用CGLIB动态代理。
核心概念:
//1.业务类(目标对象)
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void addUser() {
System.out.println("执行 UserService.addUser()");
}
}
//2.切面类(ASpect+advice)
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
// 切点:匹配 UserService 的 addUser 方法
@Pointcut("execution(* com.example.demo.UserService.addUser(..))")
public void addUserPointcut() {}
// 前置通知
@Before("addUserPointcut()")
public void beforeAddUser() {
System.out.println("Before: 准备添加用户");
}
// 后置通知
@After("addUserPointcut()")
public void afterAddUser() {
System.out.println("After: 添加用户完成");
}
}
//3.启动类(开启AOP)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy // 开启 AOP 支持
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
UserService userService = context.getBean(UserService.class);
userService.addUser();
}
}
//结果
Before: 准备添加用户
执行 UserService.addUser()
After: 添加用户完成
下一篇:JUC: 线程锁
Hutool 的 `TimedCache` 到期会自动清理吗?