Spring特点

轻量级:Spring 是非侵入性的 - 基于 Spring 开发的应用中的对象可以不依赖于 Spring 的 API
依赖注入(DI — dependency injection、IOC)
面向切面编程(AOP — aspect oriented programming)
容器: Spring 是一个容器, 因为它包含并且管理应用对象的生命周期
框架: Spring 实现了使用简单的组件配置组合成一个复杂的应用. 在 Spring 中可以使用 XML 和 Java 注解组合这些对象
一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库 (实际上 Spring 自身也提供了展现层的 SpringMVC 和 持久层的 Spring JDBC)

Bean的配置

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

    <!--
        class:bean的全类名,通过反射的方式在在IoC容器中创建bean,所以要求bean中应有一个无参构造方法;
        id(name):用于标识bean,也可用name表示;在同一个容器中,id应该是惟一的;id名称一般是类名第一个字母小写;
    -->
    <bean class="beans.HelloWorld" id="helloWorld">
        <property name="name" value="Spring"></property>
    </bean>
</beans>

POJO类

package beans;

/**
 * @author 
 * @Description: Spring-HelloWorld
 * @date 2018/12/6下午10:12
 **/
public class HelloWorld {

    public HelloWorld() {

    }

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

    private String name;

    public void hello(){
        System.out.println("hello:"+getName());
    }

    public String getName() {
        return name;
    }

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

测试

@Test
    public void getBeanByName(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = (HelloWorld) applicationContext.getBean("helloWorld");
        helloWorld.hello();
    }

IoC容器

BeanFactory接口

IoC容器的基本实现,定义了IoC容器最基本的功能,所有的Spring IoC容器都是在这个接口的基础上实现的

ApplicationContext

提供了更多的高级特性,是BeanFactory的子接口

ApplicationContext的主要实现类

类名作用
ClassPathXmlApplicationContext从 类路径下加载配置文件
FileSystemXmlApplicationContext从文件系统中加载配置文件
ConfigurableApplicationContext扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close(), 让 ApplicationContext 具有启动、刷新和关闭上下文的能力
WebApplicationContext专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作

获取Bean的方式

通过id(name)

@Test
    public void getBeanByName(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = (HelloWorld) applicationContext.getBean("helloWorld");
        helloWorld.hello();
    }

通过bean的类型

    @Test
    public void getBeanByType(){
//        当applicationContext配置文件中同时存在两个同类型的bean时,这样获取会报错
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Car car = applicationContext.getBean(Car.class);
        System.out.println(car.toString());
    }

报错信息

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [beans.Car] is defined: expected single matching bean but found 2: car,car2

	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:312)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
	at beans.Main.getBeanByType(Main.java:25)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

属性注入的方式

Setter方法注入

需要在类中为属性都写上getter和setter方法

	<bean class="beans.HelloWorld" id="helloWorld">
        <property name="name" value="Spring"></property>
    </bean>

构造器注入(需提供有参构造方法和无参构造方法)

  1. 按位置注入值(当存在参数相同的构造方法时,使用此种方法会造成注入错误)
	<bean class="beans.Car" id="car">
        <constructor-arg value="baoma" index="0"></constructor-arg>
        <constructor-arg value="1000000" index="1"></constructor-arg>
	</bean>
  1. 按类型注入值
   <bean class="beans.Car" id="car2">
        <constructor-arg value="benchi" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120.5" type="java.lang.Double"></constructor-arg>
    </bean>

各种类型属性的注入

基本类型

使用value进行注入

    <bean class="beans.HelloWorld" id="helloWorld">
        <property name="name" value="Spring"></property>
    </bean>

引用类型

使用ref进行注入

    <!--引用类型值的注入-->
    <bean class="beans.Person" id="person">
        <property name="car" ref="car"></property>
    </bean>

特殊字符与null值

    <!--特殊字符值的注入-->
    <bean class="beans.Person" id="person">
        <property name="name" value="![DATA][<张三>]"></property>
        <property name="age" value="![DATA][<null/>]"></property>
    </bean>

嵌套注入

    <bean class="beans.Person" id="person3">
        <constructor-arg index="0">
        <!--直接配置一个bean作为属性,该bean不可以被其他bean引用-->
            <bean class="beans.Car" >
                <constructor-arg value="baoma" index="0"></constructor-arg>
                <constructor-arg value="1000000" index="1"></constructor-arg>
            </bean>
        </constructor-arg>
    </bean>

集合类型

List

    <!--集合类型list-->
    <bean class="beans.Student" id="student">
        <property name="cars">
            <list>
                <ref bean="car"></ref>
                <ref bean="car2"></ref>
            </list>
        </property>
    </bean>

Map

    <!--集合类型map-->
    <bean class="beans.NewPerson" id="newPerson">
        <property name="carMap">
            <map>
                <entry key="aa">
                    <ref bean="car"></ref>
                </entry>
                <entry key="bb" value-ref="car2"></entry>
            </map>
        </property>
    </bean>

将集合类型配置为bean,以供其他bean引用

    <!--集合bean-->
    <util:list id="listFactoryBean">
        <ref bean="car"></ref>
        <ref bean="car2"></ref>
    </util:list>
    <bean class="beans.Student" id="student2">
        <property name="cars">
            <ref bean="listFactoryBean"></ref>
        </property>
    </bean>
    <!--**************************-->
    <util:map id="map">
        <entry key="aa">
            <ref bean="car"></ref>
        </entry>
        <entry key="bb" value-ref="car2"></entry>
    </util:map>
    <bean class="beans.NewPerson" id="newPerson2">
        <property name="carMap">
            <ref bean="map"></ref>
        </property>
    </bean>

Bean的继承

我们先来观察以下两个bean

    <bean class="beans.Address" id="address" p:city="周口" p:address="大庆路" abstract="true"></bean>

    <bean class="beans.Address" id="address2" p:city="周口" p:address="建设路"></bean>

可以发现,两个bean除了address属性不同,其余属性都是相同的,如果按照这样配置的话,会显得有点麻烦,因此我们可以使用parent参数来完成bean之间的继承

    <bean class="beans.Address" id="address" p:city="周口" p:address="大庆路" abstract="true"></bean>

    <bean class="beans.Address" id="address3" parent="address" p:address="顺丰街"></bean>

现在测试一下

    @Test
    public void testBeanExtends(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Address address = (Address) applicationContext.getBean("address3");
        System.out.println(address);
    }
// 	输出:Address{city='周口', address='顺丰街'}
  1. 父 Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean
  2. 并不是 元素里的所有属性都会被继承. 比如: autowire, abstract 等.
  3. 也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true

现在加上abstract="true"进行测试

    <bean class="beans.Address" id="address" p:city="周口" p:address="大庆路" abstract="true"></bean>

    <bean class="beans.Address" id="address3" parent="address" p:address="顺丰街"></bean>
    @Test
    public void testBeanExtends(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Address parent = (Address) applicationContext.getBean("address");
    }

会出现如下报错:应为加了abstract属性的bean不会被Spring容器实例化

org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'address': Bean definition is abstract

Bean的依赖

假设我们现在有以下一个bean

	<bean class="beans.Person" id="person">
        <property name="car" ref="car"></property>
    </bean>
    
    <bean class="beans.Car" id="car">
        <constructor-arg value="baoma" index="0"></constructor-arg>
        <constructor-arg value="1000000" index="1"></constructor-arg>
    </bean>

进行测试可以得到如下结果

    @Test
    public void testRef(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person.toString());
    }
//	输出:Person{car=Car{name='baoma', price=1000000, maxSpeed=null}}
  1. 如果我们想要在进行实例化person之间必须先实例化他的car属性,则可以通过depends-on参数来设置,Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
  2. 如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称
	<bean class="beans.Person" id="person" depends-on="car">
        <property name="car" ref="car"></property>
    </bean>
    
    <bean class="beans.Car" id="car">
        <constructor-arg value="baoma" index="0"></constructor-arg>
        <constructor-arg value="1000000" index="1"></constructor-arg>
    </bean>

在设置这个参数之后入门实例化person时没有实例化car,则会报错

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'car' is defined

Spring的作用域

Spring默认创建的bean都是单例的,当我们使用ApplicationContext.getBaen(name)方法时,Spring会先在容器里查找有没有已经创建该bean,若创建了则直接返回,没有就新建一个。现在我们进行测试验证:
配置文件

    <bean class="beans.Person" id="person">
        <property name="car" ref="car"></property>
    </bean>

测试代码

    @Test
    public void testScope(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person1 = (Person) applicationContext.getBean("person");
        Person person2 = (Person) applicationContext.getBean("person");
        System.out.println(person1==person2);
    }
    // 输出结果为:true,说明默认的bean是单例的

若我们想创建多例的bean则可以使用scope参数,该参数默认值为singleton(单例),创建多例bean可以将该参数的值改为scope=“propotype”。在进行测试
配置文件

    <bean class="beans.Person" id="person" scope="prototype">
        <property name="car" ref="car"></property>
    </bean>

测试代码

    @Test
    public void testScope(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person1 = (Person) applicationContext.getBean("person");
        Person person2 = (Person) applicationContext.getBean("person");
        System.out.println(person1==person2);
    }
    // 输出结果为:false,说明该bean是多例的

使用外部属性文件

在我们开发应用程序的时候难免需要与其他程序(如数据库)进行连接,如果我们将连接信息配置在spring的配置文件中就会导致后序的修改变得很麻烦,因此我们需要将信息写入外部属性文件,然后再spring里在进行链接。

db.properties

username=root
password=123
driverClass = com.mysql.jdbc.Driver
jdbcUrl = jdbc:mysql:///rtlzz

applicationContext.xml



    <!--导入属性文件-->
    <context:property-placeholder location="db.properties"></context:property-placeholder>

    <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
        <property name="user" value="${username}"></property>
        <property name="password" value="${password}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
    </bean>
    
</beans>

测试类

    @Test
    public void testConnection() throws SQLException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("base-properties.xml");
        DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
        System.out.println(dataSource.getConnection());
    }
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐