浅析Spring的@Bean注解:注解分类、简介,作用、好处及代码使用示例、Bean的xml方式和注解方式使用对比、@Bean的源码分析

  Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后将这个Bean对象交给Spring管理, 和xml配置中的bean标签的作用是一样的。

  @Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里,添加的bean的id为方法名。

  @Configuration与@Bean结合使用。@Configuration可理解为用spring的时候xml里面的<beans>标签,@Bean可理解为用spring的时候xml里面的<bean>标签。Spring Boot不是spring的加强版,所以@Configuration和@Bean同样可以用在普通的spring项目中,而不是Spring Boot特有的,只是在spring用的时候,注意加上扫包配置。

  Bean注解的作用之一就是能够管理第三方jar包内的类到容器中。

  现在我们引入一个第三方的jar包,这其中的某个类,StringUtil需要注入到我们的IndexService类中,因为我们没有源码,不能再StringUtil中增加@Component或者@Service注解。这时候我们可以通过使用@Bean的方式,把这个类交到Spring容器进行管理,最终就能够被注入到IndexService实例中。

  在@Configuration中被@Bean标记的方法,会被Spring进行CGLIB代理,从而进行增强。

一、Spring注解分类

  从广义上Spring注解可以分为两类:

1、一类注解是用于注册Bean

  假如IOC容器就是一间空屋子,首先这间空屋子啥都没有,我们要吃大餐,我们就要从外部搬运食材和餐具进来。这里把某一样食材或者某一样餐具搬进空屋子的操作就相当于每个注册Bean的注解作用类似。

  注册Bean的注解作用就是往IOC容器中放(注册)东西!

  用于注册Bean的注解: 比如@Component , @Repository , @ Controller , @Service , @Configration,这些注解就是用于注册Bean,放进IOC容器中,一来交给spring管理方便解耦,二来还可以进行二次使用。

  那么什么是二次使用呢?这里的二次使用可以理解为:在你开始从外部搬运食材和餐具进空屋子的时候,一次性搬运了猪肉、羊肉、铁勺、筷子四样东西,这个时候你要开始吃大餐,首先你吃东西的时候肯定要用筷子或者铁勺,别说你手抓,只要你需要,你就会去找,这个时候发现你已经把筷子或者铁勺放进了屋子,你就不用再去外部拿筷子进屋子了,意思就是IOC容器中已经存在,就可以直接拿去用,而不必再去注册!

  而拿屋子里已有的东西的操作就是下面要讲的用于使用Bean的注解。

2、另一类注解是用于使用Bean

  用于使用Bean的注解:比如@Autowired、@Resource注解,这些注解就是把屋子里的东西直接拿来用。

  如果你要拿,前提一定是屋子(IOC容器)里有的,不然就会报错,比如你要做一道牛肉拼盘,需要五头牛做原材料才行,你现在锅里只有四头牛,这个时候你知道,自己往屋子里搬过五头牛,这个时候就直接把屋子里的那头牛直接放进锅里,完成牛肉拼盘的组装。

  这些注解就是需要啥想要啥,只要容器中有就往容器中拿!而这些注解又有各自的区别:比如@Autowired用在筷子上,这筷子你可能只想用木质的,或许只想用铁质的,@Autowired作用在什么属性的筷子就那什么筷子,而@Resource如果用在安格斯牛肉上面,就指定要名字就是安格斯牛肉的牛肉。

3、总结:

  使用Bean的注解,就是把已经在xml文件中配置好的Bean拿来用,完成属性、方法的组装;比如@Autowired , @Resource,可以通过byTYPE(@Autowired)、byNAME(@Resource)的方式获取Bean;

  而注册Bean的注解,比如:@Component , @Repository , @ Controller , @Service , @Configration,就是把你要实例化的对象转化成一个Bean,放在IOC容器中,等你要用的时候,它会和上面的@Autowired , @Resource配合到一起,把对象、属性、方法完美组装。

二、@Bean注解概述

  @Bean这个注解属于用于注册Bean的注解。

  @Bean 就放在方法上,其作用就是让方法去产生一个 Bean,然后交给Spring容器。

  产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

  Spring的@Bean注解明确的指示了一个方法,什么方法呢 —— 产生一个 Bean 的方法,并且交给 Spring 容器去管理。从这我们就明白了为啥 @Bean 是放在方法上了,因为它明确的告诉了被注解的方法,你给我产生一个Bean,然后交给Spring容器,剩下的你就不用管了。

  如下就能让accountDao方法产生一个 AccountDao 对象,然后这个AccountDao 对象交给Spring管理

class A{
  @Bean
  public AccountDao accountDao(){
    return new AccountDao();
  }
}

  实际上,@Bean注解和xml配置中的bean标签的作用是一样的。

三、为什么要有@Bean注解?

  不知道大家有没有想过,用于注册Bean的注解的有那么多个,为何还要出现@Bean注解?

  原因很简单:类似@Component , @Repository , @ Controller , @Service 这些注册Bean的注解存在局限性,只能局限作用于自己编写的类,如果是一个jar包第三方库要加入IOC容器的话,这些注解就手无缚鸡之力了。

  是的,@Bean注解就可以做到这一点。

  当然除了@Bean注解能做到外,还有@Import也能把第三方库中的类实例交给spring管理,而且@Import更加方便快捷。

  使用@Bean注解的另一个好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。

四、@Bean注解总结

1、Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。 产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

2、@Component , @Repository , @ Controller , @Service 这些注解只局限于自己编写的类,而@Bean注解能把第三方库中的类实例加入IOC容器中并交给spring管理。

3、@Bean注解的另一个好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。

4、记住:@Bean就放在方法上,就是让方法去产生一个Bean,然后交给Spring容器,剩下的你就别管了。

五、使用代码示例

  我们使用一个案例来解释:

// 1、功能性的Bean:主要展示 sayHello 方法
public class FunctionService {
    public String sayHello(String word){
        return "hello,"+word+"!";
    }
}

// 2、使用功能性的Bean:使用上面定义的Bean的 sayHello 方法
public class UseFunctionService {
    FunctionService functionService;

    public void setFunctionService(FunctionService functionService){
        this.functionService = functionService;
    }
    public String sayHello(String word){
        return this.functionService.sayHello(word);
    }
}

// 3、配置类
@Configuration
public class JavaConfig {
  @Bean  // 注册上面那个功能性Bean进IOC容器
  public FunctionService functionService() {
    return new FunctionService();
  }
  @Bean  // 注册上面那个使用功能性的Bean进IOC容器
  public UseFunctionService useFunctionService(FunctionService functionService){
    UseFunctionService useFunctionService = new UseFunctionService();
    useFunctionService.setFunctionService(functionService);
    return useFunctionService;
  }
}
// 测试
public class Main {
    public static void main(String args[]){
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext(JavaConfig.class);
        UseFunctionService useFunctionService = context.getBean(UseFunctionService.class);
        System.out.println(useFunctionService.sayHello("java config"));
        context.close();
    }
}

  代码解释:

1、我们使用的是Java配置的方式和注解混合配置。没有使用xml配置加注解的方式,Java配置的方式是Spring 4.x推荐的配置方式,可以完全代替xml配置。

2、使用@Configuration表明当前类是一个配置类。此处没有使用包扫描,是因为所有的Bean都在类中定义了。

3、Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。使用@Bean注解的好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。或者说将Spring和其他组件分离(其他组件不依赖Spring,但是又想Spring管理生成的bean)。

4、这种方法相当于在xml中配置bean标签,配置类相当于配置文件,方法名是bean的id。

六、Spring的三种装配机制

Spring中提供了三种主要的装配机制:

1、在xml中进行显式配置

  也就是在xml配置文件中使用bean标签声明。

2、在java中进行显式配置

  也就是我们代码示例里使用的这种。

3、隐式bean发现机制和自动装配

  隐式bean发现机制就是:Spring支持扫描使用@Service、@Compent、@Repository、@Controller的类,并注册成Bean。

  自动装配指的就是:Spring会尝试满足在使用@Autowired的属性值和方法相对应的依赖(属性值就是本身的实体,方法就是参数中的依赖),并将符合的bean填充过来。

七、基于XML方式Bean使用

// 定义一个user用户bean对象如下
public class User {
    private String userName;
    private Integer age;
}

  在srcmain esources目录下边新建一个beans.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    <!-- 定义一个id为user的bean对象 -->
    <bean id="user" class="com.zhang.bean.User">
        <property name="age" value="26"></property>
        <property name="userName" value="gg"></property>
    </bean>
</beans>
// 测试
public class ApplicationTest {
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        // 使用ClassPathXmlApplicationContext获取spring容器ApplicationContext
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        // 根据bean id获取bean对象
        User bean = (User) applicationContext.getBean("user");
        System.out.println(bean);
    }
}
// 运行结果如下:User [userName=gg, age=26]

  将如上例子用注解实现:首先定义一个注解配置文件如下

// 定义一个注解配置文件 必须要加上@Configuration注解
@Configuration
public class MainConfig {
    // 定义一个bean对象
    @Bean
    public User getUser(){
        return new User("gg",26);
    }
}
// 使用AnnotationConfigApplicationContext获取spring容器ApplicationContext2
ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
System.out.println(bean2);

八、@Bean注解的源代码分析

//能够作用在方法和注解上,作用在方法上和作用在注解上的用处不同,通常作用在方法上。
//作用在注解上时会使得该注解也可以用于注册主键,属于Bean的衍生注解,与@Component类似。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface Bean {

    //设置生成的组件的id,如果不设置时,组件id默认为注解到方法名。
    @AliasFor("name")
    String[] value() default {};

    //与value属性互为别名,作用一样。
    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    //在spring5.1之后该属性不推荐使用。设置Autowire自动装配时首选的装配方式。
    @Deprecated
    Autowire autowire() default Autowire.NO;


    //该组件能否用@Autowired注解进行自动装配,默认true。
    //如果设置为false,则用Autowired注解进行自动装配时会报错。
    boolean autowireCandidate() default true;

    //指定bean创建的初始化方法
    String initMethod() default "";

    //指定bean的销毁回调方法。
    String destroyMethod() default "(inferred)";
}

  @baen注解的@Target是ElementType.METHOD,ElementType.ANNOTATION_TYPE也就说@Bean注解可以在使用在方法上,以及一个注释类型声明。

  具体参数如下:

value -- bean别名和name是相互依赖关联的,value,name如果都使用的话值必须要一致

name -- bean名称,如果不写会默认为注解的方法名称

autowire -- 自定装配默认是不开启的,建议尽量不要开启,因为自动装配不能装配基本数据类型、字符串、数组等,这是自动装配设计的局限性,以及自动装配不如显示依赖注入精确

boolean autowireCandidate() default true;该属性设置该组件能否用@Autowired注解进行自动装配,默认true。如果设置为false,则用Autowired注解进行自动装配时会报错。

initMethod -- bean的初始化之前的执行方法,该参数一般不怎么用,因为可以完全可以在代码中实现

destroyMethod -- bean销毁执行的方法

1、为user用户bean增加注解名称如下:@Bean(value="user0",name="user0")

  如果value和name内容不一样如:@Bean(value="user0",name="user1"),会报错:attribute 'name' and its alias 'value' are declared with values of [{user1}] and [{user0}], but only one is permitted

2、在用户bean中增加初始化和销毁的方法如下:

public void initUser(){
    System.out.println("初始化用户bean之前执行");
}
public void destroyUser(){
    System.out.println("bean销毁之后执行");
}

  注解如下:@Bean(value="user0",name="user0",initMethod="initUser",destroyMethod="destroyUser")

(1)String initMethod() default “”;

  设置组件实例化时的初始化方法,会在依赖注入之后执行。属于bean生命周期的其中一个时期。初始化方法不能带有参数。

(2)String destroyMethod() default “(inferred)”;

  设置组件实例化时的销毁方法,通常用于释放一些资源,触发时机是IOC容器关闭或者该组件从IOC容器中移除时触发该方法。销毁方法也不能带有参数。

原文地址:https://www.cnblogs.com/goloving/p/14926322.html