一个普通JAVA类无法使用@Autowired等注解注入一个bean

本文探讨了在Spring框架下,如何在非@Component注解的普通Java类中注入Bean的问题。当直接使用@Autowired注解无法生效时,提供了两种解决方案:1) 将普通类标记为@Component,并通过Controller层的@Autowired注入;2) 实现ApplicationContextAware接口,利用SpringUtil工具类获取Bean。这两种方法使得非组件类也能利用Spring管理的Bean。

在普通Java类中注入bean,普通Java类中用@Autowired等注解是无法注入bean的

验证

准备需要被注入的bean,使用@Service注解(@Component等注解均可)

package com.halon;

import org.springframework.stereotype.Service;

/**
 * @Author halon
 * @create 2021/9
 */
@Service
public class TestService {
    public void serviceTest(){
        System.out.println("serviceTest Running ....");
    }
}

准备一个普通的JAVA对象

package com.halon;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @Author halon
 * @create 2021/9
 */
public class TestEntity {
    private final Logger LOGGER = LoggerFactory.getLogger(TestEntity.class);
    @Autowired
    private TestService testService;

    public void setTestService() {
        try {
            testService.serviceTest();
            LOGGER.info("【Autowired SUCCESS】");
        } catch (NullPointerException e) {
            LOGGER.warn("【Autowired ERROR】");
        }
    }
}

写一个Controller测试一哈

package com.halon;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author halon
 * @create 2021/9
 */
@RestController
public class TestController {

    @PostMapping("/post")
    public void testController() {
        TestEntity testEntity = new TestEntity();
        testEntity.setTestService();
    }
}

请求

POST localhost:8080/post

控制台输出

 WARN 8412 --- [nio-8080-exec-1] com.halon.TestEntity                     :Autowired ERROR】

在普通Java类中注入bean,普通Java类中用@Autowired是无法注入bean的。
如果在普通的JAVA类上加上@Component注解会发生什么呢?

改变

@Component
public class TestEntity {
...

控制台输出:

 WARN 8412 --- [nio-8080-exec-1] com.halon.TestEntity                     :Autowired ERROR】

为什么会出现这种情况呢?
原因是在controller层中使用的是new创建一个对象,并不是通过Spring创建一个bean,这样就不能走完整的SpringBean的生命周期也没有办法实现AOP导致即使在普通的JAVA类上加了@Component注解使Spring扫描带该类但是仍然无法在Controller层通过new创建对象使普通JAVA类中的注解生效。

解决方法:

1.在controller 层通过注解注入加了@Component注解普通JAVA类

在上述的基础上修改controller层的代码如下:

package com.halon;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author halon
 * @create 2021/9
 */
@RestController
public class TestController {
    @Autowired
    private TestEntity testEntity;

    @PostMapping("/post")
    public void testController() {
//        TestEntity testEntity = new TestEntity();
        testEntity.setTestService();
    }
}

发起请求后控制台输出结果

serviceTest Running ....
INFO 6704 --- [nio-8080-exec-1] com.halon.TestEntity                     :Autowired SUCCESS】

如果我就是不想在普通的JAVA类中加上@Component注解,但还是想在普通JAVA类中使用Spring扫描到的Bean怎么办?

2.实现ApplicationContextAware接口

实现ApplicationContextAware接口,并加入Component注解,让spring扫描到该bean,用于在普通Java类中注入bean。(普通Java类中用@Autowired是无法注入bean)

创建一个工具类

package com.halon.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;


/**
 * @Author halon
 * @create 2021/9
 */
@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
    }

    /**
     * 获取applicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }
}

修改普通JAVA类代码如下:

package com.halon;

import com.halon.util.SpringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @Author halon
 * @create 2021/9
 */
public class TestEntity {
    private final Logger LOGGER = LoggerFactory.getLogger(TestEntity.class);
    @Autowired
    private TestService testService;

    public void setTestService() {
        try {
            testService.serviceTest();
            LOGGER.info("【Autowired SUCCESS】");
        } catch (NullPointerException e) {
            LOGGER.warn("【Autowired ERROR】");
            try {
                SpringUtil.getBean(TestService.class).serviceTest();
                LOGGER.info("【getBean SUCCESS】");
            } catch (Exception exception) {
                LOGGER.warn("【getBean ERROR】");
            }
        }
    }
}

controller代码

@RestController
public class TestController {

    @PostMapping("/post")
    public void testController() {
        TestEntity testEntity = new TestEntity();
        testEntity.setTestService();
    }
}

发起请求,控制台输出结果

WARN 23500 --- [nio-8080-exec-1] com.halon.TestEntity                     :Autowired ERROR】
serviceTest Running ....
INFO 23500 --- [nio-8080-exec-1] com.halon.TestEntity                     : 【getBean SUCCESS】```

使用 Netty 框架时,若希望在继承自 `ChannelHandler` 的中通过 `@Autowired` 注入 Spring 管理的 Bean,可能会遇到注入失败、字段为 `null` 的问题。这是由于 Netty 的 Handler 实例通常由开发者手动创建,而非由 Spring 容器管理,导致 Spring 无法自动完成依赖注入。 ### 解决方案 #### 1. 使用手动注入方式 由于 Netty 的 Handler 通常不是由 Spring 创建的,因此可以通过手动方式将 Spring 容器中的 Bean 注入到 Handler 实例中。具体做法是将需要注入Bean 作为构造参数传入 Handler,或者在 Handler 初始化时通过 setter 方法设置。 ```java @Component public class MyHandler extends ChannelInboundHandlerAdapter { private final MyService myService; public MyHandler(MyService myService) { this.myService = myService; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { myService.doSomething(); // 处理逻辑 } } ``` 在创建 `MyHandler` 实例时,从 Spring 容器中获取 `MyService` 实例并传入: ```java @Autowired private MyService myService; ChannelHandler handler = new MyHandler(myService); ``` #### 2. 使用 Spring 的 ApplicationContext 获取 Bean 另一种方式是通过静态工具获取 Spring 容器中的 Bean,从而避免直接使用 `@Autowired` 注解。 ```java @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) { context = applicationContext; } public static <T> T getBean(Class<T> beanClass) { return context.getBean(beanClass); } } ``` 在 Handler 中使用该工具获取 Bean: ```java public class MyHandler extends ChannelInboundHandlerAdapter { private MyService myService; @Override public void handlerAdded(ChannelHandlerContext ctx) { myService = SpringContextUtil.getBean(MyService.class); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { myService.doSomething(); // 处理逻辑 } } ``` #### 3. 使用 Spring AOP 代理或自定义注解处理器 若项目中使用Spring Boot 集成 Netty,并希望保持 Handler 的自动管理,可以考虑结合 Spring AOP 或自定义注解处理器来实现自动注入。此方式较为复杂,需确保 Handler 实例由 Spring 管理,通常需要对 Netty 的启动流程进行定制化处理。 #### 4. 避免在 Handler 中直接注入复杂依赖 若注入Bean 仅用于执行简单逻辑,可考虑将业务逻辑封装在独立的服务中,由 Handler 调用该服务的静态方法或通过其他方式间接使用,避免直接依赖注入。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值