spring-boot关于spring全注解IOC

什么是IOC容器:

Spring IoC 容器是一个管理Bean 的容器,在S pring 的定义中,它要求所有的IoC 容器都需要实
现接口BeanFactory ,它是一个顶级容器接口

IoC 是一种通过描述来生成或者获取对象的技术,而这个技术不是Spring 甚至不是Java 独有的。
对于Java 初学者更多的时候所熟悉的是使用new 关键字来创建对象,

 

spring-boot和spring中IOC容器的区别

Spring 中,它是通过描述来创建对象。只是S pring Boot 并不建议使用XML ,而是通过注解的描述生成对象,

所以他的原理还是一样的。

IOC容器的作用:

的班级、同学和老师这3 个对象关系,我们需要一个容器。在S pring 中把每一个
需要管理的对象称为Spring Bean (简称Bean ),而Spring 管理这些Bean 的容器,被我们称为S pring
IoC 容器(或者简称IoC 容器) 。IoC 容器需要具备两个基本的功能:
•@通过描述管理Bean , 包括发布和获取Bean; .

@通过描述完成Bean 之间的依赖关系。

 

简单的注入Bean

在Spring 中允许我们通过XML 或者Java 配置文件装配Bean , 但是由于Spring Boot 是基于注
解的方式,所以通过注解方式实现

 

需要加载近IOC容器的累pojo

public class User {
    private String name;
    private int id;

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

 

添加bean配置累:

@ Configuration 代表这是一个Java 配置文件, Sprin g 的容器会根据
它来生成IoC 容器去装配Bean;

@Configuration
public class TestConfig {
    @Bean("user")
    public User userTest(){
        return new User("quan",23);
    }
}

这里@bean注解的类方法,将类方法的返回值加载到ioc容器当中,其中里面的字符串代表注入容器Bean的名字。不写默认是方法名称

 

可以通过下面的测试方法进行测试:

public class userTest {
//    private Logger logger = LoggerFactory.getLogger(getClass());
    private static Logger logger = LoggerFactory.getLogger(userTest.class);

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
        User user = (User) context.getBean("user");
        logger.info(user.getName());
    }
}

 

 

springboot中更方便的装配Bean:

11通过扫描装配的Bean

如果一个个的Bean 使用注解@Bean 注入Spring loC 容器中,那将是一件很麻烦的事情。

 

#####################################################################

 

Spring 还允许我们进行扫描装配Bean 到loC 容器中,对于扫描装配而言使用的注解是

 

@Component
@ComponentScan 。

 

@Component 是标明l哪个类被扫描进入Spring IoC 容器,

@ComponentScan则是标明采用何种策略去扫描装配Bean

 

#########################################################################

 

 

 

 User:

/*
这个注解表面这个类被springIOC容器扫描装配
user是作为Bean的名称,如果不配置就按照你的类名第一个小写,其他不变
 */
@Component("user")
public class User {
//    指定具体的值,
    @Value("quan")
    private String name;
    @Value("12")
    private int id;

    public User() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

注意:这里需要加入无参构造函数!!!!!!!!!!!!注入bean需要一个无参构造函数

配置累:,后面可以不用了。

@Configuration
//默认只会扫描这个类所在的包或子包
@ComponentScan
public class TestConfig {
}

测试结果也是一样的

@ComponentScan允许自定义扫描包的:

 

将User放到com.quan.learning.DAO下之后:

@Configuration
//默认只会扫描这个类所在的包或子包
@ComponentScan("com.quan.learning.DAO")
public class TestConfig {
}

也是可以扫秒到的

 

可以使用正则表达式匹配;

 

@Configuration
//默认只会扫描这个类所在的包或子包
@ComponentScan("com.quan.learning.*")
public class TestConfig {
}

 

 

不想加载服务类的bean

 

 

@Configuration
//默认只会扫描这个类所在的包或子包
@ComponentScan(value = "com.quan.learning.*",excludeFilters = {@Filter(classes = {Service.class})})
public class TestConfig {
}

 

@Filter的源码;

 @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }

 

测试累加上:

UserService service = (UserService) context.getBean("userService");

 

 

 

 

 

 

自定义第三方bean“

 

加入第三方依赖:

<!--        自定义第三方bean-->
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>

 

自定义BEan

@Configuration
//默认只会扫描这个类所在的包或子包
@ComponentScan(value = "com.quan.learning.*",excludeFilters = {@Filter(classes = {Service.class})})
public class TestConfig {

    @Bean("datasource")
    public DataSource getDataSource(){
        Properties  properties = new Properties();
        properties.setProperty("driver","com.mysql.jdbc.Driver");
        properties.setProperty("url","jdbc:mysql://localhost:3306/ApolloConfigDB");
        properties.setProperty("username","root");
        properties.setProperty("password","1997");
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}

 

 

依赖的注入:

装配bean之后就是要获取bean,那么获取bean的过程就是依赖的注入Dependence injection DI

 

 

 

 

 

 

/*
编程思路:定义动物和人的接口层,分别去实现这两个接口
在实现人的接口的时候,需要加入animal的属性,所以需要引入依赖
 */
//动物接口
public interface Animal {
    public void use();
}

################
//人的接口
public interface Person {
    public void service();

}


###############
@Component
public class Dog implements Animal{
    @Override
    public void use() {
        System.out.println(Dog.class.getSimpleName()+"kanmen");
    }
}




##############
@Component
public class Rpersion implements Person{
    @Autowired
    private Animal animal = null;

    @Override
    public void service() {
        this.animal.use();
    }
}

 

test一下:

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
        Person person = context.getBean(Rpersion.class);
        person.service();
    }

 

 

 

 

//根据属性的类型找到对应的Bean进行注入,Dog是动物的一个实现类,
//所以将Dog的实例注入到Rpersion中,这样使用Rpersion的时候就能够使用Dog实例来服务

 

 

@Autowired 注解

 

 

如果我们加入多一个animal的实现类,继续单单使用@Autowired的话,

 

@Component
public class Cat implements Animal{
    @Override
    public void use() {
        System.out.println("catcat");
    }
}

 

 

 找到两个实例,springboot并不能判断我们要注入的是cat还是dog

 

通过修改属性的名字即可,类型不变:

 

 这鸭子就可以注入成功了,因为如果有两个同样类型的实例,@Autowired会根据属性名称去匹配

 

如果允许所加载的bean可以为null,一是使用上面的直接等于的方法,

另一种推荐用法为

 

 

   @Autowired(required = false)
    private Animal dog ;

 

 

 

 

不修改属性名消除歧义的方法:

@Primary:一般在装配的bean上,解决如果有多个同一个类型的实例,优先注入给这个标注的bean

 

@Component
@Primary
public class Cat implements Animal{
    @Override
    public void use() {
        System.out.println("catcat");
    }
}

这样子不会产生奇异,想使用那个就往那个类加上@Primary

 

 

如果cat已经使用了@Primary,那我们需要用dog,这时候就要使用

@Qualify

和@Autowired一起实现 IOC容器里面bean名字的查询,不再需要修改属性名字

 

 

 

带有参数的构造方法的装配

他的构造方法带有参数,需要注入其他bean依赖

因为默认

@Component
public class Rpersion implements Person{

    private Animal animal ;

    public Rpersion(@Autowired @Qualifier("dog") Animal animal) {
        this.animal = animal;
    }

    @Override
    public void service() {
        this.animal.use();
    }
}

 

 

Bean的生命周期:

@Component
public class Rpersion implements Person , BeanNameAware, BeanFactoryAware, 
        ApplicationContextAware, InitializingBean, DisposableBean {


    private Animal animal = null;

    @Override
    public void service() {
        this.animal.use();
    }

    @Autowired(required = false)
    @Qualifier("dog")
    public void setAnimal(Animal animal) {
        System.out.println("依赖注入!!!!");
        this.animal = animal;
    }

    //接口;BeanNameAware
    @Override
    public void setBeanName(String s) {
        System.out.println(this.getClass().getSimpleName()+"  接口;BeanNameAware=setBeanName");
    }

    //接口:BeanFactoryAware
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println(this.getClass().getSimpleName()+"  接口;BeanFactoryAware=setBeanFactory");
    }

    //接口:ApplicationContextAware
     @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         System.out.println(this.getClass().getSimpleName()+"  接口;ApplicationContextAware=setApplicationContext");
        }

    //接口:InitializingBean
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(this.getClass().getSimpleName()+"  接口;InitializingBean=afterPropertiesSet");
    }

    //接口:DisposableBean
    @Override
    public void destroy() throws Exception {
        System.out.println(this.getClass().getSimpleName()+"  接口;DisposableBean=destroy");
    }

    //定义初始化方法
    @PostConstruct
    public void init(){
        System.out.println(this.getClass().getSimpleName()+"  注解;@PostConstruct=init");
    }

    //定义销毁方法
    @PreDestroy
    public void predestory(){
        System.out.println(this.getClass().getSimpleName()+"  注解; @PreDestroy=predestory");
    }

}

 

测试结果:

Rpersion  接口;BeanNameAware=setBeanName
Rpersion  接口;BeanFactoryAware=setBeanFactory
Rpersion  接口;ApplicationContextAware=setApplicationContext
Rpersion  注解;@PostConstruct=init
Rpersion  接口;InitializingBean=afterPropertiesSet
Dogkanmen
16:42:20.949 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6073f712, started on Fri Aug 07 16:42:20 CST 2020
Rpersion  注解; @PreDestroy=predestory
Rpersion  接口;DisposableBean=destroy

 

当我们使用第三方bean的时候,可能会使用到@bean注解,可以通过注解去自定义初始化或销毁的方法:

@Bean(value = "datasource",initMethod = "init",destroyMethod = "destory")

 

 

条件装载bean:

场景:当用户没有配置数据库的四个变量,或者不完整的时候,不对bean进行装配,因为装配之后用的时候回有错误

这时候可以使用@Conditional注解完成

先编写装配条件类:

public class DatabaseConditional implements Condition {

    /**
     *
     * @param conditionContext   条件上下文
     * @param annotatedTypeMetadata 注释的类型的元数据
     * @return ture装配,false不装配
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();//获取环境配置
        return environment.containsProperty("driver")
                &&environment.containsProperty("url")
                &&environment.containsProperty("username")
                &&environment.containsProperty("password");
    }
}

注意:条件类必须实现接口Condition

 

在需要加入条件注解的类上加入,并且需要设置条件类属性@Conditional(DatabaseConditional.class)

@Configuration
@PropertySource(value = {"classpath:jdbc.properties"},ignoreResourceNotFound = true)
@ComponentScan(value = "com.quan.learning.*",excludeFilters = {@Filter(classes = {Service.class})} )
public class TestConfig {

    @Bean(value = "datasource")
    @Conditional(DatabaseConditional.class)
    public DataSource getDataSource(
            @Value("${driver}") String driver,
            @Value("${url}") String url,
            @Value("${username}") String username,
            @Value("${password}") String password

    ){
        Properties  properties = new Properties();
        properties.setProperty("driver",driver);
        properties.setProperty("url",url);
        properties.setProperty("username",username);
        properties.setProperty("password",password);
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}

 

如果配置文件里面的值不存在的话,这时候就不会再装配这个类,

 

 

 

注解源码集合########################

@Bean

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")

注解在方法上,和注解上

ElementType.ANNOTATION_TYPE 只可以标记注解类型

 

@Con

 

posted @ 2020-08-08 08:11  小丑quan  阅读(763)  评论(0)    收藏  举报