Java
Java Web框架

Spring(1)

简介:Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。

Spring框架概述

什么是Spring

Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式)轻量级开源框架。

轻量级:与EJB对比,依赖资源少,销毁的资源少。
分层: 一站式,每一个层都提供的解决方案
Web层:Struts、Spring-MVC
Service层:Spring
Dao层:hibernate、mybatis、JdbcTemplate、Spring-data

Spring由来

  • Expert One-to-One J2EE Design and Development;
  • Expert One-to-One J2EE Development without EJB。

Spring核心

  • Spring的核心是控制反转(IoC)和面向切面(AOP)。

Spring优点

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

Spring体系结构图

Spring 框架是一个分层架构,它包含一系列的功能要素并被分为大约20个模块。这些模块分为Core Container、Data Access/Integration、Web、AOP(Aspect Oriented Programming)、Instrumentation和测试部分,如下图所示:

1.1-1.Spring体系结构图.png

核心容器:beans、core、context、expression

入门案例:IoC

导入jar包

  • 4个核心(beans、core、context、expression)
  • 1个依赖(commons-loggins…jar)

目标类

以前的步骤

  1. 提供UserService接口和实现类
  2. 获得UserService实现类的实例

之前开发中,直接new一个对象即可。学习Spring之后,将由Spring创建对象实例,即IoC 控制反转(Inverse of Control),之后需要实例对象时,从Spring工厂(容器)中获得,需要将实现类的全限定名称配置到xml文件中。

  • public interface UserService {
  • public void addUser();
  • }
  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("a_ico add user");
  • }
  • }

配置文件

  • 位置:任意,开发中一般在classpath下(即src下);
  • 名称:任意,开发中常用applicationContext.xml
  • 内容:添加schema约束,约束文件位置:spring-framework-3.2.0.RELEASE\docs\spring-framework-reference\html\xsd-config.html

/Project_Name/src/com/coderap/ioc/beans.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">
  • <!-- 配置service
  • <bean> 配置需要创建的对象
  • id :用于之后从spring容器获得实例时使用的
  • class :需要创建实例的全限定类名
  • -->
  • <bean id="userServiceId" class="com.coderap.a_ioc.UserServiceImpl"></bean>
  • </beans>

2.4 测试

  • @Test
  • public void demo01() {
  • // 之前开发
  • UserService userService = new UserServiceImpl();
  • userService.addUser();
  • }
  • @Test
  • public void demo02() {
  • // 从spring容器获得
  • // 1 获得容器
  • String xmlPath = "com/coderap/a_ioc/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • // 2获得内容 --不需要自己new,都是从spring容器获得
  • UserService userService = (UserService) applicationContext
  • .getBean("userServiceId");
  • userService.addUser();
  • }

入门案例:DI

DI Dependency Injection ,依赖注入

  • is a:是一个,继承。
  • has a:有一个,成员变量,依赖。
  • class B {
  • private A a; //B类依赖A类
  • }

依赖:一个对象需要使用另一个对象
注入:通过setter方法进行另一个对象实例设置。

例如:

  • class BookServiceImpl{
  • // 之前开发:接口 = 实现类 (service和dao耦合)
  • // private BookDao bookDao = new BookDaoImpl();
  • // Spring之后 (解耦:service实现类使用dao接口,不知道具体的实现类)
  • private BookDao bookDao;
  • setter方法
  • }

模拟spring执行过程

  1. 创建service实例:

BookService bookService = new BookServiceImpl()即IoC,<bean>配置

  1. 创建dao实例:

BookDao bookDao = new BookDaoImple(),即IoC

  1. 将dao设置给service:

bookService.setBookDao(bookDao);,即DI,<property>配置

目标类

  1. 创建BookService接口和实现类:
  • public interface BookDao {
  • public void save();
  • }
  • public class BookDaoImpl implements BookDao {
  • @Override
  • public void save() {
  • System.out.println("di add book");
  • }
  • }
  1. 创建BookDao接口和实现类:
  • public interface BookService {
  • public abstract void addBook();
  • }
  • public class BookServiceImpl implements BookService {
  • // 方式1:之前,接口 = 实现类
  • // private BookDao bookDao = new BookDaoImpl();
  • // 方式2:接口 + setter
  • private BookDao bookDao;
  • public void setBookDao(BookDao bookDao) {
  • this.bookDao = bookDao;
  • }
  • @Override
  • public void addBook(){
  • this.bookDao.save();
  • }
  • }
  1. 将dao和service配置到xml文件;

/Project_Name/src/com/coderap/di/beans.xml文件:

  • <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">
  • <!--
  • 模拟spring执行过程
  • 创建service实例:BookService bookService = new BookServiceImpl() IoC <bean>
  • 创建dao实例:BookDao bookDao = new BookDaoImpl() IoC
  • 将dao设置给service:bookService.setBookDao(bookDao); DI <property>
  • <property> 用于进行属性注入
  • name:bean的属性名,通过setter方法获得
  • setBookDao ##> BookDao ##> bookDao
  • ref :另一个bean的id值的引用
  • -->
  • <!-- 创建service -->
  • <bean id="bookServiceId" class="com.coderap.b_di.BookServiceImpl">
  • <property name="bookDao" ref="bookDaoId"></property>
  • </bean>
  • <!-- 创建dao实例 -->
  • <bean id="bookDaoId" class="com.coderap.b_di.BookDaoImpl"></bean>
  • </beans>

注:xsd文件位置在spring-framework-3.2.0.RELEASE\schema\beans

  1. 使用api测试
  • @Test
  • public void demo01() {
  • // 从spring容器获得
  • String xmlPath = "com/coderap/b_di/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • BookService bookService = (BookService) applicationContext
  • .getBean("bookServiceId");
  • bookService.addBook();
  • }

核心API

1.1-2.Spring核心API结构图.png

  • BeanFactory:这是一个工厂,用于生成任意bean。采取延迟加载,第一次getBean时才会初始化Bean。
  • ApplicationContext:是BeanFactory的子接口,功能更强大(国际化处理、事件传递、Bean自动装配、各种不同应用层的Context实现)。当配置文件被加载,就进行对象实例化。
  1. ClassPathXmlApplicationContext用于加载classpath(类路径、src)下的xml,加载xml运行时位置,即/WEB-INF/classes/
  2. FileSystemXmlApplicationContext用于加载指定盘符下的xml,加载xml运行时位置,即/WEB-INF/
  3. 通过Java Web的ServletContext.getRealPath()获得具体盘符。
  • @Test
  • public void demo02() {
  • // 使用BeanFactory,第一次条用getBean实例化
  • String xmlPath = "com/coderap/b_di/beans.xml";
  • BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(
  • xmlPath));
  • BookService bookService = (BookService) beanFactory
  • .getBean("bookServiceId");
  • bookService.addBook();
  • }

装配Bean基于XML

实例化方式

我们一共有三种bean实例化方式:默认构造、静态工厂、实例工厂

默认构造:

<bean id="" class="">,这种方式必须提供默认构造。

静态工厂,常用与spring整合其他框架(工具)

注:静态工厂:用于生成实例对象,所有的方法必须是static

<bean id="" class="工厂全限定类名" factory-method="静态方法">

静态工厂定义:

  • public class MyBeanFactory {
  • /**
  • * 创建实例
  • * @return
  • */
  • public static UserService createService(){
  • return new UserServiceImpl();
  • }
  • }

Spring配置:

  • <!-- 将静态工厂创建的实例交予spring
  • class 确定静态工厂全限定类名
  • factory-method 确定静态方法名
  • -->
  • <bean id="userServiceId" class="com.coderap.c_inject.b_static_factory.MyBeanFactory" factory-method="createService"></bean>

在上面的Spring配置中,class="com.coderap.c_inject.b_static_factory.MyBeanFactory"表明了静态工厂类,factory-method="createService"表明了创建该Javabean对象的静态工厂对应的方法。

获取对象的方法:

  • @Test
  • public void demo01() {
  • // 自定义工厂
  • UserService userService = MyBeanFactory.createService();
  • userService.addUser();
  • }
  • @Test
  • public void demo02() {
  • // Spring工厂
  • String xmlPath = "com/coderap/c_inject/b_static_factory/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • UserService userService = applicationContext.getBean("userServiceId",
  • UserService.class);
  • userService.addUser();
  • }

实例工厂

必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。

实例工厂定义:

  • /**
  • * 实例工厂,所有方法非静态
  • *
  • */
  • public class MyBeanFactory {
  • /**
  • * 创建实例
  • * @return
  • */
  • public UserService createService(){
  • return new UserServiceImpl();
  • }
  • }

Spring配置:

  • <?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="myBeanFactoryId" class="com.coderap.c_inject.c_factory.MyBeanFactory"></bean>
  • <!-- 获得userservice
  • * factory-bean:确定工厂实例
  • * factory-method:确定普通方法
  • -->
  • <bean id="userServiceId" factory-bean="myBeanFactoryId"
  • factory-method="createService"></bean>
  • </beans>

在上面的Spring配置中,factory-bean="myBeanFactoryId""表明了实例工厂的实例对象,factory-method="createService"表明了创建该Javabean对象的实例工厂对应的方法。

获取对象的方法:

  • @Test
  • public void demo01() {
  • // 自定义实例工厂
  • // 1 创建工厂
  • MyBeanFactory myBeanFactory = new MyBeanFactory();
  • // 2 通过工厂实例,获得对象
  • UserService userService = myBeanFactory.createService();
  • userService.addUser();
  • }
  • @Test
  • public void demo02() {
  • // Spring工厂
  • String xmlPath = "com/coderap/c_inject/c_factory/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • UserService userService = applicationContext.getBean("userServiceId",
  • UserService.class);
  • userService.addUser();
  • }

Bean种类

  1. 普通bean:之前操作的都是普通bean。<bean id="" class="A"> ,Spring直接创建A实例,并返回.

  2. FactoryBean:是一个特殊的bean,具有工厂生成对象能力,只能生成特定的对象。bean必须使用 FactoryBean接口,此接口提供方法getObject()用于获得特定bean。<bean id="" class="FB">先创建FB实例,然后调用getObject()方法,并返回方法的返回值,例如以下代码:

  • FB fb = new FB();
  • return fb.getObject();
  1. BeanFactory 和 FactoryBean 对比?
  • BeanFactory:工厂,用于生成任意bean。
  • FactoryBean:特殊bean,用于生成另一个特定的bean。例如:ProxyFactoryBean ,此工厂bean用于生产代理。<bean id="" class="....ProxyFactoryBean">获得的是代理对象实例,会在AOP中使用。

作用域

作用域用于确定Spring创建bean实例个数。

类别 说明
singleton 在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session 共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext 环境
globalSession 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext 环境

取值:

  • singleton单例,默认值。
  • prototype多例,每执行一次getBean将获得一个实例。例如:Struts整合Spring,配置Action多例。

配置信息:<bean id="" class="" scope="">,其中scope用于配置作用域。

测试如下:

先配置相应的xml文件,/Project_name/src/com/coderap/d_scope/beans.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">
  • <bean id="userServiceId" class="com.coderap.d_scope.UserServiceImpl"
  • scope="prototype"></bean>
  • </beans>

此时的作用域是多例的,有以下测试代码:

  • @Test
  • public void demo02() {
  • // spring 工厂
  • String xmlPath = "com/coderap/d_scope/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • UserService userService = applicationContext.getBean("userServiceId",
  • UserService.class);
  • UserService userService2 = applicationContext.getBean("userServiceId",
  • UserService.class);
  • System.out.println(userService);
  • System.out.println(userService2);
  • }

此时打印的userService和userService2的地址是不一样的。而如果将其改为单例模式:

  • <?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="userServiceId" class="com.coderap.d_scope.UserServiceImpl"
  • scope="singleton"></bean>
  • </beans>

此时还是用上面的测试代码会发现两个对象的地址是一样的,此时创建的两个对象是一个单例对象。

生命周期

初始化和销毁

目标方法执行前后执行后,将进行初始化或销毁。可以在<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称">中来配置初始化和销毁会调用的指定方法。目标类的编写如下:

  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("e_lifecycle add user");
  • }
  • public void myInit(){
  • System.out.println("初始化");
  • }
  • public void myDestroy(){
  • System.out.println("销毁");
  • }
  • }

/Project_Name/src/com/coderap/e_lifecycle/beans.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">
  • <!--
  • init-method:用于配置初始化方法,准备数据等;
  • destroy-method:用于配置销毁方法,清理资源等
  • -->
  • <bean id="userServiceId" class="com.coderap.e_lifecycle.UserServiceImpl"
  • init-method="myInit" destroy-method="myDestroy"></bean>
  • </beans>

测试代码如下:

  • @Test
  • public void demo02() throws Exception {
  • // spring 工厂
  • String xmlPath = "com/coderap/e_lifecycle/beans.xml";
  • ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • UserService userService = (UserService) applicationContext
  • .getBean("userServiceId");
  • userService.addUser();
  • // 要求:1.容器必须close,销毁方法执行; 2.必须是单例的
  • // applicationContext.getClass().getMethod("close").invoke(applicationContext);
  • // * 此方法接口中没有定义,实现类提供
  • applicationContext.close();
  • }

需要注意的是,要想JavaBean销毁,Spring容器必须关闭,同时JavaBean对象需要是单例的。我们可以通过applicationContext.getClass().getMethod("close").invoke(applicationContext);来使用反射关闭ClassPathXmlApplicationContext,也可以直接将创建的applicationContext转换成ClassPathXmlApplicationContext类型的对象,该类型的对象实现了close()方法;上述测试代码打印的内容如下:

  • 初始化
  • e_lifecycle add user
  • 销毁

BeanPostProcessor后处理Bean

Spring 提供一种机制,只要实现此接口BeanPostProcessor,并将实现类提供给Spring容器,Spring容器将自动执行,在初始化方法前执行before(),在初始化方法后执行after()

- Factory hook(勾子) that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies. (pring提供工厂勾子,用于修改实例对象,可以生成代理对象,是AOP底层)。模拟如下:

  • A a =new A();
  • a = B.before(a); //将a的实例对象传递给后处理bean,可以生成代理对象并返回。
  • a.init();
  • a = B.after(a);
  • a.addUser(); //生成代理对象,目的在目标方法前后执行(例如:开启事务、提交事务)
  • a.destroy()

编写实现类

  • public class MyBeanPostProcessor implements BeanPostProcessor {
  • @Override
  • public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  • System.out.println("前方法 : " + beanName);
  • return bean;
  • }
  • @Override
  • public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
  • System.out.println("后方法 : " + beanName);
  • // bean 目标对象
  • // 生成 jdk 代理
  • return Proxy.newProxyInstance(
  • MyBeanPostProcessor.class.getClassLoader(),
  • bean.getClass().getInterfaces(),
  • new InvocationHandler(){
  • @Override
  • public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  • System.out.println("------开启事务");
  • //执行目标方法
  • Object obj = method.invoke(bean, args);
  • System.out.println("------提交事务");
  • return obj;
  • }});
  • }
  • }

/Project_Name/src/com/coderap/e_lifecycle/beans.xml文件的配置如下:

  • <!-- 将后处理的实现类注册给Spring -->
  • <bean class="com.coderap.e_lifecycle.MyBeanPostProcessor"></bean>
  1. 问题1:后处理bean作用某一个目标类,还是所有目标类?答:所有.
  2. 问题2:如何只作用一个?答:通过参数beanName进行控制。

测试代码如下:

  • @Test
  • public void demo02() throws Exception {
  • // spring 工厂
  • String xmlPath = "com/coderap/e_lifecycle/beans.xml";
  • ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • UserService userService = (UserService) applicationContext
  • .getBean("userServiceId");
  • userService.addUser();
  • applicationContext.close();
  • }

测试打印信息如下:

  • 前方法 : userServiceId
  • 初始化
  • 后方法 : userServiceId
  • ------开启事务
  • e_lifecycle add user
  • ------提交事务
  • 销毁

属性依赖注入

基于构造方法的注入

当我们自定义了一个类的构造方法后,它的默认构造方法就会破坏了,如下类代码:

  • package com.coderap.f_xml.a_constructor;
  • public class User {
  • private Integer uid;
  • private String username;
  • private Integer age;
  • public User(Integer uid, String username) {
  • super();
  • this.uid = uid;
  • this.username = username;
  • }
  • public User(String username, Integer age) {
  • super();
  • this.username = username;
  • this.age = age;
  • }
  • public Integer getUid() {
  • return uid;
  • }
  • public void setUid(Integer uid) {
  • this.uid = uid;
  • }
  • public String getUsername() {
  • return username;
  • }
  • public void setUsername(String username) {
  • this.username = username;
  • }
  • public Integer getAge() {
  • return age;
  • }
  • public void setAge(Integer age) {
  • this.age = age;
  • }
  • @Override
  • public String toString() {
  • return "User [uid=" + uid + ", username=" + username + ", age=" + age + "]";
  • }
  • }

在上面的代码中,自定义了两个构造方法,此时配置以下配置文件:

/ee19_spring_day01/src/com/coderap/f_xml/a_constructor/beans.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">
  • <bean id="userId" class="com.coderap.f_xml.a_constructor.User" ></bean>
  • </beans>

并进行初始化测试时会由于找不到默认构造方法而报错,此时我们则需要在配置文件中指定相应的构造方法:

  • <?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">
  • <!-- 构造方法注入
  • * <constructor-arg> 用于配置构造方法一个参数argument
  • name :参数的名称
  • value:设置普通数据
  • ref:引用数据,一般是另一个bean id值
  • index :参数的索引号,从0开始 。如果只有索引,匹配到了多个构造方法时,默认使用第一个。
  • type :确定参数类型
  • 例如:使用名称name
  • <constructor-arg name="username" value="jack"></constructor-arg>
  • <constructor-arg name="age" value="18"></constructor-arg>
  • 例如2:类型type 和 索引 index
  • <constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
  • <constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
  • -->
  • <bean id="userId" class="com.coderap.f_xml.a_constructor.User" >
  • <constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
  • <constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
  • </bean>
  • </beans>

通过<constructor-arg>标签可以为bean初始化指定构造方法所需要的参数。

基于setter方法的注入

我们可以通过为目标类提供相应的setter方法,在配置中进行相应的属性注入,例如下面的目标类:

  • package com.coderap.f_xml.b_setter;
  • public class Person {
  • private String pname;
  • private Integer age;
  • private Address homeAddr; // 家庭地址
  • private Address companyAddr; // 公司地址
  • public String getPname() {
  • return pname;
  • }
  • public void setPname(String pname) {
  • this.pname = pname;
  • }
  • public Integer getAge() {
  • return age;
  • }
  • public void setAge(Integer age) {
  • this.age = age;
  • }
  • public Address getHomeAddr() {
  • return homeAddr;
  • }
  • public void setHomeAddr(Address homeAddr) {
  • this.homeAddr = homeAddr;
  • }
  • public Address getCompanyAddr() {
  • return companyAddr;
  • }
  • public void setCompanyAddr(Address companyAddr) {
  • this.companyAddr = companyAddr;
  • }
  • @Override
  • public String toString() {
  • return "Person [pname=" + pname + ", age=" + age + ", homeAddr="
  • + homeAddr + ", companyAddr=" + companyAddr + "]";
  • }
  • }

该类中有一个对象类型属性Address代码如下:

  • package com.coderap.f_xml.b_setter;
  • public class Address {
  • private String addr; // 地址信息
  • private String tel; // 电话
  • public String getAddr() {
  • return addr;
  • }
  • public void setAddr(String addr) {
  • this.addr = addr;
  • }
  • public String getTel() {
  • return tel;
  • }
  • public void setTel(String tel) {
  • this.tel = tel;
  • }
  • @Override
  • public String toString() {
  • return "Address [addr=" + addr + ", tel=" + tel + "]";
  • }
  • }

在上面的两个类中,都为属性提供了相应的setter方法,在xml配置中则可以进行如下配置:

/Project_Name/src/com/coderap/f_xml/b_setter/beans.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">
  • <!-- setter方法注入
  • * 普通数据
  • <property name="" value="值">
  • 等效
  • <property name="">
  • <value>值
  • * 引用数据
  • <property name="" ref="另一个bean">
  • 等效
  • <property name="">
  • <ref bean="另一个bean"/>
  • -->
  • <bean id="personId" class="com.coderap.f_xml.b_setter.Person">
  • <property name="pname" value="Jack"></property>
  • <property name="age">
  • <value>22</value>
  • </property>
  • <property name="homeAddr" ref="homeAddrId"></property>
  • <property name="companyAddr">
  • <ref bean="companyAddrId"/>
  • </property>
  • </bean>
  • <bean id="homeAddrId" class="com.coderap.f_xml.b_setter.Address">
  • <property name="addr" value="beijing"></property>
  • <property name="tel" value="133333333"></property>
  • </bean>
  • <bean id="companyAddrId" class="com.coderap.f_xml.b_setter.Address">
  • <property name="addr" value="北京"></property>
  • <property name="tel" value="133222222"></property>
  • </bean>
  • </beans>

可以得知,使用<property>标签可以对目标类进行属性的注入。测试代码如下:

  • @Test
  • public void demo01() {
  • // 从spring容器获得
  • String xmlPath = "com/coderap/f_xml/b_setter/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • Person person = (Person) applicationContext.getBean("personId");
  • System.out.println(person);
  • }

测试打印结果如下:

  • Person [pname=Jack, age=22, homeAddr=Address [addr=Beijing, tel=133333333], companyAddr=Address [addr=北京, tel=133222222]]

P命令空间

P命令空间是对setter方法注入进行简化,替换<property name="属性名">,而是在<bean p:属性名="普通值" p:属性名-ref="引用值">,p命名空间在使用前必须添加命名空间:

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

我们使用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="personId" class="com.coderap.f_xml.c_p.Person" p:pname="Jack"
  • p:age="22" p:homeAddr-ref="homeAddrId" p:companyAddr-ref="companyAddrId">
  • </bean>
  • <bean id="homeAddrId" class="com.coderap.f_xml.c_p.Address"
  • p:addr="beijing" p:tel="133333333">
  • </bean>
  • <bean id="companyAddrId" class="com.coderap.f_xml.c_p.Address"
  • p:addr="北京" p:tel="133222222">
  • </bean>
  • </beans>

打印信息与之前的是一样的。

SpEL

SpEL对<property>进行统一编程,所有的内容都使用value:<property name="" value="#{表达式}">,常用的表达式有以下几种:

#{123}#{'jack'}:数字、字符串
#{beanId}:另一个bean引用
#{beanId.propName}:操作数据
#{beanId.toString()}:执行方法
#{T(类).字段|方法}:静态方法或字段

以下是示例代码:

  • <?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">
  • <!-- <property name="cname" value="#{'jack'}"></property> <property name="cname"
  • value="#{customerId.cname.toUpperCase()}"></property> 通过另一个bean,获得属性,调用的方法
  • <property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
  • ?. 如果对象不为null,将调用方法 -->
  • <bean id="customerId" class="com.coderap.f_xml.d_spel.Customer">
  • <property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
  • <property name="pi" value="#{T(java.lang.Math).PI}"></property>
  • </bean>
  • </beans>

集合注入

对于集合注入,Spring也提供了相应的方法。首先我们的目标类如下:

  • package com.coderap.f_xml.e_coll;
  • import java.util.Arrays;
  • import java.util.List;
  • import java.util.Map;
  • import java.util.Properties;
  • import java.util.Set;
  • public class CollData {
  • private String[] arrayData;
  • private List<String> listData;
  • private Set<String> setData;
  • private Map<String, String> mapData;
  • private Properties propsData;
  • public String[] getArrayData() {
  • return arrayData;
  • }
  • public void setArrayData(String[] arrayData) {
  • this.arrayData = arrayData;
  • }
  • public List<String> getListData() {
  • return listData;
  • }
  • public void setListData(List<String> listData) {
  • this.listData = listData;
  • }
  • public Set<String> getSetData() {
  • return setData;
  • }
  • public void setSetData(Set<String> setData) {
  • this.setData = setData;
  • }
  • public Map<String, String> getMapData() {
  • return mapData;
  • }
  • public void setMapData(Map<String, String> mapData) {
  • this.mapData = mapData;
  • }
  • public Properties getPropsData() {
  • return propsData;
  • }
  • public void setPropsData(Properties propsData) {
  • this.propsData = propsData;
  • }
  • @Override
  • public String toString() {
  • return "CollData [\narrayData=" + Arrays.toString(arrayData)
  • + ", \nlistData=" + listData + ", \nsetData=" + setData
  • + ", \nmapData=" + mapData + ", \npropsData=" + propsData
  • + "\n]";
  • }
  • }

对于以上的目标类,我们可以配置相应的xml文件如下:

/Project_Name/src/com/coderap/f_xml/e_coll/beans.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">
  • <!--
  • 集合的注入都是给<property>添加子标签
  • 数组:<array>
  • List:<list>
  • Set:<set>
  • Map:<map> ,map存放k/v 键值对,使用<entry>描述
  • Properties:<props> <prop key=""></prop>
  • 普通数据:<value>
  • 引用数据:<ref>
  • -->
  • <bean id="collDataId" class="com.coderap.f_xml.e_coll.CollData">
  • <property name="arrayData">
  • <array>
  • <value>数组值1</value>
  • <value>数组值2</value>
  • <value>数组值3</value>
  • <value>数组值4</value>
  • </array>
  • </property>
  • <property name="listData">
  • <list>
  • <value>List值1</value>
  • <value>List值2</value>
  • <value>List值3</value>
  • <value>List值4</value>
  • </list>
  • </property>
  • <property name="setData">
  • <set>
  • <value>Set值1</value>
  • <value>Set值2</value>
  • <value>Set值3</value>
  • </set>
  • </property>
  • <property name="mapData">
  • <map>
  • <entry key="map_key1" value="map_value1"></entry>
  • <entry>
  • <key>
  • <value>map_key2</value>
  • </key>
  • <value>map_value2</value>
  • </entry>
  • </map>
  • </property>
  • <property name="propsData">
  • <props>
  • <prop key="pro_key1">pro_value1</prop>
  • <prop key="pro_key2">pro_value2</prop>
  • <prop key="pro_key3">pro_value3</prop>
  • </props>
  • </property>
  • </bean>
  • </beans>

打印信息如下:

  • CollData [
  • arrayData=[数组值1, 数组值2, 数组值3, 数组值4],
  • listData=[List值1, List值2, List值3, List值4],
  • setData=[Set值1, Set值2, Set值3],
  • mapData={map_key1=map_value1, map_key2=map_value2},
  • propsData={pro_key3=pro_value3, pro_key2=pro_value2, pro_key1=pro_value1}
  • ]

装配Bean 基于注解

注解就是一个类,使用@注解名称,在开发中,我们可以使用注解 取代xml配置文件。

  1. @Component取代<bean class="">,即@Component("id")取代<bean id="" class="">

  2. Web开发,提供三个@Component注解衍生注解(功能一样)取代<bean class="">

  • @Repository:Dao层
  • @Service:Service层
  • @Controller:Web层
  1. 依赖注入,给私有字段设置,也可以给setter方法设置
  • 普通值:@Value("")
  • 引用值:

方式1:按照【类型】注入,@Autowired
方式2:按照【名称】注入,@Autowired@Qualifier("名称")
方式3:按照【名称】注入,@Resource("名称")

  1. 生命周期
  • 初始化:@PostConstruct
  • 销毁:@PreDestroy
  1. 作用域,@Scope("prototype")

注解使用前提,添加命名空间,让Spring扫描含有注解类:

  • <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:component-scan base-package="com.coderap.g_annotation.a_ioc"></context:component-scan>
  • </beans>

其中xmlns:context="http://www.springframework.org/schema/context"http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd是新添加的命名空间。

Schemea命名空间解释:

  1. 命名空间声明:

默认:xmlns=”“,此种情况下,标签名可以直接写,比如<bean>
显式:xmlns:别名=”“,此种情况下,标签名需要使用别名,比如<别名:标签名>

  1. 确定schema xsd文件的位置,固定格式为:
  • xsi:schemaLocation="名称1 位置1 名称2 位置2"

其中名称和位置是成对出现的。

接下来使用注解演示Web三层的属性注入:

首先是Web层:

  • package com.coderap.g_annotation.b_web;
  • import org.springframework.beans.factory.annotation.Autowired;
  • import org.springframework.stereotype.Controller;
  • @Controller("studentActionId")
  • public class StudentAction {
  • // 默认按照类型
  • @Autowired
  • private StudentService studentService;
  • public void execute() {
  • studentService.addStudent();
  • }
  • }

在Web层中使用@Controller("studentActionId")声明了自己层次,同时StudentService类型的对象studentService使用了注解的按照类型注入的方式。

接下来是Service层:

  • package com.coderap.g_annotation.b_web;
  • public interface StudentService {
  • void addStudent();
  • }
  • package com.coderap.g_annotation.b_web;
  • import org.springframework.beans.factory.annotation.Autowired;
  • import org.springframework.beans.factory.annotation.Qualifier;
  • import org.springframework.stereotype.Service;
  • @Service
  • public class StudentServiceImpl implements StudentService {
  • private StudentDao studentDao;
  • @Autowired
  • @Qualifier("studentDaoId")
  • public void setStudentDao(StudentDao studentDao) {
  • this.studentDao = studentDao;
  • }
  • @Override
  • public void addStudent() {
  • studentDao.save();
  • }
  • }

在Service层使用了@Autowired@Qualifier("studentDaoId")来按照名称注入DAO对象。

接下来是DAO层:

  • package com.coderap.g_annotation.b_web;
  • public interface StudentDao {
  • void save();
  • }
  • package com.coderap.g_annotation.b_web;
  • import org.springframework.stereotype.Repository;
  • @Repository("studentDaoId")
  • public class StudentDaoImpl implements StudentDao {
  • @Override
  • public void save() {
  • System.out.println("dao");
  • }
  • }

由于在Service层使用了@Autowired@Qualifier("studentDaoId")来按照名称注入DAO对象,所以需要使用@Repository("studentDaoId")来声明DAO层的对象名称。