@AutoWried依赖注入失败

  • @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
    • 1、共同点:两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
    • 2、不同点
      • (1)@Autowired:@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

      • @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

    • (2)@Resource:@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

    • 注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。
  • @Resource装配顺序:
    • ①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
    • ②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
    • ③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
    • ④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
    • @Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入
<think>我们正在处理一个关于OpenRewrite的自定义Recipe创建任务,目标是将Spring中使用@Autowired注解的字段注入转换为构造器注入。步骤分析:1.理解需求:将类中所有使用@Autowired注解的字段注入移除,改为通过构造器注入。同时,需要生成一个包含这些字段的构造器。2.在OpenRewrite中,我们通过实现Recipe接口来创建自定义的转换规则。主要工作是在JavaIsoVisitor中重写相应的方法来修改AST(抽象语法树)。3.具体转换步骤:a.寻找目标:查找所有被@Autowired注解的字段(注意:可能包括private、protected等,但构造器注入通常使用private final,不过转换后字段可以不变,但注入方式改变)。b.移除字段上的@Autowired注解。c.收集这些字段的信息(类型和名称)。d.为当前类创建一个构造器(如果不存在),该构造器参数为这些字段,并在构造器体内将参数赋值给字段。e.同时,在构造器上添加@Autowired注解(因为构造器注入也需要@Autowired,但在Spring4.3以后,如果只有一个构造器可以省略,但为了兼容性可以添加)。f.注意:如果类中已经存在构造器,需要判断是否已经有一个构造器包含了所有需要注入的字段?如果没有,则需要添加一个新的构造器,或者修改现有构造器?但通常转换是创建一个新的构造器,并且如果已有构造器,我们可能需要考虑是否覆盖或者如何处理?这里我们简化处理:只处理没有显式定义构造器的情况,或者如果已经有构造器,则添加一个新的构造器(但这样可能产生多个构造器,Spring会选择哪个?)。因此,更安全的做法是:如果类中没有显式定义任何构造器,则创建一个构造器;如果已经有构造器,则修改其中一个构造器(比如无参构造器)为有参构造器?但这样可能破坏原有逻辑。另一种做法:只处理没有构造器的情况,或者要求用户先确保没有自定义构造器?但这不是很友好。考虑到复杂性,我们这里采取以下策略:-只处理没有显式定义任何构造器的类(即只有默认构造器)。-我们创建一个新的构造器,包含所有被@Autowired注解的字段。-同时,移除这些字段上的@Autowired注解,并在新构造器上添加@Autowired注解(如果构造器只有一个,Spring会自动注入,但为了明确,我们加上)。4.实现步骤(在JavaIsoVisitor中):a.访问类声明(ClassDeclaration):-检查类中是否存在构造器(Constructor)。如果没有,则进行转换。-收集类中所有被@Autowired注解的字段(FieldDeclaration),并记录每个字段的类型和名称。-如果存在这样的字段,则:i.移除这些字段上的@Autowired注解(同时,如果字段有多个注解,需要保留其他注解)。ii.创建一个新的构造器,参数列表为这些字段(参数类型和字段类型相同,参数名为字段名),在构造器体内将每个参数赋值给对应的字段。iii.给这个构造器添加@Autowired注解(如果类中已经存在其他构造器,我们这里先不处理,所以只处理无显式构造器的情况)。iv.将新构造器添加到类体中。b.注意:如果类中没有任何被@Autowired注解的字段,则不需要做任何操作。5.需要考虑的细节:-字段可能有多个注解,移除@Autowired时不能影响其他注解。-字段可能有初始化表达式,我们不需要改变,因为构造器注入后,字段赋值会覆盖初始化表达式?但Spring的字段注入是在对象创建后通过反射设置,所以初始化表达式会先执行,然后被注入覆盖。而构造器注入是在对象创建时赋值,所以初始化表达式会被忽略(因为字段在构造器中重新赋值)。因此,为了保持行为一致,我们可以在构造器注入后,移除字段的初始化表达式吗?但这样可能不安全,因为有些初始化表达式可能是必要的(比如默认值)。所以,我们保留初始化表达式,但要注意:在构造器注入时,字段会被重新赋值,因此初始化表达式实际上会被覆盖。所以转换后行为一致。6.代码实现(伪代码):在自定义的Visitor中(继承自JavaIsoVisitor),重写visitClassDeclaration方法:```java@Overridepublic ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl,ExecutionContextexecutionContext) {classDecl= super.visitClassDeclaration(classDecl, executionContext);//检查类中是否有构造器booleanhasConstructor= classDecl.getBody().getStatements().stream().anyMatch(s-> sinstanceof J.MethodDeclaration &&((J.MethodDeclaration) s).isConstructor());if(hasConstructor){//如果有构造器,我们暂时不处理(根据我们的策略,只处理没有构造器的类)returnclassDecl;}//收集所有被@Autowired注解的字段List<FieldDeclaration> autowiredFields= newArrayList<>();for(Statementstatement :classDecl.getBody().getStatements()) {if(statementinstanceof FieldDeclaration){FieldDeclarationfield =(FieldDeclaration)statement;//检查字段是否有@Autowired注解booleanhasAutowired= field.getLeadingAnnotations().stream().anyMatch(anno-> "Autowired".equals(anno.getSimpleName()));if (hasAutowired) {autowiredFields.add(field);}}}if(autowiredFields.isEmpty()){return classDecl;}//创建构造器//1.构造器的参数列表:每个字段一个参数,类型和字段相同,名称和字段相同(注意:字段可能有多个变量声明,但我们通常一个字段声明一个变量,所以这里取第一个变量)List<J.MethodDeclaration>constructors =new ArrayList<>();for (FieldDeclarationfield :autowiredFields){//移除字段上的@Autowired注解field =(FieldDeclaration) newRemoveAutowiredAnnotation().visit(field,executionContext);//注意:这里我们只是移除了@Autowired,其他注解保留}//创建构造器参数List<Statement> parameters= newArrayList<>();List<Statement> assignments= newArrayList<>();for(FieldDeclaration field: autowiredFields) {//每个字段对应一个参数//字段可能有多个变量声明?但通常字段声明只有一个变量,所以我们取第一个if(!field.getVariables().isEmpty()){J.VariableDeclarations.NamedVariablevariable =field.getVariables().get(0);//参数类型TypeTreeparamType =field.getType();//参数名StringparamName= variable.getSimpleName();//创建参数parameters.add(newJ.VariableDeclarations(Tree.randomId(),//随机IDSpace.EMPTY,Markers.EMPTY,newJ.Identifier(Tree.randomId(),Space.EMPTY,Markers.EMPTY, paramName,null, null),paramType,null));//创建赋值语句:this.field= field;assignments.add(new J.Assignment(Tree.randomId(),Space.EMPTY,newJ.Identifier(Tree.randomId(),Space.EMPTY,Markers.EMPTY, "this."+ paramName,null,null),newJ.Identifier(Tree.randomId(),Space.EMPTY,Markers.EMPTY, paramName,null,null)));}}//创建构造器体J.Blockbody =new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY,assignments, Space.EMPTY);//创建构造器方法声明J.MethodDeclarationconstructor =new J.MethodDeclaration(Tree.randomId(),Space.EMPTY,Markers.EMPTY, //后面会添加@Autowired注解newJ.Identifier(Tree.randomId(),Space.EMPTY,Markers.EMPTY, classDecl.getSimpleName(), null, null),null,null,null,null,parameters,null,body,null);//给构造器添加@Autowired注解constructor= constructor.withLeadingAnnotations(Collections.singletonList(newJ.Annotation(Tree.randomId(),Space.EMPTY,Markers.EMPTY,newJ.Identifier(Tree.randomId(),Space.EMPTY,Markers.EMPTY, "Autowired",null,null),null)));//将构造器添加到类体中List<Statement> statements= newArrayList<>(classDecl.getBody().getStatements());statements.add(constructor);classDecl =classDecl.withBody(classDecl.getBody().withStatements(statements));returnclassDecl;}```同时,我们需要一个移除字段上@Autowired注解的Visitor(内部类):```javaprivate staticclass RemoveAutowiredAnnotationextends JavaIsoVisitor<ExecutionContext>{@Overridepublic FieldDeclaration visitFieldDeclaration(FieldDeclaration field, ExecutionContext executionContext){field =super.visitFieldDeclaration(field,executionContext);List<J.Annotation> annotations= field.getLeadingAnnotations();List<J.Annotation> newAnnotations =annotations.stream().filter(anno-> !"Autowired".equals(anno.getSimpleName())).collect(Collectors.toList());return field.withLeadingAnnotations(newAnnotations);}}```7.注意:以上代码是伪代码,实际OpenRewrite的AST操作需要使用其提供的API。具体实现时,需要创建合适的Tree节点,并注意Space和Markers的处理(为了保留格式)。8.另外,我们还需要考虑测试:编写转换前后的Java代码,验证转换是否正确。9.由于OpenRewrite的API可能较为复杂,建议参考官方文档和示例Recipe。10.以上策略只处理了没有显式构造器的类,对于已有构造器的类,我们可以考虑更复杂的处理(例如,将字段注入添加到已有构造器中,或者创建一个新的构造器并标记@Autowired,但这样可能产生多个构造器,需要用户调整)。在实际应用中,可能需要根据项目情况调整策略。11.最后,将自定义Recipe打包,并配置到OpenRewrite的执行流程中。由于这是一个复杂的任务,以上只是一个基本框架。下面,我们将给出一个更具体的实现代码示例(基于OpenRewrite7.x版本)。</think>### 在 OpenRewrite 中创建自定义 Recipe 将 `@Autowired` 字段注入转换为构造器注入 以下是创建自定义 Recipe 的完整步骤,基于 OpenRewrite 的 AST(抽象语法树)操作实现转换: #### 1. **核心实现逻辑** ```java import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.*; import org.openrewrite.java.tree.J; public class AutowiredFieldToConstructorRecipe extends Recipe { @Override public String getDisplayName() { return "Convert @Autowired fields to constructor injection"; } @Override protected TreeVisitor<?, ExecutionContext> getVisitor() { return new AutowiredFieldToConstructorVisitor(); } private static class AutowiredFieldToConstructorVisitor extends JavaIsoVisitor<ExecutionContext> { @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { // 1. 收集所有被@Autowired注解的字段 List<J.VariableDeclarations> autowiredFields = new ArrayList<>(); for (J.BlockStatement statement : classDecl.getBody().getStatements()) { if (statement.getStatement() instanceof J.VariableDeclarations) { J.VariableDeclarations field = (J.VariableDeclarations) statement.getStatement(); if (field.getLeadingAnnotations().stream() .anyMatch(anno -> "Autowired".equals(anno.getSimpleName()))) { autowiredFields.add(field); } } } if (autowiredFields.isEmpty()) { return classDecl; } // 2. 生成构造器参数和赋值语句 JavaTemplate constructorTemplate = JavaTemplate.builder(this::getCursor, "public #{}(#{}) { #{}}") .build(); // 3. 构建参数列表和赋值语句 StringBuilder params = new StringBuilder(); StringBuilder assignments = new StringBuilder(); for (J.VariableDeclarations field : autowiredFields) { String fieldType = field.getTypeExpression().printTrimmed(); String fieldName = field.getVariables().get(0).getSimpleName(); params.append(fieldType).append(" ").append(fieldName).append(", "); assignments.append("this.").append(fieldName).append(" = ").append(fieldName).append(";\n"); } params.setLength(params.length() - 2); // 移除末尾逗号 // 4. 添加构造器到类 classDecl = classDecl.withBody( classDecl.getBody().withTemplate( constructorTemplate, classDecl.getBody().getCoordinates().lastStatement(), classDecl.getSimpleName(), // 类名 params.toString(), // 参数列表 assignments.toString() // 赋值语句 ) ); // 5. 移除字段上的@Autowired注解 for (J.VariableDeclarations field : autowiredFields) { doAfterVisit(new RemoveAutowiredAnnotationVisitor(field)); } return classDecl; } } private static class RemoveAutowiredAnnotationVisitor extends JavaIsoVisitor<ExecutionContext> { private final J.VariableDeclarations targetField; public RemoveAutowiredAnnotationVisitor(J.VariableDeclarations targetField) { this.targetField = targetField; } @Override public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations varDecl, ExecutionContext ctx) { if (varDecl == targetField) { // 过滤掉@Autowired注解 List<J.Annotation> newAnnotations = varDecl.getLeadingAnnotations().stream() .filter(anno -> !"Autowired".equals(anno.getSimpleName())) .collect(Collectors.toList()); return varDecl.withLeadingAnnotations(newAnnotations); } return varDecl; } } } ``` #### 2. **转换效果示例** **转换前代码**: ```java public class UserService { @Autowired private UserRepository userRepo; @Autowired private EmailService emailService; } ``` **转换后代码**: ```java public class UserService { private UserRepository userRepo; private EmailService emailService; public UserService(UserRepository userRepo, EmailService emailService) { this.userRepo = userRepo; this.emailService = emailService; } } ``` #### 3. **关键实现说明** 1. **字段收集** 通过扫描类体中的 `J.VariableDeclarations`,识别带有 `@Autowired` 注解的字段。 2. **构造器生成** 使用 OpenRewrite 的 `JavaTemplate` 动态生成构造器: - 参数列表:基于字段类型和名称生成(如 `UserRepository userRepo`) - 赋值语句:生成 `this.field = field` 形式的初始化逻辑 3. **注解移除** 通过 `RemoveAutowiredAnnotationVisitor` 移除字段上的 `@Autowired` 注解,保持代码清洁。 4. **AST 操作安全** OpenRewrite 的 AST 操作保证: - 保留原始代码格式(缩进、空格) - 自动处理 import 语句(自动移除未使用的 `Autowired` 导入) #### 4. **测试与部署** 1. **单元测试**(使用 OpenRewrite 测试框架): ```java @Test void convertsAutowiredFields() { rewriteRun( java( """ public class Test { @Autowired private Dependency dep; } """, """ public class Test { private Dependency dep; public Test(Dependency dep) { this.dep = dep; } } """ ) ); } ``` 2. **集成到构建流程**: ```gradle rewrite { activeRecipe("com.yourpackage.AutowiredFieldToConstructorRecipe") } ``` #### 5. **注意事项** - **Lombok 兼容性**:若项目使用 Lombok `@RequiredArgsConstructor`,需额外处理(建议先移除 Lombok) - **循环依赖**:构造器注入可能暴露循环依赖问题,需确保 Spring 上下文可启动 - **多构造器场景**:当前实现仅处理无显式构造器的类,需扩展逻辑处理已有构造器 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值