本文最后更新于 2025年6月22日 晚上
IoC与DI IoC(控制反转)和di(依赖注入) 。 即通过注解的方式,将业务流程中对对象的创建和管理的 控制权转移给Spring框架来进行控制。Spring框架将对象创建与业务解耦,使编程人员能够更加专注于业务实现,而不再需要着重考虑使用对象的生命周期。
整体流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 类扫描(@ComponentScan ) ↓ BeanDefinition注册 ↓ Bean实例化(构造器 or 工厂方法) ↓ 依赖注入(@Autowired 、@Value ) ↓ 初始化(@PostConstruct 、InitializingBean、initMethod) ↓ AOP代理(若满足切面条件) ↓ 应用运行中 ↓ 销毁(@PreDestroy 、DisposableBean、destroyMethod)
Bean Bean 是 被Spring 管理的对象的 统称
Bean 配置 1.@Component - @Autowire( 类注解 + 自动注入)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Component public class UserDao { public void getUser () ( System.out.println("Hello Spring" ) ) }@Component public class UserService { @Autowired UserDao ud ; public void getUser () { ud.getUserDao(); } }@SpringBootApplication Test01{ @Test public void test () { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (SpringConfig.class); IUserService userService = context.getBean("userService" ,IUserService.class); userService.getUser(); } }
以及Component 衍生注解
@Service
@Repository
@Configuration
2 @Bean
通过方法将对象注册到Spring
必须放置在配置类里面
@Configuration和@Component 注解下 @bean行为的区别
@Configuration 类下的 @Bean 方法调用(代理增强 )当一个类被 @Configuration 注解标记时,Spring 会用 CGLIB 动态代理 增强这个类,形成一个代理对象。
这个代理对象在内部维护了 Spring 容器的上下文;
当你在 @Configuration 类内部调用 @Bean 方法时,其实是通过代理对象去调用的;
代理对象会拦截这个调用,先检查 Spring 容器中是否已经存在对应的单例 Bean,如果存在直接返回,否则创建并注册。
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 java 复制编辑@Configuration public class AppConfig { @Bean public A a () { return new A (b()); } @Bean public B b () { return new B (); } }
当 a() 方法调用 b() 时,其实走的是 AppConfig 代理对象的 b() 方法,因此它会走 Spring 容器逻辑,而不是直接的方法调用。
@Component 类下的 @Bean 方法调用(无代理增强 )当你的类只是用 @Component 标注时,Spring 不会 使用 CGLIB 增强它。
此时:
这个类就是一个普通的被 Spring 托管的对象;
其中的 @Bean 注解方法在容器初始化时仍然会被扫描并注册为 Bean(主要取决于你是不是把 @Component 类纳入了 @ComponentScan 范围);
但是:方法之间的调用是普通的 Java 方法调用,没有代理,没有容器拦截 ;
所以在 @Component 下,如果你自己在一个 @Bean 方法中调用另一个 @Bean 方法,其实就是直接调用普通方法 —— 它不会走 Spring 容器逻辑,也就没有 Bean 生命周期、依赖注入、单例复用等特性。
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 java 复制编辑@Component public class MyComponent { @Bean public A a () { return new A (b()); } @Bean public B b () { return new B (); } }
此时 b() 方法就是普通的对象方法,不经过 Spring 容器,你实际上会创建了两个 B 实例:
一个被 Spring 容器注册;
一个直接被 a() 方法内部调用创建。
3 @Import (对类注解)
在当前配置类中手动导入其他 Bean 定义。
1 声明导入配置类或普通类
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration @Import(OtherConfig.class) public class MainConfig { }@Configuration public class OtherConfig { @Bean public MyBean myBean () { return new MyBean (); } }
实现ImportSelector接口,传入字符数组导入相关类
1 2 3 4 5 6 7 8 9 10 11 12 public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String []{"com.example.MyService" , "com.example.MyRepository" }; } }@Configuration @Import(MyImportSelector.class) public class MainConfig { }
实现ImportBeanDefinitionRegistrar接口,进行注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions ( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition beanDefinition = new RootBeanDefinition (MyBean.class); registry.registerBeanDefinition("myBean" , beanDefinition); } }@Configuration @Import(MyRegistrar.class) public class MainConfig { }
Bean的实例化 Bean 实例化时默认选择 无参构造器进行构造
如果 不存在无参构造器,只存在一个构造器,则使用该构造器构造,并自动注入参数对象
如果 存在多个构造器, 则会报错
@Component 类注册定义模式 只是把类结构信息注册进去,实例化时反射创建实例,依赖注入走标准构造器或属性。
@Bean 工厂方法定义模式 , Spring 注册了 “如何执行这个方法去获得实例” 的元数据,不直接关心类信息,而是把方法当成实例来源。
通过可以bean注解 来指定 具体使用的构造方法
FactoryBean 接口
通过实现FactoryBean 接口 来将一个工厂类注入到Spring中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class OrderService implements FactoryBean { @Override public Object getObject () throws Exception{ return new UserService (); } @Override public Class<?>getObjectType(){ return UserService.class; } }@SpringBootApplication public class C3IocApplication { ConfigurableApplicationContext ioc = SpringApplication.run(C3IocApplication.class) System.out.println(iot.getBean("&orderService" )); }
依赖注入 @Autowired 自动装配 作用域 字段,构造方法,参数,函数
多个类型注入 ,优先按类型,再按名字
@Primary 同一个类,优先匹配这个注解的实现
@Qualifier 指定别名匹配
@Autowired(required=false)
Autowired 的注入位置
在 对象声明位置进行自动注入
1 2 @Autowired UserService userservice;
在 构造器处进行自动注入,显式指定创建对象使用的构造器
1 2 3 4 5 6 7 8 class AutoService { @Autowired AutoService(ProductService productservice){ this .productservice = productservice; } }
在 传入参数时 使用Autowired 进行自动注入,设置required 允许 该参数传入为空
1 2 3 4 5 6 class AutoService { @Autowired AutoService(@Autowired(required=false) ProductService productservice){ this .productservice = productservice; } }
在函数定义上增加Autowired , 在 该方法的对象创建后自动执行,Spring不关注方法的返回值
1 2 3 4 5 6 7 8 9 10 11 class AutoService { @Autowired AutoService(ProductService productservice){ this .productservice = productservice; } @Autowired public void getAutoService () { System.out.println("fetch the AutoService" ); } }
其他自动注入注解 @Inject
1 2 3 4 5 6 <dependency> <groupId>jakarta.inject</groupId> <artifactId>jakarta.inject-api</artifactId> <version>2.0 .1 </version> </dependency>
@Resource
先根据名字查找,再根据类型查找
值注入 @Value 向声明的变量进行赋值
1 2 @Value("18") private Integer age ;
@PropertySource() 指定提取属性的位置 (默认从application.properties中查询)
1 2 3 @Value("${person.age}") @PropertySource("person.property") private Integer age ;
spel 向 容器赋值
1 2 3 4 @Value("#{{'语文':'90','数学':'88'}}") private Map<String,Integer> score ;@Value("#{{'音乐,电影,话剧'}}") private List<String> habbies ;
@Order 定义注入同一个接口实现下的不同实现类的加载顺序。 值小的先被加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @SpringBootTest(classes = TestOrder.class) public class TestOrder { @Bean @Order(1) public A a () { return new A (); } @Bean @Order(0) public B b () { return new B (); } @Test public void test (@Autowired List<I> i ) { System.out.println(i); } }public class B implements I , Ordered { public B () { System.out.println("B" ); @Override public int getOrder () { return 0 } } }
@Dependson 定义不同类之间的加载先后关系
1 2 3 4 5 6 7 8 9 10 11 12 13 @SpringBootDependsOnTest(classes = TestDependson.class) public class TestDependson { @Bean @DependsOn("d") public C c () { return new C (); } @Bean public D d () { return new D (); } }
@Lazy 被@Lazy注解的对象直到 被使用时才会被加载
@Scope 定义对象的创建(单例或非单例)
1 2 3 4 5 6 7 8 9 @Bean @Scope("Singleton") UserService userservice@Bean @Scope("prototype") UserSercice userservice
@Conditional 动态决定某个bean 是否生效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @SpringBootTest(classes = TestConditional.class) public class TAestConditional { @Bean @Conditional(MyCondition.class) public ConditionalService conditionalService () { return new ConditionalService (); } }public class MyCondition implements Condition { @Override public boolena matches (CondtionContext context , AnnotatedTypeMetadata metadata) { return false ; } }
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public interface IDB { public void connection () {}; }public class MySqlDB implements IDB { @Override public void connection () { System.out.println("连接MySql" ); } }public class OracleDB implements IDB { @Override public void connection () { System.out.println("连接Oreacle" ); } }@SpringBootTest(classes = TestDB.class) public class TestDB { @Bean public IDB mysqlDB () { return new MySqlDB (); } @Bean public IDB oracleDB () { } }public class MysqlCondition implements Condition { @Override public boolean matches (ConditionContext context , AnnotatedTypeMetadata metadata) { try { context.getClassLoader().loadClass("MySQLDBConnection_class_path" ) return true ; }catch {ClassNotFoundException e}{ return false ; } return false ; } }public class MysqlCondition implements Condition { @Override public boolean matches (ConditionContext context , AnnotatedTypeMetadata metadata) { try { context.getClassLoader().loadClass("OracleDBConnection_class_path" ) return true ; }catch {ClassNotFoundException e}{ return false ; } return false ; } }
Bean的生命周期 配置编码阶段 对需要注入容器的类进行注解
1 2 3 4 5 6 7 8 @Component class BaseService { @Bean public UserService getuserservice () { return new UserService (); } }
在Springboot 应用中注入依赖
1 2 3 4 5 6 7 8 9 @SpringBootApplication class Worker { @Autowired UserService userservice public void ExecService (UserService userserive) { userservice.getservice(); } }
启动Spring 程序 加载Spring 容器
实例化托管对象
根据注解注入对象到业务流程中
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class UserService implements InitializingBean { @Override public void afterPropertiesSet () throws Exception { System.out.println("初始化" ); } }public class UserService { @PostConstruct public void afterPropertiesSet () throws Exception { System.out.println("初始化afterPropertiesSet" ); } }public class UserService { public void init () throws Exception { System.out.println("init方法初始化" ); } }@Bean(initMethod="init") public UserService userServie () {return new UserService ();}
放入 Map<beanName,bean对象>
spring容器.getBean(”beanName”)
spring容器关闭 , bean 销毁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class UserService implements DisposableBean { @Override public void destory () throws Exception { System.out.println("接口实现销毁" ); } }public class UserService { @PreDestory public void afterPropertiesSet () throws Exception { System.out.println("PreDestory销毁" ); } }public class UserService { public void destory () throws Exception { System.out.println("destory销毁" ); } } }@Bean(destoryMethod="destory") public UserService userService () {return new UserService ();}
循环依赖 在Spring中可能存在编码时A和B俩个bean 需要相互调用的问题。而在Springboot中这种循环依赖的对象创建会导致直接报错
打开允许循环依赖对象创建设置
1 spring.main.allow-circular-references=true
代码设计上进行控制
1 2 a. 把依赖的方法写在本类中 b. 使用组合,将bean A 和 bean B 都由bean C 进行调用
延迟注入
1 2 a. 添加需要依赖的构造函数参数 b. 添加lazy注解
AOP(面向切面编程) 如果说 AOP 是 数据与业务分离,那么AOP的设计则是”运维代码(日志统计,观测,测试)” 与业务的分离。通过定义AOP类向目标业务类中注入切面类,来完成处理过程中对业务流程执行的拦截,获取想要的数据以及进行请求/响应改造过程。
定义业务类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Service public class UserService { public void add () { System.out.println("增加" ); } public void delete () { System.out.println("删除" ); } public void update () { System.out.println("修改" ) } public void query () { System.out.println("查询" ) } }
定义切面类 1 2 3 4 5 6 7 8 @Around("execution(* class_path.*(..))") public Object log (ProceedingJoinPoint pjp) throws Throwable { long begin = System.currentTimeMillis(); Object result = pjp.proceed(); long end = System.currentTimeMillis(); System.out.println("用时:" +(end-begin)+"ms" ); return result; }
@EnableAspectJAutoProxy 使用SpingBoot 自动添加 使能
术语 目标对象 :要增强的对象
切面 :要增强的代码放入那个类叫做切面类 , @Aspect
通知 :用来增强目标对象的函数,叫做通知
切点 :通知的目标方法,使用切点表达式指定
连接点 :通知和目标方的桥梁
顾问 :存储切入类对象的对象
织入 : 切入类中的方法 组合到目标对象的过程
通知 @Before 定义在目标方法之前执行的方法
1 2 3 4 5 6 7 8 9 10 11 @Aspect @Component public class LogAspect { @Before("execution(*.com.example.*(..))") public void before (JoinPoint joinpoint) { String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); System.out.println("当前方法是" +methodName+"参数" +Array.toString(Args)); } }
@After 定义在目标方法之后执行的方法
@AfterReturning 定义在目标方法返回前执行的方法
@AfterThrowing 定义在目标方法异常时(Catch中)会执行的方法
前置—执行方法—返回通知—后置通知
抽取切点表达式 通过注解 @Pointcut 创建一个函数,后续都可以使用该函数作为切点表达式
1 2 3 4 5 @Pointcut("execution( public int (target_method_path))") public void myPoint () {}; 后续 切面方法可以使用@After("myPoint") 进行注解
通配符规则
1 2 3 4 5 "execution(* public void target_method_path)" "within( package_name.class_name" )"@annotation(annotation_name)"