8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

为什么我的 Spring @Autowired 字段为空?

Suraj Menon 2月前

93 0

注意:这旨在成为常见问题的规范答案。我有一个 Spring @Service 类(MileageFeeCalculator),它有一个 @Autowired 字段(rateService),但是当我...时该字段为空...

注意:这旨在成为常见问题的规范答案。

我有一个 Spring @Service 类 ( MileageFeeCalculator ),它有一个 @Autowired 字段 ( rateService ),但是 null 当我尝试使用它时,该字段是。日志显示 bean MileageFeeCalculator MileageRateService 每当我尝试调用 NullPointerException ,我都会得到一个 mileageCharge 。为什么 Spring 没有自动装配该字段?

控制器类:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = new MileageFeeCalculator();
        return calc.mileageCharge(miles);
    }
}

服务等级:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- should be autowired, is null

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); // <--- throws NPE
    }
}

应该自动装配 MileageFeeCalculator 但实际上没有自动装配的服务 bean:

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}

当我尝试时 GET /mileage/3 ,出现此异常:

java.lang.NullPointerException: null
    at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
    at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
    ...
帖子版权声明 1、本帖标题:为什么我的 Spring @Autowired 字段为空?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Suraj Menon在本站《spring-mvc》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 注释该字段 @Autowired null 因为 Spring 不知道 MileageFeeCalculator 您创建的 new ,也不知道如何自动连接它。

    Spring 控制反转 (IoC) 容器 有三个主要逻辑组件:一个 ApplicationContext 可供应用程序使用的组件 (bean) 的注册表 (称为 ),一个通过将依赖项与上下文中的 bean 匹配来向对象注入对象依赖项的配置器系统,以及一个依赖项解析器,它可以查看许多不同 bean 的配置并确定如何以必要的顺序实例化和配置它们。

    IoC 容器并不神奇,除非您以某种方式通知它,否则它无法了解 Java 对象。当您调用 时 new ,JVM 会实例化新对象的副本并将其直接交给您 - 它从不经过配置过程。有三种方法可以配置您的 bean。

    我已经将所有这些代码(使用 Spring Boot 启动)发布到 这个 GitHub 项目 ;您可以查看每种方法的完整运行项目,以了解使其工作所需的一切。 Tag with the NullPointerException : nonworking

    注入 bean

    最可取的选项是让 Spring 自动装配所有 bean;这需要的代码量最少,并且最易于维护。要使自动装配按您的需要工作,还可以像这样自动装配 MileageFeeCalculator

    @Controller
    public class MileageFeeController {
    
        @Autowired
        private MileageFeeCalculator calc;
    
        @RequestMapping("/mileage/{miles}")
        @ResponseBody
        public float mileageFee(@PathVariable int miles) {
            return calc.mileageCharge(miles);
        }
    }
    

    如果你需要为不同的请求创建新的服务对象实例,你仍然可以使用 Spring bean 作用域 .

    通过注入@MileageFeeCalculator 服务对象起作用的标签:working-inject-bean

    使用 @Configurable

    如果您确实需要使用 创建的对象 new 自动装配,则可以 use the Spring @Configurable annotation along with AspectJ compile-time weaving 来注入对象。此方法将代码插入对象的构造函数中,以提醒 Spring 它正在创建,以便 Spring 可以配置新实例。这需要在构建中进行一些配置(例如使用 进行编译 ajc )并打开 Spring 的运行时配置处理程序( @EnableSpringConfigured 使用 JavaConfig 语法)。Roo Active Record 系统使用此方法允许 new 您的实体实例获得注入的必要持久性信息。

    @Service
    @Configurable
    public class MileageFeeCalculator {
    
        @Autowired
        private MileageRateService rateService;
    
        public float mileageCharge(final int miles) {
            return (miles * rateService.ratePerMile());
        }
    }
    

    在服务对象上使用 @Configurable 起作用的标签:working-configurable

    手动 bean 查找:不推荐

    这种方法只适用于在特殊情况下与遗留代码交互。创建一个 Spring 可以自动装配并且遗留代码可以调用的单例适配器类几乎总是更好的选择,但也可以直接向 Spring 应用程序上下文请求 bean。

    为此,您需要一个 Spring 可以为其提供对象引用的类 ApplicationContext

    @Component
    public class ApplicationContextHolder implements ApplicationContextAware {
        private static ApplicationContext context;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            context = applicationContext;   
        }
    
        public static ApplicationContext getContext() {
            return context;
        }
    }
    

    然后你的遗留代码可以调用 getContext() 并检索它所需的 bean:

    @Controller
    public class MileageFeeController {    
        @RequestMapping("/mileage/{miles}")
        @ResponseBody
        public float mileageFee(@PathVariable int miles) {
            MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
            return calc.mileageCharge(miles);
        }
    }
    

    通过在 Spring 上下文中手动查找服务对象来工作的标签:working-manual-lookup

  • 要注意的另一件事是在 @Configuration bean 中为 bean 创建对象,其中创建特定 bean 类的实例的方法用 @Bean 注释。

  • @DonalFellows 我不太清楚您在说什么(“making” 这个词含糊不清)。您是在说使用 Spring Proxy AOP 时多次调用 @Bean 方法的问题吗?

  • 您好,我遇到了类似的问题,但是当我使用您的第一个建议时,我的应用程序在调用 \'mileageFee\' 方法时认为 \'calc\' 为空。就好像它从未初始化 @Autowired MileageFeeCalculator calc 一样。有什么想法吗?

  • 我认为您应该在答案顶部添加一个条目,解释检索第一个 bean(您执行所有操作的根)应该通过 ApplicationContext 完成。有些用户(我已将其作为重复项关闭)不理解这一点。

  • BcK 2月前 0 只看Ta
    引用 7

    如果我错了,请纠正我,但根据 Spring AOP 文档,在 MilegageFeeCalculator 上同时指定 @Service 和 @Configurable 注释可能是不正确的:...请确保不要在作为常规 Spring bean 注册到容器的 bean 类上使用 @Configurable:否则,您将获得双重初始化,一次通过容器,一次通过方面。因此,从本质上讲,您应该只选择其中之一。

  • 如果您没有编写 Web 应用程序,请确保在其中完成 @Autowiring 的类是 Spring bean。通常,Spring 容器不会知道我们可能认为是 Spring bean 的类。我们必须告诉 Spring 容器有关我们的 Spring 类的信息。

    这可以通过在 appln-contxt 中配置来实现,或者 更好的方法 是将类注释为 @Component ,并且请不要使用 new 运算符创建带注释的类。确保从 Appln-context 获取它,如下所示。

    @Component
    public class MyDemo {
    
    
        @Autowired
        private MyService  myService; 
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
                System.out.println("test");
                ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
                System.out.println("ctx>>"+ctx);
    
                Customer c1=null;
                MyDemo myDemo=ctx.getBean(MyDemo.class);
                System.out.println(myDemo);
                myDemo.callService(ctx);
    
    
        }
    
        public void callService(ApplicationContext ctx) {
            // TODO Auto-generated method stub
            System.out.println("---callService---");
            System.out.println(myService);
            myService.callMydao();
    
        }
    
    }
    
  • 你好,我看过你的解决方案了,没错。这里我想知道为什么我们不使用 new 运算符创建注释类的实例,我可以知道背后的原因吗?

  • 如果你使用 new 创建对象,你将处理 ​​bean 的生命周期,这与 IOC 的概念相矛盾。我们需要让容器来做这件事,容器会以更好的方式完成这件事

  • 实际上,您应该使用 JVM 管理的对象或 Spring 管理的对象来调用方法。从控制器类中的上述代码中,您正在创建一个新对象来调用具有自动连接对象的服务类。

    MileageFeeCalculator calc = new MileageFeeCalculator();
    

    所以它不会那样工作。

    解决方案是让 MileageFeeCalculator 作为控制器本身的自动连接对象。

    像下面这样更改您的控制器类。

    @Controller
    public class MileageFeeController {
    
        @Autowired
        MileageFeeCalculator calc;  
    
        @RequestMapping("/mileage/{miles}")
        @ResponseBody
        public float mileageFee(@PathVariable int miles) {
            return calc.mileageCharge(miles);
        }
    }
    
  • 这就是答案。由于您自己实例化了一个新的 MilageFeeCalculator,因此 Spring 不参与实例化,因此 Spring 不知道该对象存在。因此,它无法对其执行任何操作,例如注入依赖项。

  • 还不太习惯 IoC 世界的生活 时,我曾经遇到过同样的问题 @Autowired 我的一个 bean 的字段在运行时为空。

    根本原因是,我没有使用 Spring IoC 容器维护的自动创建的 bean(其 @Autowired 字段确实已正确注入), new 而是创建了该 bean 类型的我自己的实例并使用它。当然,这个字段 @Autowired 为空,因为 Spring 没有机会注入它。

  • 您的问题是新的(以 Java 风格创建对象)

    MileageFeeCalculator calc = new MileageFeeCalculator();
    

    使用注释 @Service , @Component , @Configuration 创建 bean
    服务器启动时,Spring 的应用程序上下文。但是当我们使用 new 运算符创建对象时,该对象未在已创建的应用程序上下文中注册。例如,我使用过 Employee.java 类。

    看看这个:

    public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            String name = "tenant";
            System.out.println("Bean factory post processor is initialized"); 
            beanFactory.registerScope("employee", new Employee());
    
            Assert.state(beanFactory instanceof BeanDefinitionRegistry,
                    "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    
            for (String beanName : beanFactory.getBeanDefinitionNames()) {
                BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
                if (name.equals(definition.getScope())) {
                    BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
                    registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
                }
            }
        }
    }
    
  • 我是 Spring 的新手,但我发现了这个可行的解决方案。请告诉我这是否是一种不推荐的方法。

    我让 Spring 注入 applicationContext 这个 bean:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SpringUtils {
    
        public static ApplicationContext ctx;
    
        /**
         * Make Spring inject the application context
         * and save it on a static variable,
         * so that it can be accessed from any point in the application. 
         */
        @Autowired
        private void setApplicationContext(ApplicationContext applicationContext) {
            ctx = applicationContext;       
        }
    }
    

    如果您愿意,您也可以将此代码放在主应用程序类中。

    其他类可以像这样使用它:

    MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
    

    这样, 任何 bean 都可以被应用程序中的任何对象 (也可以用 实例化 new 以静态方式 .

  • 这种模式对于使 Spring bean 可供遗留代码访问是必要的,但应避免在新代码中使用。

  • 在我的例子中,我需要这样做,因为第三方类很少。Spring(IOC)无法控制它们。这些类从未从我的 Spring Boot 应用程序中调用过。我遵循了这种方法,它对我有用。

  • Bear 2月前 0 只看Ta
    引用 18

    这似乎是罕见的情况,但以下是发生在我身上的事情:

    我们使用的 @Inject @Autowired Spring 支持的 JavaEE 标准。每个地方都运行良好,Bean 注入正确,而不是一个地方。Bean 注入似乎相同

    @Inject
    Calculator myCalculator
    

    最后我们发现错误在于我们(实际上是 Eclipse 自动完成功能)导入的 com.opensymphony.xwork2.Inject 是 而不是 javax.inject.Inject

    总而言之,确保您的注释( @Autowired , @Inject , @Service ,......)有正确的包!

  • 这里没有提到的内容在 本文 的“执行顺序”段落中进行了描述。

    在“学习”到我必须使用@Component或其衍生品@Service或@Repository(我猜还有更多)注释一个类,以便自动装配其中的其他组件之后,我发现这些其他组件在父组件的构造函数中仍然为空。

    使用@PostConstruct 可以解决这个问题:

    @SpringBootApplication
    public class Application {
        @Autowired MyComponent comp;
    }
    

    和:

    @Component
    public class MyComponent {
        @Autowired ComponentDAO dao;
    
        public MyComponent() {
            // dao is null here
        }
    
        @PostConstruct
        public void init() {
            // dao is initialized here
        }
    }
    
  • 下面的方法之一就可以了:

    1. p1

    2. p2

    如果以上两个不起作用....开始放置 System.out.println() 并找出问题所在。

返回
作者最近主题: