Idea和Spring都不推荐使用@Autowired注解,你们都知道为什么吗?

前言

最近看到了很多关于@Autowired不一样的用法代码,现在将收获到的知识分享给大家。

@Autowired比你想象中更强大,它主要作用范围包含构造器、方法、参数、成员变量、注解

1. @Autowired的默认装配

熟悉Spring的都知道@Autowired注解,是用来自动装配对象的。

示例如下:

package com.sue.cache.service;

import org.springframework.stereotype.Service;

@Service
public class ProductService {
    public void product() {//无参构造
      
    }
}
package com.sue.cache.service;

import org.springframework.stereotype.Service;

@Service
public class ProductService2 {

    @Autowired//注入对象
    private ProductService productService;

    public void product2() {//午餐构造
      
    }
}

正常情况下是可以装配成功的,都知道spring默认按照类型注入的(即byType方式)

看过底层源码的应该知道,@Autowired注解有一个required参数,其值默认为true(默认开启自动装配),如果不想使用自动装配可将该参数设置成false。

2. 多个相同类型对象如何处理的?

可以设想一下,如果多个相同类型的对象,再用@Autowired来注入这同类型的对象会发生什么?

项目新建一个目录,创建一个同名的类ProductService。示例如下:

package com.sue.cache.service.test;

import org.springframework.stereotype.Service;

@Service
public class ProductService {

    public void product() {
    }
}

再启动项目时,抛出如下的异常:

Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'testService1' for bean class [com.sue.cache.service.test.TestService1] conflicts with existing, non-compatible bean definition of same name and class [com.sue.cache.service.ProductService]

这个异常时告诉我们类名称有冲突,然后项目嗝屁了,起不来了。

注意:

其实这并不是因为@Autowired注入了两个同类型的对象导致的。其实是spring中@Service注解不允许有同名的类,spring自动把类首字母大写转为小写作为它管理的bean名称,默认情况下bean名称必须是唯一的。

如何产生两个相同的类型bean?:

public class ProductService1 {

    public void product1() {
    }
}
@Service
public class ProductService2 {

    @Autowired
    private ProductService1 productService1;

    public void product2() {
    }
}
@Configuration
public class ProductConfig {

    @Bean("productService1")
    public ProductService1 productService1() {
        return new ProductService1();
    }

    @Bean("productService2")
    public ProductService1 productService2() {
        return new ProductService1();
    }
}

在ProductConfig类中手动创建ProductService1实例。

重启项目抛出异常:

抛出异常,提示productService1是单例的,却找到两个实例。

两个类实现同一个接口后,另一个类@Autowired这个接口,也会产生两个相同的类型bean异常,实例如下:

public interface IUser {
    void say();
}
@Service
public class User1 implements IUser{
    @Override
    public void say() {
    }
}
@Service
public class User2 implements IUser{
    @Override
    public void say() {
    }
}
@Service
public class UserService {

    @Autowired
    private IUser user;
}

项目重启抛出异常:

和上面一样的配出同样的异常信息。

不难看出,实际项目中第二种情况在我们实际项目中遇到的更多!

3. @Qualifier、@Primary

当然在项目中用@Autowired装配对象时,是解决不了上面的问题。这是就是spring的强大之处了,spring还给我们提供了改用按名称装配实例。

此时用@Autowired + @Qualifier("beanName“)可以完美解决上述问题:

@Service
public class UserService {

    @Autowired
    @Qualifier("user1")
    private IUser user;
}

调整之后,再重启项目就ok了。

Qualifier一般都是和Autowired结合使用,通过Qualifier的参数指定一个bean的名称,实现多个同对象的装配。

Spring还给我们提供了@Primary注解解决上面的问题。在一个对象上加上@Primary注解:

@Primary
@Service
public class User1 implements IUser{
    @Override
    public void say() {
    }
}

此时另一个对象只使用@Autowired注解即可:

@Service
public class UserService {

    @Autowired
    private IUser user;
}

重启项目,一样解决了问题。

当用@Autowired自动装配对象时,有多个同名对象,其中一个使用@Primary注解修饰,就会把该对象作为候选者被选中,来作为自动配置的值。

注:其实项目中这种方式不是很常用。

4. @Autowired作用范围

下面我们聊一聊@Autowired注解除了作用在成员变量上,还有哪些作用范围?

看看源码中@Autowired注解的定义:

不难看出@Autowired作用在5种目标类型上,图例总结一下:

接下来,我们重点看看在其他地方该怎么用?

4.1 成员变量

@Autowired作用在成员变量上:

@Service
public class UserService {

    @Autowired
    private IUser user;
}

4.2 构造器

@Autowired作用在构造器上:

@Service
public class UserService {

    private IUser user;

    @Autowired
    public UserService(IUser user) {
        this.user = user;
        System.out.println("user:" + user);
    }
}

注意:@Autowired作用在构造器上,实际仍然用Autowired装配方式,并非构造器装配。

4.3 方法

@Autowired作用在普通方法上:

@Service
public class UserService {

    @Autowired
    public void test(IUser user) {
       user.say();
    }
}

项目启动中,Spring自动调用一次加了@Autowired的方法此时可以在该方法内做一些初始化工作。

@Autowired也可以作用在setter方法上:

@Service
public class UserService {

    private IUser user;

    @Autowired
    public void setUser(IUser user) {
        this.user = user;
    }
}

4.4 参数

@Autowired作用在构造器的入参上:

@Service
public class UserService {

    private IUser user;

    public UserService(@Autowired IUser user) {
        this.user = user;
        System.out.println("user:" + user);
    }
}

@Autowired作用在非静态方法的入参上:

@Service
public class UserService {

    public void test(@Autowired IUser user) {
       user.say();
    }
}

4.5 注解

基本没人用,偷个懒不介绍了。。。

5. @Autowired的高级玩法

上面几种@Autowired都是自动装配单实例,其实它也能自动装配多个实例,惊喜补,怎么回事呢?

把UserService成员变量变通一下,用List集合接收IUser类型的参数:

@Service
public class UserService {

    @Autowired
    private List userList;

    @Autowired
    private Set userSet;

    @Autowired
    private Map userMap;

    public void test() {
        System.out.println("userList:" + userList);
        System.out.println("userSet:" + userSet);
        System.out.println("userMap:" + userMap);
    }
}

创建一个UController:

@RequestMapping("/u")
@RestController
public class UController {

    @Autowired
    private UserService userService;

    @RequestMapping("/test")
    public String test() {
        userService.test();
        return "success";
    }
}

启动项目并调用接口:

控制台中:userList、userSet和userMap都分别打印出来了各自的元素,并没有抛出异常,说明@Autowired会自动把相同类型的IUser对象收集到集合中。

6. @Autowired一定能装配成功?

有时候就算用了@Autowired装配的对象结果还是null,这又是怎么回事呢?

6.1 不打@Service注解

在类上面不加@Controller、@Service、@Component、@Repository等注解,这种情况下Spring就不能完成对象的装配,示例如下:

public class UserService {

    @Autowired
    private IUser user;

    public void test() {
        user.say();
    }
}

6.2 注入Filter或Listener(装配过滤器或监听器)

web项目启动的顺序:listener>filter>servlet

示例如下:

public class UserFilter implements Filter {

    @Autowired
    private IUser user;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        user.say();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    }

    @Override
    public void destroy() {
    }
}
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new UserFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }
}

此时我们启动项目一样会抛出异常,并告诉我们tomcat不能正常启动:

这又是什么原因呢?

Springmvc的启动主要依靠DisptachServlet(常说的核心处理器),而核心处理器是在listener和filter之后执行的。那么此时我们在监听器或者过滤器里面用@Autowired装配某个对象时,因此此时核心处理器还没执行,bean还没有初始化,自然无法完成自动注入的。

下面我们讲一讲项目中遇到这种情况该怎么处理呢?

public class UserFilter  implements Filter {

    private IUser user;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
        this.user = ((IUser)(applicationContext.getBean("user1")));
        user.say();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    }

    @Override
    public void destroy() {

    }
}

这里用到了WebApplicationContextUtils.getWebApplicationContext获取当前的ApplicationContext,再通过它获取到bean实例(其实这里实际用到的是Java里面的反射来完成的,小伙伴们可以了解一下)。

6.3 注解未被@ComponentScan扫描

Spring中@Component、@Repository、@Controller、@Service、@Configuration等注解,是需要通过@ComponentScan注解扫描,收集元数据的。

试想一下,不加@ComponentScan,或配置的@ComponentScan扫描的包路径错误,或包路径范围太小,此时也会导致有些注解的元数据无法收集,最后导致@Autowired不能自动注入bean。

当然Springboot项目中就不存在此类问题,Springboot的@SpringBootApplication注解,已经内置了@ComponentScan注解。

6.4 循环依赖问题

循环依赖,相信伙伴们都遇到过吧,这里简单说一下,如果A依赖于B,B依赖于C,C又依赖于A,就形成了死循环依赖。

Spring的bean默认是单例的,单例对象用@Autowired自动注入,大多数情况能解决循环依赖问题。

那么当bean是多例的情况下,就会出现循环依赖,导致bean无法自动注入。

7. @Autowired和@Resouce的区别

有时候@Autowiredw无法解决,改成@Resource却能解决问题。接下来,我们重点看看@Autowired和@Resource的区别。

@Autowired的装配顺序如下:

@Resource的装配顺序如下:

1.如果同时指定了name和type

2.如果指定了name

3.如果指定了type

4.如果既没有指定name,也没有指定type:

关注公众号:编程怪咖,学习更多知识。

展开阅读全文

页面更新:2024-04-23

标签:注解   变量   实例   异常   对象   名称   参数   类型   方法   项目

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top