1.Spring入门
Spring全家桶:
1.1.Spring Framework系统架构
Spring Framework是Spring生态圈最基础的项目,是其它项目的根基。
如上图所示:
Core Container:核心容器
AOP:面向切面编程----Aspects:AOP思想的实现
Data Access/Integration:数据访问/集成
Transactions:事务
Web:Web开发
Test:单元测试与集成测试
1.2.IOC--控制反转
1.2.1IOC
IoC(Inversion of Control):对象的创建由程序转移到外部,转换为外部提供的对象,这种思想被称为控制反转(对象创建控制权发生变化)
Spring技术对IOC思想进行了实现。Spring提供了一个容器,称为IoC容器
IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在Ioc容器中统称为Bean.
1.2.2DI 依赖注入
Dependency Injection(简写为DI) -----依赖注入:在容器中建立Bean与Bean之间的依赖关系的整个过程,称为依赖注入。
1.2.3Ioc jar包介绍
Spring-core:Spring的核心工具包,spring的其它包都要用到该jar中的类
Spring-beans:包含配置文件,bean的创建等,所有jar包都需要用到
Spring-context:在上面两个jar完成基础IOC功能上提供扩展服务,此外还提供许多企业级服务的支持,有邮件服务、任务调度、JNDI定位,EJB集成、远程访问、缓存以及多种视图层框架的支持。
Spring-expression:Spring表达式语言,spel表达式,SpEL支持标准数学运算符,关系运算符,逻辑运算符,条件运算符,集合和正则表达式等。${url}
Spring-context-support:Spring context的扩展支持,用于MVC方面,比如支持freemarker等模板引擎
1.3Ioc案例(基于XML)
1.创建maven项目:
2.在Pom.xml里面导入依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<!--Spring上下文,提供了IOC容器等最基础的功能-->
<artifactId>spring-context</artifactId>
<version>6.0.9</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<!--Spring上下文,提供了IOC容器等最基础的功能-->
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
3.建立代码分层
BookDao,BookDaoImpl,BookService,BookServiceImpl相关代码如下:
public interface BookDao {
void save();
}
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("BookDaoImpl里的save()执行了。。。。。。");
}
}
public interface BookService {
void save();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao=new BookDaoImpl();
@Override
public void save() {
System.out.println("Book ServiceImpl Save方法执行了。。。。。。。。。。");
bookDao.save();
}
}
4.右键resources目录下创建一个新的配置文件
5.配置Bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--id(bean名字)必须唯一-->
<!--class:映射类路径-->
<bean id="bookDao" class="com.hb.mapper.impl.BookDaoImpl" ></bean>
<bean id="bookService" class="com.hb.service.impl.BookServiceImpl"></bean>
</beans>
6.对Dao进行测试:
package com.hb;
import com.hb.mapper.BookDao;
import com.hb.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//获取Ioc容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean
System.out.println("----------Dao--------------");
BookDao bookDao=(BookDao) ctx.getBean("bookDao");
bookDao.save();
System.out.println("--------------Service--------------");
BookService service=(BookService) ctx.getBean("bookService");
service.save();
}
}
运行结果:
1.4DI案例(基于XML)
基于上一个案例
1.提供BookServiceImpl中的属性bookDao的setter
public class BookServiceImpl implements BookService {
private BookDao bookDao;
@Override
public void save() {
System.out.println("Book ServiceImpl Save方法执行了。。。。。。。。。。");
bookDao.save();
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
2.在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">
<!--id(bean名字)必须唯一-->
<!--class:映射类路径-->
<bean id="bookDao1" class="com.hb.mapper.impl.BookDaoImpl" ></bean>
<bean id="bookService" class="com.hb.service.impl.BookServiceImpl">
<!--配置Service与Dao的关系-->
<!--name:属性名
ref:参考Bean名-->
<property name="bookDao" ref="bookDao1"/>
</bean>
</beans>
运行效果:
1.5bean基于XML配置
1.5.1基本配置
id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id唯一
class:bean的类型,即配置bean的类路径名
name:bean的别名,多个别名之间可以用逗号/空格分割
1.5.2bean作用范围配置
scope:控制bean作用范围,默认为singleton(单例),可以改为prototype(非单例)
适合造单例的bean(适合交给容器管理的bean):
表现层对象
业务层对象
数据层对象
工具对象
不适合造单例的bean(不适合交给容器管理的bean):
封装实体的域对象
1.6bean的四种实例化形式
1.6.1.基于构造方法实例化(默认,常用)
调用无参构造方法(不管构造方法私有还是公有,因为底层使用反射)
1.6.2.使用静态工厂
创建静态工厂示例代码如下(注意方法必须为静态方法):
这里利用BookDaoFactory对于BookDao进行管理,所以需要修改配置(注意:class配置的是工厂类名,factory-method配置工厂类中的静态方法):
测试相关代码段如下:
//获取Ioc容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean
System.out.println("----------Mapper--------------");
BookDao bookDao=(BookDao) ctx.getBean("bookMapper");
bookDao.save();
运行结果:
该方式比较灵活,可以在工厂类中做其它配置(实际上是个代理)
1.6.3使用实例工厂
创建实例工厂(注意:此时不再是静态方法):
public class BookDaoFactory2 {
public BookDao getBookDao(){
return new BookDaoImpl();
}
}
修改XML配置:
测试相关代码如下:
1.6.4.使用FactoryBean实例化
本方式是实例工厂的延伸,因为第三种的缺点是在不必要的情况下多实例化了一个工厂类,而使用FactoryBean可以解决这个问题,FactoryBean类定义如下(注意:需要实现Factory接口并重写创建Bean实例和Bean类型的方法):
package com.hb.factory;
import com.hb.mapper.BookDao;
import com.hb.mapper.impl.BookDaoImpl;
import org.springframework.beans.factory.FactoryBean;
public class BookDaoFactoryBean implements FactoryBean<BookDao> {
/**
* 代替原始实例工厂中创建对象的方法(即BookDaoFactory2中的getBookDao())
* */
@Override
public BookDao getObject() throws Exception {
return new BookDaoImpl();
}
@Override
public Class<?> getObjectType() {
return BookDao.class;
}
}
然后,需要修改XML文件里有关Bean的配置如下:
测试结果如下:
可以看到,Bean被实例化了,基于以上优点,这种方式在框架整合时用得很多。
同时如果,需要用此种方式指定Bean作用范围,这需要在FactoryBean重写isSingleton()方法,返回true代表单例,false代表非单例。
1.7Bean的生命周期
bean在初始化的过程中经历了如下阶段:
初始化容器:
1.创建对象(内存分配---new)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法
使用bean:
1.在Bean中执行相关自定义业务操作
关闭/销毁容器:
1.执行Bean方法
1.7.1自定义周期函数控制生命周期(基于XML)
Bean生命周期中比较重要的两个是Bean实例化前和Bean销毁前的两个时期,相关代码如下:
import com.hb.mapper.BookDao;
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("BookDaoImpl里的save()执行了。。。。。。");
}
//表示Bean初始化对应的操作
public void init(){
System.out.println("init...加载连接!");
}
//表示Bean销毁前的操作
public void destroy(){
System.out.println("destroy...关闭连接!");
}
}
以上代码中init()和destory()为自定义方法,同时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">
<!--id(bean名字)必须唯一-->
<!--class:映射类路径-->
<bean id="bookDao1" name="bookDaoImpl bookMapper" class="com.hb.mapper.impl.BookDaoImpl" init-method="init" destroy-method="destroy"></bean>
<!-- <bean id="bookDaoFactory2" class="com.hb.factory.BookDaoFactory2"></bean>
<bean id="bookMapper" factory-bean="bookDaoFactory2" factory-method="getBookDao"></bean>-->
<bean id="bookService" class="com.hb.service.impl.BookServiceImpl">
<!--配置Service与Dao的关系-->
<!--name:属性名
ref:参考Bean名-->
<property name="bookDao" ref="bookMapper"/>
</bean>
</beans>
同时,需要注意,Bean必须设置为单例。
测试代码如下:
import com.hb.mapper.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppLifeCircle {
public static void main(String[] args) {
//获取Ioc容器
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean
System.out.println("----------Mapper--------------");
BookDao bookDao=(BookDao) ctx.getBean("bookMapper");
bookDao.save();
/**
* 注册关闭钩子
* */
ctx.registerShutdownHook();
/**
* 暴力关闭
* */
//ctx.close();
}
}
在以上代码中,容器关闭前触发Bean的销毁,执行close()和registerShutdownHook()都会关闭容器(ctx对象),进而将Bean销毁。
两者的运行结果都是差不多的。(如下)
两者的不同点在于前者为暴力关闭容器,直接关闭;后者为设置关闭钩子,,放在任何时间都可以(如下)
1.7.2使用接口定义周期函数控制Bean生命周期
上述过程需要在XML文件里面对生命周期函数进行配置,比较麻烦,可以使用接口的方式简化配置:这需要让Bean实现InitializingBean, DisposableBean两个接口,前者定义初始化前的生命周期过程,后者定义了销毁前的生命周期过程,然后重写两个接口的方法,如下:
import com.hb.mapper.BookDao;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BookDaoImpl implements BookDao , InitializingBean, DisposableBean {
@Override
public void save() {
System.out.println("BookDaoImpl里的save()执行了。。。。。。");
}
//表示Bean销毁前的操作
@Override
public void destroy() throws Exception {
System.out.println("destroy...关闭连接!");
}
//表示Bean初始化对应的操作
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init...加载连接!");
}
}
此时,XML文件不许要额外配置,应为已经遵循了Spring的相关规范。如下:
运行结果如下:
2依赖注入
2.1setter注入
使用setter注入引用数据类型的例子在前面已经列举过了,即使用ref属性配置;那么如何传基本数据类型和String类型的值呢?这时候就需要使用value来进行配置了。
示例代码如下:
public class BookDaoImpl implements BookDao {
//连接数
private int connectionNum;
//数据库名称
private String databaseName;
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
@Override
public void save() {
System.out.println("BookDaoImpl里的save()执行了。。。。。。");
System.out.println("连接数:"+connectionNum+" 数据库名称:"+databaseName);
}
}
BookDao下有两个属性,一个表示连接数,一个表示数据库名,分别为int和String类型,并定义了相关的setter方便在依赖注入的时候传值。
然后,需要在XML文件中对两个属性进行加载配置,如下:
配置后,测试结果如下:
我们可以看到,为属性传值成功。
2.2构造器注入
标准书写构造器注入代码的耦合度比较高。过程如下:
将以上的BookDaoImpl加上有参构造方法,如下;
import java.util.Date;
public class BookDaoImpl implements BookDao {
//连接数
private int connectionNum;
//数据库名称
private String databaseName;
public BookDaoImpl(){
System.out.println(new Date() +":执行了无参构造方法");
}
private BookDaoImpl( int connectionNum,String databaseName){
this.databaseName=databaseName;
this.connectionNum=connectionNum;
System.out.println(new Date() +":执行了有参构造方法");
}
@Override
public void save() {
System.out.println("BookDaoImpl里的save()执行了。。。。。。");
System.out.println("连接数:"+connectionNum+" 数据库名称:"+databaseName);
}
}
然后在XML中修改配置如下(byName):
<?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">
<!--id(bean名字)必须唯一-->
<!--class:映射类路径-->
<bean id="bookDao1" name="bookDaoImpl bookMapper" class="com.hb.mapper.impl.BookDaoImpl">
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
<!-- <bean id="bookDaoFactory2" class="com.hb.factory.BookDaoFactory2"></bean>
<bean id="bookMapper" factory-bean="bookDaoFactory2" factory-method="getBookDao"></bean>-->
<bean id="bookService" class="com.hb.service.impl.BookServiceImpl">
<!--配置Service与Dao的关系-->
<!--name:属性名
ref:参考Bean名-->
<property name="bookDao" ref="bookMapper"/>
</bean>
</beans>
测试结果如下:
同时也可已通过构造函数属性类型进行参数注入(byType),这样可以解决形参名称的问题,与形参名不耦合了,这样形参名无论如何改动,xml配置里面的东西都不会改动,修改XML配置如下:
2.3自动装配
Ioc容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程(自动装配用于引用类型的依赖注入,不能对简单类型进行操作;自动装配的优先级低于setter注入和构造器注入,同时出现会导致自动装配配置失效)。自动装配的方式有以下四种:
1.按类型(常用):通过属性的类型查找JavaBean依赖的对象并为其注入。使用Seter方法为其注入。
2.按名称:通过属性的名字的方式查找JavaBean依赖的对象并为其注入,使用Seter方法为其注入。
3.按构造方法:通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用Seter方法注入,而是使用构造方法注入
4.不启用:即不启用自动装配。Autowire默认的值。
2.3.1ByType
测试:
//BookServiceImpl
public class BookServiceImpl implements BookService {
private BookDao bookDao;
@Override
public void save() {
System.out.println("Book ServiceImpl Save方法执行了。。。。。。。。。。");
bookDao.save();
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
//BookDaoImpl
public class BookDaoImpl implements BookDao {
//连接数
private int connectionNum;
//数据库名称
private String databaseName;
public BookDaoImpl(){
System.out.println(new Date() +":执行了无参构造方法");
}
private BookDaoImpl( int connectionNum,String databaseName){
this.databaseName=databaseName;
this.connectionNum=connectionNum;
System.out.println(new Date() +":执行了有参构造方法");
}
@Override
public void save() {
System.out.println("BookDaoImpl里的save()执行了。。。。。。");
System.out.println("连接数:"+connectionNum+" 数据库名称:"+databaseName);
}
}
//测试类
public class App {
public static void main(String[] args) {
//获取Ioc容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean
System.out.println("----------Mapper--------------");
BookDao bookDao=(BookDao) ctx.getBean("bookMapper");
bookDao.save();
System.out.println("--------------Service--------------");
BookService service=(BookService) ctx.getBean("bookService");
service.save();
}
}
测试结果:
按类型装配要求类型是唯一的;因此,使用byType时,必须保障容器中相同类型的bean唯一,推荐使用。
2.3.2ByName
以上代码和测试类不变,修改XML文件里面的配置如下:
运行结果:
byName必须保证容器中具有指定名称的bean,因变量名和配置耦合,不推荐使用