全注解下的IOC

​ spring的两个核心理念,控制反转(Inversion of Control,IOC)和面向切面编程(Aspect Oriented Programming ,AOP)

​ 通常情况下使用new来创建对象,而spring通过描述创建对象。spring boot不建议使用XML,而是通过注解的方式来创建对象,由于对象之间存在着依赖关系,所以spring 还提供了依赖注入的功能,来管理各个对象之间的关系

​ spring把每一个需要其管理的队形称之为Bean,spring为管理这些Bean的容器,所以成为IOC容器

一、IOC 容器

​ 所有的IOC容器都需要实现BeanFactory

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    // 多个 getBean 方法
    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    // 是否包含bean
    boolean containsBean(String var1);
    // bean是否单例
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    // bean是否原型
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
    //bean是否类型匹配
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
    //获取bean的类型
    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
    //获取bean的别名
    String[] getAliases(String var1);
}

​ 多个getBean方法可以从IOC容器中获取到bean ,它允许我们可以按类型和名称和获取bean

​ isSingleton和isPrototype功能相反,判断bean是单例还是原型

​ 在BeanFactory的基础上有高级一点的接口ApplicationContext,我们使用的

大部分的IOC容器都是ApplicationContext的实现类

​ ApplicationContext在BeanFactory 的基础上,扩展了消息国际化接口( MessageSource )、环境可配置接口 ( EnvironmentCapable )、应用事件发布接口( ApplicationEventPublisher ) 和资源模式解析接口( ResourcePatternResolver )

​ 在全注解的模式下我么使用AnnotationConfigApplicationContext 来作为IOC容器

​ 首先使用@Configuration和@Bean来构建配置类,

  • @Configuration 代表这是一个 Java 配置类 , Spring 的容器会根据它来生成 IoC 容器去装配 Bean;
  • @Bean 代表将 方法返回的 POJO 装配到 IoC 容器中,而其属性 name 定义这个 Bean 的名称,如果没有配置它,则将方法名称方法名作为 Bean 的名称保存到 Spring IoC 容器中

​ 如:

user.java

package com.springboot.chapter3.pojo

public class User{
    private Long id;
    private String userName;
    private String note
    
    /** setter and getter**/
}

AppConfig.java

package com.springboot.chapter3.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.springboot.chapter3.pojo.User;


@Configuration
public class AppConfig {
    
    @Bean(name ="user")
    public User initUser () {
        User user= new User();
        user.setId (1L) ;
        user.setUserName ("user_name_1"):
        user.setNote ("note_1" ) ;
        return user;
    }
}

​ 然后可以使用AnnotationConfigApplicationContext来构建IOC容器

package com.springboot.chapter3.config;

import org.apache.log4j.Logger ;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.springboot.chapter3.pojo.User;
public class IoCTest {  
    private static Logger log= Logger . getLogger(IoCTest.class);
    public static 飞roid main (String [] args) {
        ApplicationContext ctx= new AnnotationConfigApplcationContext(AppConfig.class);
        User user= ctx.getBean(User . class);
        log.info(user.getid ());
   }
}

二、装配Bean

1.通过扫描装配Bean

​ 如果一个个的 Bean 使用注解@Bean 注入 Spring loC 容器中,那将是一件很麻烦的事情。好在Spring 还允许我们进行扫描装配 Bean 到 loC 容器中,对于扫描装配而言使用 的注解是@Component和@ComponentScan。@Component 是标明哪个类被扫描进入 Spring IoC 容器,而@ComponentScan则是标明采用何种策略去扫描装配 Bean

​ 我么需要将User.java移动到com.springboot.chapter.config内,对其修改

package com.springboot.chapter3.config;

@Component("user")

public class User{
    @Value("1")
    private Long id;
    @Value("user_name_1")
    private String userName;
    @Value("note_1")
    private String note
    
    /** setter and getter**/
}
  • @Component 表明这个类将被 Spring IoC 容器扫描装配 ,其中配置的“ user"则是作为Bean 的名称,当然你也可以不配置这个字符串 ,那么 IoC 容器就会把类名第一个字母作为小写,其他不变作为 Bean 名称放入到 IoC 容器中
  • @Value 将对应的字面量注入到相应的属性当中。

然后需要改造Appconfig.java

package com.springboot.chapter3.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.springboot.chapter3.pojo.User;


@Configuration
@ComponentScan
public class AppConfig {
}
  • @ComponentScan 用于扫描包, 当没有定义的情况下,只会扫描当前包和其子包

测试Bean已经被扫描进入了IOC容器

ApplicationContext ctx
=new AnnotationConfigApplicationContext{AppConfig.class) ;
User user= ctx.getBean(User.class);
log.info(user.getid());

当User 类放到包 com . springboot.chapter3.pojo 中 AppConfig中的注解应该修改为

@ComponentScan ("com . springboot.chapter3 . * ")
或
@ComponentScan(basePackages={"com.springboot.chapter3.pojo"})
或
ComponentScan(basePackageClasses = {User . class})

2.自定义第三方Bean

@Bean(name = "dataSource") 
public DataSource getDataSource () {
    Properties props= new Properties();

    rops.setProperty("driver","com.mysql.jdbc.Driver" );
    props.setProperty("url","jdbc:mysql://localhost:3306/chapter3");
    props.setProperty ("username","root");
    props.setProperty ("password","123456");
    DataSource dataSource = null 
    try {
        dataSource = BasicDataSourceFactory.createDataSource (props);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return dataSource;
}

@Bean 定义了其配置项 name 为“ dataSource 飞 那么 Spring 就会把它返回的对象用名称“ dataSource ” 保存在 loC 容器中

三、依赖注入

1.@AutoWired

  • @ Autowired 它注入的机制最基本的一条是根据类型( by type) ,通过BeanFactory,就可以知道 IoC 容器是通过 getBean 方法获取对应 Bean 的,而 getBean 又支持根据类型( by type)或者根据名称( by name )

  • 首先它会根据类型找到对应的 Bean,如果对应类型的 Bean 不是唯一 的,那么它会根据其属性名称和 Bean 的名称进行匹配。如果匹配得上,就会使用该 Bean:如果还无法匹配,就会抛出异常。

2. @ Primary 和@Quelifier

@Component
@Primary
public class Cat implements Animal {
}

@Primary 的含义告诉 S pring IoC 容器 , 当发现有多个同样类型的 Bean 时请优先使用我进行注入

@Autowired
@Qualifier ( " dog")
private Animal animal = null; 

@Quelifier 它的配置项 value 需要一个字符串去定义,它将与@Autowired 组合在一起,通过类型和名称一起找到 Bean

四、生命周期

​ Spring IoC 初始化和销毁 Bean 的过程,这便是 Bean 的生命周期的过程 , 它大致分为 Bean 定义、Bean 的初始化、 Bean 的生存期和 Bean 的销 毁 4 个部分

Bean定义的过程:

  • Spring 通过我们的配置,如@ComponentScan 定义的扫描路径去找到带有@Component 的类 ,这个过程就是一个资源定位的过程 。
  • 一旦找到了资源,那么它就开始解析,并且将定义的信息保存起来 。注意 ,此时还没有初始化 Bean,也就没有 Bean 的实例,它有的仅仅是 Bean 的定义。
  • 然后就会把 Bean 定义发布到 Spring IoC 容器中 。 此时, IoC 容器也只有 Bean 的定义,还是没有 Bean 的实例生成。

Bean初始化过程:

​ 默认让那些 Bean 只是将定义发布到 IoC 容器而不做实例化和依赖注入, 当我们取出来的时候才做初始化和依赖注入等操作 。@ComponentScan 中还有一个配置项 lazyInit,只可以配置 Boolean 值,且默认值为 false ,也就是默认不进行延迟初始化,因此在默认的情况下 Spring 会对 Bean 进行实例化和依赖注入对应的属性值。

@ComponentScan(basePackages = "com.springboot.chapter3.*", lazyinit = true)

Bean生存期:

五、条件装配

package com . springboot chapter3 .condition;

public class DatabaseConditional implements Condition {


    @Override
    public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata) {
        //取出环境配置
        Environment env=context.getEnvironment();
        //判断属性文件是否存在对应的数据库配置
        return env.containsProperty("database.driverName")
            && env.containsProperty("database.url")
            && env.containsProperty("database.username")
            && env.containsProperty("database.password
     }
 }    
    
@Bean (name = ” dataSource ” , destroyMethod = ” close ” )
@ Conditional(DatabaseConditional.class)
public DataSource getDataSource (
        @Value("${database .driverName}") String driver,
        @Value("${database . url}" ) String url ,
        @Value("${database .username}") String username ,
        @Value("${database . password}") String password
        ){
    Properties props= new Properties ();
    props.setProperty ("driver", driver );
    props.setProperty( "url", url);
    props.setProperty ("username", username);
    props.setProperty ("password", password);
    DataSource dataSource = null ;
    try {
    dataSource = BasicDataSourceFactory.createDataSource(props);
    ) catch (Exception e) {
        e.printStackTrace();
    }
    return dataSource ;
}

@Conditional 帮助我们完成条件装配。而它需要配合另 外一个接口
Condition ( org.springframework .context.annotation.Condition )来完成对应的功能 ,对于 Condition 接口 则要求实现 matches 方法 ,当它返回True时才进行装配

六、Bean 的作用域

作用域类型 使用范围 描述
singleton 所有 S pring 应用 默认值 , loC 容器只存在单例
prototype 所有 S pring 应用 每当从 IoC 容器中取出一个 Bean ,则创建一个新的 Bean
session Spring Web 应用 HTTP 会话
application Spring Web 应用 Web 工程生命周期
request Spring Web 应用 Web 工程单次请求 ( request)
globalSession Spring Web 应用 在一个全局的 HTTP Session 中, 一个 Bean 定义对应一个实例 。 实践中基本不使用

七、引入XML配置Bean

​尽管 Spring Boot 建议使用注解和扫描配置 Bean,我们也可以在 Spring Boot 中使用 XML 对 Bean 进行配置 。 这里需要使用的是注解@ImportResource ,通过它可以引入对应的 XML 文件,用 以加载 Bean 。

猜你喜欢

转载自www.cnblogs.com/yanquhonggui/p/11072919.html
今日推荐