@Component和@Bean和@Autowired之间的区别

@Component(@Controller、@Service、@Repository):自动创建一个实例并装配到Spring容器中(放到IOC中)
【Ps.写好的其他项目jar放到pom,Spring自动装配,简单好用,简直冒了泡了】

@Bean :手动创建一个实例,并保留在IOC中。
@Bean的好处:麻烦一点,但自定义性更强。当我们引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现~(因为你并不能改他的源代码在他类上加个@Component ,所以只能这么玩了。
引申:代码回调函数也是起这个作用)

@SpringBootApplication
public class Application {

    @Bean
    BookingService bookingService() {
        return new BookingService();
    }

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
        BookingService bookingService = ctx.getBean(BookingService.class);
        bookingService.book("Alice", "Bob", "Carol");
    }
}

@Autowired:织入(Spring上下文已有实例(已注入IOC),@Autowired只是取一下)
@Autowired说“请给我一个该类的实例,例如,我之前用@Bean注释创建的一个实例进入IOC了”。

@Autowired一般用在变量上,spring容器会自动注入值。

如果用在方法上:

@Autowired(required = false)
public void setRedisTemplate(RedisTemplate redisTemplate) {
    RedisSerializer stringSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringSerializer);
    redisTemplate.setValueSerializer(stringSerializer);
    redisTemplate.setHashKeySerializer(stringSerializer);
    redisTemplate.setHashValueSerializer(stringSerializer);
    this.redisTemplate = redisTemplate;
}
spring容器会在类加载后自动注入这个方法的参数,并执行一遍方法。

总结: @Autowired注解作用在方法上
(1)该方法如果有参数,会使用autowired的方式在容器中查找是否有该参数
(2)会执行该方法
 (3)这就像static{}块语句会初始化执行,但顺序不一样(static{}比↑快)。

实际项目一般是@Component配合@Autowired联用
但是改成这样玩也行:

@SpringBootApplication
public class Application {

  @Autowired
  BookingService bookingService;

  @Bean
  BookingService bookingService() {
    return new BookingService();
  }

  public static void main(String[] args) {
    bookingService.book("Alice", "Bob", "Carol");
  }
}

在这种情况下,@Bean注释为Spring提供了BookingService,并加以@Autowired利用。但这是一个毫无意义的示例,因为你都在同一个类中使用它们…不如下面就直接new对象呢还能少写上面那几行呢。
但是如果你在一个类中@Bean定义对象实例(注册到IOC),而在另一个类中@Autowired使用。那@Bean+@Autowired就变得很有用。

补充阅读
@Configuration和@Component区别


最近知道: A注解上面有几个注解,其他地方用到这个A注解,会是叠加效果~那么,这就又可以(借助注解)抽象一层了(不过注解本质就是接口啊,所以,能实现抽象抽取的效果,好像也是理所当然的哈_
例如,下面的oaScenePeopleHandlers能自动注入自动织入成功

   private Map<String, OaScenePeopleHandler> oaScenePeopleHandleMap;

    @Autowired
    public void setOaScenePeopleHandleMap(List<OaScenePeopleHandler> oaScenePeopleHandlers) {
        // 注入各种场景类型的处理类
        oaScenePeopleHandleMap = oaScenePeopleHandlers.stream().collect(
                Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), OaScenePeopleHandlerType.class).sceneType(),
                        v -> v, (v1, v2) -> v1));
    }

是因为

public interface OaScenePeopleHandler {

    void sendSecnePeoplePermissionHandle(OaScenePeopleEntity oaScenePeopleEntity);
}
@OaScenePeopleHandlerType(sceneType="1")
public class ChangJingHandler implements OaScenePeopleHandler {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service  
public @interface OaScenePeopleHandlerType {
    // 1.场景   2.设备
    String sceneType();
}

破案了,@Service出现了,原来在这呢。
如果注掉了就会报错了
在这里插入图片描述
以后有空可以把上述代码用@Bean重写一遍玩玩(这不找麻烦呢不是,故意变复杂)。

Ps.
能用@Autowired等注解有个另外的隐藏前提(除了织入的类本身得有先注入到IOC):被写这些注解代码的这个类,也得教给了IOC(Spring管理),才能里面写了这些注解,这些注解会被扫描(?会有意义?),否则报空指针异常
如:

     @Autowired
    private WeChatSendMsgUtils weChatSendMsgUtils;
    
     @Test
    public void testSendWx2(){

        String templateId=systemProperties.getExamineConfirm();
        String url="www.baidu.com";
        //我的测试号
        String toUserWxId="oLh_dvztrOLfkDm5RJ6F_ngBgByQ";
        //2020年6月30日 考虑思路:sendWxMsg可以是静态方法不好用,但那么wxMpService注入也得静态的。WeChatSendMsgUtils 得交给Spring。或者  new WeChatSendMsgUtils().sendWxMsg也行【但是weChatSendMsgUtils内容里有注解,所以该类本身也得交给IOC才能里面的注解有意义,那么,既然该类也交给IOC,我何不也用Spring注入呢,所以,这里没用new。那么,静态也没啥更方便了,所以静态又没有用】
        weChatSendMsgUtils.sendWxMsg(
                templateId,
                url,
                toUserWxId,
                new WxMsgDto("AA的随行BB接受了访客邀请!",
                        "普普",
                        "面试",
                        "2019-01-02 9:00 - 2019-01-02 9:00",
                        "1666666666",
                        "更多信息,请点击详情进行查看"));
    }

@Component
public class WeChatSendMsgUtils {

    @Resource
    private WxMpService wxMpService;

    /**
     * 只支持三行或四行的微信模板
     * @param templateId 模板id
     * @param url  跳转url
     * @param toUserWxId  目标wxId
     * @param wxMsgDto   信息数组/集合
     */
    public  void sendWxMsg(String templateId, String url, String toUserWxId, WxMsgDto wxMsgDto) {
     try {
            msg = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);

如果WeChatSendMsgUtils.java不加@Component(或者@Service…),那么 @Resource
private WxMpService wxMpService; 会是无效的,使用时会是空的。(可能是里面的注解会无意义,Spring会不扫描吧)

代码里黏贴出来: //2020年6月30日 考虑思路:sendWxMsg可以是静态方法不好用,但那么wxMpService注入也得静态的。WeChatSendMsgUtils 得交给Spring。或者 new WeChatSendMsgUtils().sendWxMsg也行【但是weChatSendMsgUtils内容里有注解,所以该类本身(“包住这些注解的那个类”,它没交给Spring,那么里面的东西也没交给Spring,感觉挺有这道理的[可以理解成“没扫描”吗😂]【他(本身)也得注入,他里面的注入(里面注解的效果)才能顺带这一起注入。他被交给Spring管理,他才能有从Spring取东西的资格?/能力?/权利?/权力?】)也得交给IOC才能里面的注解有意义,那么,既然该类也交给IOC,我何不也用Spring注入呢,所以,这里没用new 干脆全用Spring管理。那么,静态也没啥更方便了,所以静态又没有用的必要】

Ps.某注解定义时,上面放注解,那么用该某注解时,是会有叠加效果的(上文说过了。。。“叠加作用/叠加效果,可以理解成接口的多继承”)。(参见springboot启动项。和我的有道笔记策略模式破案了。)

--------一次实践2020年7月6日

@Component
public class WechatAccount {

    @Autowired
    private static   WeChatAccountConfig weChatAccountConfig ;

    //(测试、正式)公众号
    public  String APPID = weChatAccountConfig.getAppId();
    public  String APPSECRET = weChatAccountConfig.getAppSecret();
    public  String TOKEN = weChatAccountConfig.getToken();
    public  String ENCODINGAESKEY =weChatAccountConfig.getAesKey();
}

报错:加载时null(应该是weChatAccountConfig 对象null了)
尝试解析/解读/分析:因为有加载顺序,全局静态变量应该快于方法吧,所以报空。
解决尝试(其实,法二:不用static):

@Component
public class WechatAccount {

    //@Autowired
   // private static   WeChatAccountConfig weChatAccountConfig ;

    //(测试、正式)公众号
    public static  String APPID;
    public static  String APPSECRET;
    public static  String TOKEN;
    public static  String ENCODINGAESKEY;

    @Autowired
    public  void  initWechatAccount(WeChatAccountConfig weChatAccountConfig){
        APPID = weChatAccountConfig.getAppId();
        System.out.println("hhhhhh"+APPID);
        APPSECRET = weChatAccountConfig.getAppSecret();
        TOKEN = weChatAccountConfig.getToken();
        ENCODINGAESKEY =weChatAccountConfig.getAesKey();
    }
}

在这里插入图片描述

我上面的做法和这↓文章意思做法差不多(就是static快没事,再赋值一次。Ps. 已知@Value和@ConfigurationProperties差不多),不过我按此没整出来,是因为我项目里还有海康SDK原因?@Value原因?@PostConstruct:
yml文件注入静态属性
漏了一个知识点 @Value和@PostConstruct 联用:
踩过的坑spring之:@PostConstruct 和 @Value

实例构造完成之后,这个时候@Value注解就会触发,使用set,@Value其实也是使用了@AutoWire的机制。
但,static在加载时就会赋值,这个时候 还没有拿配置文件的数据(实例化之前),导致为null。那我重新方法里赋值为啥没起作用?可能@Value作用于方法并没有什么卵用吧

2020年9月2日: 没有static的必要就不用static,发现会给自己挖了很多坑
最终:没有解决,可能是海康SDK(很大概率)+@PostConstruct有毒,海康SDK接口常量(interface,所以一定是静态,所以无法不用static)的加载赋值快于实例化,而此时这个常量还是空,所以总是在这就开始报错。观察可知:SDK初始化需要用到这个值,而此时日志可知还没到@PostConstruct
总结:发现规律,@PostConstruct(启动时运行的方法)里面用到@Value的值,那么这个值不能是静态的,否则取不到;如果不是@PostConstruct里面用,那倒可以用我上面的方式,通过方法给静态变量再赋值。然而,这个海康SDK接口里的变量,肯定是静态,然后又需要启动时运行(@PostConstruct),所以这个值无解→无法通过放到配置文件间接?动态?获得。
本次实际最终处理: SDK接口初始化只能直接赋值(写死)喽,放到配置文件里并不能提前加载到。发现海康sdk它init不仅比@PostConstruct还快,而且比@Autowired快,更别说@Value了,所以放手。。既然无法实现完全放到配置文件中无脑发布,那么发布还是要改东西,部分没啥意义,干脆回滚,不做这个优化了,省的新代码未测试即上线而自找麻烦节外生枝。


今天碰到一个bug:
为什么项目里直接new对象A,对象调方法,方法里存在spring管理的织入的对象,然后这个对方会是null?因为对象A并不是Spring中取出的,没有交给spring管理,那么它也没资格从spring中取对象,所以那个句柄一直没有可指向的对象,自然仍然是空。为什么把它(对象A)Autowired的织入就可以了呢?因为把它交给spring管理了——它是谁?就是启动时注入ioc的对象——spring启动时会注入几个对象啊?怎么确保取出的是哪个对象啊?(并不影响能调用ioc容器里的东西,但会不知道取出的是哪个对象)——所以,spring ioc一般是单例,这就规避了这个蛋疼多虑[小麻烦罢了,最多像service @Resource时指定对象名字就好了,麻烦程度还好]——(所以,)更大的原因是,还发现单例有其他好处:节省开销,内存资源,提高效率,节约不必要的开销,建立节约型可持续发展的中国特色代码生态圈,以及单例的其他好处,当然,不要单例也是可以的。

-------------------多数据源配置(然后通过另配的注解切面指定使用的session)

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties("druid.first")
    public DataSource firstDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("druid.second")
    public DataSource secondDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("druid.third")
    public DataSource thirdDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource,DataSource thirdDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
        targetDataSources.put(DataSourceNames.THIRD, thirdDataSource);
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
}

----------@Bean小练习
在这里插入图片描述
如上,显然,这个项目启动时会报重名:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-02-15 14:24:05.475 -ERROR  [  restartedMain] LoggingFailureAnalysisReporter : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'producer', defined in class path resource[...] 
, could not be registered. A bean with that name has already been defined in class path resource[....]

评论 2 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

Stack Piston

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值