2.1、组件添加
1、@Configuration(不懂看下面文字描述)
- 基本使用
- Full模式与Lite模式
- 示例
- 最佳实战
- 配置 类组件无依赖关系用Lite模式(
@Configuration(proxyBeanMethods = false)
)加速容器启动过程,减少判断 - 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(
@Configuration(proxyBeanMethods = true)
)
- 配置 类组件无依赖关系用Lite模式(
怎么给容器里添加组件
准备了俩个类:
User.java
package com.chentianyu.boot.bean;
/**
* 用户类
*/
public class User {
private Integer age;
private String name;
public User(){}
public User(Integer age , String name){
this.age = age;
this.name = name;
}
public Integer getAge(){return age;}
public void setAge(Integer age){this.age = age;}
public String getName(){return name;}
public void setName(String name){this.name = name;}
public String toString(){
return "User{" +
"name = " + name +
", age = " + age +
"}";
}
}
Pet.java
public class Pet {
private String name;
public Pet(){}
public Pet(String name){this.name = name;}
public String getName(){return name;}
public void setName(String name){this.name = name;}
public String toString(){
return "Pet{" +
"name = " + name + '\'' +
"}";
}
}
如果用以前原生的Spring
得:
resources -> new -> XML Configuration File -> Spring Config -> 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user01" class="com.chentianyu.boot.bean.User">
<!--俩个属性赋个默认值-->
<property name="name" value="user01"></property>
<property name="age" value="18"></property>
</bean>
<!--我们给容器中注册了一个组件-->
<bean id="cat" class="com.chentianyu.boot.bean.Pet">
<property name="name" value="tomcat"></property>
</bean>
</beans>
这是以前用spring
xml形式
springboot
不是这种xml形式,springboot
怎么给容器中添加组件第一种:
springboot不写配置文件了在springboot底层可以使用
@Configuration
注解package com.chentianyu.boot.config; import org.springframework.context.annotation.Configuration; @Configuration//告诉SpringBoot这是一个配置类(把这个类说明@Configuration类似于告诉spring这个文件是配置文件) public class MyConfig {//这就等于创建了一个文件(beans.xml) }
我们虽然不能写标签了,但是能写方法:
@Bean //给容器中添加组件。以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例(我们不用<Bean>标签,我们用@Bean注解) public User user01(){ return new User(18, "lisi"); }
不想方法名作为组件名:
@Bean("名字")
验证:在主程序中:
MainAppliction.java
//从容器中获取组件 run.getBean(组件名,组件的类型); 例如: Pet tom01 = run.getBean("tom",Pet.class);
是不是单实例(验证):
System.out.println("组件:" + (tom01 == tom02));
组件:true
注册的组件就是单实例的
/** * @Configuration 标注在一个类上告诉它(Spring)这是一个配置类 * 配置类里面我们可以使用@Bean标注在方法上给容器注册组件,默认也是单实例的 * 需要注意的是@Configuration标注的这个类,它本身也是一个组件 */
验证
MyConfig.java
是否是一个组件:MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean);
结果:
com.chentianyu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$1edd5731@286b39c2
最大的特性:在SpringBoot2.0以后的版本里多了一个
boolean proxyBeanMethods() default true;
是基于Spring5.2以后多的属性:Configuration.java
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // 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;//默认是true }
@Configuration(proxyBeanMethods = true)
proxyBeanMethods:代理Bean的方法
我们这个
bean
配置类里面申明过User
和Pet
俩个方法,能不能调用配置类的俩个方法:bean.user01();
我们把配置类的
user01()
方法多调用几遍,会不会得到不一样的对象?配置类它的作用就是这个
user01()
方法是给容器中添加组件的(我们使用@Bean标注了)但是我们在外部把这个user01()
方法多调了俩遍,那我们这个方法返回的这个对象是从容器中拿呢?还是就是普通的调用方法?答案是
true
也就是说我们配置类里面一旦组件注册的这个方法,你在外面调用多少遍,它都是拿到容器中的那个实例
/** * 外部无论对配置类中的这个组件注册方法调用多少次,获取的都是之前注册容器中的单实例对象 * @return */ @Bean //我们不用<Bean>标签,我们用@Bean注解 public User user01(){ return new User(18, "lisi"); }
相等的原因就是我们
proxyBeanMethods
属性。我们这个
proxyBeanMethods
属性说这个方法会不会被代理,默认会(true),会的情况下我们就看到我们容器(MyConfig
)获取到我们组件的这个对象。MyConfig
本身就不是一个普通的对象(com.chentianyu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$7ab85b6@4905c46b
)它是我们这个EnhancerBySpringCGLIB
相当于被SpringCGLIB
增强了的代理对象。//这里获取到的本身就是代理对象 MyConfig bean = run.getBean(MyConfig.class);
代理对象调用
user01()
我们springboot
里面的默认逻辑就是:如果@Configuration(proxyBeanMethods = true)
是true , 我们这个类获取到的就是代理对象。调用方法。我们springboot
就会默认检查容器中有没有这个方法以及返回的组件如果有就拿,如果没有就调用新创//@Configuration(proxyBeanMethods = true)代理对象调用方法。springboot总会检查这个组件是否存在于容器中 //保持组件单实例 User user01 = bean.user01();
如果
@Configuration(proxyBeanMethods = false)
呢?我们从容器中拿到(
getBean()
)的这个对象类型是什么类型?我们发现拿到的类型不再是
代理对象
(com.chentianyu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$7ab85b6@4905c46b
)而是com.chentianyu.boot.config.MyConfig@2adddc06
。User user01 = bean.user01();
多次调用方法就不是true
相等的值。这个就衍生出了:
SpringBoot
在底层@Configuration的俩个配置:一个是Full(全配置@Configuration(proxyBeanMethods = true))和Lite(轻量级配置@Configuration(proxyBeanMethods = false))。也就是说以后我们想要给容器中添加组件的时候,我们编写一个配置类,然后如果我们proxyBeanMethods
为true
的情况下,我们配置类每一个给容器中每一个给组件注册的方法,在外边随便调用,它都会从容器中找组件;但是如果proxyBeanMethods
为false
的情况下,我们MyConfig
配置类再也不会保存代理对象,你在外边无限次调用这个方法,每一次调用都会产生一个新的方法(new User(18,"lisi")
)。这个来解决什么场景呢?
组件依赖
举个例子:
有一个
User.java
package com.chentianyu.boot.bean; /** * 用户类 */ public class User { private Integer age; private String name; private Pet pet;//假设用户要养一个宠物 public User(){} public User(Integer age , String name){ this.age = age; this.name = name; } public Integer getAge(){return age;} public void setAge(Integer age){this.age = age;} public String getName(){return name;} public void setName(String name){this.name = name;} public Pet getPet(){return pet;} public void setPet(Pet pet){this.pet = pet;} public String toString(){ return "User{" + "name = " + name + ", age = " + age + ", pet = " + pet + "}"; } }
在
MyConfig.java
中我们的用户想要用我们容器中之前注册的这个tomcatPet()
方法(宠物),那如果我调成@Configuration(proxyBeanMethods = false)
模式,因为:@Bean //我们不用<Bean>标签,我们用@Bean注解 public User user01(){ User lisi = new User(18, "lisi"); lisi.setPet(tomcatPet()); retrun lisi; }
但是如果是
@Configuration(proxyBeanMethods = true)
模式的话,这个依赖就是成立的,我们用户(lisi
)用的这个宠物(setPet()
)跟容器中用的宠物一模一样说明我们User
组件依赖了tomcatPet
组件。测试:
User user01 = run.getBean("user01",User.class); Pet tom = run.getBean("tom",Pet.class); System.out.println("用户的宠物:" + (user01.getPet() == tom);
在
@Configuration(proxyBeanMethods = true)
情况下为true
:用户的宠物就是容器里的宠物。但如果是
@Configuration(proxyBeanMethods = false)
现象是:我这个用户想要用的宠物,我们把人家的方法调一遍,但这个方法又不是经过代理的,相当于这个方法直接调,它又帮我们new了一个宠物,虽然宠物的名字都是tomcat , 但是不是容器中的宠物。springboot最大的更新
我们Lite模式(轻量级配置@Configuration(proxyBeanMethods = false))就是说调成
false
的优点是说我们SpringBoot
不会来检查我们这个方法返回的值在容器(@Bean
)中有没有,跳过检查,springboot
直接启动,运行起来就非常快;调成true
你每一次外界对它的调用它都会检查容器中是否有。*Full和Lite最佳实战:
如果我们对容器中单单注册组件,别人也不依赖这些组件我们一般都调成
false
这样我们springboot
启动起来会非常快加载起来也特别快;如果我们的这个组件明显下面还要用、还要依赖我们就把proxyBeanMethods
调成true
保证它依赖的组件就是容器中的组件