Spring--IOC注解方式注入

Posted kingja

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring--IOC注解方式注入相关的知识,希望对你有一定的参考价值。

Spring实现IOC注入的方式有xml注解两种方式,异曲同工,这里我们讲述注解方式,因为这也是一种趋势,主要优点实现简便,代码可读性强(个人理解)。
注解方式实现IOC注入,主要涉及以下几个注解

  • @Configuration:添加该注解的类被视为上下文,里面带有@Bean注解的都将被注入到IOC容器
  • @ComponentScan:扫描注定包下的所有带@Component的类,注入到IOC容器
  • @Bean:添加该注解的方法将返回一个实例,并被注入到IOC容器
  • @Component:添加该注解的方法自动被注入到IOC容器,需要被@ComponentScan扫描到
  • @Scope:指定实例的作用域
  • @PostConstruct:添加该注解的方法在实例初始化的时候被执行
  • @PreDestory:添加该注解的方法在实例销毁的时候被执行

注解方式实现的一个简单的案例

第一步:@Configuration配置一个上下文环境,并在里面使用@Bean注解返回需要注入的对象,指定beanId

@Configuration
public class BeanConfiguration {

    //将一个bean交由spring创建并管理
    @Bean(value = "bean1")
    public Bean1 getBean1() {
        return new Bean1();
    }
}

第二部:根据beanId获取需要的实例对象

public class IoCAnnotationTest {

    @Test
    public void test() {
        //获取Spring上下文
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(BeanConfiguration.class);
        //获取bean
//        Bean1 bean1 = (Bean1) context.getBean("getBean1");//beanId默认为方法名
        Bean1 bean1 = (Bean1) context.getBean("bean1");//通过@Bean value属性设置beanId
        System.out.println(bean1);
    }
}

如果有很多bean要注入,那么要写很多@Bean注解吗,这里有更方便的写法

//创建一个class配置文件
@Configuration
//扫描指定包下的所有带@Component的bean,交由Spring管理
@ComponentScan(value = "com.kingja.iocannotation.bean")
public class BeanScanConfiguration {

}

在需要注入的类上添加@Component注解,并设置beanId

//通过value设置beanId
@Component(value = "bean2")
//默认beanId是类名(首字母小写bean2)
public class Bean2 {
}

注入Bean的几种方式

通过方法注入Bean

  • 构造方法注入Bean
  • setter方法注入Bean
通过构造方法注入
@Component
public class OutterBean {
    private InnerBean innerBean;
    @Autowired
    public OutterBean(InnerBean innerBean) {
        this.innerBean = innerBean;
    }
}
通过setter方法注入
@Autowired
public void setInnerBean2(InnerBean innerBean2) {
    this.innerBean2 = innerBean2;
}
通过属性注入
@Component
public class OutterBean {
    @Autowired
    private InnerBean innerBean3;
注入集合
/**
 * 1.寻找所有统一泛型的集合,方法名符合参数名的集合进行注入
 * 2.寻找所有类型为泛型类型,组合成集合然后进行注入
 * 3.如果需要精确注入,则用@Qualifier("myStringList")进行制定beanId
 * 方式2优先于方式1
 * 将会在容器里寻找List<String>的集合自动注入,参数名stringList对应@Bean注解的的方法名
 */
@Autowired
@Qualifier("myStringList")
public void setStringList(List<String> stringList) {
    this.stringList = stringList;
}
@Configuration
//扫描指定包下的所有带@Component的bean,交由Spring管理
@ComponentScan(value = "com.kingja.iocannotation.bean")
public class BeanScanConfiguration {
@Bean("myStringList")
public List<String> stringList3() {
    List<String> list = new ArrayList<>();
    list.add("ooo");
    list.add("kkk");
    return list;
}
注入普通字段
@Component
public class OutterBean {
    private String stringValue;
    private Integer intValue;

    @Value("666")
    public void setStringValue(String stringValue) {
        this.stringValue = stringValue;
    }

    /**
     * @Value将字符串直接转成整型进行注入
     */
    @Value("999")
    public void setIntValue(Integer intValue) {
        this.intValue = intValue;
    }
注入直接可用的接口
@Component
public class OutterBean {
    private ApplicationContext applicationContext;
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

IOC的作用域

实际需求中,我们可能需要每次返回的对象是同一个,也可能每次创建不同的实例对象,也可能根据不同的场景,比如不同请求,不同会话等返回不同实例,这就涉及到Bean的作用域。
Bean的作用域主要包含以下几种:

  • singleton:单例模式,创建的实例是同一个
  • prototype:每次创建不同的实例
  • request:每次请求创建不同的实例
  • session:每个回话周期内创建同一个实例
  • application:服务器运行周期内创建内一个实例
  • websocket:每次websocket连接创建同一个实例
    通过测试我们来检验singleton和prototype两种作用域
@Configuration
public class BeanScopeConfiguration {

    @Bean(value = "defaultBean")
    public Bean1 getDefaultBean() {
        return new Bean1();
    }
    @Bean(value = "singletonBean")
    @Scope(value = "singleton")
    public Bean1 getSingletonBean() {
        return new Bean1();
    }
    @Bean(value = "prototypeBean")
    @Scope(value = "prototype")
    public Bean1 getPrototypeBean() {
        return new Bean1();
    }
}
@Test
public void testScope() {
    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(BeanScopeConfiguration.class);
    Bean1 defaultBean1 = (Bean1) context.getBean("defaultBean");
    Bean1 defaultBean2 = (Bean1) context.getBean("defaultBean");
    System.out.println("defaultBean1 : "+defaultBean1);
    System.out.println("defaultBean2 : "+defaultBean2);
    Bean1 singletonBean1 = (Bean1) context.getBean("singletonBean");
    Bean1 singletonBean2 = (Bean1) context.getBean("singletonBean");
    System.out.println("singletonBean1 : "+singletonBean1);
    System.out.println("singletonBean1 : "+singletonBean2);
    Bean1 prototypeBean1 = (Bean1) context.getBean("prototypeBean");
    Bean1 prototypeBean2 = (Bean1) context.getBean("prototypeBean");
    System.out.println("prototypeBean1 : "+prototypeBean1);
    System.out.println("prototypeBean2 : "+prototypeBean2);
}

defaultBean1 : com.kingja.iocannotation.bean.Bean1@70cf32e3
defaultBean2 : com.kingja.iocannotation.bean.Bean1@70cf32e3
singletonBean1 : com.kingja.iocannotation.bean.Bean1@5a59ca5e
singletonBean1 : com.kingja.iocannotation.bean.Bean1@5a59ca5e
prototypeBean1 : com.kingja.iocannotation.bean.Bean1@4d1bf319
prototypeBean2 : com.kingja.iocannotation.bean.Bean1@6f53b8a

根据日志可以看出,默认scope为singleton,prototype就是每次调用都是创建新的实例,但是默认单例创建的实例和添加单例注解创建的实例不是一样的。

@Lazy 懒加载

默认创建实例是在获取上下文的同时进行创建,但是特殊要求也可以满足在需要Bean的时候才创建,这就涉及到Bean的懒加载
@Lazy可以和以下几种注解进行配合使用:

  • @Configuration: 当前上下文下的所有实例
  • @Bean:当前实例
  • @Component:当前组件(实例)
@Configuration
//@Lazy //全局配置懒加载
public class BeanLazyConfiguration {

    @Bean(value = "lazyBean")
    @Lazy
    public Bean1 getLazyBean() {
        return new Bean1();
    }
}
public class Bean1 {
    public Bean1() {
        System.out.println("Bean1 init");
    }
}
@Test
public void testLazy() {
    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(BeanLazyConfiguration.class);
    System.out.println("context init");
    Bean1 bean = (Bean1) context.getBean("lazyBean");
    System.out.println(bean);
}

看下输出日志

context init
Bean1 init
com.kingja.iocannotation.bean.Bean1@3e08ff24

在看下去掉懒加载后的日志输出

    @Bean(value = "lazyBean")
//    @Lazy
    public Bean1 getLazyBean() {
        return new Bean1();
    }
Bean1 init
context init
com.kingja.iocannotation.bean.Bean1@7c137fd5

可以看出去掉懒加载后也就是默认模式是在初始化上下文的时候呀直接创建了实例对象

bean的初始化和销毁

实际场景中,我们需要监听IOC容器中,Bean的生命周期,有三种方法可以实现。
方法一:实现InitializingBean, DisposableBean接口

@Component
public class LifecycleBean implements InitializingBean, DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet");
    }
}
afterPropertiesSet
com.kingja.iocannotation.bean.LifecycleBean@183ec003

我们看到,销毁的方法并没有执行,因为上下文没有销毁,我们尝试把当前的上下文关闭,把
AnnotationConfigApplicationContext 换成AbstractApplicationContext,并调用close()。


    @Test
    public void testLifecycle() {
//        AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(CommonBeanConfiguration.class);
        AbstractApplicationContext context =new AnnotationConfigApplicationContext(CommonBeanConfiguration.class);
        LifecycleBean bean = (LifecycleBean) context.getBean("lifecycleBean");
        System.out.println(bean);
        context.close();
    }
afterPropertiesSet
com.kingja.iocannotation.bean.LifecycleBean@183ec003
destroy

这样,销毁方法也调用了,可见Bean的销毁是由上下文的销毁触发的。
方法二:Bean类上设置@PostConstruct和@PreDestory注解,自定义初始化和销毁方法

@PostConstruct
public void onInit() throws Exception {
    System.out.println("onInit");
}
@PreDestroy
public void onDestory() throws Exception {
    System.out.println("onDestory");
}
onInit
afterPropertiesSet
com.kingja.iocannotation.bean.LifecycleBean@1fe20588
onDestory
destroy

同样,自定义的方法也正常执行了
方法二:
设置@Bean的initMethod和destroyMethod属性

@Bean(value = "lifecycleBean" ,initMethod = "onInitByAnnotation",destroyMethod = "onDestoryByAnnotation")
public LifecycleBean getLifecycleBean() {
    return new LifecycleBean();
}
onInit
afterPropertiesSet
onInitByAnnotation
com.kingja.iocannotation.bean.LifecycleBean@6973bf95
onDestory
destroy
onDestoryByAnnotation

同样也正常执行了
总结

以上内容包含了IOC容器注解方式注入的大部分功能,这是基于Spring方式,如果是Spring Boot方式,还有更加简单,请听下回分解。











以上是关于Spring--IOC注解方式注入的主要内容,如果未能解决你的问题,请参考以下文章

Spring IoC 容器配置(全注解方式 )

Spring IoC 容器配置(半注解方式 )

java框架篇---spring IOC依赖注入

spring IOC快速入门,属性注入,注解开发

[Spring5]IOC容器_Bean管理XML方式_创建对象_set注入属性and有参构造注入属性

Java54spring:IOC完成账户的CRUD,基于注解的IOC配置