Spring(1):控制反转和依赖注入、Spring注解的使用

一、依赖注入DI 与 控制反转IOC
1、实例化对象方面,Spring和传统模式的区别
  • 传统的方式: 通过new 关键字主动创建一个对象
  • Spring方式:对象的生命周期由Spring来管理,在加载类的时候由Spring容器统一创建类的实例,使用实例的时候,直接从Spring容器中去获取该类的实例对象。IOC是反转控制(Inversion Of Control)的缩写,就像控制权从本来在自己手里,交给了Spring。 
 
2、定义控制反转的Bean类的两种方式
    (1)在Bean配置文件中,通过配置<bean>节点来配置
<!-- 在xml文件中,配置bean -->
<bean id="person" class="com.newbie.domain.Person">
    <property name="name" value="荆小六"/>
</bean>
public class Person {
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
  
}
 
(2)通过注解的方式,来配置Bean
步骤一:使用注解的方式,配置bean
<!-- 使用注解的方式,配置bean-->
<!—- 在Bean配置文件中(如:applicationContext.xml),增加自动扫描组件的配置 —>
<context:annotation-config/>
<context:component-scan base-package="com.newbie"/>
步骤二:定义类组件时,添加@Component注解
//使用注解方式进行依赖注入时,Spring容器自动扫描@Component注解的类,进行对象实例化
@Component            
public class Person {
    //如需给属性设置初始值,可使用@Value注解方式,给实体类的属性赋值
    @Value("荆小六”)    
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
  
}
步骤三:使用Bean实例对象
//需要使用Bean实例对象时,通过ApplicationContext.getBean(“”)进行获取
public class Test {
    
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
        Person person =(Person) context.getBean("person");
        System.out.println(person);
    }
}


3、依赖注入,使用注解的方式自动装配Bean实例对象
   (1) @Autowired注解:自动装配属性。
    在Spring中,可以使用 @Autowired 注解通过字段、setter方法或构造方法来自动装配Bean。
实现自动装配的步骤:
步骤一:注册AutowiredAnnotationBeanPostProcessor
  要启用@Autowired,必须注册“AutowiredAnnotationBeanPostProcessor', 通过在Bean配置文件中,添加 <context:annotation-config />配置实现。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx.xsd">
 
    <context:annotation-config/>
 
</beans>
步骤二:定义组件类时,给自动装配的属性添加 @Autowired 注解。它可以在字段、 setter 方法、构造方法中使用。
@Component
public class Team {
    @Autowired
    private Person leader;
 
    @Autowired(required = false)
    private Dept dept;
 
    //Getter、Setter方法
}
 
    (2)required = false|true,指定是否进行依赖检查
    默认情况下,@Autowired将执行相关检查,以确保属性已经装配正常。当Spring无法找到匹配的Bean装配,它会抛出异常。要解决这个问题,可以通过 @Autowired 的“required”属性设置为false来禁用此检查功能。
@Autowired(required = false)
 
 

4、解决自动装配时的歧义性(@Primary和@Qualifier)

    我们使用 @Autowired 注解来自动注入一个 Source 类型的 Bean 资源,但如果我们现在有两个 Srouce 类型的资源,Spring IoC 就会不知所措,不知道究竟该引入哪一个 Bean。我们可以会想到 Spring IoC最底层的容器接口——BeanFactory 的定义,它存在一个按照类型获取 Bean 的方法,显然通过 Source.class 作为参数无法判断使用哪个类实例进行返回,这就是自动装配的歧义性。
  为了消除歧义性,Spring 提供了两个注解:
  @Primary 注解:代表首要的,当 Spring IoC 检测到有多个相同类型的 Bean 资源的时候,会优先注入使用该注解的类。
  • 问题:该注解只是解决了首要的问题,但是并没有选择性的问题
  • @Qualifier 注解:上面所谈及的歧义性,一个重要的原因是 Spring 在寻找依赖注入的时候是按照类型注入引起的。除了按类型查找 Bean,Spring IoC 容器最底层的接口 BeanFactory 还提供了按名字查找的方法,如果按照名字来查找和注入不就能消除歧义性了吗?
  • 使用方法: 指定注入名称为 "source1" 的 Bean 资源
/* 包名和import */
class JuiceMaker {
    @Autowired
    @Qualifier("source1")
    public void setSource(Source source) {
        this.source = source;
    }
}
 
 
二、使用注解的方式,让Sping容器自动扫描组件
1、在配置文件中,配置由Spring自动扫描组件
    通过注解的方式,Spring容器能够自动扫描包中的文件,然后由Spring容器注册Bean实例。这样,可以有效减少在XML文件中繁琐的Bean类声明。
    配置方式如下:
    (1)首先,在Bean配置文件中,添加<context:component-scan base-pachage=“”>,这意味着,在 Spring 中启用自动扫描功能。其中,base-package 是指明存储组件的包,Spring将扫描所指定的包,并找出注解为@Component的Bean,将其实例注册到 Spring 容器。
<context:component-scan base-package=“Spring要扫描的包(中间以逗号分隔)" />
    (2)然后,在定义组件类时,显示地添加@Component注解
@Component
class TeamDemo  {
    …….
}
2、自动扫描组件的四种注释类型
在Spring2.5中,有4种类型的组件自动扫描注释类型,如下:
  • @Component – 指示自动扫描组件。
  • @Repository – 表示在持久层DAO组件。
  • @Service – 表示在业务层服务组件。
  • @Controller – 表示在表示层控制器组件。
查看源代码会发现, @Repository、@Service 和 @Controller 都被注解为 @Component类型。因此,以上三种注解 和 @Component注解的功能完全相同,Spring会自动扫描所有组件的 @Component 、@Repository、 @Service 和 @Controller注解。为便于阅读,应该始终声明@Repository,@ Service 或 @Controller 在指定的层,使你的代码更易于阅读。
//@Controller 注解的源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    String value() default "";
}
3、自定义自动扫描组件的名称
   (1)默认情况下,采用类的首字母小写的形式,作为Bean的名称
@Component
class TeamDemo  {
    …….
}
//获取Bean实例的方式
TeamDemo bean = (TeamDemo) context.getBean("teamDemo");
(2)显示地指定Bean的名称
@Component("teamDemoSimple")
class TeamDemo  {
    …….
}
//获取Bean实例的方式
TeamDemo bean = (TeamDemo) context.getBean("teamDemoSimple");
4、Spring Bean作用域
在Spring中,bean作用域用于确定哪种类型的 bean 实例应该从Spring容器中返回给调用者。bean支持的5种范围域:
  • 单例 - 每个Spring IoC 容器返回一个bean实例
  • 原型- 当每次请求时返回一个新的bean实例
  • 请求 - 返回每个HTTP请求的一个Bean实例
  • 会话 - 返回每个HTTP会话的一个bean实例
  • 全局会话- 返回全局HTTP会话的一个bean实例
  在大多数情况下,可能只处理了 Spring 的核心作用域 - 单例和原型,默认作用域是单例。
  如果 bean 配置文件中没有指定 bean 的范围,默认作用域是单例。在单例中,每个Spring IoC容器只有一个实例,无论多少次调用 getBean()方法获取它,它总是返回同一个实例。
  如果要将作用域改为原型模式,可在类定义时,增加@Scope("prototype”)  注解,这样每次调用ApplicationContext.getBean(“customer”)是,Sping容器都会重新new一个新的bean实例,进行返回。
//注解方式,将作用域改为原型
@Component 
@Scope("prototype”) 
public class Customer{
    …….
}
 
 
三、给实体类的属性赋值的方式
1、使用@Value注解的方式,给实体类的属性赋值
  利用spring注解,可以为实体类的属性赋值,作用就类似于在配置文件里赋值一样。
  赋值时的值类型可以是值内容,也可以进行SpEL表达式,同时也可以引入配置文件的值。
  使用@Value赋值时,值类型如下:
  • 基本数值: @Value("荆小六”) 
  • SpEL表达式; @Value("#{24+4}”) ,  @Value("#{teamDemo.name}”)
  • 使用${...}取出配置文件【properties】中的值(在运行环境变量里面的值)。如需配置文件,则需要在配置类上指定文件的路径位置,使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值
@Component
@PropertySource(value = {"classpath:/params.properties"})
public class Person {
    //1、基本数值
    @Value("荆小六”)        
    private String name;
 
    //2、使用计算结果赋值,age=28
    @Value("#{24+4}”)       
    private Integer age;
 
    //3、取出配置文件【params.properties】中key/valuez值:"sex=男"
    @Value("${sex}”)       
    private String sex;
 
}
SpEL表达式的使用介绍:https://www.yiibai.com/spring/spring-el-hello-world-example.html
 2、使用PropertyPlaceholderConfigurer类,从外部配置文件中读取参数值
  很多时候,大多数Spring开发人员只是把整个部署的详细信息(数据库的详细信息,日志文件的路径)写在XML bean配置文件中,但是,在企业环境中,部署的细节通常只可以由系统管理员或数据库管理员来'触碰',他们可能会拒绝直接访问你的bean的配置文件,它们会要求部署配置一个单独的文件,例如,一个简单的性能(properties)文件,仅具有部署细节。可以使用 PropertyPlaceholderConfigurer 类通过一个特殊的格式在外部部署细节到一个属性(properties )文件,以及访问bean的配置文件 – ${variable}.
 
步骤一:创建一个属性文件(database.properties),包括数据库的详细信息,把它放到你的项目类路径
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=123456
步骤二:在声明bean配置文件中,提供一个PropertyPlaceholderConfigurer映射到 刚才创建的“database.properties”属性文件。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-2.5.xsd">
 
 
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>database.properties</value>
        </property>
    </bean>
 
 
    <bean id="driverManager" class="com.newbie.propertyPlaceholderConfigurer.DriverManager">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="datebaseURL" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
 
 
</beans>
步骤三:实体类DriverManager的定义结构
public class DriverManager {
    private String driverClassName;
    private String datebaseURL;
    private String username;
    private String password;
    
    //Getter、Setter方法
}
 
 
四、Spring在创建对象实例或销毁实例时,设置默认执行的方法
1、在Bean配置文件中设置:
  在bean 配置文件中定义<bean>节点时,使用 init-method 和 destroy-method 属性指定用于在bean初始化和销毁时,执行某些动作的方法
<bean id="teamDemo" class="com.newbie.initMethod.TeamDemo"
      init-method="afterPropertiesSet" destroy-method="destroy"/>
@Component
class TeamDemo  {
    public void afterPropertiesSet() throws Exception {
        System.out.println(“初始化对象时,执行的方法");
    }
    public void destroy() throws Exception {
        System.out.println(“销毁对象实例时,执行的方法");
    }
}
2、使用注解的方式进行设置:
  在类定义时,为类初始化和销毁时要执行的方法,添加 @PostConstruct 和 @PreDestroy 注解。其中,@PostConstruct注解 和 init-method功能相同,标识初始化时要执行的方法;@PreDestroy注解 和 destroy-method 功能相同,标识类销毁时要执行的方法。
  注:@PostConstruct 和 @PreDestroy 注解不属于 Spring,它包含在J2EE库- common-annotations.jar中。
<!--XML文件中,配置“context:annotation-config”支持-->
<context:annotation-config/>
<context:component-scan base-package="com.newbie.initMethod"/>
@Component
class TeamDemo  {
    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化对象时,执行的方法");
    }
    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("销毁对象实例时,执行的方法");
    }
}
//测试类
class Test{
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println(context.getBean("teamDemo"));
        //ConfigurableApplicationContext.close()将关闭该应用程序的上下文,释放所有资源,并销毁所有缓存的单例bean。
        context.close(); 
    }
}

五、使用@Bean 装配外部引用文件中的Bean

  • 问题: 以上都是通过 @Component 注解来装配 Bean ,并且只能注解在类上,当你需要引用第三方包的(jar 文件),而且往往并没有这些包的源码,这时候将无法为这些包的类加入 @Component 注解,让它们变成开发环境中的 Bean 资源。
  • 解决方案:
    1.自己创建一个新的类来扩展包里的类,然后再新类上使用 @Component 注解,但这样很 low
    2.使用 @Bean 注解,注解到方法之上,使其成为 Spring 中返回对象为 Spring 的 Bean 资源。(注意:@Configuration 注解相当于 XML 文件的根元素,必须要,有了它才能解析其中的 @Bean 注解)。
  @Bean 的配置项中包含 4 个配置项:
  • name: 是一个字符串数组,允许配置多个 BeanName
  • autowire: 标志是否是一个引用的 Bean 对象,默认值是 Autowire.NO
  • initMethod: 自定义初始化方法
  • destroyMethod: 自定义销毁方法
  使用 @Bean 注解的好处就是能够动态获取一个 Bean 对象,能够根据环境不同得到不同的 Bean 对象。或者说将 Spring 和其他组件分离(其他组件不依赖 Spring,但是又想 Spring 管理生成的 Bean)
 
/**
 * 在方法上使用@Bean注解,将外部类的实例注册到Spring容器中
 */
@Configuration
class ConfigBeanDemo {
    /**
     * 由Spring容器,实例化一个java.lang.String对象,实例的名称为"pName"
     */
    @Bean(name = "pName")
    public String getString() {
        return "简三十六";
    }
 
    /**
     * 由Spring容器,实例化一个java.io.InputStream对象,实例的名称为"pInput"
     */
    @Bean(name = "pInput")
    public InputStream getInputStream() throws Exception {
        return new FileInputStream("/Users/newbie/Documents/project/aaa");
    }
}
 
/**
*  @ComponentScan注解:代表进行扫描,默认是扫描当前包的路径,扫描所有带有 @Component 注解的 POJO。
*/
@ComponentScan(basePackages = "com.newbie.domain4")
class ApplicationContextConfig {
}
 
/**
 * 在测试类中编写代码,从 Spring IoC 容器中获取到这个 Bean 
 */
public class TestStringBean {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
        String name = (String) context.getBean("pName");
        InputStream input = (InputStream)context.getBean("pInput");
        System.out.println(name);
        System.out.println(input);
    }
}
原文地址:https://www.cnblogs.com/newbie27/p/10723233.html