清茶书香

一杯清茶,一本书籍,一个下午。


  • 首页

  • 归档

  • 分类

  • 关于

  • 搜索
Redis JPA Solr SpringData SpringMVC localRepository local Mapper 事务 Mybatis JDBC AOP DI IOC 常用函数 触发器 存储过程 Promise Gateway SpringCloud vue-cli axios es6 webpack npm vue 个性化 zsh 终端 caffeine jvm缓存 guava cache validation Mapping MapStruct comment 小程序 建站 WeHalo config logback plugins database idea maven spring https http nginx password RabbitMQ 秒杀系统 Windows MySQL 数据备份 halo SpringBoot shell Linux ip Optional Stream Lambda k8s Docker 列编辑 vim MacOS 图片合成 Java 远程联调 nps 内网穿透

Spring Core之IOC与DI

发表于 2019-06-17 | 分类于 spring系列 | 0 | 阅读次数 330

Spring Framework概览

Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。

Spring是个分层架构的框架,主要是由七大模块组成,分别是Core、AOP、ORM、DAO、Web、Context、Web MVC,它们的整体结构如下图所示。

Spring Framework

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring Context:Spring Context是一个配置文件,向 Spring 框架提供上下文信息。Spring Context包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。

Spring的安装配置

  • 首先,需要将spring的jar包导入项目的资源文件中,如果没有是常规的项目需要自己去maven仓库下载需要用到的spring框架的jar包(JaveSE项目一般会用到spring-context、spring-core、spring-beans、spring-aop、 spring-expression、spring commons logging bridge)。如果项目是maven项目,那么在项目的pom.xml文件添加spring的依赖即可
<dependencies>
    <!-- Spring IOC最小依赖是beans、context,我们引入context依赖,maven会自动将beans依赖一并引入 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
</dependencies>
  • 其次,需要新建一个spring的xml配置文件,在IDEA中一般这个文件放在resources文件夹下。配置文件的头部约束可以参考下面的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 开启注解支持(如果不需要使用注解就不用指定这个了)-->
    <context:annotation-config/>
    <!-- 开启注解支持,并指定注解扫描包路径(使用这个,上面那一条就可省略了)-->
    <context:component-scan base-package="com.spring.demo" />

</beans>

Spring的控制反转(IOC)

IOC的概念

SpringIOC

SpringIOC(Inversion of Control)是一个容器,也就是我们通常所说的控制反转。 IOC容器将我们的javabean和一些用于描述这些bean应该如何初始化、组装的配置信息进行整合。提供给我们开发人员一个配置、组装完成的上下文给我们使用,我们可以方便的通过IOC容器将繁杂的对象创建、管理工作托管给IOC容器。所以称之为控制反转。(由原来的开发人员自己控制对象的创建、组装改为交由Spring IOC容器负责)

配置元数据

配置元数据用于告诉SpringIOC容器,你的javabean应该怎么初始化、装配,以及它的生命周期(单例、原型等)。

配置元数据有xml和annotation两种方式。

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="..." class="...">   
        <!-- 这里可以配置要为bean对象注入的属性或构造参数等 -->
    </bean>
    <bean id="..." class="...">
        <!-- 这里可以配置要为bean对象注入的属性或构造参数等 -->
    </bean>
    <!-- 下面可以跟着继续配置bean,有多少个配置多少个 -->
</beans>
  • id属性代表该bean在Spring容器中的唯一标识
  • class属性指定该bean的类型,需要指定类的全名

Annotation配置方式

这里只给出部分注解,常用的注解详见附录

注解配置的优缺点:

  • 优点:配置简单。由于Java类中已经包含了上下文信息,所有在Java类上直接加注解可以省略很多属性。
  • 缺点:对代码有侵入性,如果改了是基于注解的配置信息改变了,需要重新编译、打包
开启注解支持

刚开始的配置文件,我直接就说了一种在xml文件中开启注解支持的方式,这里再介绍一种注解开启注解配置支持的方式。

@Configuration// 相当于<beans>
@ComponentScan("com.spring.demo")// 注解的扫描包路径
public class AppConfig  {
    
  	@Bean// 相当于<bean>
    public TestDao testDao() {
       return new TestDao();
    }
  	// 其他bean配置....
      
    public static void main(String[] args) {
      // 如果在非web工程中使用这种方式开启注解支持,需要使用下面的方式初始化ioc容器,否则@ComponentScan注解会被忽略
      ConfigurableApplicationContext config = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}
注解扫描过滤(一般用不到)

Spring默认会扫描componentScan配置的basePackages包下所有带有扩展自@Component注解(如:@Repository、@Service、@Controller)的类。我们可以通过给componentscan添加filter来进一步控制扫描哪些注解、忽略哪些注解

如:下面展示了如何忽略@Repository注解,而用含Stub关键字前缀的自定义Repository注解代替

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

对应的xml配置方法

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

注解和XML文件混合配置

由于xml文件配置和注解配置都各有优劣,因此我们可以根据使用的场景选择性使用配置方式。甚至可以选择同时使用它们两种方式进行配合使用,都是可以的。比如数据源的配置, 就不适合通过注解来配置。否则数据源配置一发生变化就得改代码。

IOC容器的使用

BeanFactory

在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化,组装和管理的对象。

Spring提供了一个顶层的BeanFactory接口来管理Spring上下文中的所有类。同时还为我们提供了ApplicationContext子接口,提供了更为强大的功能。

ApplicationContext

ApplicationContext是BeanFactory的一个子接口,它代表了SpringIOC容器,通过它我们可以实现对SpringIOC容器的所有操作:实例化、配置、组装bean。

通常我们在开发中直接用到的是ApplicatoinContext接口和它的各种实现类。

ClassPathXmlApplicationContext

用于加载类路径下的spring配置文件,通常用于控制台程序

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService ss = ctx.getBean(StudentService.class);
System.out.println(ss);
AnnotationConfigApplicationContext

用于初始化通过注解方式配置的ioc容器

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
  
  	public static void main(String[] args) {
      ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
      MyService myService = ctx.getBean(MyService.class);
      myService.doStuff();
		}
}
FileSystemXmlApplicationContext

用于加载本地目录中的spring文件文件

ApplicationContext ctx = new FileSystemXmlApplicationContext("" +
                "F:/applicationContext.xml");
StudentService ss = ctx.getBean(StudentService.class);
System.out.println(ss);
XmlWebApplicationContext

XmlWebApplicatoinContext用于在Web工程中初始化SpringIOC容器,不过我们一般不会手动通过它来初始化IOC容器,Spring针对Web工程专门给我们提供了一个监听器来完成IOC容器的初始化工作,用法如下:

在项目的web.xml中配置

<!-- 通过Spring提供的ContextLoaderListener监听器完成Spring容器初始化 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在ContextLoaderListener中Spring会优先判断用户是否在web.xml中配置了名为contextClass的<context-param>参数,如果配置了优先使用用户制定的ApplicationContext实现类来初始化IOC,反之则使用默认的ApplicationContext实现类:org.springframework.web.context.support.XmlWebApplicationContext来完成IOC容器的初始化工作。

注:XmlWebApplicationContext默认加载路径是/WEB-INF/applicationContext.xml,如果我们的配置spring文件是放在这里的,并且只有这一个,我们可以不配置contextConfigLocation参数。

Spring Bean

Spring容器通过在xml中通过<bean>标签来添加一个JavaBean到IOC容器中(注解中通过@Bean、@Component、@Service等注解来添加一个Bean到容器中)

bean的属性

Spring给我们提供了下面这些属性来管理bean的实例化、生命周期(初始化、销毁)、参数&属性注入、作用域、装配方式等。

属性含义
class全路径类名,表示该bean的类型
name在ioc容器中的名称,在获取bean的时候可以通过名称获取
scope见下表scope
constructor-arg (属性或子标签)注入构造方法的参数
property (属性或子标签)注入属性
autowire自动装配模式
lazy-init是否启用懒加载: true/false
init-method允许我们指定一个自定义的初始化方法
destroy-method允许我们指定一个自定义的销毁方法

Bean的作用域

ScopeDescription
singleton单例。在整个ioc容器中只有一个此类型的示例。(默认值)
prototype原型。每次使用都用创建一个新的对象。
request对象的实例仅在一个request请求周期内有效,仅限在web环境中使用。
session对象的实例仅在一个session会话周期内有效,仅限在web环境中使用。
application对象的实例在整个application生命周期内有效,仅限在web环境中使用。
websocket对象的实例仅在一个websocket生命周期内有效,仅限在web环境中使用。

下面以单例和原型为例介绍了一下bean作用域的作用

单例:singleton(默认)

<bean id="md" class="com.spring.dao.daoimpl.MessageDaoImpl" scorp="singleton"/>

无论调用多少次getBean()获取到的都是同一个对象。

原型:propotype

<bean id="md" class="com.spring.dao.daoimpl.MessageDaoImpl" scorp="prototype"/>

每调用一次getBean()获取到的都是新的对象。

命名beans

使用id属性命名bean

id属性的值只能指定一个,不建议含有特殊字符(虽然自spring3.1开始id属性的值可以包含任意字符)。

使用name属性命名bean

使用name属性可以给一个bean指定多个名称,多个值之间可以用","或";"或空格分开

注:无论是id属性还是bean属性都要保证整个IOC容器内唯一,并且id属性的值和name属性的值也不能重复

我们可以同时指定id和name属性。 也可以都不指定,如果都不指定,IOC容器会为该bean生成一个唯一的名称。

实例化beans

通过构造方法实例化

这种方式是最常用的方式,适合绝大多数的javabean,因为我们的java类无需继承任何父类或实现任何接口。但是我们通常需要提供一个无参的或者有参的构造方法。

<!-- 无参构造 -->
<bean id="exampleBean" class="examples.ExampleBean"/>
<!-- 有参构造方法 -->
<bean name="anotherExample" class="examples.ExampleBeanTwo">
	<constructor-arg name="thingTwo" value="参数为非引用类型"/>
    <!-- 参数为引用类型 -->
    <constructor-arg name="thingThree" ref="exampleBean"/>
</bean>

通过静态工厂方法实例化

<bean id="clientService" class="examples.ClientService" factory-method="createInstance">
	<!-- 如果工厂方法需要参数,通过此标签传参 -->
  <constructor-arg name="cname" value="TestService" />
</bean>
public class ClientService {
    private static ClientService clientService = null;
  	private String cname;
    private ClientService() {}
  	private ClientService(String cname) {this.cname = cname;}

    public synchronized static ClientService createInstance(String cname) {
        if(clientService == null) {
           clientService = new ClientService(cname);
        }
      return clientService;
    }
  
  	public synchronized static ClientService createInstance() {
        if(clientService == null) {
           clientService = new ClientService();
        }
      return clientService;
    }
}

这种方式适合需要让Spring管理自己实现的单例类,用的很少。因为通过Spring IOC容器我们只需配置一下scope="singleton"就可以实现单例了。

通过对象工厂方法实例化

这种方式用的也不多,只有特定场合才会用到。首先给出示例的Java类

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

  	private static AccountService accountService = new AccountServiceImpl();
  
    public ClientService createClientServiceInstance() {
        return clientService;
    }
  
  	public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

该Java类对应的xml配置如下

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 注入这个bean所有需要的依赖项 -->
</bean>

<!-- 这个bean需要通过factory bean创建实例 -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<!-- 这个bean需要通过factory bean创建实例 -->
<bean id="accountService"
    factory-bean="serviceLocator"
      factory-method="createAccountServiceInstance">

  	<!-- 如果工厂方法需要参数,通过此标签传参 -->
  	<!-- <constructor-arg name="cname" value="TestService" /> -->
  
</bean>

Bean的生命周期

给出一个bean的初始化方法和销毁的方法

public class MessageDaoImple implements MessageDao{
    public void init(){
		System.out.println("init 被调用");
	}

    public void destroy(){
        System.out.println("destroy 被调用");
    }
}

通过xml配置文件配置bean

<bean id="md" class="com.spring.dao.daoimpl.MessageDaoImpl" init-method="init"
          destroy-method="destroy"/>

bean的懒加载

ioc中的bean默认在ioc容器启动时就都初始化了(加载配置文件时,而不是调用getBean()时),我们可以通过给bean添加一个lazy-init="true"属性延迟到调用getBean()时初始化bean

<!-- 正常懒加载 -->
<bean id="lazyBean" class="com.spring.bean.LazyInit" init-method="init" lazy-init="true"/>
<!--  如果将懒加载的bean注入到非懒加载的bean中,那么懒加载的bean也会失去懒加载的特性  -->
<bean id="md" class="com.spring.dao.daoimpl.MessageDaoImpl" init-method="init"
          destroy-method="destroy" lazy-init="true"/>
<bean id="app2" class="com.lanou3g.spring.App">
	<property name="messageDao" ref="md"/>
</bean>

Spring依赖注入——DI

Spring DI(Spring Dependency Injection),可以自动帮我们解决类与类之间的各种依赖问题。通常公司中的项目都包含很多的类,而这些类与类之间都有各种依赖关系,比如:组合、聚合、依赖等。通过Spring的DI功能,结合IOC容器, 我们可以把这些复杂的关系交由Spring来管理, 我们在使用时,Spring会自动帮我们把依赖的对象注入进来,大大降低开发难度。

Spring中的依赖注入主要包含两种:通过构造方法中的注入和通过Setter方法注入

构造参数的注入

package x.y;

public class ThingOne {
		private int age;
  	private String sname;
  	private int gender;
  	private ThingTwo thingTwo;
    public ThingOne(int age, ThingTwo thingTwo, String sname, int gender) {
        this.age = age;
				this.thingTwo = thingTwo;
      	this.sname = sname;
      	this.gender = gender;
  	    // ...
    }
}

// 为了方便这个类就也写在这里了,读者记得新建一个类
public class ThingTwo {
  // ...
}

通过xml配置为Bean对象中的属性注入值,示例如下

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <!-- 通过此标签注入构造方法中的参数 -->
      	<constructor-arg  name="age" value="18" />
      	<constructor-arg  type="java.lang.String" value="张三" />	
      	<constructor-arg ref="beanTwo"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>
</beans>

constructor-arg标签属性

属性名取值含义
name字符串构造参数的名称
value数值、字符串构造参数的属性值,只适用于简单类型
type类的全名构造参数的类型,如果是简单类型,需要指定对应的封装类型 如果构造参数中有多个命中的类型,按照配置的先后顺序注入(可以通过配合index属性改变默认顺序)
index数值构造参数的位置,从0开始
ref其他在Spring容器中配置的bean的name或者id将容器中的其他bean作为构造参数注入

通过C命名空间注入构造参数

使用C命名空间注入需要在xml文件的头部添加声明xmlns:c="http://www.springframework.org/schema/c"

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- 传统方式注入构造参数 
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean> -->

    <!-- c命名空间方式注入构造参数 -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>

Setter方式注入

package com.demo;
public class Driver {
    private Car car;
  	private String dname;
    public void setCar(Car car) {
        this.car = car;
    }
  	public void setDname(String dname) {
      	this.dname = dname;
    }
}
// 还是为了方便,哈哈哈
public class Car{}

通过xml配置文件注入如下

<bean id="driver" class="com.demo.Driver">
	<!-- 注入引入类型属性 -->
  <property name="car" ref="car" />
	<!-- 注入简单类型属性 -->
  <property name="dname" value="张三" />
</bean>
<bean id="car" class="com.demo.Car" />

通过P命名空间注入属性

和C一样需要声明xmlns:p="http://www.springframework.org/schema/p"

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="driver" class="com.demo.Driver" p:car-ref="car" p:dname="张三" />
  <bean id="car" class="com.demo.Car" />
</beans>

注入复杂属性

首选给出需要注入的类

// 下面这两个注解相当于在这个类中为每个属性生成了Set、Get方法
@Setter
@Getter
public class Student{
    private List<Object> hobbies;
    private Map<String,Object> gameTitle;
    private String nickName;
    private Fruit fruit;
    private LazyInit lazyInit;
}

其中Fruit是一个接口类,可以定义几个实现类用来根据需求切换它的实现类

LazyInit里有一个age的属性

@Setter
@Getter
public class LazyInit{
    private age;
}

注入集合类型属性

<bean id="student2" class="com.spring.bean.Student">
        <!-- 注入集合类型属性 -->
        <property name="hobbies">
            <list>
                <value>编程</value>
                <value type="java.lang.Integer">0099</value>
                <value>游泳</value>
                <value>唱歌</value>
                <value>氪金</value>
            </list>
        </property>
    </bean>

注入Map类型属性

<bean id="student2" class="com.spring.bean.Student">
        <!-- 注入Map类型属性 -->
        <property name="gameTitle">
            <map>
                <entry key="LOL" value="辣鸡黑铁"/>
                <entry key="王者农药" value="嘴强王者"/>
                <entry key="斗地主" value="不屈白银"/>
                <entry key="和平精英">
                    <!-- 注入空类型属性 -->
                    <null/>
                </entry>
            </map>
    </bean>

注入空类型属性

<bean id="student2" class="com.spring.bean.Student">
        <!-- 注入空类型属性 -->
        <property name="nickName">
            <null/>
        </property>
    </bean>

注入匿名内部bean类型属性

<bean id="student2" class="com.spring.bean.Student">
        <!-- 注入匿名内部bean类型属性 -->
        <property name="fruit">
            <bean class="com.spring.fruit.Banana"/>
        </property>
        <property name="lazyInit">
            <bean class="com.spring.bean.LazyInit"/>
        </property>
    </bean>

注入复合属性

<bean id="student2" class="com.spring.bean.Student">
        <!-- 注入复合属性 -->
        <property name="lazyInit.age" value="18"/>
    </bean>

引入外部的properties文件

jdbc.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/demo?charsetEncoding=UTF-8
jdbc.user=root
jdbc.password=root

引入方式一

<bean id="jdbcConf" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:jdbc.properties"/>
</bean>-->

引入方式二

<context:property-placeholder location="classpath:jdbc.properties"/>

注入外部文件的属性

<!-- 将外部properties文件中的属性注入到bean中 -->
    <bean id="jdbcConf" class="com.lanou3g.spring.bean.JDBCConf">
        <property name="url" value="${jdbc.url}" />
        <property name="driver" value="${jdbc.driver}" />
        <property name="userName" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

附录

常用注解

@Required

用于标注在setter方法之上,表示此属性必须要注入。如果容器初始化该bean时没有合适的值注入到该属性则直接抛出异常。

在spring5.1之后弃用,因为必须要注入的属性,我们可以通过构造参数来注入。

@Autowired

根据依赖的类型自动注入。该注解可以用于标注在 属性、setter方法、构造方法、或带参数的普通方法之上。Spring会将符合的参数自动注入到属性、或方法参数中。

如果@Autowired注入的对象是一个数组类型、集合类型或者Map类型,Spring会将上下文中所有符合类型的bean都注入进来。如果你需要注入进来的bean的顺序,可以通过让这些Bean实现Ordered接口或添加@Order注解或者是标准的@Priority注解(@Priority注解不能指定通过@Bean配置的bean的顺序)。

注:如果注入的属性是一个单值类型,但Spring上下文中有多个匹配类型的候选Bean,那将会直接报错,因为Spring不知道该用哪个注入

所有可用@Autowired的地方,我们也可以直接使用 JSR 330’s @Inject注解,两者效果一致。

注:@Autowired、@Inject、@Value和@Resource注解都是通过BeanPostProcessor实现的注入。这意味着在我们自定义的BeanPostProcessor和BeanFactoryPostProcessor中将无法使用这些注解,只能通过xml配置的方式注入。

@Resource

根据依赖bean的名称自动注入。除了可以通过Spring特有的@Autowired注解进行依赖注入外,Spring也支持原生JSR-250中的 @Resource注解。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

@Resource注解的name属性不是必须的,如果不指定,那默认的名称将取决于变量名称、setter方法的名称(取决于注解在变量上还是setter方法上)

@Resource注解还有个好处,如果按名称无法找到一个匹配bean的时候,它会自动按照类型查找注入。

@Primary

当我们通过@Autowired注解来注入属性或者参数时,如果遇到上面说的单值属性有多个匹配类型候选bean,如果其中一个候选Bean上配置了@Primary注解或者在xml配置中设置了primary="true",那将不会报错,Spring会优先将带primary标记的候选bean注入(当然,如果有多个带primary标记的匹配类型还是会报错滴)。

@Qualifier

该注解可以让我们通过名称在多个候选bean中进一步限定。

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

通过@Qualifier限定构造参数注入

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

给bean添加qualifier标识符

在xml中我们可以通过<qualifier>子标签来给一个bean添加标识符, 以供注入时@Qualifier使用。如果不显示指定标识符,spring会用id、name代替。

<bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 
</bean>

如果bean是通过注解的方式配置的,我们可以这样做

@Component
@Qualifier("Action")	// 给bean添加qualifier标识符
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@PostConstruct、@PreDestroy

指定bean的生命周期方法

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component("md")
public class TestLifeCycle {
    
    @PostConstruct
    public void init() {
        System.out.println("init 被调用");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("destroy 被调用");
    }
}

@Component、@Repository、 @Service、@Controller

@Component注解是一个通用注解,代表一个组件,可以用来标识所有希望让Spring管理的bean。

@Repository、 @Service、@Controller三个注解扩展自@Component,带有具体的语义色彩,专门用于修饰dao、service、controller层的类

上面这几个注解都可以通过给定value参数来指定bean的name,如果没有给定参数。那将由Spring的AnnotationBeanNameGenerator来自动生成(其实底层就是调用了java.beans.Introspector#decapitalize()方法来实现)

@Bean、@Configuration

@Configuration 注解的类相当于xml配置文件中的<beans>

@Bean注解允许我们通过注解的方式定义在Java代码中定义bean的配置元数据,相当于xml配置文件中的<bean >

示例:

@Configuration	// 类上面必须添加此注解,否则里面用@Bean配置无法使用自动注入其他参数(被称为"精简"bean“)
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")	// 定义bean的标识符相当于xml方式给<bean>标签指定子标签<qualifier>
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // 自动注入特定bean方法参数,和普通字符串类型参数
    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

  	// 指定生命周期init方法,相当于xml中给<bean>添加init-method属性
  	@Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

  	// 指定生命周期destroy方法,相当于xml中给<bean>添加destroy-method属性
    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
  
	  //通过name属性指定bean的名称,相当于xml中给<bean>添加name属性
  	//如果不指定name属性,默认取值为方法名
    @Bean(name="testBean")	
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope	// 指定scope, 相当于xml配置给<bean>添加scope属性
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }
}

@Scope

@Scope注解可以限定通过注解配置的bean的作用域。适用于通过@Component(或其派生注解)、@Bean注解配置的bean。@Scope注解的默认值是singleton,可取得值参见《Bean的作用域》章节

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}

@Qualifier

@Qualifier注解可以限定通过注解配置的bean的qualifier标识符。适用于通过@Component(或其派生注解)、@Bean注解配置的bean。 相当于xml配置的<bean>的子标签<qualifier>

@Nullable

@Nullable 可用于字段、方法参数和返回值,用于将非空语义覆盖为可空语义。通常配合@Autowired或@Inject注解使用。作用与jdk8中的java.util.Optional类似

@Import

通过注解的方式导入其他注解配置
示例:

@Import({MyConf.class})
@Configuration
public class Application {

    @Bean
    public StudentService studentService() {
      return new StudentServiceImpl();
    }
	
    ...
}

@Configuration
public class MyConf {

    @Bean
    public StudentDao studentDao() {
      return new StudentDaoImpl();
    }
	
    ...
}

@ImportResource

通过注解的方式引入xml配置

示例:

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

properties-config.xml

<beans>
    <context:property-placeholder location="classpath:jdbc.properties"/>
</beans>

jdbc.properties

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=sa

使用入口:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

@PropertySource

此注解用于加载properties文件到环境中,然后我们就可以通过env获取properties文件中定义的属性

使用示例:

@Configuration
@PropertySource("classpath:app.properties")
public class AppConfig {

    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}

@Lookup

@Lookup注解用于实现基于Lookup方式的方法注入。

  1. 定义两个prototype bean及其父类
@Component
@Scope("prototype")
public class Apple extends Fruit {

    public Apple() {
        System.out.println("Apple construct!");
    }
}

@Component
@Scope("prototype")
public class Banana extends Fruit {

    public Banana() {
        System.out.println("Banana construct!");
    }
}


public class Fruit {
    public Fruit() {
        System.out.println("Fruit construct!");
    }

}
  1. 定义抽象类,其中有一个需要注入具体实现方法的抽象方法。并通过@Lookup注解注入具体方法实现
/**
 * 果盘
 */
@Component
public abstract class FruitPlate {

    /**
     * 每次从果盘中拿到的都是一个新的水果(实例)
     * @return
     */
    @Lookup("apple")	// 如果不传注解的参数,将按照匹配类型注入
    public abstract Fruit getFruit();
}
  1. 使用FruitPlate
public static void main(String[] args) throws SQLException {

        ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);

        FruitPlate applePlate = ctx.getBean(FruitPlate.class);
        applePlate.getFruit();
    }

使用JSR 330标准注解

从Spring3.0开始,我们除了可以使用spring提供的注解完成依赖注入外,还可以直接使用标准的JSR 330注解完成依赖注入。要使用它们需要添加如下依赖到工程中:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
@Inject、@Named

@Inject注解作用和Spring的@Autowired注解效果一致

@Named 注解有两个作用。 当用作方法的参数时和Spring的@Qualifier注解效果类似;而用作类上面的注解时作用和Spring的@Component注解类似

JSR 330标准注解与Spring注解对比
Springjavax.inject.*javax.inject 限制 / 描述
@Autowired@Inject@Inject没有required属性。 可以与Java 8的Optional一起使用。
@Component@Named / @ManagedBeanJSR-330 没有提供可组合的方式,Spring注解可以通过自由组合创建扩展自@Component的自定义的注解。
@Scope("singleton")@SingletonJSR-330中只提供了一个@Singleton可用,虽然也提供了一个@Scope注解,但只是用于我们扩展自己的自定义注解,无法直接像Spring提供的@Scope一样直接应用。
@Qualifier@Qualifier / @Namedjavax.inject.Qualifier 只是一个元注解(注解的注解)。 要想实现类似Spring中@Qualifier注解的功能,我们可以用 @Named注解
@Value-
@Required-
@Lazy-
Bennett wechat
欢迎收藏我的微信小程序,方便查看更新的文章。
  • 本文作者: Bennett
  • 本文链接: https://hibennett.cn/archives/spring-ioc-di
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# spring # IOC # DI
maven管理spring,运行项目时出现spring-beans.xsd文档找不到异常
spring Core之AOP
  • 文章目录
  • 站点概览
Bennett

Bennett

60 日志
28 分类
74 标签
RSS
Github E-mail Gitee QQ
Creative Commons
Links
  • MacWk
  • 知了
0%
© 2020 — 2023 hibennett.cn版权所有
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4

浙公网安备 33010802011246号

    |    浙ICP备2020040857号-1