Spring 是干什么的

Spring 是开源框架,为简化企业级应用开发而生的,使用Spring可以简化JavaBean实现,以前只有ELB才能实现的功能. Spring 是JavaSE/EE的一站式框架(Java Standard Edition Java技术的核心和基础,是Java ME和Java EE编程的基础)Java Platform,Enterprise Edition推出的企业级应用程序版本。这个版本以前称为 J2EE。能够帮助我们开发和部署可移植、健壮、可伸缩且安全的服务器端 Java应用程序。Java EE 是在 Java SE 的基础上构建的,它提供Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web 3.0应用程序。

  • 方便解耦,简化开发(Spring就是一个大工厂,可以将所有对象创建和依赖关系维护交给Spring管理)
  • AOP 编程的支持,Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
  • 声明式事务的支持,只需要通过配置就可以完成对事务的管理,而无需手动编程
  • 方便程序测试,Spring对junit4支持,可以通过注解方便测试Spring程序
  • 方便继承各种优秀框架,Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(Struts Hibernate Mybatis 等等)的直接支持
  • 降低JavaEE API的使用难度,Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低

Spring Framework 包括的模块图:
image.png

Spring IOC

Spring IOC 底层原理

Spring框架就是解决开发的问题的,一般传统开发:

  1. 直接new一个对象,没有面向接口编程

UserService us = new UserService();
演变⬇️

  1. 通过new 接口的实现类

UserService us = new UserServiceImpl();
OCP原则:open-close 原则,对程序扩展open,对修改程序代码close,不修改程序源码,实现对程序的扩展,上述并没有遵循此原则
⬇️演变

  1. 工厂模式,通过工厂类创建实例对象,那么接口和工厂类又存在耦合了

    1. class Factory{
    2. public UserService get(){
    3. return new UserServiceImpl()
    4. }
    5. .......
    6. }
    1. ⬇️演变
  2. 工厂 + 反射 + 配置文件

    1. <bean id="us" class="...UserServiceImpl">
  1. class Factory{
  2. public Object getBean(String id){
  3. //找到配置文件
  4. //反射
  5. }
  6. .......
  7. }

通过一个工厂类,传递配置文件中的ID,然后配置文件找到相关类,在反射实例化对象. IOC就是控制反转和DI依赖注入,对象被动的接受依赖类.通过动态代理实现接口

Spring IOC 涉及到了工厂模式和单例模式

  • IOC Inverse of Control 控制反转 ,就是将原本程序中手动创建UserService对象的控制权,交由Spring框架管理.
  • 简单来说就是实现接口的类,交给Spring进行实例化了
  • DI Dependency Injection 依赖注入的概念,就是在Spring创建这个对象的过程中,将这个对象所依赖的属性注入进去.依赖注入的目的是在代码之外管理程序间组件的依赖关系;依赖注入减低了程序组件间的耦合.

引入Spring

创建新的项目,在pom.xml 中引入依赖

  1. <!-- 引入Spring相关的依赖 -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-core</artifactId>
  5. <version>4.2.4.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-context</artifactId>
  10. <version>4.2.4.RELEASE</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-beans</artifactId>
  15. <version>4.2.4.RELEASE</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework</groupId>
  19. <artifactId>spring-expression</artifactId>
  20. <version>4.2.4.RELEASE</version>
  21. </dependency>

然后在resources中创建applicationContext.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>
 </beans>

配置完这些后,我们就将Spring引入项目了.下面我们看一下Spring是如何使用的
创建一个类Food

public class Food {
    private String name;
    private String taste;
    private String kind;

    public String getName() {
        return name;
    }

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

    public String getTaste() {
        return taste;
    }

    public void setTaste(String taste) {
        this.taste = taste;
    }

    public String getKind() {
        return kind;
    }

    public void setKind(String kind) {
        this.kind = kind;
    }

    @Override
    public String toString() {
        return "Food{" +
                "name='" + name + '\'' +
                ", taste='" + taste + '\'' +
                ", kind='" + kind + '\'' +
                '}';
    }
}

然后在applicationContext.xml 中添加bean.
标签<bean> 属性:id表示bean的唯一别名; class 表示类的全路径
标签<property> 表示注入属性值

        <bean id="food" class="org.prim.ioc.Food">
            <property name="name" value="香蕉"/>
            <property name="taste" value="水果"/>
            <property name="kind" value="甜甜的"/>
        </bean>

xml bean信息配置完毕后,就可以调用了,首先需要初始化spring的工厂,有两个工厂一个是ClassPathXmlApplicationContext 加载类路径下的配置文件,另一个是FileSystemXmlApplicationContext 加载磁盘某个目录下的配置文件,一般情况下都是用的是ClassPathXmlApplicationContext 传递xml配置文件名.通过getBean获取到Food类的实例对象


public static void main(String[] args) {
        //初始化工厂
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        //ClassPathXmlApplicationContext 加载类路径下的配置文件


        //FileSystemXmlApplicationContext 加载磁盘某个目录下的配置文件
//        ApplicationContext applicationContext =
//                new FileSystemXmlApplicationContext("Users/prim/Desktop/applicationContext.xml");
        //通过工厂来获的类的实例
        Food food = (Food) applicationContext.getBean("food");
        System.out.println("args = " + food.toString());
    }

运行结果:args = Food{name='香蕉', taste='水果', kind='甜甜的'} Spring帮助我们创建了bean实例,并且实现了对属性的注入,大大提高了开发效率,只需要通过Spring工厂获取指定的bean的id,不用在每次用到bean的时候再去new Bean ,然后调用setter getter 方法去给属性赋值,Spring正好帮助我们省去了这些重复的工作.

Spring Bean 管理

下面我们来详细的讲解Spring如何对bean进行初始化和管理的.
如下图所示: Spring通过工厂来实现对Bean的管理

image.png

Bean 实例化的三种方式

  • 使用类构造器实例化(默认无参数)
  • 使用静态工厂方法实例化(简单工厂模式)
  • 使用实例工厂方法实例化(工厂方法模式)

第一种方式,也是常用的方式:使用类构造器实例化.直接上代码大家就明白了

首先,创建bean类在bean的无参构造方法中打印

public class Bean1 {
    public Bean1() {
        System.out.println("Bean1");
    }
}

然后在applicationContext.xml中配置

<bean id="bean1" class="org.prim.ioc.bean.Bean1"/>

最后调用该bean


ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //如下即使不写下面的方式,applicationContext也会默认实例化Bean1
        Bean1 bean1 = (Bean1) applicationContext.getBean("bean1");

打印出:Bean1

其实在我们初始化Spring工厂的时候,就会去解析xml然后,通过反射实例化xml中配置的bean对象,如下我们不去调用bean,只是初始化spring工厂,来看看bean,会不会被实例化.

ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

运行打印结果: 如下同样会打印出Bean1来,这也就是说明了上述中的说法.

Bean1

第二种方式:静态工厂实例化
首先创建bean2

public class Bean2 {
    public Bean2() {
        System.out.println("Bean2.Bean2");
    }
}

然后,创建Bean2的静态工厂类

public class Bean2Factory {
    public Bean2Factory() {
        System.out.println("Bean2Factory");
    }

    public static Bean2 createBean2() {
        System.out.println("Bean2Factory -> createBean2");
        return new Bean2();
    }
}

然后在applicationContext.xml中配置,如下大家肯定注意到了上述第一种方式配置不一样了,在<bean>的标签下class中是bean的工厂类, 同时多出了一个属性factory-method,这个属性配置的就是工厂的静态方法.在静态方法中返回Bean的实例

 <bean id="bean2" class="org.prim.ioc.bean.Bean2Factory" factory-method="createBean2"/>

我们来进行测试

 ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
Bean2 bean2 = (Bean2) applicationContext.getBean("bean2");

运行打印结果:

通过结果分析:在静态工厂方式实例化bean的时候,初始化Spring工厂并没有实例化工厂类Bean2Factory,而是在调用getBean的时候调用了工厂的静态方法.

Bean2Factory -> createBean2
Bean2.Bean2

第三种方式:实例工厂方法实例化
创建bean类和工厂类

public class Bean3 {
    public Bean3() {
        System.out.println("Bean3.Bean3");
    }
}
public class Bean3Factory {
    public Bean3Factory() {
        System.out.println("Bean3Factory");
    }

    public Bean3 createBean3() {
        System.out.println("Bean3Factory -> createBean3");
        return new Bean3();
    }
}

applicationContext.xml中配置
使用第三种方式,就需要配置工厂和bean,在配置bean的时候配置属性factory-bean 设置工厂的bean - id,factory-method 配置工厂实例化bean的方法.

    <bean id="bean3Factory" class="org.prim.ioc.bean.Bean3Factory"/>
    <bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>
Bean3 bean3 = (Bean3) applicationContext.getBean("bean3");

运行结果:

使用工厂实例化的方式实例化bean,会先创建工厂实例,在getBean的时候创建bean的实例.

Bean3Factory
Bean3Factory -> createBean3
Bean3.Bean3

对于bean的三种实例化,其实第二种和第三种类似,一般默认使用第一种方式,至于工厂方式去实现bean的实例化,我的猜想是,如果在一个非常大的项目如果有1000个bean,那么在初始化Spring工厂的时候,就会实例化1000个bean,这样无疑是非常慢的,使用工厂的方式去实例化bean,在初始化Spring工厂的时候,只需要实例化一个工厂类即可,在我们使用某个bean的时候,才会通过工厂去实例化bean.这样一对比就会非常的明显,在初始化spring工厂的时候一下初始化1000个bean好,还是只初始化一个工厂类好呢? Spring框架的设计思想非常值得学习,它考虑到了非常多的细节问题.

Bean 常用的配置

id 和 name 一般情况下,装配一个Bean时,通过指定一个ID属性作为Bean名称. id属性在IOC容器中必须是唯一的 如果Bean的名称中含有特殊字符,就需要使用name属性 class 用于设置一个类的完全路径名称,主要作用是IOC容器生成类的实例

Spring 帮助创建的类是单例的还是每次都返回一个新的实例呢? 就需要使用Bean的作用域

singleton 在SpringIOC容器中仅存在一个Bean实例,Bean以单例的方式存在
prototype 每次调用getBean()时返回一个新的实例
request 每次http请求都会创建一个新的bean,该作用域仅适用于WebApplicationContext环境
session 同一个http session共享一个bean,不同的HTTP Session使用不同的Bean.该作用域仅适用于WebApplicationContext环境

request 和 session 这里我们先不讲,一般只有在javaWeb开发中才会用到,纯Java后台用不到.

这里主要讲一下singletonprototype的区别
我们在applicationContext.xml中配置 scope属性,如果不配置scope属性默认为singleton,创建的对象都是单例的.
我们先看一下单例的方式

<bean id="person" class="org.prim.ioc.demo3.Persion"/>
Persion persion = (Persion) applicationContext.getBean("person");
Persion persion2 = (Persion) applicationContext.getBean("person");
System.out.println("persion = " + persion + " persion2 = " + persion2);

运行结果: 获取的都是同一个对象

persion = org.prim.ioc.demo3.Persion@679b62af persion2 = org.prim.ioc.demo3.Persion@679b62af

将scope属性设置为prototype

<bean id="person" class="org.prim.ioc.demo3.Persion" scope="prototype"/>

运行结果: 可以看出每次getBean获取的实例都是重新创建的

persion = org.prim.ioc.demo3.Persion@679b62af persion2 = org.prim.ioc.demo3.Persion@5cdd8682

Spring 容器中Bean的生命周期

Spring初始化bean或销毁bean时,有时需要做一些处理工作,因此Spring可以在创建和销毁bean的时候调用bean的两个生命周期方法:

init-method = "init"
destory-method = "destory"

当bean被载入到容器的时候调用init. 当bean从容器中删除的时候调用destory(scope=singleton才会有效).
Bean的完整生命周期

  1. instantiate bean 实例化对象
  2. populate properties 设置属性
  3. 如果Bean实现**BeanNameAware** 执行setBeanName
  4. 如果Bean实现**BeanFactoryAware** 或者 **ApplicationContextAware** 设置工厂**setBeanFactory** 或者上下文对象 **setApplicationContext**
  5. 如果存在类实现 **BeanPostProcessor**(后处理Bean),执行**postProcessBeforeInitialization**
  6. 如果Bean实现**InitializingBean** 执行**afterPropertiesSet**
  7. 如果 配置了**init-method**属性则调用Bean中的方法
  8. 如果存在类实现**BeanPostProcessor**(处理Bean),执行**postProcessAfterInitialization**.
  9. 执行业务处理
  10. 如果Bean实现了**DisposableBean** 执行**destroy**
  11. 调用<bean destroy-method="custome-destroy"> 指定的销毁方法custome-destroy

接下来我们来模拟一下Bean的完整周期
首先创建Bean类


public class Man implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
    public Man() {
        System.out.println("Man()");
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("第二步:设置属性");
        this.name = name;
    }

    public void init() {
        System.out.println("第七步:Man被初始化了");
    }

    public void beanclose() {
        System.out.println("第11步:Man被销毁了");
    }


    @Override
    public void destroy() throws Exception {
        System.out.println("第10步:执行spring销毁方法");
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("第三步:设置 Bean的名称 也就是<bean> id的值 = " + s);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:了解工厂的信息 = " + applicationContext);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第六步:属性设置后执行");
    }

    public void run(){
        System.out.println("第九步:执行自身业务方法");
    }
}

创建MyBeanPostProcessor 实现BeanPostProcessor类,配置在xml中不需要给ID,Spring会自动调用

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        System.out.println("第五步:初始化前方法...");
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("第八步:初始化后方法...");
        return bean;
    }
}

applicationContext.xml 配置

    <!-- Bean的生命周期 -->
        <bean id="man" class="org.prim.ioc.demo3.Man" init-method="init" destroy-method="beanclose">
            <property name="name" value="name"/>
        </bean>

<!--     在生成类的实例过程中自动调用 -->
        <bean class="org.prim.ioc.demo3.MyBeanPostProcessor"/>

测试运行结果:

        Man man = (Man) applicationContext.getBean("man");
        man.run();
        applicationContext.close();

运行结果如下:这11步就是Spring Bean的完整生命周期
image.png

了解Spring Bean的生命周期对后续Spring AOP以及Spring框架设计和实际的开发是非常重要的.


BeanPostProcessor 的作用

BeanPostProcessor 可实现对类对增强 是SpringAOP 的关键也就是动态代理,我们来看例子

public interface UserDao {
    void findAll();

    void save();

    void update();

    void delete();
}


public class UserDaoImpl implements UserDao {
    @Override
    public void findAll() {
        System.out.println("findAll");
    }

    @Override
    public void save() {
        System.out.println("save");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }
}

在BeanPostProcessor的postProcessAfterInitialization 中处理在上述中,我们知道postProcessAfterInitialization是在Bean调用初始化方法后调用的,在这个方法中,我们可以增强了处理一下方法.

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        /**
         * 增强类 动态代理
         */
        if ("userDao".equals(beanName)) {
            //增强类 实现接口类
            Object o = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if ("save".equals(method.getName())) {
                                System.out.println("proxy = 权限校验");
                                return method.invoke(bean, args);
                            }
                            return method.invoke(bean, args);
                        }
                    });
            return o;
        }

        return bean;
    }
}

下面调用测试一下:

        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        userDao.findAll();
        userDao.save();
        userDao.update();
        userDao.delete();

运行结果如下: 在BeanPostProcessor中处理了save方法,可以看到. 在BeanPostProcessor中通过动态代理机制增强了类的实现

findAll proxy = 权限校验 save update delete

Spring 属性注入

对于类成员变量,注入方式有三种:构造函数的属性注入、属性setter方法注入、接口注入
Spring支持前两种方式.

构造方法注入

通过构造方法注入bean的属性值或依赖的对象,它保证了bean实例在实例化后就可以使用 构造器注入在 元素里声明的属性

直接看例子,非常简单

public class User {
    private String name;
    private Integer age;

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

<bean id="user" class="org.prim.ioc.demo4.User">
        <constructor-arg name="name" value="lily"/>
        <constructor-arg name="age" value="18"/>
</bean>

setter方法注入

使用setter方法注入,在Spring配置文件中,通过设置注入的属性. ref 属性可以注入其他bean.

public class Person {
    private String name;
    private Integer age;
    private Cat cat;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", cat=" + cat +
                '}';
    }
}
public class Cat {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

xml 中进行配置

        <bean id="person" class="org.prim.ioc.demo4.Person">
            <property name="name" value="jakeprim"/>
            <property name="age" value="28"/> <!-- 注入普通值 -->
            <property name="cat" ref="cat"/> <!-- 注入其他bean -->
        </bean>

        <bean id="cat" class="org.prim.ioc.demo4.Cat">
            <property name="name" value="折耳猫"/>
        </bean>

p名称空间属性注入

使用p命名空间 p:<属性名>=”xxx”引入常量值 p:<属性名>-ref=”xxx”引入其他bean对象

首先在applicationContext.xml 中添加xmlns

xmlns:p="http://www.springframework.org/schema/p"

如下的方式注入属性

    <bean id="person" class="org.prim.ioc.demo4.Person" p:name="lily" p:age="27" p:cat-ref="cat"/>
    <bean id="cat" class="org.prim.ioc.demo4.Cat" p:name="小花"/>

SpEL 属性注入

SpEL:spring expression language,spring表达式语言,对依赖注入进行简化 语法:#{表达式}

{‘hello’} : 使用字符串

{beanId}: 使用另一个bean

{beanId.content.toUpperCase()}:使用指定名属性,并使用方法

{T(java.lang.Math).PI}:使用静态字段或方法

SpEL 可以调用指定的beanid中的方法

        <bean id="product" class="org.prim.ioc.demo4.Produce">
            <property name="name" value="#{'男装'}"/>
            <property name="price" value="#{productInfo.calculatePrice()}"/>
            <property name="category" value="#{category}"/>
        </bean>
        <bean id="category" class="org.prim.ioc.demo4.Category">
            <property name="name" value="#{'手机'}"/>
        </bean>
        <bean id="productInfo" class="org.prim.ioc.demo4.ProductInfo"/>

复杂类型的属性注入

数组类型的属性注入 list集合属性注入 Set集合属性注入 Map集合属性注入 Properties 类型的属性注入

      <bean id="collection" class="org.prim.ioc.demo5.CollectionBean">
            <!-- 数组类型 -->
            <property name="arrs">
                <list>
                    <value>aaa</value>
                    <value>ab</value>
                    <value>abc</value>
                </list>
            </property>
            <!-- list类型 -->
            <property name="list">
                <list>
                    <value>l1</value>
                    <value>l2</value>
                    <value>l3</value>
                </list>
            </property>
            <!-- set 注入 -->
            <property name="set">
                <set>
                    <value>s1</value>
                    <value>s2</value>
                    <value>s3</value>
                </set>
            </property>
            <!-- map -->
            <property name="map">
                <map>
                    <entry key="m1" value="1" />
                    <entry key="m2" value="2" />
                    <entry key="m3" value="3" />
                </map>
            </property>
            <!-- properties -->
            <property name="properties">
                <props>
                    <prop key="username">root</prop>
                    <prop key="password">123456</prop>
                </props>
            </property>
        </bean>

Spring Bean 管理 - 注解方式

@Component 描述Spring框架中Bean 在配置文件引入:

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

然后开启注解扫描,base-package属性表示要扫描的包名.

<context:component-scan base-package="org.prim.ioc.demo6"/>

Bean类注解

常用的注解如下:

除了@Component外,Spring提供了3个功能基本和@Component等效的注解 @Repository 用于对DAO类实现类进行标注 @Service 用于对Service实现类进行标注 @Controller 用于对Controller实现类进行标注 这三个注解是为了让标注类本身的用途清晰,Spring在后续版本会对其增强

在Bean类上加入注解

//@Component("userService")
@Service("userService")
public class UserService {
    public String sayHello(String name) {
        return "hello:" + name;
    }
    public void eat() {
        System.out.println("eat:");
    }
    public void save() {
        System.out.println("Service中保存用户的方法");
    }
}

加上注解后,我们就可以直接通过Spring调用了

ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.eat();
        userService.save();

属性注入的注解

@Value @Autowired 默认按照类型进行注入 如果存在两个相同Bean类型相同,则按照名称注入 @Autowired 注入时可以针对成员变量或者set方法 通过@Autowired的required属性:true(默认true),设置一定要找到匹配的Bean 使用@Qualifier 指定注入Bean的名称 Spring提供对JSR-250中定义@Resource标准注解的支持,这个注解相当于@Autowired和@Qualifier 同时使用,可以简写为@Resource

注意:类中如果有setter方法,那么注解需要加到setter方法上方,不可以加到getter方法上方

@Repository("userDao")
public class UserDao {
    public void save(){
        System.out.println("DAO保存用户....");
    }
}

如下注入属性,同时调用其他的bean类

//@Component("userService")
@Service("userService")
public class UserService {
    public String sayHello(String name) {
        return "hello:" + name;
    }

    @Value("米饭")
    private String someting;

//    @Autowired
//    @Qualifier("userDao") //必须匹配UserDao的设置的名称
    @Resource(name = "userDao")
    private UserDao userDao;

    public void eat() {
        System.out.println("eat:" + someting);
    }

    public void save() {
        System.out.println("Service中保存用户的方法");
        userDao.save();
    }
}

输出的结果如下:

userService = hello:张三

eat:米饭

Service中保存用户的方法

DAO保存用户….


Spring 的其他注解

Spring 初始化bean或销毁bean时,有时需要作一些处理工作,因此Spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法 当bean被载入到容器的时候调用: @PostConstruct 添加到初始化方法上 当bean从容器中删除的时候调用: @PreDestory 添加到销毁方法上

@Component("bean1")
@Scope("prototype")
public class Bean1 {
    @PostConstruct
    public void init() {
        System.out.println("init");
    }

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

    public void say() {
        System.out.println("say...");
    }
}

@Scope 注解来指定Bean的作用范围,单例或每次创建实例

 ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean1 bean1 = (Bean1) applicationContext.getBean("bean1");
        Bean1 bean2 = (Bean1) applicationContext.getBean("bean1");
        bean1.say();
        System.out.println("bean2 = " + bean2 + " bean1=" + bean1);
        applicationContext.close();

Spring的XML和注解整合开发

XML 方式的优势:结构清晰,易于阅读 注解方式的优势: 开发便捷,属性注入方便 XML 用于管理类,注解用于完成属性注入,各司其职 XML 与 注解的整合开发

  1. 引入context命名空间
  2. 在配置文件中添加context:annotation-config标签

配置属性注入

public class ProductService {

    @Resource(name = "categoryDao")
    private CategoryDao categoryDao;

    @Resource(name = "productDao")
    private ProductDao productDao;

    public void save() {
        System.out.println("ProductService -> save");
        productDao.save();
        categoryDao.save();
    }
}
public class ProductDao {
    public void save(){
        System.out.println("ProductDao.save");
    }
}

在xml中配置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:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">   
    <bean id="productService" class="org.prim.ioc.demo7.ProductService"/>
    <bean id="productDao" class="org.prim.ioc.demo7.ProductDao"/>
    <bean id="categoryDao" class="org.prim.ioc.demo7.CategoryDao"/>
    <context:component-scan base-package="org.prim.ioc.demo7"/>
</bean>

在实际开发中,经常用到Spring的xml和注解整合开发的方式,使项目更容易管理