首先,我们先简单描述一下这三个的作用:
@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean。另外@Bean注解的方法返回值是对象,可以在方法中为对象设置属性。
@Configuration即用来代替Spring配置文件的,它就是一个@Component组件,接收一个value值也就是bean的名字,value可以不填。
但是上面的讲解都只是冰冷的概念,决定重新捡起来,好好研究这些配置,因为虽说平时自己写的时候都是机械性的,甚至是试探性的,但是还是要更深入一点。
为什么要先讲这三个概念,是因为他们之间的关系,有些你中有我我中有你,最好先有一个大概的了解。
@Component:通用的注解! 通俗的讲:@Compent就是说,只要你想将你写的类交给Spring容器来管理,那就可以用它!
1、@controller:controller控制器层(注入服务)
2、@service:service服务层(注入dao)
3、@repository:dao持久层(实现dao访问)
为什么摆出上面这三个,因为他们其实就是@Compent,例如我们进入到@Controller里看一下,发现在Controller上就有一个@Component,剩下的两个同理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package org.springframework.stereotype; import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { @AliasFor( annotation = Component.class ) String value () default "" ; }
一步一步来,先看最简单的功能
先写一个类,用@Compent注解上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.newcrud .learn ; import lombok.Data ;import org.springframework .stereotype .Component ; @Data @Component public class KangShiFuTwo { String name; Integer age; public void getKangShiFuTwo ( ){ System .out .println ("getKangShiFuTwo" ); } }
再写一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.newcrud.learn; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;import org.testng.annotations.Test; @SpringBootTest public class KangShiFuTwoTest extends AbstractTestNGSpringContextTests { @Autowired KangShiFuTwo kangShiFuTwo; @Test public void testGetKangShiFuTwo () { kangShiFuTwo.getKangShiFuTwo(); } }
执行结果
可以用@Autowired获取到说明已经在Spring容器里了
@Bean 我们先看一下@Bean的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package org.springframework.context.annotation; import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.beans.factory.annotation.Autowire;import org.springframework.core.annotation.AliasFor; @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Bean { @AliasFor("name") String[] value() default {}; @AliasFor("value") String[] name() default {}; @Deprecated Autowire autowire () default Autowire.NO; boolean autowireCandidate () default true ; String initMethod () default "" ; String destroyMethod () default "(inferred)" ; }
emmm,他没有@Compent
引入第三方的类 那,我们首先创建一个没有@Compent的类
1 2 3 4 5 6 7 8 9 10 11 12 package com.newcrud .learn ; import lombok.Data ; @Data public class KangShiFuOne { String name; Integer age; public void getKangShiFuOne ( ){ System .out .println ("getKangShiFuOne" ); } }
再来写一个配置类
1 2 3 4 5 6 7 8 9 10 11 12 package com.newcrud.learn; import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration; @Configuration public class MyConfiguration { @Bean public KangShiFuOne getKangShiFuOneConfig () { return new KangShiFuOne (); } }
再来写一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.newcrud.learn; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;import org.testng.annotations.Test;@SpringBootTest public class MyConfigurationTest extends AbstractTestNGSpringContextTests { @Autowired KangShiFuOne kangShiFuOne; @Test public void testGetKangShiFuTwoConfig () { kangShiFuOne.getKangShiFuOne(); } }
结果如下
能被@Autowired获取到,是因为@Configuration的作用,那@Bean的作用是什么呢,因为他有一个@Compent没有的功能,那就是他可以将第三方包里的类放到Spring容器里,比如说你在pom文件中引入的工具包,你并不能把@Compent放到工具包里面去对不对,那你就可以通过@Bean的方式来获取。
这个涉及到springboot的自动装载的概念,当你的bean不在你jar包的扫描目录下时,是没法实例化的给spring管理的。
好吧,上面的例子好像并不能展示出引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现的,也不能展示出@Bean的精准创建的功能,但是引用第三方库中的类,也实在懒得写了,我们就来再看一下精准创建的功能
@Bean精准创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.newcrud.learn;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class MyBean { @Bean("getKangShiFuOneHello") public KangShiFuOne getKangShiFuOne () { KangShiFuOne kangShiFuOne=new KangShiFuOne (); kangShiFuOne.setAge(19 ); kangShiFuOne.setName("zhangsan" ); kangShiFuOne.getKangShiFuOne(); return kangShiFuOne; } }
再写一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.newcrud.learn;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.context.ApplicationContext;import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;import org.testng.annotations.Test;import static org.testng.Assert.*;@SpringBootTest public class MyBeanTest extends AbstractTestNGSpringContextTests { @Autowired ApplicationContext applicationContext; @Test public void testGetKangShiFuOne () { KangShiFuOne kangShiFuOne=(KangShiFuOne) applicationContext.getBean("getKangShiFuOneHello" ); System.out.println(kangShiFuOne); } }
结果
1 KangShiFuOne(name =zhangsan, age=19 )
发现已经从上下文中获取到了我们精准定制化的getKangShiFuOneHello。
@Configuration 官方文档描述:用@Configuration注释类表明其主要目的是作为bean定义的源
我们再来看一下@Configuration的源代码,发现在上面也有一个@Compent,那就说明,它也有@Compent的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package org.springframework.context.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor;import org.springframework.stereotype.Component;@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor( annotation = Component.class ) String value () default "" ; boolean proxyBeanMethods () default true ; }
通俗的来讲,这个就是代表它所注释的类是一个配置类,但什么才是配置类呢,上面我们也看到了@Configuration好像也就是个@Compent啊,没啥大用
那我们先来编写两个类
1 2 3 4 5 6 7 8 9 10 11 12 package com.newcrud.learn;import lombok.Data;@Data public class KangShiFuFour { public KangShiFuFour () { System.out.println("getKangShiFuFour" ); } String name; Integer age; }
1 2 3 4 5 6 7 8 9 10 11 12 package com.newcrud.learn;import lombok.Data;@Data public class KangShiFuThree { public KangShiFuThree () { System.out.println("getKangShiFuThree" ); } String name; Integer age; }
再来写我们的@Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.newcrud .learn ; import org.springframework .context .annotation .Bean ;import org.springframework .context .annotation .Configuration ;@Configuration public class MyConfigurationTwo { @Bean public KangShiFuThree getKangShiFuThree ( ){ return new KangShiFuThree (); } @Bean public KangShiFuFour getKangShiFuFour ( ){ getKangShiFuThree (); return new KangShiFuFour (); } }
再写我们的测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.newcrud.learn;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;import org.testng.annotations.Test;@SpringBootTest public class MyConfigurationTwoTest extends AbstractTestNGSpringContextTests { @Test public void testGetKangShiFuThree () { } }
在启动后会打印两个类的初始化函数输出
1 2 getKangShiFuThree getKangShiFuFour
但是我们仔细想一下,我们是调用了两次getKangShiFuThree的构造函数的,但是只打印了一次,这也是他的一个功能:保持单例模式!
我们再来看一下他的另一个功能
@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.newcrud .learn ; import org.springframework .context .annotation .Bean ;public class MyConfigurationTwo { @Bean public KangShiFuThree getKangShiFuThree ( ){ return new KangShiFuThree (); } @Bean public KangShiFuFour getKangShiFuFour ( ){ getKangShiFuThree (); return new KangShiFuFour (); } }
@Bean注解修饰带参数方法
1 2 3 4 5 6 @Bean public TestBean createTestBean (Demo demo1) { TestBean testBean = new TestBean (); testBean.setDemo(demo1); return testBean; }
如果Spring容器中只有一个Demo类型的bean, 则默认按照类型匹配注入Demo的bean,如果有多个Demo类型的bean,则按照名称(此处应为demo1)匹配注入对应的bean。
如果@Bean标注的方法,其参数若为List集合,则获取继承或实现该类的所有已注入的Bean,验证如下:
Person.java
1 2 3 4 5 6 7 public interface Person { String inf () ; }
Student.java
1 2 3 4 5 6 7 8 9 10 public class Student implements Person { private String work; public Student (String work) { this .work=this .getClass().getName()+":" +work; } @Override public String inf () { return this .work; } }
Worker.java
1 2 3 4 5 6 7 8 9 10 public class Worker implements Person { private String work; public Worker (String work) { this .work=this .getClass().getName()+":" +work; } @Override public String inf () { return this .work; } }
AnimalAutoConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Configuration public class AnimalAutoConfiguration { @Bean public Student getUser () { return new Student ("学生" ); } @Bean public Worker getWorker () { return new Worker ("上班族" ); } @Bean public List<Person> getAnimals (List<Person> list) { return list; } }
Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RestController public class Test { @Autowired private Student student; @Autowired private Worker worker; @Autowired private List<Person> animals; @GetMapping(value = "/get/info") public void test () { System.out.println("student--->" +student.inf());; System.out.println("worker--->" +worker.inf());; for (Person person:animals) { System.out.println("animals--->" +person.inf());; } } }
输出:
1 2 3 4 student--->com.ioc .Student :学生 worker--->com.ioc .Worker :上班族 animals--->com.ioc .Student :学生 animals--->com.ioc .Worker :上班族