Spring总结二:依赖注入

Bean配置的三种方式

  • 基于XML的配置方式
  • 基于注解的配置方式
  • 基于Java类的配置方式

一、基于XML的配置 

配置多个资源文件

  1. 在配置文件中使用import来导入所需的配置文件。
  2. 将多个配置文件构造为一个数组,然后传递给ApplicationContext实现加载多个配置文件。

这两种方式都是通过调用BeanDefinitionReader来读取定义文件的,在内部实现上没有任何的区别。

在xml配置文件中引入:<import resource="applicationContext2.xml"/>

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]
    {"applicationContext1.xml","applicationContext2.xml","applicationContext3.xml"});

bean的属性注入方式

1、setter方式(要求bean提供一个默认的构造函数,并且得为需要注入的属性提供set方法。)

setter注入是Spring中最主流的注入方式,它利用JavaBean规范所定义的setter方法来完成注入,灵活且可读性高。它消除了使用构造器注入时出现多个参数的可能性,首先可以把构造方法声明为无参数的,然后使用setter注入为其设置对应的值,其实也是通过Java反射技术得以现实的。

注入属性name 

     类中的定义

private String name;

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

     xml中的定义

<bean id="user" class="com.review.bean.User" >
		<property name="name" value="hello" />
</bean>

2、通过构造器来注入

private String name;

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

①.构造函数参数类型匹配

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

②构造函数参数索引(从0开始)

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

③构造函数参数名称

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

bean属性注入集合类型

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

p和c名称空间

p名称空间

p-namespace允许您使用bean元素的属性(而不是嵌套 <property/>元素)来描述属性值协作bean,或两者。

<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 name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

p名称空间没有标准的XML格式定义灵活,比如说,bean的属性名是以Ref结尾的,那么采用p名称空间定义就会导致冲突

 

c名称空间

Spring 3.1中引入的c-namespace允许使用内联属性来配置构造函数参数,而不是嵌套constructor-arg元素。

<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"/>

    <!-- traditional declaration with optional argument names -->
    <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-namespace declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

翻译:

由于XML语法,索引表示法要求存在前导_,因为XML属性名称不能以数字开头(即使某些IDE允许)。对于<constructor-arg>元素也可以使用相应的索引符号,但不常用,因为通常的声明顺序通常就足够了。

SpEl

Spring Expression Language(简称“SpEL”)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于Unified EL,但提供了其他功能,最值得注意的是方法调用和基本字符串模板功能。

SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。 

使用字面量

整数:<property name="count" value="#{5}"/>

小数:<property name="frequency" value="#{89.7}"/>

科学计数法:<property name="capacity" value="#{1e4}"/>

String类型的字面量可以使用单引号或者双引号作为字符串的定界符号

<property name=”name” value="#{'Chuck'}"/>

<property name='name' value='#{"Chuck"}'/>

Boolean:<property name="enabled" value="#{false}"/>

引用其他bean

<bean id="emp" class="com.review.bean.Employee">
	<property name="empId" value="1003"/>
	<property name="empName" value="Kate"/>
	<property name="age" value="21"/>
	<property name="detp" value="#{dept}"/>
</bean>

 

引用其他bean的属性值作为自己某个属性的值

<bean id="emp2" class="com.review.bean.Employee">
	<property name="empId" value="1003"/>
	<property name="empName" value="Kate"/>
	<property name="age" value="21"/>
	<property name="deptName" value="#{dept.deptName}"/>
</bean>

SpEL表达式

SpEL表达式总结

 Spring Expression Language (SpEL)

小结

1.基于constructor的注入,会固定依赖注入的顺序;该方式不允许我们创建bean对象之间的循环依赖关系,这种限制其实是一种利用构造器来注入的益处 - 当你甚至没有注意到使用setter注入的时候,Spring能解决循环依赖的问题;

2.基于setter的注入,只有当对象是需要被注入的时候它才会帮助我们注入依赖,而不是在初始化的时候就注入;另一方面如果你使用基于constructor注入,CGLIB不能创建一个代理,迫使你使用基于接口的代理或虚拟的无参数构造函数。

注意点:

1.若字面量中包含特殊字符, 特殊字符注入 <![CDATA[ 带有特殊字符的值 ]]>

2.设置null值

<property name="name"><null/></property>

内部bean

当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property>或<constructor-arg>元素里,不需要设置任何id或name属性 内部bean不能使用在任何其他地方

<bean id="shop2" class="com.review.bean.Shop" >
    <property name= "book">
           <!--内部bean-->       
           <bean class= "com.review.bean.Book" >
           <property name= "bookId" value ="1000"/>
           <property name= "bookName" value="innerBook" />
           <property name= "author" value="innerAuthor" />
           <property name= "price" value ="50"/>
        </bean>
    </property>
</bean >

Spring 级联属性

Spring 级联属性是当两个bean 关联时  从一个bean 给 另一个bean 赋值。即当一个类引用另一个类时也可以通过在Bean中分别为引用的类注入值。为级联属性赋值,属性需要先初始化才可以为级联属性赋值,否则会有异常

例如person内部引用car,然后将car的价格改变了。 

注意:使用构造器注入时,除了需要重写构造方法,Person中需要有getCar方法,Car中需要有setPrice()方法

<bean id="person" class="com.review.beans.Person">
        <constructor-arg type="java.lang.String" value="Tom"></constructor-arg>
        <constructor-arg type="java.lang.String" value="11"></constructor-arg>
        <constructor-arg ref="car"></constructor-arg>
        <property name="car.price" value="1111"></property>
    </bean>

 

二、基于注解的配置方式

1.定义bean

相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。

 

@Component注解

@Component
public class UserDao {
  ...
}

等价于

<bean id="userDao " class="com.review.Dao ">

除了 @Component 注解外, Spring 还提供了 3 个功能与 @Component 等效的注解:

注解说明
@Repository标注 DAO 实现类。
@Service标注 Service 实现类。
@Controller标注 Controller 实现类。(SpringMVC)
  • @Component:一个泛化的概念,表示一个组件(Bean),可作用在任何层次

官方文档翻译:Spring提供进一步典型化注解:@Component@Service,和 @Controller@Component是任何Spring管理组件的通用构造型。@Repository@Service和,@Controller@Component更具体的用例的专业化(分别在持久性,服务和表示层)。因此,您可以来注解你的组件类有 @Component,但是,通过与注解它们@Repository@Service或者@Controller ,你的类能更好地被工具处理,或与切面进行关联。例如,这些刻板印象注释成为切入点的理想目标。@Repository@Service并且@Controller还可以在Spring Framework的未来版本中携带其他语义。因此,如果您在使用之间进行选择@Component或者@Service对于您的服务层,@Service显然是更好的选择。同样,如前所述,@Repository已经支持将其作为持久层中自动异常转换的标记。

2.扫描组件

组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到

①指定被扫描的package

<context:component-scan base-package="com.review"></context:component-scan>

 

[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。

[2]当需要扫描多个包时可以使用逗号分隔。

[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:

<context:component-scan 
	base-package="com.review.component" 
	resource-pattern="autowire/*.class"/>

<context:component-scan> 提供了 include-filter 与 exclude-filter 子元素,它们可以对需要过滤的包进行更精细的控制。

●<context:include-filter>子节点表示要包含的目标类

注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。

●<context:exclude-filter>子节点表示要排除在外的目标类 

●component-scan下可以拥有若干个include-filter和exclude-filter子节点

官网Filter Types

Filter TypeExample ExpressionDescription

annotation (default)

org.example.SomeAnnotation

An annotation to be present at the type level in target components.

assignable

org.example.SomeClass

A class (or interface) that the target components are assignable to (extend or implement).

aspectj

org.example..*Service+

An AspectJ type expression to be matched by the target components.

regex

org\.example\.Default.*

A regex expression to be matched by the target components class names.

custom

org.example.MyTypeFilter

A custom implementation of the org.springframework.core.type .TypeFilter interface.

说明:

类别

示例

说明

annotation

com.example.XxxAnnotation

过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤。

assignable

com.example.BaseXxx

过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。

aspectj

com.example.*Service+

所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。

regex

com\.example\.anno\.*

所有com.atguigu.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。

custom

com.example.XxxTypeFilter

使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口

 

3.自动装载 Bean

在指定要扫描的包时,<context:component-scan> 元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了@Autowired、@Resource或@Inject注解的属性。

@Value赋值

如果bean被添加到IOC容器,对其属性不做任何初始化,那么相关属性是null或者默认值(基本类型)。
可以通过@Value赋值,赋值方式有

  • 基本字符
  • spring EL表达式#{} 
  • 加载外部属性${配置文件中参数名}
//@ComponentScan("com.review") 已经在xml文件自动扫描了。
@Configuration
@PropertySource("classpath:jdbc.properties")//配合value注解使用,加载配置文件。
public class JavaAnnoBean {

    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.driverClassName}")
    String driverClassName;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
Spring4有: @PropertySources可以配置多个属性文件
@PropertySources({
       @PropertySource("classpath:config.properties"),
        @PropertySource("classpath:db.properties")
        })

@Autowired注解

[1]根据类型实现自动装配。

[2]构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解

[3]默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。

[4]若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false

[5]默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。

[6]@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。

[7]@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。

[8]@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。

 

泛型依赖注入

 

三.基于Java类的配置

用@Configuration注解该类,等价 与XML中配置beans;用@Bean标注方法等价于XML中配置bean。

使用bean注解的方法不能是private、final的(static经过测试可以...)

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

上面的AppConfig类等效于以下Spring <beans/>XML:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

要启用组件扫描,您可以@Configuration按如下方式注释您的类:

 

@Configuration
@ComponentScan(basePackages = "com.acme") //不加参数默认会扫描该类所在的包下所有的配置类
public class AppConfig  {
    ...
}

 

可以看看这篇文章:IOC之基于Java类的配置Bean

官方文档

总结:不同配置方式比较

 

基于XML的配置主要使用场景:

第三方类库,如DataSource、JdbcTemplate等;
命名空间,如aop、context等;
基于注解的配置主要使用场景:
Bean的实现类是当前项目开发的,可直接在Java类中使用注解配置
基于Java类的配置主要使用场景:
对于实例化Bean的逻辑比较复杂,则比较适合用基于Java类配置的方式
在日常的开发中我们主要是使用XML配置和注解配置方式向结合的开发方式,一般不推荐使用基于Java类的配置方式。
 

自动装配

 

注解相同点注解提供是否支持required参数是否支持@Primary的Bean优先注入是否支持指定beanId注入
@Autowire可实现bean的依赖注入Spring的专有注解支持支持通过@Qualifier指定注入特定bean
@Resource可实现bean的依赖注入JSR250规范不支持不支持通过参数name指定注入bean
@Inject可实现bean的依赖注JSR330规范不支持支持通过@Named注解指定注入bean

@AutoWried按by type自动注入,而@Resource默认按byName自动注入。

@Resources是javaEE提供的

注解@Inject 和@Named的例子

加载资源

Spring 允许通过 <import> 将多个配置文件引入到一个文件中,进行配置文件的集成。这样在启动 Spring 容器时,仅需要指定这个合并好的配置文件就可以。

import 元素的 resource 属性支持 Spring 的标准的路径资源

    <import resource="classpath:annotation.xml"/>
    <import resource="classpath*::annotation.xml"/>
    <import resource="file::annotation.xml"/>
    <import resource="http::annotation.xml"/>

 

引用外部属性文件

当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置

<!-- 直接配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="user" value="root"/>
	<property name="password" value="root"/>
	<property name="jdbcUrl" value="jdbc:mysql:///test"/>
	<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>

<!-- 指定properties属性文件的位置 -->

<!-- classpath:xxx 表示属性文件位于类路径下 -->

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

spring classpath:和classpath*:区别和实际应用

关于<context:property-placeholder>的一个有趣现象

 

推荐:

四种依赖注入方式

工程方式创建bean

sping官方文档

Bean配置的三种方式(XML、注解、Java类)介绍与对比

Spring注解大全

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值