框架学习之 Spring IOC 初见

今天笔者带大家来学习框架的内容了,框架可以帮助我们进行更有效的开发,但框架也是要把基础学好,才能使用的,下面就让笔者为大家介绍一个大家耳熟能详的 Spring 框架~

Spring 框架

概念

Spring 是众多开源java项目中的一员,基于分层的javaEE应用——站式轻量级开源框架,主要核心是 IOC(控制反转/依赖注入)与 AOP(面向切面)两大技术,实现项目在开发过程中的轻松解耦,提高项目的开发效率。

在项目中引入 Spring 立即可以带来下面的好处 降低组件之间的耦合度,实现软件各层之间的解耦。可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务,也不需处理复杂的事务传播。 容器提供单例模式支持,开发人员不再需要 自己编写实现代码。 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。

源码架构

Spring 总共大约有20个模块,由1300多个不同的文件构成。而这些组件被分别整合在核心容器(Core Container)、Aop(Aspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成 (Data Access/Integeration)、Web、报文发送(Messaging)、测试6个模块集合中。(以下内容可看,可不看,熟悉 Spring 框架内的模块

  1. 核心容器:Spring-beans 和 Spring-core 模块是 Spring 框架的核心模块,包含控制反转(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI),核心容器提供 Spring 框架的基本功能。 核心容器的主要组件是 BeanFactory,工厂模式的实现。BeanFactory 使用控制反转(IOC) 思想将应用程序的配置和依赖性规范与实际的应用程序代码分开。                                                       Spring 上下文Spring Context:Spring 上下文是⼀个配置文件,向 Spring 框架提供上下文信息。 Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。                                                                                                                             Spring-Expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等。它的语法类似于传统EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。
  2. Spring-AOP:Spring-aop 是 Spring 的另一个核心模块, 在 Spring 中,他是以 JVM 的动态代理技术为基础,然后设计出了一系列的 Aop 横切实现,比如前置通知、返回通知、异常通知等。通过其配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。
  3. Spring Data Access(数据访问):由Spring-jdbc、Spring-tx、Spring-orm、Spring-jms和Spring-oxm 5 个模块组成 Spring-jdbc 模块是 Spring 提供的JDBC抽象框架的主要实现模块,用于简化 Spring JDBC。                                                                                         Spring-tx 模块是 SpringJDBC 事务控制实现模块。使用 Spring 框架,它对事务做了很好的封装,通过它的 Aop 配置,可以灵活的配置在任何一层。                                                                                                                                                                                       Spring-Orm 模块是 ORM 框架支持模块,主要集成 hibernate, Java Persistence API (JPA) 和 Java Data Objects (JDO) 用于资源管理、数据访问对象(DAO)的实现和事务策略。                                                                                                                                         Spring-Jms 模块(Java Messaging Service)能够发送和接受信息。                                                                                                     Spring-Oxm 模块主要提供一个抽象层以支撑OXM(OXM 是 Object-to-XML-Mapping 的缩写,它是一个 O/M-mapper,将java 对象映射成 XML 数据,或者将 XML 数据映射成 java 对象),例如:JAXB, Castor, XMLBeans, JiBX 和 XStream 等。
  4. Web 模块:由 Spring-web、Spring-webmvc、Spring-websocket 和 Spring-webmvc-portlet 4个模块组成,Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。 Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  5. 报文发送:即 Spring-messaging 模块。 Spring-messaging 是 Spring4 新加入的一个模块,主要职责是为 Spring 框架集成一些基础的报文传送应用。
  6. 单元测试:即 Spring-test 模块。Spring-test 模块主要为测试提供支持

Spring 框架环境搭建

环境要求

JDK 版本: JDK 1.7 及以上版本

Spring版本: Spring 5.x版本

新建 Maven 项目

创建 Maven 的普通 Java 项目

添加 Spring 框架的依赖坐标

<!-- 添加Spring框架的核⼼依赖 -->
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>5.2.4.RELEASE</version>
</dependency>

编写 Bean 对象

package com.test.service;
public class MyService {
    public void test1 ()
    {
        System.out.println("Hello Spring!");
    }
}

添加 Spring 配置文件

  1. 在项目的 src 下的 main 下创建文件夹 resources
  2. 将 resources 标记为资源目录
  3. 在 src\main\resources 目录下新建 spring.xml 文件,并拷贝官网文档提供的模板内容到 xml 中。

配置 bean 到 xml 中,把对应 bean 纳入到 Spring 容器来管理

spring.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
 <!--
 xmlns 即 xml namespace xml使⽤的命名空间
 xmlns:xsi 即xml schema instance xml 遵守的具体规范
 xsi:schemaLocation 本⽂档xml遵守的规范 官⽅指定
 -->
    <bean id="myService" class="com.test.service.MyService"></bean>
</beans>    

加载配置文件,获取实例化对象

package com.test;

import com.test.service.MyService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        // 获取Spring上下⽂环境 (加载配置⽂件)
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring.xml");
        // 通过getBean⽅法得到Spring容器中实例化好的Bean对象 (实例化Bean对象)
        // userService代表的是配置⽂件中bean标签的id属性值
        MyService myService = (MyService) ac.getBean("myService");
        // 调⽤⽅法 (使⽤实例化对象)
        myService.test1();
    }
}

 

Spring IOC 配置文件加载

根据相对路径加载资源

ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");

根据绝对路径加载资源

ApplicationContext ac = new
FileSystemXmlApplicationContext("C:/IdeaWorkspace/spring01/src/main/resources/spring.x
ml"); 

Spring 多配置文件加载

Spring 框架启动时可以加载多个配置文件到环境中。对于比较复杂的项目,可能对应的配置文件有多个,项目在启动部署时会将多个配置文件同时加载进来。

spring.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    xmlns 即 xml namespace xml使⽤的命名空间
    xmlns:xsi 即xml schema instance xml 遵守的具体规范
    xsi:schemaLocation 本⽂档xml遵守的规范 官⽅指定
    -->
    <bean id="MyService" class="com.test.service.MyService"></bean>
</beans>

spring1.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    xmlns 即 xml namespace xml使⽤的命名空间
    xmlns:xsi 即xml schema instance xml 遵守的具体规范
    xsi:schemaLocation 本⽂档xml遵守的规范 官⽅指定
    -->
    <bean id="MyDao" class="com.test.dao.MyDao"></bean>
</beans>

可变参数,传入多个文件名

// 同时加载多个资源⽂件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml","spring1.xml");

通过总的配置文件 import 其他配置文件

spring3.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--导⼊需要包含的资源⽂件-->
    <import resource="spring"/>
    <import resource="spring1"/>
</beans>

加载时只需加载总的配置文件即可

// 加载总的资源⽂件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring3.xml");

Spring IOC 容器 Bean 对象实例化

构造器实例化

注:通过默认构造器创建空构造方法必须存在 否则创建失败

静态工厂实例化

注: 要有该工厂类及工厂方法             工厂方法为静态的

定义静态工厂类

package com.xxxx.factory;
import com.xxxx.service.UserService;
/**
* 定义静态⼯⼚类
*/
public class StaticFactory {
 /**
 * 定义对应的静态⽅法,返回实例化对象
 * @return
 */
 public static UserService createUserService() {
 return new UserService();
 }
}

设置配置文件 spring.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
 <!--静态⼯⼚-->
 <bean id="userService" class="com.xxxx.factory.StaticFactory" factorymethod="createUserService"></bean>
</beans>

获取实例化对象

ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();

当我们指定Spring使用静态工厂方法来创建 Bean 实例时,Spring 将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为 Bean 实例,在这个过程中,Spring 不再负责创建 Bean 实例,Bean 实例是由用户提供的静态工厂方法提供的。

实例化工厂实例化

注: 工厂方法为非静态方法需要配置工厂 bean,并在业务 bean 中配置factory-bean,factory-method 属性

定义工厂类

package com.xxxx.factory;
import com.xxxx.service.UserService;
/**
* 定义⼯⼚类
*/
public class InstanceFactory {
 /**
 * 定义⽅法,返回实例化对象
 * @return
 */
 public UserService createUserService() {
 return new UserService();
 }
}

设置配置文件 spring.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
 <!--
 实例化⼯⼚
 1.定义实例化⼯⼚bean
 2.引⽤⼯⼚bean 指定⼯⼚创建⽅法(⽅法为⾮静态)
 -->
 <bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean>
 <bean id="userService" factory-bean="instanceFactory" factorymethod="createUserService"></bean>
</beans>

获取实例化对象

ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();

Spring三种实例化Bean的方式比较

  • 方式一:通过bean的缺省构造函数创建,当各个 bean 的业务逻辑相互比较独立的时候或者和外界关联较少的时候可以使用。
  • 方式二:利用静态factory 方法创建,可以统一管理各个 bean 的创建,如各个 bean 在创建之前需要相同的初始化处理,则可用这个 factory 方法险进行统一的处理等等。
  • 方式三:利用实例化 factory 方法创建,即将 factory 方法也作为了业务 bean 来控制,1可用于集成其他框架的 bean 创建管理方法,2能够使 bean 和 factory 的角色互换。

开发中项目一般使用一种方式实例化 bean,项目开发基本采用第一种方式,交给 Spring 托管,使用时直接拿来使用即可。另外两种了解就行,平常用不到,除非有特殊情况

Spring IOC 注入

手动实例化与外部引入

对比发现:图二中对于 UserDao 对象的创建并没有像图一那样主动的去实例化,而是通过带参方法形式将 UserDao 传入过来,从而实现 UserService 对 UserDao类的依赖。

实际创建对象的幕后对象即是交给了外部来创建。

Spring IOC 手动装配(注入)

Spring 支持的注入方式共有四种:set 注入、构造器注入、静态工厂注入、实例化工厂注入。

set方法注入

注: 属性字段需要提供set方法     四种方式,推荐使用set方法注入,所以笔者只讲 set方法注入

业务对象 JavaBean

提供 set 方法

public class UserService {
 // 业务对象UserDao set注⼊(提供set⽅法)
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
}

配置文件的 bean 标签设置 property 标签

<?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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
 IOC通过property标签⼿动装配(注⼊):
 Set⽅法注⼊
 name:bean对象中属性字段的名称
 ref:指定bean标签的id属性值
 -->
 <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
 <bean id="userService" class="com.xxxx.service.UserService">
 <!--业务对象 注⼊-->
 <property name="userDao" ref="userDao"/>
 </bean>
</beans>

常用对象和基本类型

属性字段提供 set 方法

public class UserService {
 // 常⽤对象String set注⼊(提供set⽅法)
 private String host;
 public void setHost(String host) {
 this.host = host;
 }
 // 基本类型Integer set注⼊(提供set⽅法)
 private Integer port;
 public void setPort(Integer port) {
 this.port = port;
 }
}

配置文件的 bean 标签设置 property 标签

<?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
 https://www.springframework.org/schema/beans/spring-beans.xsd">

 <!--
 IOC通过property标签⼿动装配(注⼊):
 Set⽅法注⼊
 name:bean对象中属性字段的名称
 value:具体的值(基本类型 常⽤对象|⽇期 集合)
 -->
 <bean id="userService" class="com.xxxx.service.UserService">
 <!-- 常⽤对象String 注⼊ -->
 <property name="host" value="127.0.0.1"/>
 <!-- 基本类型注⼊ -->
 <property name="port" value="8080"/>
 </bean>
</beans>

集合类型和属性对象

属性字段提供 set 方法

public class UserService {
 // List集合 set注⼊(提供set⽅法)
 public List<String> list;
 public void setList(List<String> list) {
 this.list = list;
 }
 // Set集合 set注⼊(提供set⽅法)
 private Set<String> set;
 public void setSet(Set<String> set) {
 this.set = set;
 }
 // Map set注⼊(提供set⽅法)
 private Map<String,Object> map;
 public void setMap(Map<String, Object> map) {
 this.map = map;
 }
 // Properties set注⼊(提供set⽅法)
 private Properties properties;
 public void setProperties(Properties properties) {
 this.properties = properties;
 }

}

配置文件的 bean 标签设置 property 标签

<?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
 https://www.springframework.org/schema/beans/spring-beans.xsd">

 <!--
 IOC通过property标签⼿动装配(注⼊):
 Set⽅法注⼊
 name:bean对象中属性字段的名称
 value:具体的值(基本类型 常⽤对象|⽇期 集合)
 -->
 <bean id="userService" class="com.xxxx.service.UserService">
 <!--List集合 注⼊-->
<property name="list">
 <list>
 <value>上海</value>
 <value>北京</value>
 <value>杭州</value>
 </list>
 </property>
 <!--Set集合注⼊-->
 <property name="set">
 <set>
 <value>上海SH</value>
 <value>北京BJ</value>
 <value>杭州HZ</value>
 </set>
 </property>
 <!--Map注⼊-->
 <property name="map">
 <map>
 <entry>
 <key><value>周杰伦</value></key>
 <value>我是如此相信</value>
 </entry>
 <entry>
 <key><value>林俊杰</value></key>
 <value>可惜没如果</value>
 </entry>
 <entry>
 <key><value>陈奕迅</value></key>
 <value>⼗年</value>
 </entry>
 </map>
 </property>
 <!--Properties注⼊-->
 <property name="properties">
 <props>
 <prop key="上海">东⽅明珠</prop>
 <prop key="北京">天安⻔</prop>
 <prop key="杭州">⻄湖</prop>
 </props>
 </property>
 </bean>
</beans>

测试代码

UserService.java

public class UserService {
// 业务对象UserDao set注⼊(提供set⽅法)
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 // 常⽤对象String set注⼊(提供set⽅法)
 private String host;
 public void setHost(String host) {
 this.host = host;
 }
 // 基本类型Integer set注⼊(提供set⽅法)
 private Integer port;
 public void setPort(Integer port) {
 this.port = port;
 }
 // List集合 set注⼊(提供set⽅法)
 public List<String> list;
 public void setList(List<String> list) {
 this.list = list;
 }
 // List集合输出
 public void printList() {
 list.forEach(s -> System.out.println(s));
 }
 // Set集合 set注⼊(提供set⽅法)
 private Set<String> set;
 public void setSet(Set<String> set) {
 this.set = set;
 }
 // Set集合输出
 public void printSet() {
 set.forEach(s -> System.out.println(s));
 }
 // Map set注⼊(提供set⽅法)
 private Map<String,Object> map;
 public void setMap(Map<String, Object> map) {
 this.map = map;
 }
 // Map输出
 public void printMap() {
 map.forEach((k,v) -> System.out.println(k + "," + v));
 }
 // Properties set注⼊(提供set⽅法)
 private Properties properties;
public void setProperties(Properties properties) {
 this.properties = properties;
 }
 // Properties输出
 public void printProperties(){
 properties.forEach((k,v) -> System.out.println(k + ","+ v ));
 }
 public void test(){
 System.out.println("UserService Test...");
 userDao.test();
 studentDao.test();
 System.out.println("Host:" + host + ",port:" + port);
 // List集合
 printList();
 // Set集合
 printSet();
 // Map
 printMap();
 // Properties
 printProperties();
 }
}

spring.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
 https://www.springframework.org/schema/beans/spring-beans.xsd">

 <!--
 IOC通过property标签⼿动装配(注⼊):
 Set⽅法注⼊
 name:bean对象中属性字段的名称
 ref:指定bean标签的id属性值
 value:具体的值(基本类型 常⽤对象|⽇期 集合)
 -->
 <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
 <bean id="userService" class="com.xxxx.service.UserService">
<!--业务对象 注⼊-->
 <property name="userDao" ref="userDao"/>
 <property name="studentDao" ref="studentDao"/>
 <!--常⽤对象String 注⼊-->
 <property name="host" value="192.168.1.109"/>
 <!--基本类型注⼊-->
 <property name="port" value="8080"/>
 <!--List集合 注⼊-->
 <property name="list">
 <list>
 <value>上海</value>
 <value>北京</value>
 <value>杭州</value>
 </list>
 </property>
 <!--Set集合注⼊-->
 <property name="set">
 <set>
 <value>上海SH</value>
 <value>北京BJ</value>
 <value>杭州HZ</value>
 </set>
 </property>
 <!--Map注⼊-->
 <property name="map">
 <map>
 <entry>
 <key><value>周杰伦</value></key>
 <value>我是如此相信</value>
 </entry>
 <entry>
 <key><value>林俊杰</value></key>
 <value>可惜没如果</value>
 </entry>
 <entry>
 <key><value>陈奕迅</value></key>
 <value>⼗年</value>
 </entry>
 </map>
 </property>
 <!--Properties注⼊-->
 <property name="properties">
 <props>
 <prop key="上海">东⽅明珠</prop>
 <prop key="北京">天安⻔</prop>
 <prop key="杭州">⻄湖</prop>
 </props>
</property>
 </bean>
</beans>

注入方式的选择

开发项目中set方式注入首选

使用构造注入可以在构建对象的同时一并完成依赖关系的建立,对象一建立则所有的一切也就准备好了,但如果要建立的对象关系很多,使用构造器注入会在构建函数上留下一串串的参数,且不易记忆,这时使用 Set 注入会是个不错的选择。   

使用 Set 注入可以有明确的名称,可以了解注入的对象会是什么,像setXXX() 这样的名称会比记忆 Constructor 上某个参数的位置代表某个对象更好。

p名称空间的使用

spring2.5以后,为了简化 setter 方法属性注入,引用p名称空间的概念,可以将子元素,简化为元素属性配置。

属性字段提供 set 方法

public class UserService {
 // 业务对象UserDao set注⼊(提供set⽅法)
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }

 // 常⽤对象String set注⼊(提供set⽅法)
 private String host;
 public void setHost(String host) {
 this.host = host;
 }
}

在配置文件 spring.xml 引入 p 名称空间

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
 https://www.springframework.org/schema/beans/spring-beans.xsd">

 <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
 <!--
 p:属性名:="xxx" 引⼊常量值
 p:属性名-ref:="xxx" 引⼊其他Bean对象的id属性值
 -->
 <bean id="userService" class="com.xxxx.service.UserService"
 p:userDao-ref="userDao"
 p:host="127.0.0.1" />
</beans>

Spring IOC 自动装配(注入)

注解方式注入 Bean

对于 bean 的注入,除了使用 xml 配置以外,可以使用注解配置。注解的配置,可以简化配置文件, 提高开发的速度,使程序看上去更简洁。对于注解的解释,Spring 对于注解有专门的解释器,对定义的注解进行解析,实现对应bean 对象的注入。通过反射技术实现。

准备环境

修改配置文件

<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
 https://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/>
<bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
<bean id="userService" class="com.xxxx.service.UserService"></bean>

给注入的 bean 对象添加注解

@Resource注解

注解实现自动注入(反射)

  • 默认根据属性字段名称查找对应的 bean 对象 (属性字段的名称与 bean 标签的 id 属性值相等)
  • 如果属性字段名称未找到,则会通过类型(Class类型)查找
  • 属性可以提供 set 方法,也可以不提供 set 方法
  • 注解可以声明在属性级别 或 set 方法级别
  • 可以设置 name 属性,name 属性值必须与 bean 标签的 id 属性值一致;如果设置了 name 属性值,就只会按照 name 属性值查找 bean 对象
  • 当注入接口时,如果接口只有一个实现则正常实例化;如果接口存在多个实现,则需要使用 name 属性指定需要被实例化的 bean 对象

代码示例

默认根据属性字段名称查找对应的 bean 对象 (属性字段的名称与 bean 标签的 id 属性值相等)

/**
* @Resource注解实现⾃动注⼊(反射)
* 默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
*/
public class UserService {
 @Resource
 private UserDao userDao; // 属性字段的名称与bean标签的id属性值相等
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
public void test() {
 // 调⽤UserDao的⽅法
 userDao.test();
 }
}

如果属性字段名称未找到,则会通过类型(Class类型)查找

/**
* @Resource注解实现⾃动注⼊(反射)
* 如果属性字段名称未找到,则会通过类型(Class类型)查找
*/
public class UserService {
 @Resource
 private UserDao ud; // 当在配置⽂件中属性字段名(ud)未找到,则会查找对应的
class(UserDao类型)
 public void setUd(UserDao ud) {
 this.ud = ud;
 }
 public void test() {
 // 调⽤UserDao的⽅法
 ud.test();
 }
}

属性可以提供 set 方法,也可以不提供 set 方法

/**
* @Resource注解实现⾃动注⼊(反射)
* 属性可以提供set⽅法,也可以不提供set⽅法
*/
public class UserService {
 @Resource
 private UserDao userDao; // 不提供set⽅法
 public void test() {
 // 调⽤UserDao的⽅法
 userDao.test();
 }
}

注解可以声明在属性级别或 set方法级别

/**
* @Resource注解实现⾃动注⼊(反射)
* 注解可以声明在属性级别 或 set⽅法级别
*/
public class UserService {
 private UserDao userDao;
 @Resource // 注解也可设置在set⽅法上
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void test() {
 // 调⽤UserDao的⽅法
 userDao.test();
 }
}

可以设置 name 属性,name 属性值必须与 bean 标签的 id 属性值一致;如果设置了 name 属性值,就只会按照 name 属性值查找 bean 对象

/**
* @Resource注解实现⾃动注⼊(反射)
* 可以设置name属性,name属性值必须与bean的id属性值⼀致;
* 如果设置了name属性值,就只会按照name属性值查找bean对象
*/
public class UserService {
 @Resource(name = "userDao") // name属性值与配置⽂件中bean标签的id属性值⼀致
 private UserDao ud;
 public void test() {
 // 调⽤UserDao的⽅法
 ud.test();
 }
}

当注入接口时,如果接口只有一个实现则正常实例化;如果接口存在多个实现,则需要使用 name 属性指定需要被实例化的 bean 对象

定义接口类 IUserDao.java

package com.xxxx.dao;
/**
* 定义接⼝类
*/
public interface IUserDao {
 public void test();
}

定义接口实现类 UserDao01.java

package com.xxxx.dao;
/**
* 接⼝实现类
*/
public class UserDao01 implements IUserDao {
 @Override
 public void test(){
 System.out.println("UserDao01...");
 }
}

定义接口实现类 UserDao02.java

package com.xxxx.dao;
/**
* 接⼝实现类
*/
public class UserDao02 implements IUserDao {
 @Override
 public void test(){
 System.out.println("UserDao02...");
 }
}

XML配置文件

<!--开启⾃动化装配(注⼊)-->
<context:annotation-config/>
<bean id="userService" class="com.xxxx.service.UserService"></bean>
<bean id="userDao01" class="com.xxxx.dao.UserDao01"></bean>
<bean id="userDao02" class="com.xxxx.dao.UserDao01"></bean>

使用注解 UserService.java

/**
* @Resource注解实现⾃动注⼊(反射)
* 当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤name属性指
定需要被实例化的bean对象
*/
public class UserService {
 @Resource(name = "userDao01") // name属性值与其中⼀个实现类的bean标签的id属性值⼀致
 private IUserDao iUserDao; // 注⼊接⼝(接⼝存在多个实现)
 public void test() {
 iUserDao.test();
 }
}

@Autowired注解

@Autowired注解实现自动化注入:

  • 默认通过类型(Class类型)查找 bean 对象 与属性字段的名称无关
  • 属性可以提供 set 方法,也可以不提供 set 方法
  • 注解可以声明在属性级别 或 set方法级别
  • 可以添加 @Qualifier 结合使用,通过 value 属性值查找 bean 对象(value 属性值必须要设置,且值要与 bean 标签的 id 属性值对应)

默认通过类型(Class类型)查找bean对象与属性字段的名称无关

/**
* @Autowired注解实现⾃动化注⼊
* 默认通过类型(Class类型)查找bean对象 与属性字段的名称⽆关
*/
public class UserService {
 @Autowired
 private UserDao userDao; // 默认通过类型(Class类型)查找bean对象 与属性字段的名称
⽆关
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void test() {
 // 调⽤UserDao的⽅法
 userDao.test();
 }
}

属性可以提供 set 方法,也可以不提供 set 方法

/**
* @Autowired注解实现⾃动化注⼊
* 属性可以提供set⽅法,也可以不提供set⽅法
*/
public class UserService {
 @Autowired
 private UserDao userDao; // 不提供set⽅法
 public void test() {
 // 调⽤UserDao的⽅法
 userDao.test();
 }
}

注解可以声明在属性级别或 set 方法级别

/**
* @Autowired注解实现⾃动化注⼊
* 注解可以声明在属性级别 或 set⽅法级别
*/
public class UserService {
 private UserDao userDao;
 @Autowired// 注解可以声明在set⽅法级别
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void test() {
 // 调⽤UserDao的⽅法
 userDao.test();
 }
}

可以添加 @Qualifier 结合使用,通过 value 属性值查找 bean 对象(value属性值必须要设置,且值要与 bean 标签的 id 属性值对应)

/**
* @Autowired注解实现⾃动化注⼊
* 可以添加@Qualifier结合使⽤,通过value属性值查找bean对象
 value属性值必须要设置,且值要与bean标签的id属性值对应
*/
public class UserService {
 @Autowired
 @Qualifier(value="userDao") // value属性值必须要设置,且值要与bean标签的id属性值对应
 private UserDao userDao;
 public void test() {
 userDao.test();
 }
}

推荐使用 @Resource 注解是属于J2EE的,减少了与 Spring 的耦合。

Spring IOC 扫描器

实际的开发中,bean 的数量非常多,采用手动配置 bean 的方式已无法满足生产需要,Spring 这时候同样提供了扫描的方式,对扫描到的 bean 对象统一进行管理,简化开发配置,提高开发效率。

Spring IOC 扫描器的配置

Spring IOC 扫描器

作用:bean 对象统一进行管理,简化开发配置,提高开发效率

1、设置自动化扫描的范围 如果 bean 对象未在指定包范围,即使声明了注解,也无法实例化

2、使用指定的注解(声明在类级别) bean对象的 id 属性默认是类的首字母小写

Dao 层: @Repository

Service 层: @Service

Controller 层: @Controller

任意类: @Component

注:开发过程中建议按照指定规则声明注解

设置自动化扫描范围

<?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
 https://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">
 <!-- 设置⾃动化扫描的范围 -->
 <context:component-scan base-package="com.xxxx"/>
</beans>

使用特定的注解

@Repository (Dao层)

@Repository
public class ResourceDao {
 public void test() {
 System.out.println("ResourceDao...");
 }
}

@Service(Service层 )

@Service
public class ResourceService {
 @Resource
 private ResourceDao resourceDao; // service层注⼊dao层的bean对象
 public void test() {
 System.out.println("ResourceService...");
 resourceDao.test();
 }
}

@Controller (Controller 层 )

@Controller
public class ResourceController {
 @Autowired
 private ResourceService resourceService; // Controller层注⼊service层的bean对象
 public void test() {
 System.out.println("ResourceController...");
 resourceService.test();
 }
}

@Component (任意层)

@Component
public class PropertyUtils {
 public void test(){
 System.out.println("PropertyUtils...");
 }
}

Spring 模拟用户登录流程

Dao层 (查询用户记录)

定义JavaBean User.java

package com.xxxx.po;
/**
* User ⽤户实体类
*/
public class User {
 private String userName; // ⽤户名称
 private String userPwd; // ⽤户密码
 public String getUserName() {
 return userName;
 }
 public void setUserName(String userName) {
 this.userName = userName;
 }
 public String getUserPwd() {
 return userPwd;
 }
 public void setUserPwd(String userPwd) {
 this.userPwd = userPwd;
 }
}

编写Dao层 UserDao.java

package com.xxxx.dao;
import com.xxxx.po.User;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {

 private final String USERNAME = "admin";
 private final String USERPWD = "admin";

 /**
 * 通过⽤户名称查询⽤户对象
 * @param userName
 * @return
 */
 public User queryUserByUserName(String userName){
 User user = null;
 // 判断⽤户名称是否正确
 if(!USERNAME.equals(userName)){
 // 如果不正确,返回null
 return null;
 }
// 如果正确,将⽤户名称和密码设置到user对象中
 user = new User();
 user.setUserName(USERNAME);
 user.setUserPwd(USERPWD);
 return user;
 }
}

Service层 (业务逻辑处理)

定义业务处理返回消息模型 MessageModel.java

package com.xxxx.po.vo;
/**
* 定义业务处理返回消息模型
* 封装返回结果
*/
public class MessageModel {
 private Integer resultCode = 1; // 结果状态码 1=成功,0=失败
 private String resultMsg = "操作成功!"; // 结果提示信息
 public Integer getResultCode() {
 return resultCode;
 }
 public void setResultCode(Integer resultCode) {
 this.resultCode = resultCode;
 }
 public String getResultMsg() {
 return resultMsg;
 }
 public void setResultMsg(String resultMsg) {
 this.resultMsg = resultMsg;
 }
}

 编写Service层 UserService.java

package com.xxxx.service;
import com.xxxx.dao.UserDao1;
import com.xxxx.po.User;
import com.xxxx.po.vo.MessageModel;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
 @Resource
 private UserDao userDao;
 /**
 * 验证⽤户登录
 * @param userName
 * @param userPwd
 * @return
 */
 public MessageModel userLoginCheck(String userName, String userPwd){
 // 定义业务处理返回消息模型
 MessageModel messageModel = new MessageModel();
 // 判断⽤户名称是否⾮空
 if(null == userName || "".equals(userName.trim())){
 messageModel.setResultCode(0);
 messageModel.setResultMsg("⽤户名不能为空!");
 return messageModel;
 }
 // 判断⽤户密码是否为空
 if(null == userPwd || "".equals(userPwd.trim())){
 messageModel.setResultCode(0);
 messageModel.setResultMsg("密码不能为空!");
 return messageModel;
 }
 // 通过⽤户名称查询⽤户对象
 User user = userDao.queryUserByUserName(userName);
 // 判断⽤户对象是否为空
 if(null == user){
 messageModel.setResultCode(0);
 messageModel.setResultMsg("该⽤户不存在!");
 return messageModel;
 }
 // 如果⽤户对象不为空,判断密码是否正确
 if(!user.getUserPwd().equals(userPwd)){
 messageModel.setResultCode(0);
 messageModel.setResultMsg("⽤户密码不正确!");
 return messageModel;
 }
 // 登录成功
 messageModel.setResultMsg("登录成功!");

 return messageModel;
 }
}

Controller层 (接收请求)

编写 Controller 层 UserController.java

package com.xxxx.controller;
import com.xxxx.po.vo.MessageModel;
import com.xxxx.service.UserService1;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController {
 @Resource
 private UserService userService;
 /**
 * ⽤户登录
 * @param userName
 * @param userPwd
 * @return
 */
 public MessageModel login(String userName, String userPwd){
 // 调⽤Dao层判断⽤户登录操作,返回结果
 MessageModel messageModel = userService.userLoginCheck(userName,
userPwd);
 return messageModel;
 }
}

通过 JUnit 进行测试

package com.xxxx;
import com.xxxx.controller.UserController;
import com.xxxx.po.vo.MessageModel;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestLogin {
 @Test
 public void test() {
 // 得到Spring容器上下⽂环境
 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
 // 得到UserController实例化对象
 UserController userController = (UserController) ac.getBean("userController");
 // 传⼊参数调⽤UserController的⽅法,返回封装类
 MessageModel messageModel= userController.login("admin", "admin");
 System.out.println("状态码:" + messageModel.getResultCode() + ",提示信息:" +
messageModel.getResultMsg());
}
}

Bean的作用域与生命周期

Bean的作用域

默认情况下,我们从 Spring 容器中拿到的对象均是单例的,对于 bean 的作用域类型如下:

singleton 作用域

注意: lazy-init 是懒加载, 如果等于 true 时作用是指 Spring 容器启动的时候不会去实例化这个 bean, 而是在程序调用时才去实例化。默认是 false 即Spring 容器启动时实例化。

默认情况下,被管理的 bean 只会 IOC 容器中存在一个实例,对于所有获取该 Bean 的操作 Spring 容器将只返回同一个Bean。

容器在启动的情况下就实例化所有 singleton 的 bean对象,并缓存与容器中

lazy-init 属性(懒加载)

如果为 false,则在 IOC 容器启动时会实例化 bean 对象,默认 false

如果为 true,则 IOC 容器启动时不会实例化 Bean 对象,在使用 bean 对象时才会实例化

lazy-init设置为false有什么好处?

  1. 可以提前发现潜在的配置问题
  2. Bean 对象存在于缓存中,使用时不用再去实例化 bean,加快程序运行效率

什么对象适合作为单例对象?

⼀般来说对于无状态或状态不可改变的对象适合使用单例模式。(不存在会改变对象状态的成员变量)

比如:controller层、service层、dao层

什么是无状态或状态不可改变的对象?

实际上对象状态的变化往往均是由于属性值得变化而引起的,比如 user 类 姓名属性会有变化,属性姓名的变化一般会引起 user 对象状态的变化。对于我们的程序来说,无状态对象没有实例变量的存在,保证了线程的安全性,service 层业务对象即是无状态对象。线程安全的。

prototype 作用域

通过 scope="prototype" 设置 bean 的类型 ,每次向 Spring 容器请求获取 Bean 都返回一个全新的 Bean,相对于"singleton"来说就是不缓存 Bean,每次都是一个根据 Bean 定义创建的全新 Bean。

Web应用中的作用域(好像不是经常用到)

request 作用域

表示每个请求需要容器创建一个全新 Bean。比如提交表单的数据必须是对每次请求新建一个 Bean 来保持这些表单数据,请求结束释放这些数据。

session 作用域

表示每个会话需要容器创建一个全新 Bean。比如对于每个用户一般会有一个会话,该用户的用户信息需要存储到会话中,此时可以将该 Bean 作用域配置为 session 级别。

globalSession 作用域

类似于 session 作用域,其用于 portlet(Portlet 是基于 Java 的 Web 组件,由 Portlet 容器管理,并由容器处理请求,生产动态内容)环境的 web 应用。如果在非 portlet 环境将视为 session 作用域。

Bean的生命周期

对比已经学过的 servlet 生命周期(容器启动装载并实例化 servlet 类,初始化 servlet,调用 service 方法,销毁 servlet)。

同样对于 Spring 容器管理的 bean 也存在生命周期的概念

在 Spring 中,Bean 的生命周期包括 Bean 的定义、初始化、使用和销毁4个阶段

Bean的定义

在 Spring 中,通常是通过配置文档的方式来定义 Bean 的。

在一个配置文档中,可以定义多个 Bean。

Bean 的初始化

默认在IOC容器加载时,实例化对象

Spring bean 初始化有两种方式:

方式一:在配置文档中通过指定 init-method 属性来完成。

public class RoleService {
 // 定义初始化时需要被调⽤的⽅法
 public void init() {
 System.out.println("RoleService init...");
 }
}
<!-- 通过init-method属性指定⽅法 -->
<bean id="roleService" class="com.xxxx.service.RoleService" init-method="init"></bean>

方式二: 实现 org.springframework.beans.factory.InitializingBean 接口。

public class RoleService implements InitializingBean {
 @Override
 public void afterPropertiesSet() throws Exception {
 System.out.println("RoleService init...");
 }
}
<bean id="roleService" class="com.xxxx.service.RoleService" ></bean>

Bean 对象实例化过程是在 Spring 容器初始化时被实例化的,但也不是不可改变的,可以通过 lazyinit="true" 属性延迟 bean 对象的初始化操作,此时再调用 getBean 方法时才会进行 bean 的初始化操作

Bean 的使用

方式一:使用 BeanFactory

// 得到Spring的上下⽂环境
BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) factory.getBean("roleService");

方式二:使用 ApplicationContext

// 得到Spring的上下⽂环境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) ac.getBean("roleService");

Bean 的销毁

实现销毁方式(Spring 容器会维护 bean 对象的管理,可以指定 bean 对象的销毁所要执行的方法)。

步骤一:实现销毁方式(Spring 容器会维护 bean 对象的管理,可以指定 bean 对象的销毁所要执行的方法)

<bean id="roleService" class="com.xxxx.service.RoleService" destroy-method="destroy">
</bean>

步骤二:通过 AbstractApplicationContext 对象,调用其 close 方法实现 bean 的销毁过程

AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
ctx.close();

总结:spring 框架基础的内容就这么多了,当然笔者估计也只是讲了一小部分,肯定又很多遗漏,望大家见谅,最后在补充一下

IOC/DI-控制反转和依赖注入,将对象实例化的创建过程转交给外部容器(IOC容器 充当工厂角色)去负责;属性赋值的操作;

路再长也有终点,夜再黑也有尽头

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值