1.组件添加

1.1.@Configuration

我们在这里准备了两个组件,宠物类和用户类,用户类包含用户姓名和年龄,宠物类包含宠物名

public class User {
    public String name;
    public int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
public class Pet {
    public String name;
    public Pet(){
        
    }

    public Pet(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }
}

如果我们是用原生的spring,我们想要把它们添加到容器中,我们可以创建一个spring的配置文件,

<?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.yujie.boot.bean.User">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>
    <bean id="cat" class="com.yujie.boot.bean.Pet">
        <property name="name" value="Tomcat"></property>
    </bean>
</beans>

而现在我们的springboot已经不需要写这些xml配置文件了,springboot怎么给容器中添加这些组件呢?

在springboot底层我们可以使用@Configuration注解

基本使用

config类

/*
1.配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的。
2.配置类本身也是组件
3.
 该注解里面有boolean proxyBeanMethods() default true;
 与springboot1的不同proxyBeanMethods属性默认为true
 代表代理bean的方法
 Full、Lite配置
 Full模式:proxyBeanMethods如果是true, 无论调用了多少次,都是从容器中找组件。Full模式
 Lite模式:将其调成false,配置类在容器中不会保存代理对象,在外面每一次调用一次,都会新创建对象
 组件依赖。
 比如我们在User类里面加入了Pet组件,此时应该把proxyBeanMethods设置为true,就是对的,说明user依赖
 了pet组件

* */
@Configuration(proxyBeanMethods=true)  //告诉springboot这是一个配置类,等同于以前的配置文件
public class MyConfig {
    /*
    给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型。
    返回的值,就是组件在容器中的实例
    外部无论对这个配置类中的这个组件注册方法调用多少次,获取的都是之前注册容器中的单实例。
    * */
    @Bean
    public User user01(){
        return new User("zhangsan",18);
    }
    @Bean("tom")     //注解中直接赋予名字,此时方法名就不作为组件的id
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }

}

主程序类

/*
* @SpringBootApplication该注解告诉这是一个springboot应用
* 主程序类。所有启动的入口
* 固定写法
* */
@SpringBootApplication(scanBasePackages="com.yujie")
public class MainApplication {
    public static void main(String[] args) {
        //1.返回我们的IOC容器
        ConfigurableApplicationContext run=SpringApplication.run(MainApplication.class,args);
        //2.查看容器里面的组件
        String [] names=run.getBeanDefinitionNames();
//        for(String name:names){
//            System.out.println(name);
//        }
        //3.从容器中获取组件
        Pet tom01=run.getBean("tom",Pet.class);
        Pet tom02=run.getBean("tom",Pet.class);
        System.out.println("组件"+(tom01==tom02));

        //配置类本身也是一个组件,我们看能不能获取配置类
        //这里本身就是代理对象
        MyConfig bean=run.getBean(MyConfig.class);
        System.out.println(bean);

        //当使用proxyBeanMethods=true时,我们直接调用配置类中的方法
        //也就是代理对象调用的方法
        //springboot总会检查这个组件是否在容器中有,如果有就不会新创建,一句话就是保持组件单实例
        //如果是false,此时就不再是代理对象,多次调用的时候输出就是false
        User user=bean.user01();
        User user1=bean.user01();
        System.out.println(user=user1);
    }
}

Full模式与Lite模式

示例
最佳实战
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式

再次总结一下轻量级模式和全模式模式:
调成false轻量级模式,springboot就不会去检查这个方法返回的东西在容器中有没有,即跳过检查,运行起来就会比较快。
调成true全模式,每一次外界对它的调用都会去容器中检查有没有。
推荐:如果只是给容器中注册组件,别人也不依赖我们的组件,我们一般都把它调成false,这样加载起来也比较快
如果我们组件在后面还要用,还要依赖,就设置为true,能保证依赖的组件就是容器中的依赖

1.2.@Bean、@Component、@Controller、@Service、@Repository

给容器中注册组件也可以使用以前的办法,比如上面,可以使用@Component代表是一个组件。@Controller、@Service、@Repository代表控制层、逻辑层、数据库层组件,默认只要这些组件写在包扫描的范围内。它们都是以前的用法

1.3.@ComponentScan、@Import

ComponentScan指定包扫描规则。
@Import可以给容器中导入组件。可以写在配置类或者组件里面。
@Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名

配置类

@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
}

测试类

//1、返回我们IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

//...

//5、获取组件
String[] beanNamesForType = run.getBeanNamesForType(User.class);

for (String s : beanNamesForType) {
    System.out.println(s);
}

DBHelper bean1 = run.getBean(DBHelper.class);
System.out.println(bean1);

1.4.@Conditional条件装配

条件装配:满足Conditional指定的条件,则进行组件注入

下图是它派生的各种注解

java 注解给实体类赋初值 java注解底层实现_java 注解给实体类赋初值


每一个注解都代表不同的功能,比如ConditionalOnBean:即当容器中存在我们指定的bean的时候,我们才干某些事情,ConditionalOnMissingBean,当容器中没有这些组件的时候,我们就干某些事情,ConditionOnclass,当容器中有某些类的时候,我们才干某种事情。ConditionalResource.当项目的类路径中存在指定的资源的时候,才干某些事情.ConditionalOnWebApplication.当我们是web应用的时候,我们才干某些事情,ConditionalOnSingleCandidate,指定组件只有一个实例,或者多个实例,但只有一个主实例,等等。

实例,当我们把MyConfig类中的tomcatPet方法上面的@Bean注解注释掉之后,

@Bean
    public User user01(){
        return new User("zhangsan",18);
    }
    //@Bean("tom")     //注解中直接赋予名字,此时方法名就不作为组件的id
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }

主程序类中输出

boolean tom=run.containsBean("tom");
        System.out.println("容器中Tom组件 :" +tom);
        boolean user01=run.containsBean("user01");
        System.out.println("容器中Tom组件 :" +user01);

java 注解给实体类赋初值 java注解底层实现_spring_02


注意,此时User类中包含组件Pet类

public class User {
    public String name;
    public int age;
    public Pet pet;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", pet=" + pet +
                '}';
    }
}

java 注解给实体类赋初值 java注解底层实现_java_03


由于我们用户依赖这个宠物,而我们容器中没有这个宠物,所以也希望容器中没有宠物的时候,也别注册用户了。所以我们可以使用@ConditionalOnBean注解,也就是在容器中有某个组件,我们就可以干嘛

@ConditionalOnBean(name=“tom”)表示当容器中有tom这个组件的时候,我们就给容器中注入user01

@ConditionalOnBean(name="tom")
    @Bean
    public User user01(){
        return new User("zhangsan",18);
    }

java 注解给实体类赋初值 java注解底层实现_spring boot_04

如果我们在类上写上

@ConditionalOnBean(name="tom")
public class MyConfig {
}

当容器中有tom组件的时候,下面一堆才生效,否则就都不生效。

条件注解写到方法上,只有当条件成立之后, 这个方法返回的组件才会被注册到类中。否则就不生效。写在类上也类似。

@ConditionalOnMissingBean(name=“tom”)表示当容器中没有tom组件,我们就创建。

2.原生配置文件引入

2.1.@ImportResource

假如我们此时也有配置文件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.yujie.boot.bean.User">
        <property name="name" value="haha"></property>
        <property name="age" value="18"></property>
    </bean>
    <bean id="cat" class="com.yujie.boot.bean.Pet">
        <property name="name" value="hehe"></property>
    </bean>
</beans>

java 注解给实体类赋初值 java注解底层实现_spring boot_05

可以看到,配置文件并没有生效,

如果我们不想放弃这个配置文件怎么办

比如,公司使用bean.xml文件生成配置bean,然而你为了省事,想继续复用bean.xml,@ImportResource粉墨登场。

@ImportResource注解可以导入spring的配置文件,让其生效。
当我们加上了注解之后

@ImportResource(“classpath:beans.xml”)

public class MyConfig {
}

java 注解给实体类赋初值 java注解底层实现_spring_06

3.配置绑定

如何利用Java读取到properties文件中的内容,并且把它封装到Javabean中,以供随时使用

传统方法:

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }

3.1.@ConfigurationProperties + @Component

Spring Boot一种配置配置绑定:

@ConfigurationProperties + @Component

假设有配置文件application.properties

mycar.brand=BYD
mycar.price=100000

只有在容器中的组件,才会拥有springboot提供的强大功能

@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
    private String brand;
    private Integer price;
    public String getBrand(){   return brand;}
    public void setBrand(String brand){ this.brand=brand;}
    public Integer getPrice() {
        return price;
    }
    public void setPrice(Integer price) {
        this.price = price;
    }
    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

控制器

public class HelloController {
    @Autowired
    Car car;
    @RequestMapping("/car")
    public Car car(){
        return car;
    }
    /*这里表示请求的路径是/hello*/
    @RequestMapping("/hello")
    public String handle01(){
        return "hello,Spring Boot 2";
    }

}

java 注解给实体类赋初值 java注解底层实现_spring_07

3.2.@EnableConfigurationProperties + @ConfigurationProperties

1.开启Car配置绑定功能
2.把这个Car这个组件自动注册到容器中

@EnableConfigurationProperties(Car.class)
public class MyConfig {
...
}
@ConfigurationProperties(prefix = "mycar")
public class Car {
...
}

要么加载到容器中,要么开启配置绑定功能