关于@Bean的使用方式
作者:飘零未归人
@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。
目的是创建一个类。当spring需要创建指定的一个类时会调用这个注解(@Bean)的方法。
一个对象
@Data @AllArgsConstructor @NoArgsConstructor public class Info { String name; int age; }
就如同在在xml配置中添加一个类的配置。这样spring在自动创建这个类时会调用配置的方法。
<bean id="info" class="zhong.test.springbootdemo.usultestdemo.demo.Info"> <property name="name" value="sjl"/> <property name="age" value="18"/> </bean>
SprignBoot中 @Bean 完美的替换了了上面的这种在xml中配置的方法。使用以下方法就能让spring在需要自动创建Info对象时,自动调用这个方法。
@Configuration public class BeanInit { /** * 测试@Bean的作用 */ @Bean(name = "info") public Info getInfo(){ return new Info("gfagduis", 2343); } @Bean(name = "info1") public Info getInfo1(){ return new Info("gfagdufasfdfdais", 2343); } }
获取这个类的方法
1、AnnotationConfigApplicationContext 类获取
@Component public class BeanTest implements CommandLineRunner { private void getBean(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanInit.class); Info info1= ctx.getBean("info",Info.class); System.out.println(info1); } @Override public void run(String... args) throws Exception { System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<"); getBean(); } }
2、在spring自动创建的类中
在构造方法中设置这个值,这样spring就会调用该类的创建方法来获取这个初始化的类。
@Component public class BeanTest implements CommandLineRunner { private Info info; /** *spring创建BeanTest对象时会调用这个构造函数。参数是info1,所以会调用 @Bean(name = "info1") 注释的方法 * */ public BeanTest (Info info1){ this.info = info1; } private void getBean2(){ System.out.println(info); } @Override public void run(String... args) throws Exception { System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<"); getBean2(); } }
在构造函数中 BeanTest (Info info1) 参数的名称是info1.所以就调用 @Bean(name = “info1”) 的方法来获取初始化对象。
结果如下:
>>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<
Info(name=gfagdufasfdfdais, age=2343)
同时也是支持多别名配置的 例如:
@Bean(name = { "info12", "info3", "info3" }) public Info getInfo2(){ return new Info("gfa4361783ais", 2343); }
测试一下
@Component public class BeanTest implements CommandLineRunner { private Info info; private Info info1; private Info info2; /** *spring创建BeanTest对象时会调用这个构造函数。参数是info1,所以会调用 @Bean(name = "info1") 注释的方法 * */ public BeanTest (Info info1, Info info2, Info info3){ this.info = info1; this.info1 = info2; this.info2 = info3; } private void getBean2(){ System.out.println(info); System.out.println(info1); System.out.println(info2); } @Override public void run(String... args) throws Exception { System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<"); getBean2(); } }
结果如下
>>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<
Info(name=gfagdufasfdfdais, age=2343)
Info(name=gfa4361783ais, age=2343)
Info(name=gfa4361783ais, age=2343)
当然也可以使用 Qualifier(“xxx”) 来指定 @Bean(name = “xxx”) 中的xxx就可以调用对应的方法。
3、使用 @Autowired 注解获取变量
如果有多个初始化方法,则会根据变量名称来获取。
在初始化类中建立以下多个初始方法
@Configuration public class BeanInit { /** * 测试@Bean的作用 */ @Bean/*(initMethod = "init")*/ public Info getInfo(){ System.out.println("进入方法"); Info info = new Info("gfagduis", 2343); System.out.println("结束方法"); return info; } @Bean(name = "info1") public Info getInfo1(){ return new Info("gfagdufasfdfdais", 2343); } @Bean(name = { "info2", "info3", "info3" }) public Info getInfo2(){ return new Info("gfa4361783ais", 2343); } @Bean public Info info(){ return new Info("gfsdfadsfad4361783ais", 23143); }
然后在测试方法中分别用 @Autowired注释Info类来获取对应的对象
@Component public class BeanTest implements CommandLineRunner { //private Info info; @Autowired private Info getInfo; @Autowired private Info info1; @Autowired private Info info3; @Autowired private Info info; /*public BeanTest (Info info){ this.info = info; }*/ private void getBean(){ /*ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanInit.class); Info person = ctx.getBean("info",Info.class); System.out.println(person);*/ System.out.println(getInfo); System.out.println(info1); System.out.println(info3); System.out.println(info); } @Override public void run(String... args) throws Exception { System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<"); getBean(); } }
测试结果如下:
>>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<
Info(name=gfagduis, age=2343)
Info(name=gfagdufasfdfdais, age=2343)
Info(name=gfa4361783ais, age=2343)
Info(name=gfsdfadsfad4361783ais, age=23143)
在使用 @Autowired 注解时,也可以与 @Qualifier(“xxx”) 指定使用哪个方法来获取对象。
@Bean 注解的属性解析
value 和 name
name 和 value 两个属性是相同的含义的, 在代码中定义了别名。 为 bean 起一个名字,如果默认没有写该属性,那么就使用方法的名称为该 bean 的名称
autowire
装配方式 有三个选项
- Autowire.NO (默认设置)
- Autowire.BY_NAME
- Autowire.BY_TYPE
指定 bean 的装配方式, 根据名称 和 根据类型 装配, 一般不设置,采用默认即可。
initMethod
bean 的初始化方法, 直接指定方法名称即可,不用带括号。初始方法要在对应的POHO中,也就是Info类中。
@Data @AllArgsConstructor @NoArgsConstructor public class Info { String name; int age; private void init(){ System.out.println("进入方法2"); name = "fhasjdf"; age = 12; } } @Configuration public class BeanInit { /** * 测试@Bean的作用 */ @Bean(name = "info", initMethod = "init") public Info getInfo(){ System.out.println("进入方法"); Info info = new Info("gfagduis", 2343); System.out.println("结束方法"); return info; }
最终结果、:
进入方法
结束方法
进入方法2
2019-09-24 14:57:11.917 INFO 11656 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-09-24 14:57:12.048 INFO 11656 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-24 14:57:12.051 INFO 11656 --- [ main] z.t.s.u.start.UsulTestStartApplication : Started UsulTestStartApplication in 1.74 seconds (JVM running for 2.246)
>>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<
Info(name=fhasjdf, age=12)
所以最终调用的是init()方法来创建类。
destroyMethod
bean 的销毁方法, 在调用 IoC 容器的 close() 方法时,会执行到该属性指定的方法。不过,只是单实例的 bean 才会调用该方法,如果是多实例的情况下,不会调用该方法。
注意:
由于spring构造的对象都是单例的,所以只要是从spring ioc中获取的对象都是同一个,他们中的属性,即便是私有的非静态属性。都是公用的。
public class MyDisposableBean implements DisposableBean { public Map<String, String> map = new HashMap<>(); int i = 0; private void init(){ System.out.println("MyDisposableBean自定义初始化方法"); } private void destroyMethod(){ System.out.println("自定义销毁方法,MyDisposableBean对象销毁"); } public void pringt(){ System.out.println("-------------------------------"); System.out.println("i=" + i); System.out.println("map.size=" + map.size()); map.entrySet().stream().forEach(item -> System.out.println("map -> "+ item.getKey() + "---" + item.getValue())); } }
@Configuration public class BeanConfigurer { @Bean(name = "MyDisposableBean", initMethod = "init", destroyMethod = "destroyMethod") public MyDisposableBean getMyDisposableBean(){ System.out.println("MyDisposableBean构造方法"); return new MyDisposableBean(); } }
@Component public class DisposableBeanTest implements CommandLineRunner { @Override public void run(String... args) throws Exception { MyDisposableBean myDisposableBean = new MyDisposableBean(); MyDisposableBean myDisposableBean1 = new MyDisposableBean(); MyDisposableBean myDisposableBean2 = SpringContextUtils.getBean(MyDisposableBean.class); myDisposableBean2.pringt(); myDisposableBean2.map.put("1", "2"); myDisposableBean2.i = 2; MyDisposableBean myDisposableBean3 = SpringContextUtils.getBean(MyDisposableBean.class); myDisposableBean3.pringt(); myDisposableBean1.pringt(); } }
测试结果:
MyDisposableBean构造方法
MyDisposableBean自定义初始化方法
-------------------------------
i=0
map.size=0
-------------------------------
i=0
map.size=0
-------------------------------
i=2
map.size=1
map -> 1---2
-------------------------------
i=0
map.size=0
可以看出,如果是我们自己在程序中new的对象(myDisposableBean 和 myDisposableBean1),其私有属性map和i是不变的。不相互影响。
另外两从spring ioc中获取的对象(myDisposableBean2 和 myDisposableBean3 )的map和i两个属性,是公用的。其中一个对象修改值,name另外一个对象的属性也相应的变化
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。