我是靠谱客的博主 健忘煎蛋,最近开发中收集的这篇文章主要介绍Spring注解驱动开发一、组件注册二、生命周期三、属性赋值四、依赖注入五、单例/多例模式问题,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、组件注册

1、@Configuration搭配 @Bean 注解

将xml配置文件变成配置类,将bean标签变成 @Bean注解,容器对象从ClassPathXmlApplicationContext变成AnnotationConfigApplicationContext

那么现在使用注解驱动开发,就不需要xml配置了

对象的注册:

@Configuration //等价于 applicationContext.xml配置文件
public class MainConfig {
    
    @Bean //等价于 <bean> 标签
    public Student student(){
        return new Student();
    }
}    

对象的创建:

public class Main {

    public static void main(String[] args) {
        //注解版的IOC容器是  AnnotationConfigApplicationContext,构造器中传入的是配置类
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
        Student bean = ctx.getBean(Student.class);
        System.out.println("bean = " + bean);
        
    }
}

1、@Bean注解注册的bean默认的bean name是方法名,可以通过 @Bean注解的name属性给bean赋予新的name

2、@Bean 注解的 initMethod 属性可以实现对象的初始化操作

2、@ComponetScan 搭配 @Component 注解

在注解开发中,将使用 @ComponentScan 注解代替< context:component-scan> 标签

@ComponentScan(basePackages = "com.qiuguan.spring")  //代替包扫描标签
@Configuration //等价于 applicationContext.xml配置文件
public class MainConfig {

    @Bean //等价于 <bean> 标签
    public Student student(){
        return new Student();
    }
}    

那么在com.qiuguan.spring 包下的标注了@Component注解的Person类将会纳入到Spring的IOC容器中。

@CompoentScan 注解会扫描指定包路径下的 @Component, @Controller, @Service, @Repository 注解。

3、@ComponentScan 指定过滤规则

@ComponentScan(basePackages = "com.qiuguan.spring", //指定包扫描路径
               //指定排除规则,不用去扫描
               excludeFilters = {
                     @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
                     @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = HelloService.class)
               //如果想要"只"包含 includeFilters 的规则,需要 useDefaultFilters = false,禁用掉默认的规则
               }, includeFilters = {
                     @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Person.class)
               }, useDefaultFilters = false
)
@Configuration //等价于 applicationContext.xml配置文件
public class MainConfig {

    //它不受@ComponentScan管理
    @Bean //等价于 <bean> 标签
    public Student student() {
        return new Student();
    }
}

过滤规则说明:

  • FilterType#ANNOTATION: 按照注解过滤
  • FilterType#ASSIGNABLE_TYPE: 按照指令的类型过滤
  • FilterType#ASPECTJ: 按照 AspectJ 规则过滤
  • FilterType#REGEX: 按照正则过滤
  • FilterType#CUSTOM: 按照自定义规则过滤

上面的栗子中,先通过 excludeFilters 属性指定在扫描的时候按照什么规则排除那些组件,其中包括了要排除 @Controller注解,以及我自定义的接口HelloService(它的所有实现类都将会排除);
同时还指定了 includeFilters 属性,用来指定扫描的时候需要包含哪些组件,意思是扫描包路径下的 Person类,注意:如果想要"只扫描" Person类,那么需要将 useDefaultFilters = false,不然默认是true, 将使用默认的过滤规则,除了Person类以外,其他符合的(比如@Component注解标注了)也将放入容器中。

还可以自定义过滤规则,自定义过滤规则需要实现 TypeFilter接口

4、@Import 注解导入Bean

使用 @Import 注解可以给容器导入bean, bean name 默认是全类名。

@Import({ Water.class, Sun.class})
@Configuration
public class MainConfig2 {

}

5、@Conditional 完成按条件注册

6、FactoryBean 注册组件

二、生命周期

1、@Bean 注解指定初始化和销毁方法

public class Bird {

    public Bird(){
        System.out.println("周命周期: Bird 构造器");
    }

    public void init(){
        System.out.println("生命周期:Bird init");
    }

    public void destroy(){
        System.out.println("生命周期:Bird destroy");
    }
}
//@ImportResource("classpath:applicationContext.xml")
@Configuration
public class MainConfig4 {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Bird bird(){
        return new Bird();
    }
}
public class Main4 {

    public static void main(String[] args) {
        //启动时会创建单实例bean, 进而会调用初始化方法
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig4.class);

        //销毁时调用 destroy 方法
        ctx.close();
    }
}

输出结果:
周命周期: Bird 构造器
生命周期:Bird init
生命周期:Bird destroy

如果 Bird的作用域是@Scope(“prototype”), 那么容器启动时不会创建对象,只有当获取的时候才会创建对象并调用初始化方法,而且Spring不会管理@Scope(“prototype”)的bean, 所以容器关闭时,不会执行 destroy 方法。

2、使用 @PostConstruct 和 @PreDestroy注解完成初始化和销毁

public class Bird {

    public Bird(){
        System.out.println("周命周期: Bird 构造器");
    }

    /**
     * 销毁注解
     */
    @PreDestroy
    public void destroy(){
        System.out.println("生命周期:Bird @PreDestroy destroy");
    }

    /**
     * 初始化注解
     */
    @PostConstruct
    public void init() {
        System.out.println("生命周期:Bird  @PostConstruct init");
    }
}
//@ImportResource("classpath:applicationContext.xml")
@Configuration
public class MainConfig4 {

    @Bean
    public Bird bird(){
        return new Bird();
    }
}
public class Main4 {

    public static void main(String[] args) {
        //启动时会创建单实例bean, 进而会调用初始化方法
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig4.class);

        //销毁时调用 destroy 方法
        ctx.close();
    }
}

输出结果:
周命周期: Bird 构造器
生命周期:Bird  @PostConstruct init
生命周期:Bird @PreDestroy destroy

三、属性赋值

在注解驱动开发中,可以使用 @Value 注解完成属性的赋值。

teacher.workDate=2022-08-16
teacher.whatTeach=physical
public class Teacher {

    //TODO:直接赋值,"false", "34"
    @Value("张三")
    private String name;

    //TODO:可以写SPEL表达式
    @Value("#{30 - 5}")
    private int age;

    @Value("#{T(Math).random()}")
    private double salary;

    //TODO: $ 表示从环境变量中取值
    @Value("${teacher.workDate}")
    private String workDate;

    //TODO:如果环境变量中没有该配置,则给一个默认值
    @Value("${teacher.whatTeach:english}")
    private String whatTeach;
    
    //TODO: toString()方法略,因为@Value是通过反射进行赋值的,客户忽略set方法

}
//TODO:将配置文件导入到环境变量中
@PropertySource("classpath:teacher.properties")
@Configuration
public class MainConfig5 {

    @Bean
    public Teacher teacher(){
        return new Teacher();
    }
}

四、依赖注入

1、@Autowired 按类型注入

@Autowired 默认是按照类型进行装配,如果类型有多个,则按照名字就行装配。完成一个bean 注入到另一个bean中

//定义一个接口
public interface OrderService {

}

//实现类1
@Service
public class OrderServiceImpl implements OrderService {

    @Override
    public String toString() {
        String number = "1号";
        return "OrderServiceImpl{" +
                "number='" + number + ''' +
                '}';
    }
}

//实现类2
@Service
public class OrderServiceImpl2 implements OrderService {

    @Override
    public String toString() {
        String number = "2号";
        return "OrderServiceImpl2{" +
                "number='" + number + ''' +
                '}';
    }
}
@Controller
public class HelloController {

    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println("orderService = " + orderService);
    }
}

会发现报错:需要注入唯一的一个bean,但是却发现了2个匹配的

我们知道 @Autowired 注解默认是按照类型进行装配的,如果发现有多个,它可以继续按照名字去匹配,找到了就注入进来。

所以 第一种解决办法:就是修改属性名,使其与bean Nmae 匹配。

@Controller
public class HelloController {

    /**
     * 将 属性名orderService 改成 beanName, 也就是 orderServiceImpl 或者 orderServiceImpl2
     */
    @Autowired
    private OrderService orderServiceImpl;

    public void test(){
        System.out.println("orderService = " + orderServiceImpl);
    }
}

第二种解决办法:搭配 @Qualifier注解

@Controller
public class HelloController {

    //TODO:指定某个bean的名字
    @Qualifier("orderServiceImpl2")
    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println("orderService = " + orderService);
    }
}

第三种解决办法:搭配 @Primary 注解

在某个类上标注 @Primary注解,那么在有多个的时候,会优先注入标注了该注解的bean

//TODO:标注优先级高的注解
@Primary
@Service
public class OrderServiceImpl2 implements OrderService {

    @Override
    public String toString() {
        String number = "2号";
        return "OrderServiceImpl2{" +
                "number='" + number + ''' +
                '}';
    }
}

注意:@Autowired 默认是必须找到某个bean进而完成注入,如果找不到,则将抛出异常,如果在找不到的情况下不抛出异常,只需要将 @Autowired 注解的 required=false 即可。

如何一次性将多个实现类全部注入进来?比如上面的 OrderService 有两个实现类,如何将这两个实现类同时注入进来?只需要用 Map, Collection, 数组 去接收就可以了。
演示:

@Controller
public class HelloController {

    @Qualifier("orderServiceImpl2")
    @Autowired
    private OrderService orderService;

    @Autowired
    private Map<String, OrderService> orderServiceMap;

    /**
     * Collection的实现类也可以,比如 List, Set
     */
    @Autowired
    private Collection<OrderService> orderServiceCollection;

    @Autowired
    private OrderService[] orderServices;

    public void test(){
        System.out.println("orderService = " + orderService);
        System.out.println("orderServiceMap = " + orderServiceMap);
        System.out.println("orderServiceCollection = " + orderServiceCollection);
        System.out.println("orderServices = " + Arrays.asList(orderServices));
    }
}

第四种解决办法: 搭配 @Priority注解

2、@Resource 按名称注入

1、如果指定了name,type,则从Spring容器中找一个名称和类型相当应的一个bean,找不到则报错。
2、如果只指定了name,则从Spring容器中找一个名称和name一样的bean,找不到则报错。
3、如果只指定了type,则从Spring容器中找一个类型和type一样的bean,找不到或者找到多个则报错。
4、如果没有指定参数,则默认找字段名称装配,找不到则按类型装配,找不到则报错。

@Controller
public class HelloController {

    @Resource
    private OrderService orderService;

    public void test() {
        System.out.println("orderService = " + orderService);
    }
}

我们知道,OrderService有两个实现类,beanName分别是 orderServiceImpl 和 orderServiceImpl2, 在上面的代码中,Spring 将按照类型找到2个实现类,但是由于没有与之匹配的beanName,所以无法完成匹配,进而抛出错误。

所以,上面代码中,改成这样就可以完成注入:

@Controller
public class HelloController {

    //TODO:指定特定的beanName完成注入,或者直接需改属性名为orderServiceImpl2
    @Resource(name = "orderServiceImpl2")
    private OrderService orderService;

    public void test() {
        System.out.println("orderService = " + orderService);
    }
}

同样的,@Resource 注解也可以完成多个bean的一次性注入:

@Controller
public class HelloController {

    @Resource(name = "orderServiceImpl2")
    private OrderService orderService;
    
    @Resource
    private Map<String, OrderService> orderServiceMap;

    /**
     * Collection的实现类也可以,比如 List, Set
     */
    @Resource
    private Collection<OrderService> orderServiceCollection;

    @Resource
    private OrderService[] orderServices;

    public void test(){
        System.out.println("orderService = " + orderService);
        System.out.println("orderServiceMap = " + orderServiceMap);
        System.out.println("orderServiceCollection = " + orderServiceCollection);
        System.out.println("orderServices = " + Arrays.asList(orderServices));
    }
}

3、@Inject 按照类型注入

它完全就可以当做 @Autowired 注解来使用。

使用时要导入依赖:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

说明:@Autowired 是Spring原生提供的注解,@Resource 和 @Inject 是Java规范提供的。

4、@Profile 按环境注册

@Profile 指定组件在哪个环境下才会被注册到容器中,如果不指定则所有环境都可以注册。

五、单例/多例模式问题

Spring的bean默认注入是单例的,它在Spring容器初始化的时候创建对象

多例模式在进行注入时,不能使用 @Autowired,否则注入的还是单例模式,实现多例模式需要使用工厂模式;

此外,@Autowired+@Qualifier(“student”) 与@Resource作用一致,当获取对象时,并不能稳定获 取到多例,在使用时需要注意。

最后

以上就是健忘煎蛋为你收集整理的Spring注解驱动开发一、组件注册二、生命周期三、属性赋值四、依赖注入五、单例/多例模式问题的全部内容,希望文章能够帮你解决Spring注解驱动开发一、组件注册二、生命周期三、属性赋值四、依赖注入五、单例/多例模式问题所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部