欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring IOC

程序员文章站 2022-07-12 14:01:05
...

Spring简介

Spring是一个IOC和AOP的容器框架,可以管理所有的组件

Spring的优良特性:

  1. 非侵入式,基于Spring开发的应用中的对象可以不依赖于Spring的API
  2. 依赖注入,IOC
  3. 面向切面编程,AOP
  4. 容器,Spring是一个容器,它管理应用对象的生命周期
  5. 组件化,使用简单的组件配置组合复杂的应用
  6. 一站式,在IOC和AOP的基础上可以整合各种优秀的第三方库

Spring的模块划分

Test:Spring的单元测试模块

Core Container:核心容器(IOC);黑色代表这部分功能由哪些JAR包组成

AOP+Aspects:面向切面编程

Data Access/Integration:数据访问/集成

Web:Spring开发web应用

Spring IOC

IOC(Inversion Of Control)控制反转、DI

控制:资源的获取方式

获取资源的方式

​ 主动式:需要什么资源都自己创建即可
​ BookServlet{

​ BookService bs = new BookService() ;

​ AirPlane ap = new AirPlane(很多参数);//复杂对象的创建偶尔度很高

​ }

​ 被动式:资源的获取是交给一个容器来创建和设置,将实现与具体进行分离

​ BookServlet{

​ BookService bs;

​ public void test01(){

​ bs.checkout();

​ }

​ }

容器:管理所有的组件的生命周期

DI(Dependency Injection)依赖注入:组件A运行时需要另一个组件B,容器通过反射的方式将准备好的B注入到A中

HelloWorld

  1. 添加依赖与环境搭建

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    

Spring IOC

public class Person {
    private String lastName;
    private Integer age ;
    private String gender;
    private String email  ;
}
  1. 配置

    <?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">
        <!--注册一个person对象 Spring会自动创建这个对象-->
        <!--一个bean标签可以注册一个组件
            class   要注册组件的全类名
            id      这个对象的唯一标识
            使用property标签为Person对象的属性赋值
                name指定属性名
                value指定对应属性的值
        -->
        <bean id="person01" class="cn.jason.bean.Person">
            <property name="lastName" value="Harry"/>
            <property name="age" value="16"/>
            <property name="email" value="aaa@qq.com"/>
            <property name="gender" value=""/>
        </bean>
    </beans>
    
  2. 测试

public void test01(){
        //ApplicationContext(IOC容器的接口)代表IOC容器
        //ClassPathXmlApplicationContext 使用当前配置文件构造IOC容器
        ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
        System.out.println("容器启动完成...");
        Person person01 = (Person)ioc.getBean("person01");
        Person person02 = (Person)ioc.getBean("person01");
        System.out.println(person01);
        System.out.println(person01 == person02);
    }
person构建完成...
容器启动完成...
Person{lastName='Harry', age=16, gender='女', email='aaa@qq.com'}
true

几个细节

  1. 组件的创建工作是容器完成的

  2. 容器中对象的创建在容器构建完成的时候就被实例化

  3. 同一个组件在IOC容器中是单实例的

  4. 容器中如果没有这个组件,获取会报异常

  5. IOC容器在创建这个组件对象的时候,会利用setter方法为javaBean赋值

  6. javaBean的属性名是由是由getter和setter方法决定的;去掉get/set后面那一串首字母小写就是属性名

    public void setLastName(String lastName)
        //属性名就是lastName
    

实验

实验一,根据bean的类型从ioc容器中获取bean实例

/*
根据bean的类型从ioc容器中获取bean实例
如果ioc容器中这个类型的bean由多个,查找会报错
*/
public void test02(){
    Person bean = ioc.getBean(Person.class);
    System.out.println(bean);
}
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cn.jason.bean.Person' available: expected single matching bean but found 2: person01,person02

或者传入指定的id和bean类型

public void test02(){
        Person bean = ioc.getBean("person01",Person.class);
        System.out.println(bean);
}

实验二,使用有参构造器创建对象

public class Person {
    private String lastName;
    private Integer age ;
    private String gender;
    private String email  ;
    public Person(){
        System.out.println("person无参构建完成...");
    }
    public Person(String lastName, Integer age, String gender, String email) {
        this.lastName = lastName;
        this.age = age;
        this.gender = gender;
        this.email = email;
        System.out.println("person有参构建完成...");
    }
}
<?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="person03" class="cn.jason.bean.Person">
        <!--调用有参构造器来创建对象-->
        <constructor-arg name="lastName" value="Jason"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="email" value="aaa@qq.com"/>
        <constructor-arg name="gender" value=""/>
    </bean>
</beans>
public void test(){
    Person person = ioc.getBean("person03",Person.class) ;
    System.out.println(person);
}
person有参构建完成...
Person{lastName='Jason', age=20, gender='男', email='aaa@qq.com'}

使用p名称空间为bean赋值

<!-- 
导入名称空间
xmlns:p="http://www.springframework.org/schema/p"
-->
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
>
    <bean id="person04" class="cn.jason.bean.Person" p:lastName="Mio" p:age="18" p:email="aaa@qq.com" p:gender=""/>
</beans>

实验四、为不同类型的属性赋值

用到的实体类

public class Person {
    //基本类型直接使用property标签自动进行类型转换赋值
    private String lastName;
    private Integer age ;
    private String gender;
    private String email  ;

    private Car car ;
    private List<Book> books ;
    private Map<String,Object> map ;
    private Properties properties ;
}
public class Car {
    private String name;
    private Integer price ;
    private String color ;
}
public class Book {
    private String name ;
    private String author ;
}

**赋值为null null标签 **

<?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="person01" class="cn.jason.bean.Person">
        <property name="lastName">
            <!--在property标签体内进行复杂的赋值-->
            <null/>
        </property>
    </bean>
    
    <!-- 错误的做法 -->
    <bean id="person01" class="cn.jason.bean.Person">
        <!-- 这样是为lastName赋值为“null”的字符串 -->
        <property name="lastName" value="null">
         </property>
    </bean>
    
</beans>


为person对象的car属性赋值

第一种

<bean id="car01" class="cn.jason.bean.Car">
    <property name="color" value="red"/>
    <property name="name" value="BMW"/>
    <property name="price" value="1000000"/>
</bean>

<bean id="person01" class="cn.jason.bean.Person">
    <!--ref 代表引用外面的一个值 car = ioc.getBean("car01")-->
    <property name="car" ref="car01"/>
</bean>
public void test(){
    Person person = ioc.getBean("person01",Person.class);
    System.out.println(person.getCar());
    System.out.println(person.getCar() == ioc.getBean("car01",Car.class));
}
Car{name='BMW', price=1000000, color='red'}
true 

第二种

<?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="person01" class="cn.jason.bean.Person">
        <property name="car">
            <!--
				在properties标签体内进行复杂的赋值
				对象可以使用bean标签创建,既car = new Car();-->
            <bean class="cn.jason.bean.Car" >
                <property name="name" value="自行车"/>
                <property name="color" value="green"/>
                <property name="price" value="12313123"/>
            </bean>
        </property>
    </bean>
</beans>
public void test(){
    Person person = ioc.getBean("person01",Person.class);
    System.out.println(person.getCar());
    System.out.println(person.getCar() == ioc.getBean("car01",Car.class));
}
Car{name='自行车', price=12313123, color='green'}
false

为List赋值 list标签

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="book01" class="cn.jason.bean.Book">
        <property name="name" value="东游记"/>
    </bean>
    <bean id="person" class="cn.jason.bean.Person">
        <!--为list类型赋值-->
        <property name="books">
            <!--books = new ArrayList<Book>();-->
            <list>
                <!--内部bean不能被获取到,只能在内部使用-->
                <bean class="cn.jason.bean.Book" p:name="西游记"/>
                <!--引用外部一个元素-->
                <ref bean="book01"/>
            </list>
        </property>
    </bean>
</beans>

为map赋值 map标签

<bean class="cn.jason.bean.Book" id="book" p:name="西游记"/>
<bean id="person" class="cn.jason.bean.Person">
    <property name="map">
        <!--map = new LinkedHashMap();-->
        <map>
            <!--一个entry代表一个key value-->
            <entry key="name" value="Mioaaa"/>
            <entry key="key02" value="23333"/>
            <entry key="book" value-ref="book"/>
            <entry key="mapInner">
                <bean id="car" class="cn.jason.bean.Car" p:name="BMW"/>
            </entry>
            <entry key="mapInner">
                <map>
                    <entry key="22" value="33"/>
                    <entry key="33" value="22"/>
                </map>
            </entry>
        </map>
    </property>
</bean>
public void test(){
    Person person = ioc.getBean("person", Person.class);
    Map<String, Object> map = person.getMap();
    System.out.println(map);
}
{name=Mioaaa, key02=23333, book=Book{name='西游记', author='null'}, mapInner={22=33, 33=22}}

为properties赋值 props标签

<bean id="person" class="cn.jason.bean.Person">
    <!--properties = new Properties();所有k=v都是string-->
    <property name="properties">
        <props>
            <prop key="key01">value01</prop>
            <prop key="key02">value02</prop>
        </props>
    </property>
</bean>

util名称空间创建集合类型的bean

xmlns:util="http://www.springframework.org/schema/util"
<?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:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <bean id="person" class="cn.jason.bean.Person">
        <property name="map" ref="myMap"/>
        <property name="books" ref="myList"/>
    </bean>
    <!--相当于new LinkedHashMap();-->
    <util:map id="myMap">
        <entry key="key01" value="value01"></entry>
        <entry key="key02" value="value02"></entry>
    </util:map>
    <util:list id="myList">
        <bean id="book" class="cn.jason.bean.Book">
            <property name="name" value="西游记"/>
        </bean>
        <bean id="book" class="cn.jason.bean.Book">
            <property name="name" value="东游记"/>
        </bean>
    </util:list>
</beans>
public void test(){
    Person person = ioc.getBean("person", Person.class);
    Map<String, Object> map = person.getMap();
    List<Book> books = person.getBooks();
    System.out.println(books);
    System.out.println(map);
}
[Book{name='西游记', author='null'}, Book{name='东游记', author='null'}]
{key01=value01, key02=value02}

为级联属性赋值

<bean id="car01" class="cn.jason.bean.Car" p:name="BMW" p:color="Red"/>
<bean id="person01" class="cn.jason.bean.Person">
    <property name="car" ref="car01"/>
    <property name="car.name" value="自行车"/>
</bean>
public void test(){
    Person person = ioc.getBean("person01", Person.class);
    Car car = person.getCar();
    System.out.println(car);
}
//结果 Car{name='自行车', color='Red'} 

通过parent属性实现配置信息的重用

<!--   abstract="true" 表示这个bean的配置是一个抽象的模板,不能获取它的实例,只能给别人继承 -->
    <bean id="person01" class="cn.jason.bean.Person" abstract="true">
        <property name="lastName" value="Mio"/>
        <property name="age" value="18"/>
        <property name="gender" value=""/>
        <property name="email" value="aaa@qq.com"/>
    </bean>
    <!--parent 指定当前bean配置信息继承于哪个bean,把person01的信息继承到person02中-->
    <bean id="person02" parent="person01">
        <!-- 将需要修改的数据重新指定 -->
        <property name="lastName" value="Harry"/>
    </bean>

改变bean之间的创建顺序

<!--    改变bean的创建顺序-->
<!--    bean之间的依赖(只是 改变创建顺序)-->
<!--    这里表示这个car对象依赖于book和person这两个对象 -->
<!--    先创建person和book再创建car-->
    <bean class="cn.jason.bean.Car" id="car" depends-on="person,book"></bean>
    <bean class="cn.jason.bean.Book" id="book"></bean>
    <bean id="person" class="cn.jason.bean.Person" ></bean>

bean的作用域,分别创建单实例和多实例bean

<!--
bean的作用域:指bean是单实例还是多实例,默认情况下是单实例的
prototype:多实例的
    容器启动默认不会去创建多实例的bean
    当要使用的时候才创建它
    每次获取都会创建一个新的实例对象

singleton:单实例的(默认的)
    单实例的在容器启动完成之前就已经创建好对象,保存在容器中
    任何时候获取都是获取之前创建好的bean
-->
    <bean class="cn.jason.bean.Book" id="book01" scope="prototype" p:name="BMW"></bean>
    <bean class="cn.jason.bean.Book" id="book02" scope="singleton" p:name="BMW"></bean>

通过自定义工厂去创建bean对象

<!--
	工厂模式:工厂帮我们创建对象,创建对象的复杂工作交给工厂,对创建bean的细节进行隐藏
    ioc容器在启动时,通过这个静态工厂来构建book对象,并且把它添加到容器中保存(单例)
    静态工厂
        class:指定静态工厂的全类名
        factory-method指定工厂方法
        constructor-arg:可以为方法传递参数
    返回类型的是该工厂方法返回的类型,也就是Book
    static Book getBook(String name)
-->
    <bean id="book" class="cn.jason.factory.StaticFactory" factory-method="getBook">
        <constructor-arg name="name" value="碧蓝航线"/>
    </bean>
</beans>
<!--    实例工厂
        先实例化工厂
-->
    <bean id="instanceFactory" class="cn.jason.factory.InstanceFactory"/>
<!--    factory-bean:指定使用哪个工厂对象来创建这个bean对象
        factory-method:指定使用该工厂的哪个方法来创建对象
-->
    <bean id="book" class="cn.jason.bean.Book" factory-bean="instanceFactory"
    factory-method="getBook">
        <constructor-arg name="name" value="碧蓝航线"/>
    </bean>

FactoryBean接口

<!--    FactoryBean(是Spring规定的一个接口) 只要是这个接口的实现类,Spring都认为是一个工厂
        实现该接口的工厂在ioc容器启动时不会创建bean对象,只有当使用时才会实例化对象
-->
    <bean id="book" class="cn.jason.factory.MyFactoryBean"></bean>
/*
* 实现了FactoryBean接口的类是Spring可以认识的工厂类
* Spring会自动的调用工厂方法创建对象的实例
* getObject就是工厂方法
* */
public class MyFactoryBean implements FactoryBean<Book> {
    //返回创建的对象
    public Book getObject() throws Exception {
        System.out.println("MyFactoryBean创建对象中");
        Book book = new Book() ;
        book.setName(UUID.randomUUID().toString());
        return book;
    }

//    Spring通过这个方法来确认创建对象的类型
    public Class<?> getObjectType() {
        return Book.class ;
    }

//    所创建的对象是否是单例的
    public boolean isSingleton() {
        return true;
    }
}

bean的生命周期方法

public class Book {
    private String name;
    private String author ;
    public void init(){
        System.out.println("初始化...");
    }
    public void destory(){
        System.out.println("销毁...");
    }
}

public class IOCTest {
    ConfigurableApplicationContext ioc = new 
        ClassPathXmlApplicationContext("ApplicationContext.xml") ;
    @Test
    public void test(){
        ioc.close();
    }
}
<!--    
创建带有生命周期方法的bean
生命周期:bean的创建到销毁的过程
    ioc容器中注册的bean
        单例的bean,容器启动时创建好,容器关闭也会销毁创建的bean
        多实例bean,获取的时候才创建
     为bean指定一些生命周期方法,Spring在创建或者是销毁的时候会调用这些方法
-->
    <bean class="cn.jason.bean.Book" id="book1" destroy-method="destory" init-method="init"/>
orgaaa@qq.com6956de9: startup
book 被创建
初始化...
org.springframework.context.support.AbstractApplicationContext doClose
销毁...

bean的后置处理器

    <bean class="cn.jason.bean.Book" id="book1" destroy-method="destory" init-method="init" scope="singleton"/>
<!--    bean的后置处理器
        Spring有一个接口BeanPostProcessor(后置处理器),可以在bean的初始化前后调用这个方法
        无论这个bean是否有初始化方法,后置处理器都会工作
-->
    <bean id="myBeanPostProcessor" class="cn.jason.bean.MyBeanPostProcessor"></bean>
/**
 * 1、编写后置处理器的实现类
 * 2、将后置处理器注册到配置文件中
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
//    在bean初始化前执行
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"要准备调用初始化方法啦");
        return bean;
    }
//    在bean初始化后执行
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"初始化结束");
        //初始化之后返回的bean,返回的是什么,容器中保存的就是什么
        return bean;
    }
}

public class IOCTest {
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml") ;
    @Test
    public  void test(){
        Book book1 = ioc.getBean("book1", Book.class);
        book1 = new Book() ;
    }
}

输出:
book 被创建
book1要准备调用初始化方法啦
初始化...
book1初始化结束
book 被创建

使用context名称空间引用外部属性

<?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名称空间加载外部配置文件
        固定写法classpath,表示引用类路径下的一个资源
		使用${}取值
-->
    <context:property-placeholder location="classpath:Druid.properties"/>
    <bean class="cn.jason.bean.Data" id="data">
        <property name="map">
            <map>
                <entry key="url" value="${url}"/>
                <entry key="username" value="${username}"/>
                <entry key="password" value="${password}"/>
                <entry key="initialSize" value="${initialSize}"/>
                <entry key="maxActive" value="${maxActive}"/>
                <entry key="maxWait" value="${maxWait}"/>
            </map>
        </property>
    </bean>
</beans>

基于XML的自动装配(自定义类型自动赋值)

Spring在创建bean对象时,自动的为某些属性赋值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--    为person里面的自定义类型的属性赋值
        properties 手动赋值
        自动赋值(自动装配)

        自动装配
        autowire="default"和 autowire="no" 不自动装配
        按照某种规则自动装配
        autowire="byName"以属性名作为id去容器中找到某个组件,将该组件赋值给它
        例如public void setCar(Car car)  属性名为car,那么就在xml文件中找id为car的组件,将该组件赋值给它
		autowire="byType"以属性的类型作为class去容器中找到某个组件,将该组件赋值给它,若找到多个会报错

        autowire="constructor"按照构造器进行赋值
		它是先按照构造器参数的类型进行匹配,找不到就为null;如果按照类型查找到了多个相同类型的则通过参数名作为id进行匹配,找不到就为null
-->
    
    <bean id="car" class="cn.jason.bean.Car" p:name="碧蓝航线" p:price="1000"/>
    <bean id="book" class="cn.jason.bean.Book" p:name="blhx" p:price="1000"/>
    <bean id="person" class="cn.jason.bean.Person" autowire="byName"/>
</beans>
public class Person {
    private Book book ;
    private Car car ;
    public Book getBook() {
        return book;
    }
    public void setBook(Book book) {
        this.book = book;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Person{" +
                "book=" + book +
                ", car=" + car +
                '}';
    }
}
public class MyTest {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    @Test
    public void test(){
        Person bean = ioc.getBean(Person.class);
        System.out.println(bean);
    }
}
Person{book=Book{name='blhx', price=1000.0}, car=Car{name='碧蓝航线', price=1000}}

SpEL (Spring Expression Language)

    <bean class="cn.jason.bean.Car" id="car">
        <property name="name" value="BMW"/>
    </bean>

    <bean class="cn.jason.bean.Book" id="book" >
        <property name="name" value="asdasd"/>
    </bean>

    <bean id="person" class="cn.jason.bean.Person">
<!--     使用运算符  -->
        <property name="salary" value="#{232.123*12}"/>
<!--      引用其他bean的某个属性值  -->
        <property name="lastName" value="#{book.name}"/>
        <property name="car" value="#{car}"/>
<!--        调用静态方法 语法规则: #{T(静态类全类名).静态方法(参数列表)}-->
        <property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"/>

<!--        调用实例方法 语法规则:#{对象.方法} -->
        <property name="gender" value="#{book.getName()}"/>
    </bean>
public  void test(){
    Person person = ioc.getBean("person", Person.class);
    System.out.println(person);
}
//结果Person{lastName='asdasd', email='4fb85', gender='asdasd', age=null, salary=2785.4759999999997, car=Car{name='BMW', color='null'}, books=null, map=null}

通过给bean上添加注解,可以快速的将bean添加到ioc容器中

<?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">
<!--    通过注解,将bean添加到IOC容器中
        Spring 有四个注解,某个类上添加上任何一个注解都能将这个组件加入到ioc容器中
        @Controller 控制器,推荐给控制器层组件添加这个注解
        @Service    业务逻辑,推荐给业务逻辑层添加这个注解
        @Repository 持久化层,推荐给持久化层添加这个注解
        @Component  组件,给不属于以上几层的组件添加这个注解

        使用注解将组件添加到容器中需要几步:
        1、给要添加的组件上标准这四个注解中的任何一个
        2、告诉Spring扫描加了注解的组件(依赖Context名称空间)
        3、导入aop包,支持注解模式

        使用注解的方式和使用配置的方式行为默认是一样的
        1、组件id,默认是类名首字母小写
        2、组件的作用域,默认是单例的
-->
<!--    context:component-scan 自动组件扫描
         base-package:指定扫描的基础包,把基础包以及他下面所有的包的所有加了注解的bean,自动的扫描进IOC容器
-->
    <context:component-scan base-package="cn.jason"/>
</beans>
/**
 *  id为bookDao
 *  作用域为多实例
 */
@Repository("bookDao")
@Scope(value = "prototype")
public class BookDao {
}

使用context:include-filter 指定扫描包时要包含的类

<!--    context:include-filter 指定只扫描哪些组件
        type="annotation" 只扫描加了该注解的组件

        use-default-filters="false
        禁用默认过滤规则(默认行为是全部扫描进来的),和include有冲突
-->
    <context:component-scan base-package="cn.jason" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>

使用context:exclude-filter 指定扫描包时不要包含的类

<!--    context:exclude-filter扫描过程中排除一些不要的组件
        type:指定排除规则
        type="annotation" 按照注解进行排除,标注了指定注解的组件不要
        expression 指定值,这里指定注解的全类名
        type="assignable" 指定排除某个具体类
        expression 指定要排除的全类名
        type="aspectj" aspectj表达式
        type="custom"  自定义一个TypeFilter,自定义使用哪些
        type="regex"   正则表达式
-->
    <context:component-scan base-package="cn.jason">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>

使用@Autowired 注解实现根据类型实现自动装配

<?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">
<!-- 
扫描指定包,将加了注解的组件加入IOC容器中
-->
    <context:component-scan base-package="cn.jason">
    </context:component-scan>
</beans>
@Repository("bookDao")
@Scope(value = "prototype")
public class BookDao {
    public void saveBook(){
        System.out.println("保存了一本图书");
    }
}

@Service
public class BookService {
    //required = false 表示找不到就装配null
    @Autowired(required = false)
    private BookDao bookDao;
    public void save(){
        System.out.println("bookDao正在保存图书");
        bookDao.saveBook();
    }
}

@Controller
public class BookServlet {

    //自动装配(自动为这个属性赋值)
    //required = false 表示找不到就装配null
    @Autowired(required = false)
    private BookService bookService ;
    public void doGet(){
        bookService.save();
    }
}

public class IOCTest {
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml") ;
    @Test
    public  void test(){
        BookServlet bookServlet = ioc.getBean("bookServlet", BookServlet.class);
        bookServlet.doGet();
    }
}
//结果bookDao
//正在保存图书
//保存了一本图书

@Autowired原理

​ @Autowired
​ private BookService bookService ;

  1. ​ 先按照类型去容器中找到对应的组件;bookService = ioc.getBean(BookService .class) ;

​ 1、如果找到一个直接赋值

​ 2、如果没找到就抛出异常

​ 3、如果找到多个,则按照变量名作为id继续在容器中进行匹配

​ a、如果没有匹配上就报错

​ b、如果匹配上就进行装配

也可以使用**@Qualifier**注解指定一个值,让Spring在查找时使用这个注解指定的值和容器中组件的id进行匹配,而不是通过变量名

现在添加一个类BookServiceExt

@Service
public class BookServiceExt extends BookService {
    @Override
    public void save() {
        System.out.println("BookServiceExt...");
        bookDao.saveBook();
    }
}
//修改BookServlet类
@Controller
public class BookServlet {

    //自动装配(自动为这个属性赋值)
    //required = false 表示找不到就装配null
    @Autowired(required = false)
    private BookService bookServiceExt ;

    public void doGet(){
        bookServiceExt.save();
    }
}

//结果
//BookServiceExt...
//保存了一本图书
@Controller
public class BookServlet {

    @Qualifier("bookService")
    //自动装配(自动为这个属性赋值)
    @Autowired(required = false)
    private BookService hahaha ;

    public void doGet(){
        hahaha.save();
    }
}
//结果
//BookService...
//保存了一本图书

方法上使用@Autowired注解和在方法的参数上使用@Qualifier注解

@Service
public class BookService {
//    required = false 表示找不到返回null,而不是报错
//    在方法上使用Autowired的话,这个参数需要的自定义类型会自动装入
//    并且这个方法会在创建bean时会自动调用
    @Autowired(required = false)
//    @Qualifier("bookDao")指示将id为bookDao的bean注入过来
    public void method(@Qualifier("bookDao") BookDao dao){
        dao.saveBook();
    }
}
public class IOCTest {
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml") ;
    @Test
    public  void test(){

    }
}

//结果
//保存了一本图书

@Autowired与@Resource的区别

@Autowired、@Resource都是自动装配

@Autowired 功能强大,是Spring的注解

@Resource是Java的标准,扩展性更强,可以切换成任意的容器,而@Autowired就不行

泛型的依赖注入

public abstract class BaseDao<T> {
    public abstract void save();
}
@Repository("bookDao")
@Scope(value = "singleton")
public class BookDao extends BaseDao<Book> {
    public void save() {
        System.out.println("保存了一本图书");
    }
}
@Repository
@Scope(value = "singleton")
public class UserDao extends BaseDao<User> {
    public void save() {
        System.out.println("保存用户信息");
    }
}
public abstract class BaseService<T> {
    @Autowired
    BaseDao<T> baseDao ;
    public void save(){
        System.out.println("自动注入的dao" + baseDao);
        baseDao.save();
    }
}
@Service
public class BookService extends BaseService<Book> {}
@Service
public class UserService extends BaseService<User> {}

public class IOCTest {
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml") ;
    @Test
    public  void test(){
        BookService bookService = ioc.getBean(BookService.class);
        UserService userService = ioc.getBean(UserService.class);
        bookService.save();
        userService.save();
    }
}
/*输出
自动注入的aaa@qq.com
保存了一本图书
自动注入的aaa@qq.com
保存用户信息
*/

/*
BaseDao<T>定义了基本的CRUD
BookDao和UserDao为具体实现,并且将他们加入ioc容器中

BaseService<T> 中聚合了一个BaseDao<T> 
BookService和UserService为具体实现,并且将他们加入ioc容器中

在test方法中分别调用了BookService和UserService的save方法,都显示出正确的结果
问题:ioc容器如何为BookService和UserService中的BaseDao<T> 正确的注入依赖呢

BookService 继承至 BaseService<Book> ,也就是聚合了BaseDao<Book> ,ioc容器为其注入依赖时先通过类型查找容器中是否有该bean ,既查找是否有BaseDao<Book>类型的bean,由于BookDao继承至BaseDao<Book>,所以就匹配到了BookDao;UserService同理
*/
相关标签: spring ioc java