[Spring]Annotation-based container configuration_AnotationBasis_02

本文深入解析Spring框架中关键注解的使用方法与应用场景,包括@Component、@Configuration、@Bean等,探讨它们之间的区别及如何利用这些注解进行依赖注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@Component @Repository @Service @Controller

本质上以上四类没有区别,但是在你定义应用分层的时候 aop的时候 这些不同语义的注解 就有不同意义,未来Spring是否会给予这些语义注解特殊的意义还并不清楚


在配置java类里面注解@ComponentScan(basePackages = "com.domain")的情况下,会自动扫描com.domain包里的所有类,扫描里面带有@Componet,@Repository,@Service..等等注解的类

该句等同于在xml配置文件里面加入了

<context:component-scan base-package="com.javaconfig.domain"/> 

同时@ComponentScan可以在加载包的时候,排除掉一些类,或者包含某些类

@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))

表示:扫描org.example包下所有带有@Component衍生的各种语义注解,同时包含该包下的匹配 .*Stub.*Repository类型的(正则匹配),排除扫描Repository.class

base-package的配置,从表面意思来看这是个“基本包”,表示下面的过滤路径是基于这个包的

更多过滤方式参考spring-reference


@Component

扫描一个类注册一个bean

@Component
public class Football {
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "football";
	}
}
这就注册了一个名为football的bean 命名规则为首字母小写,如果起始的两个字母都是大写,则不小写首字母。

@Component
public class XYclass {

}
注册了一个XYclass的bean
XYclass xyz = ctx.getBean("XYclass",XYclass.class);
获得该bean



@Bean

以类似工厂的方式产生bean,注意@Bean所在类必须要被spring所扫描到

Tool1:

public class Hammer implements Tool {
	public void doWork() {
		System.out.println("using hammer to smash something");
	}
}
Tool2:

public class Scissors implements Tool{
	public void doWork() {
		System.out.println("using scissors to cut something");
	}
}

ToolBox:

public class ToolBox {
	private Tool[] tools;

	public ToolBox(Tool t1, Tool t2) {
		tools = new Tool[] { t1, t2 };
	}

	public Tool[] getTools() {
		return tools;
	}

	public void setTools(Tool[] tools) {
		this.tools = tools;
	}

}



BeanFactory:
@Component
public class ToolFactory {

	@Bean
	@Qualifier("t1")
	public Tool hammer() {
		return new Hammer();
	}

	@Bean
	@Qualifier("t2")
	public Tool scissors() {
		return new Scissors();
	}

	@Bean
	public ToolBox toolBox(@Qualifier("t1") Tool t1, @Qualifier("t2") Tool t2,
			@Value("this is just tool box") String description) {
		System.out.println(description);
		return new ToolBox(t1, t2);
	}
}

Unit Test:

	@Test
	public void beanTest() {
		Tool t1 = ctx.getBean("hammer",Tool.class);
		Tool t2 = ctx.getBean("scissors",Tool.class);
		t1.doWork();
		t2.doWork();
		ToolBox tb = ctx.getBean("toolBox",ToolBox.class);
		
	}

@Bean产生的bean命名方式 就是方法名字 同时@Bean也会按照类型注入参数


@Configuration 和 @Component 的区别 

@Configuration也是从@Component 衍生出来的注解,但是@Component 相比@Configuration不同点在于,@Component 并没有通过CGLIB 增强调用所注册bean的内部方法和fields调用的。还有@Configuration内的@bean注解的方法 不能为final,因为这需要被CGLIB所重写。


@Component @Repository @Service @Controller等注解 也提供了 生成bean非默认名称方式命名

@Service("myMovieLister")
public class SimpleMovieLister { // ...
}
比如该bean名称就会是myMovielister



@Scope

提供了在注册bean的时候申明该bean的lifecycle

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}


@Configuration

是类级注册bean的注解,该类自身被注册为bean,并且其内的@Bean注解的方法 都会注册为bean。并且顾名思义,这就是一个配置类。

@Configuration
public class AppConfig {
	
	@Bean
	public Foo foo(Bar bar){
		return new Foo(bar);
	}
	
	@Bean
	public Bar bar(){
		return new Bar();
	}
}

这里申明的都是单例实类

Unit Test:

@Test
	public void beanTest2() {
		Foo foo = ctx.getBean("foo", Foo.class);
		Foo foo2 = ctx.getBean("foo", Foo.class);
		Bar bar = ctx.getBean("bar", Bar.class);
		Bar bar2 = ctx.getBean("bar", Bar.class);
		assertEquals(foo, foo2);
		assertEquals(bar, bar2);
		assertEquals(foo.getBar(), foo2.getBar());
	}

测试通过


@Import

导入配置类

@Configuration @Import(ConfigA.class) public class ConfigB {
@Bean
public B b() { return new B();
} }



@ImportResource

导入配置文件

@ImportResource(value = "TestCase_01.xml")



@Condtional

@Conditional注解主要用在以下位置:
类级别可以放在注标识有@Component(包含@Configuration)的类上
作为一个meta-annotation,组成自定义注解
方法级别可以放在标识由@Bean的方法上
如果一个@Configuration的类标记了@Conditional,所有标识了@Bean的方法和@Import注解导入的相关类将遵从这些条件。

简单的说就是根据某些情况选择合适的类进行注册

首先需要编写条件,条件类必须实现Condtion接口的matches方法

判断是否为Mac os

public class MacOsCondition implements Condition{
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		
		return context.getEnvironment().getProperty("os.name").contains("Mac OS X");
	}
}
判断是否为Linux

public class LinuxCondition implements Condition{
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// TODO Auto-generated method stub
		return context.getEnvironment().getProperty("os.name").contains("Linux");
	}
}

注册bean的时候或者注册整个类的时候用上

	@Bean(name = "systemService")
	@Conditional(LinuxCondition.class)
	public SystemService linuxService() {
		return new LinuxService();
	}

	@Bean(name = "systemService")
	@Conditional(MacOsCondition.class)
	public SystemService macService() {
		return new MacOsService();
	}

根据操作系统不同选择不同的服务

Unit Test:

@Test
	public void conditionTest() {
		SystemService service = ctx.getBean("systemService",SystemService.class);
		service.service();
	}



@Profile

如果在开发时进行一些数据库测试,希望链接到一个测试的数据库,以避免对开发数据库的影响。

该注解提供了灵活的在多个副本之间切换的方法

注册了一个用于开发测试的mockDBService和一个release的MysqlDBService

	@Bean(name = "dbService")
	@Profile("dev")
	public DBService mockDBService(){
		return new MockDBService();
	}
	
	@Bean(name = "dbService")
	@Profile("rc")
	public DBService MysqlDBService(){
		return new MysqlDBService();
	}
	

在进行单元测试的时候

使用@ActiveProfiles(value = "dev")激活所要使用的副本

Unit Test:

@Test
	public void profileTest() {
		DBService dbService = ctx.getBean(DBService.class);
		System.out.println(dbService.count("select * from users"));
	}

也可以使用java激活方式

ctx.getEnvironment().setActiveProfiles("dev");
		ctx.register(AppConfig.class);
		ctx.refresh();




C:\Users\nine8\.jdks\openjdk-23.0.2\bin\java.exe "-javaagent:E:\JAVA\.idea\IntelliJ IDEA 2024.3.4.1\lib\idea_rt.jar=57488" -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath E:\JAVA\Java_Web\Demo001\demo1\target\test-classes;E:\JAVA\Java_Web\Demo001\demo1\target\classes;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\springframework\boot\spring-boot-starter-web\3.4.3\spring-boot-starter-web-3.4.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\springframework\boot\spring-boot-starter\3.4.3\spring-boot-starter-3.4.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\springframework\boot\spring-boot\3.4.3\spring-boot-3.4.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\springframework\boot\spring-boot-autoconfigure\3.4.3\spring-boot-autoconfigure-3.4.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\springframework\boot\spring-boot-starter-logging\3.4.3\spring-boot-starter-logging-3.4.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\ch\qos\logback\logback-classic\1.5.16\logback-classic-1.5.16.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\ch\qos\logback\logback-core\1.5.16\logback-core-1.5.16.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\apache\logging\log4j\log4j-to-slf4j\2.24.3\log4j-to-slf4j-2.24.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\apache\logging\log4j\log4j-api\2.24.3\log4j-api-2.24.3.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\org\slf4j\jul-to-slf4j\2.0.16\jul-to-slf4j-2.0.16.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-maven-3.8.8\mav_repo\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;E:\JAVA\Java_Web\Maven\apache-maven-3.8.8-bin\apache-mave
03-18
<think>好的,我现在需要帮助用户排查Spring Boot 3.4.3项目中Maven依赖冲突或配置问题,特别是涉及logback-classic、log4j-to-slf4j、jul-to-slf4jjakarta.annotation-api这几个依赖的情况。首先,我应该回忆一下常见的依赖冲突排查方法,以及Spring Boot在依赖管理上的机制。 首先,用户提到的是依赖冲突,这通常发生在不同的库引入了相同组件的不同版本,或者有重复的桥接器,比如多个日志框架的桥接包。Spring Boot通过spring-boot-starter-parent提供了依赖管理,可以统一版本,避免冲突。但有时候,当用户引入第三方库时,可能会覆盖这些版本,导致问题。 接下来,用户提到的logback-classic、log4j-to-slf4j、jul-to-slf4j都是日志相关的依赖。logback-classic是SLF4J的默认实现,而log4j-to-slf4jjul-to-slf4j则是将Log4j2Java Util Logging的日志转到SLF4J的桥接器。如果同时存在多个桥接器或者版本不一致,可能会导致栈溢出或者日志无法输出。例如,如果同时引入了log4j-to-slf4jlog4j-core,就会产生冲突,因为log4j-to-slf4j是用来桥接到SLF4J的,而log4j-core是Log4j2本身的实现,两者不能共存。 然后是jakarta.annotation-api,这个依赖在Java EE迁移到Jakarta EE后包名从javax变更为jakarta,如果项目中混合使用了新旧版本的注解,可能会导致类找不到的问题。比如,Spring Boot 3.x开始使用Jakarta EE 9+,所以如果用户引入的库还在使用javax.annotation-api,就会有问题。 现在,我需要一步步引导用户检查这些点。首先,确认是否正确使用了Spring Boot的依赖管理,即parent可能的dependencyManagement。然后,使用Maven命令分析依赖树,找出冲突的库。对于日志依赖,要检查是否有重复的桥接器或冲突的实现库。对于jakarta.annotation-api,要确保所有相关依赖都升级到Jakarta版本。 用户提供的引用信息中,引用[1]提到spring-boot-starter-parent的作用,引用[2]指出dependencyManagement中添加spring-cloud-dependencies来解决版本冲突,这可能对用户有帮助。引用[3]中的异常可能与配置有关,但用户当前的问题更偏向依赖冲突,所以暂时可能不直接相关。引用[4]的版本列表可能用于核对其他组件的兼容性,但用户目前的问题集中在Spring Boot 3.4.3,需要确认这些组件是否支持该版本。 可能的解决方案包括:使用mvn dependency:tree查看依赖树,排除重复或冲突的依赖,确保使用Spring Boot管理的版本,检查jakarta.annotation-api是否被多个库引入不同版本,或者是否有旧的javax库残留。另外,检查是否有不必要的日志桥接器,如同时存在log4j-to-slf4jlog4j-core。 最后,生成相关问题,可能涉及如何查看依赖树、排除依赖的方法、Spring Boot依赖管理机制、日志框架冲突解决等。</think>### 排查Spring Boot 3.4.3依赖冲突与日志配置问题 #### 1. **确认依赖管理配置** Spring Boot通过`spring-boot-starter-parent`统一管理依赖版本。请确保`pom.xml`中已正确声明父级依赖: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.3</version> </parent> ``` 若项目同时使用Spring Cloud,需在`<dependencyManagement>`中声明对应版本(参考引用[2]): ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.1</version> <!--Spring Boot 3.4.3兼容的版本 --> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` --- #### 2. **分析依赖冲突** 使用Maven命令生成依赖树,定位冲突: ```bash mvn dependency:tree -Dverbose -Dincludes=org.slf4j,ch.qos.logback,org.apache.logging.log4j,jakarta.annotation ``` - **日志框架冲突**: - `logback-classic`是SLF4J的默认实现。 - `log4j-to-slf4j``jul-to-slf4j`是日志桥接器,需确保**不共存**于以下组件: - 其他日志实现(如`log4j-core`) - 重复的桥接器(如`jcl-over-slf4j`) - **若存在冲突**,在依赖声明中添加`<exclusion>`: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> </exclusion> </exclusions> </dependency> ``` - **`jakarta.annotation-api`冲突**: Spring Boot 3.x默认使用Jakarta EE 10,若依赖中混用`javax.annotation-api`会导致`ClassNotFoundException`。需检查所有第三方库是否兼容Jakarta EE,必要时升级或排除旧版本。 --- #### 3. **验证日志配置** 在`src/main/resources`中添加`logback-spring.xml`,配置日志输出格式级别: ```xml <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration> ``` --- #### 4. **常见问题与解决** - **栈溢出异常**:通常由**循环桥接**(如同时引入`log4j-to-slf4j``slf4j-log4j12`)导致。需排除冲突的桥接器。 - **类未找到异常**:检查`jakarta.annotation-api`与其他Jakarta/Javax组件的版本一致性。 - **启动失败**:若出现`Failed to start bean 'documentationPluginsBootstrapper'`(引用[3]),可能是Swagger与Spring Boot版本不兼容,尝试升级`springdoc-openapi`至最新版本。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值