首页 > 编程语言 >Spring IOC官方文档学习笔记(十二)之基于Java的容器配置

Spring IOC官方文档学习笔记(十二)之基于Java的容器配置

时间:2023-02-22 22:56:50浏览次数:37  
标签:Java ExampleA Spring bean public Bean ExampleB IOC class

1.@Bean与@Configuration

(1) 标注于类之上的@Configuration注解与标注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解标注的方法用于实例化bean并将其注入至容器中,它与基于xml配置中的<bean/>标签起着相同的作用,@Bean可用在任何被@Component注解标注的类中,不过绝大部分情况下它们都被用于被@Configuration注解标注的类中;被@Configuration注解标注的类通常作为bean的定义源,如同基于xml配置中的<beans/>标签,此外,还可在@Configuration标注的类中配置bean之间的依赖关系,如下

//两个普通的类,其中ExampleB依赖ExampleA
public class ExampleA { }

public class ExampleB {

    private ExampleA exampleA;

    public ExampleB(ExampleA exampleA) {
        this.exampleA = exampleA;
    }
}

//配置类
@Configuration
public class Config {
    //注入bean ExampleA
    @Bean
    public ExampleA exampleA() {
        return new ExampleA();
    }

    //调用exampleA()方法来配置ExampleB
    @Bean
    public ExampleB exampleB() {
        return new ExampleB(exampleA());
    }

    //上下这两个exampleB方法等价,注意,在下面这个例子中,Spring会自动帮我们注入ExampleA对象
//    @Bean
//    public ExampleB exampleB(ExampleA exampleA) {
//        return new ExampleB(exampleA);
//    }
}

//启动容器,打印注入的对象是否相同
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ctx.getBean(ExampleB.class).getExampleA() == ctx.getBean(ExampleA.class));

//观察结果,可见Spring执行了依赖注入,注入了容器中的ExampleA,而非new出来了一个新的ExampleA
true

(2) @Bean也可用在任何被@Component注解标注的类中,此时,我们称其为lite @Bean模式,而在这种lite模式下,我们不能配置bean之间的依赖关系,如下所示

//取上面的例子,其他保持不变,只将Config类上的@Configuration注解变更为@Component注解,此时其中的@Bean注解就处于lite @Bean模式
@Component
public class Config {

    //保持不变...
}

//启动容器,观察打印结果,为false,可见此时Spring并没有执行依赖注入,而是直接new出来了一个新的ExampleA给ExampleB,因此在这种lite模式下,我们不能配置bean之间的依赖关系

2.通过AnnotationConfigApplicationContext实例化Spring容器

(1) AnnotationConfigApplicationContext作为Spring的容器,它不仅可以接受@Configuration类(同时这个类本身也会被注册为一个bean)作为参数,还可以接受@Component类或用JSR-330注解标注的类作为参数,如下

//例一: 一个Spring配置类
@Configuration
public class Config {

}

public static void main(String[] args) {
    //使用@Configuration类作为输入,完全摆脱掉基于xml的配置  
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
    //打印,会观察到Config也被注入到了容器中
    Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
}

//例二: ExampleB被JSR 330标准注解标注
@Component
public class ExampleA {

}

@Named
public class ExampleB {
    @Inject
    private ExampleA exampleA;

    public ExampleA getExampleA() {
        return exampleA;
    }
}

public static void main(String[] args) {
    //使用@Component类或用JSR-330注解标注的类作为参数,容器会对它们进行依赖注入
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ExampleA.class, ExampleB.class);
    System.out.println(ctx.getBean(ExampleB.class).getExampleA());
}

(2) 可以使用AnnotationConfigApplicationContext类的register(Class<?>…​)方法编程式的向容器中注册,如下

public class ExampleA { }

public static void main(String[] args) {
    //使用无参构造函数
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    //编程式的向容器中注册bean ExampleA
    ctx.register(ExampleA.class);
    //refresh()方法用于处理bean,一定要调用此方法,否则容器将抛出异常
    ctx.refresh();
    System.out.println(ctx.getBean(ExampleA.class));
}

(3) 我们可以使用@ComponentScan(basePackages = "...")注解或<context:component-scan base-package="..."/>标签来开启注解扫描,此外,AnnotationConfigApplicationContext也提供了scan(String…​)方法来开启注解扫描,如下

//ExampleA位于cn.example.spring.boke包下
@Component
public class ExampleA { }

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    //开启注解扫描,扫描cn.example.spring.boke"包下的bean
    ctx.scan("cn.example.spring.boke");
    ctx.refresh();
    System.out.println(ctx.getBean(ExampleA.class));
}

(4) 在web环境中,可以使用AnnotationConfigWebApplicationContext,来配置ContextLoaderListener监听器或DispatcherServlet,具体配置细节可参考官方文档

3.使用@Bean注解

(1) @Bean注解是一个方法级别的注解,类似于基于xml配置中的<bean/>标签,我们可以在@Configuration类或@Component类中使用该注解,它常用于声明一个bean,如下

@Configuration
public class Config {
    //@Bean注解用于将方法的返回值注册为容器中的一个bean,方法返回类型就是该bean的类型,默认情况下,方法名就是bean的名称
    //下面这个例子:向容器中注入一个类型为ExampleA,名称为exampleA的bean 
    @Bean
    public ExampleA exampleA() {
        return new ExampleA();
    }
}

(2) 针对@Bean方法的返回类型,也有些细节需要注意,如下所示

//有两个接口A和B
public interface A { }

public interface B { }

//ExampleA实现了这两个接口
public class ExampleA implements A, B { }

//而另一个bean ExampleB,它依赖了类型为B的bean
@Component
public class ExampleB {

    @Autowired
    private B b;

    public B getB() {
        return b;
    }
}

//配置类
@Configuration
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config {
    //注意,我们返回了ExampleA的实例,不过却将返回类型声明为了A
    @Bean
    public A exampleA() {
        return new ExampleA();
    }
}

//接着,启动容器,会发现容器抛出NoSuchBeanDefinitionException: No qualifying bean of type 'cn.example.spring.boke.B'的异常,可见容器将返回的ExampleA仅视作了A类型然后就用于注入,虽然它也可以被视作B类型
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ctx.getBean(ExampleB.class).getB());

//针对上面的情况,有两种办法解决:
//方法一:最直接,直接返回类型B而非A,如下
@Bean
public B exampleA() {
    return new ExampleA();
}

//方法二:其他的不变,只在我们的ExampleB上,加上@Lazy注解,让容器有时间去充分的识别ExampleA,使它意识到ExampleA还可以视作B类型
@Component
@Lazy
public class ExampleB {

    //省略...
}

(3) @Bean方法可以拥有任意数量的参数,这些参数就是这个bean的依赖项,如下

@Configuration
public class Config {
    
    @Bean
    public ExampleB exampleB() {
        return new ExampleB();
    }
    
    //ExampleA依赖了ExampleB,Spring会为我们自动注入容器中的这个ExampleB对象,类似于基于构造函数的依赖注入
    @Bean
    public ExampleA exampleA(ExampleB exampleB) {
        return new ExampleA(exampleB);
    }
}

(4) 同普通的bean,由@Bean方法注入的bean也支持由JSR-250所定义的生命周期回调注解,支持InitializingBean,DisposableBean或Lifecycle等接口,支持各种Aware接口用于注入容器内置组件,同时@Bean还提供了initMethod和destroyMethod属性用于配置初始化和销毁回调,同<bean/>标签的属性,如下

public class ExampleA implements ApplicationContextAware {
    
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    
    @PostConstruct
    public void init() {
        System.out.println("init..."); 
    }
    
    public void destroy() {
        System.out.println("destroy...");
    }
}

@Configuration
public class Config {

    @Bean(destroyMethod = "destroy")
    public ExampleA exampleA() {
        return new ExampleA();
    }
}

(5) 默认情况下,当我们使用基于Java的配置来定义一个bean的时候,如果在这个bean中有声明public修饰的close或shutdown方法,那么这些方法会随着该bean的销毁回调的触发而同时被调用,这是Spring提供的一个默认机制,无需任何的配置都会生效,但如果我们想关闭掉这一机制,可通过设置@Bean中的属性destroyMethod=""来达到这一目的,如下

//设置destroyMethod = "",禁用掉Spring会自动触发public修饰的close或shutdown方法的机制
@Bean(destroyMethod = "")
public ExampleA exampleA() {
    return new ExampleA();
}

(6) 我们可以通过使用@Scope注解来指定bean的作用域,如下

@Configuration
public class Config {
    //由@Bean方法所声明的bean的作用域默认为singleton
    //@Scope注解还可用于类上
    @Bean
    @Scope("prototype")
    public ExampleA exampleA() {
        return new ExampleA();
    }
}

标签:Java,ExampleA,Spring,bean,public,Bean,ExampleB,IOC,class
From: https://www.cnblogs.com/shame11/p/17133937.html

相关文章

  • Java序列化
    什么是序列化序列化:将对象转化为可传输字节序列的过程为序列化反序列化:把字节序列还原为对象的过程称之为反序列化为什么要序列化序列化的最终目的是为了对象可以跨平......
  • SpringBean的生命周期
    springBean的生命周期实例化阶段spring框架取出beanDefinition之前会判断信息进行判断当前bean的范围是否是单例,是否延迟加载,是否是factoryBean等,然后再通过反射进行实......
  • 【Spring AOP】【八】Spring AOP源码解析-拦截器/通知器链的执行顺序
    1 前言上节我们看过了代理对象执行方法的大致过程,我们留着通知器链的具体执行没说,这节我们先讲解一下通知器的执行顺序。通知器或者叫拦截器,叫法不一样,这里我们还是都......
  • java之并发编程(上)
    回顾1、线程与进程进程:正在运行的程序,进程包含至少一个或多个线程2、创建线程的方式实现Runable接口继承Thread类(不建议使用,java是单继承,可扩展性差),用start方法通知c......
  • spring-申明式事务
    一.什么是事务事务的原则是:处于事务中的sql代码块会保持一致状态,即要么都能改变数据库,要么都不能改变数据库ACID原则:原子性一致性隔离性多个业务可能操作同一个业......
  • java基础之网络编程
    1.1网络编程中的两个主要问题:如何准确定位网络中的一台主机主机之间如何通信1.2网络中的主要元素IP地址,端口号port,套接字socket主要的通信协议tcp,udpIP地......
  • ssm学习笔记23001-spring+mybatis修改删除和查询
    spring+mybatis修改删除和查询1、在UserMapper接口类中,创建接口在UserMapper接口类中,创建接口packagecom.wjw.mybatis.mapper;importcom.wjw.mybatis.pojo.User;......
  • Java集合
    Java集合Java集合类型分为:Collection和Map,Collection子接口有:Set、Queue和List接口,每一种接口描述了一种数据结构。1、ListList集合是有序的,可以重复出现。List接口的......
  • JavaScript迭代协议解读
    JavaScript迭代协议解读迭代协议分为可迭代协议和迭代器协议。协议指约定俗成的一系列规则。可迭代协议可迭代协议规定了怎么样算是一个可迭代对象:可迭代对象或其原......
  • spring5随笔
    1、Spring1.1、简介Spring:春天----->给软件行业带来了春天!2002,首次推出了Spring框架的雏形:interface21框架!Spring框架即以interface21框架为基础,经过重新设计,......