@Component和@Bean和@Autowired之间的区别
@Component(@Controller、@Service、@Repository):自动创建一个实例并装配到Spring容器中(放到IOC中)【Ps.写好的其他项目jar放到pom,Spring自动装配,简单好用,简直冒了泡了】@Bean :手动创建一个实例,并保留在IOC中。@Bean的好处:麻烦一点,但自定义性更强。当我们引用第三方库中的类需要装配到Spring容器时,则只能通过@Bea
@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的方式在容器中查找是否有该参数 (2022年11月4日 @Bean加方法也有这能力?)
(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[....]
更多推荐
所有评论(0)