Spring

一、Spring 概述

1.1 - 什么是 Spring

是一个开源框架

Spring是分层的Java SE/EE应用Full-Stack轻量级开源框架,以IOC - 反转控制 (Inverse Of Control)AOP - 面向切面编程 (Aspecr Oriented Programming)内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事物管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。

1.2 - Sping 的发展历程

  • 1997,IBM提出EJB的思想
  • 1998,SUN制定开发标准EJB1.0
  • 1999,EJB 1.1发布
  • 2001,EJB 2.0发布
  • 2003,EJB 2.1发布
  • 2006,EJB 3.0发布

Rod Johson ( Spring 之父 )

Expert One-to-One J2EE Design and Development (2002)

阐述了 J2EE 使用 EJB 开发设计的优点及解决方案

Expert One-to-One J2EE Development without EJB (2004)

阐述了 J2EE 开发不适用 EJB 的解决方案 ( Spring 雏形 )

1.3 - Spring 的优势

方便解耦,简化开发

通过 Spring 提供的 Ioc 容器,可以将对象间得到依赖关系交由 Spring 进行控制,避免硬编码所在成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用

AOP 编程的支持

通过 SpringAOP 工能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付

声明式事务的支持

可以将我们从单调烦闷的事物管理代码中解脱出来,通过声明式方式灵活的进行事物的管理,提高开发效率和质量

方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情

方便集成各种优秀框架

Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibermate、Hessian、Quartz等)的直接支持

降低 JavaEE API 的使用难度

SpringJavaEE API ( 如 JDBCJavaMail、远程调用等 ) 进行了薄薄的封装层,使这些 API 的使用难度大为降低

Java 源码是经典学习范例

Spring 的源码设计精妙,结构清晰,匠心独用,处处体现着对 Java 设计模式的灵活运用以及对 Java 技术的高深造诣。它的源代码无疑是 Java 技术的最佳实践范例

1.4 - Spring 体系结构

Spring_Framework

Core Container

Spring 的核心容器,IoC 部分

1.5 - 耦合

程序间的依赖关系

  • 类之间的依赖
  • 方法之间的依赖

解耦

降低程序间的依赖关系

应该做到

编译时不依赖

运行时才依赖

解耦的思路

  • 使用反射来创建对象,避免使用关键字new
  • 通过读取配置文件,获取要创建的对象全限定类名

1.6 - Bean对象

Bean 在计算机英语中有可重用组件的含义

  • JavaBean > 实体类 – 用 Java 类编写的可重用组件,就是创建我们的 ServiceDao
    • 需要一个配置文件来配置我们的 Servicedao
      • 配置的内容 – 唯一标识=全限定类名( key = value )
    • 通过读取配置文件中的信息,反射创建对象

1.7 - 解决技术

Spring 在 JavaEE 的三层结构中,每一层都提供不同的解决技术

  • web 层 – SpringMVC
  • service 层 – Spring 的 IOC
  • dao 层 – Spring 的 JDBCTemplate

二、IOC 控制反转

  • 比如有一个类,在类里面有方法(不是静态方法),调用类里面的方法,创建类的对象,使用对象调用方法,创建类对象的过程,需要实例化对象
  • 把对象的创建不是通过实例化实现,而是交给Spring配置创建类对象

2.1 - IOC 的底层原理

  • xml 配置文件
  • dom4j 解决 xml
  • 工厂设计模式
  • 反射

2.1.1 - IOC 的实现

  • 将类名写在 xml 文件中
1
<bean id='UserService' class='包名+文件名'/>
  • 在工厂中,通过 dom4j 解析 xml 文件,通过 id 获取到类名
    • String className = ‘class属性值’
  • 利用反射创建对象
1
Class clazz = Class.forName(ClassName);
  • 创建并返回类对象
1
return clazz.newInstance();

2.1.2 - IOC 入门案例

  • 导入 jar
  • 创建类,在类里面创建方法
  • 创建 Spring 配置文件,配置创建类
  • 写代码测试对象创建

2.2 - bean 管理

2.2.1 - 实例化的三种方式

  • 使用类的无参构造
  • 使用静态工厂创建
  • 使用实例工厂创建

2.2.2 - Bean 的常用属性

  • id – 起名,不得含特殊符号
  • class – 类的全路径
  • name – 起名,可有特殊符号
  • scope – 实例的类型
    • singleton – 单例
    • prototype – 多例
    • requese – 创建对象把对象放到 request 域里面
    • session – 创建对象把对象放到 session 域里面
    • globalSession – 创建对象把对象放到 globalSession

2.3 - 属性注入

2.3.1 - constructor 注入

设有下类

1
2
3
4
5
6
7
8
package 包路径;

public class A {
String key;
public A(String key) {
this.key = key
}
}

则在配置文件中应如下所示

1
2
3
<bean id="自定义id" class="包路径.A">
<constructor-arg name="key" value="要赋的值"/>
</bean>

2.3.2 - setter 注入

设有下类,且每个属性都要有对应的 setter 方法

1
2
3
4
5
6
7
8
package 包路径;

public class A {
String key;
public void setKey(String key) {
this.key = key;
}
}

则在配置文件中应如下所示

1
2
3
<bean id="自定义id" class="包路径.A">
<property name="key" value="要赋的值"/>
</bean>

2.4 - 对象注入

2.4.1 - constructor 注入

设有以下两个类,B 中有 A 类属性,则 B 类中要有对应的 setter 方法

1
2
3
4
5
package 包路径;

public class A {

}
1
2
3
4
5
6
7
8
9
10
package 包路径;

public class B {

A a;
public B(A a) {
this.a = a;
}

}

则在配置文件中应如下所示

1
2
3
4
<bean id="a" class="包路径.A"/>
<bean id="b" class="包路径.B">
<constructor-arg name="a" ref="a"/>
</bean>

2.4.2 - setter 注入

设有以下两个类,B 中有 A 类属性,则 B 类中要有对应的 setter 方法

1
2
3
4
5
package 包路径;

public class A {

}
1
2
3
4
5
6
7
8
9
10
package 包路径;

public class B {

A a;
public void setA(A a) {
this.a = a;
}

}

则在配置文件中应如下所示

1
2
3
4
<bean id="a" class="包路径.A"/>
<bean id="b" class="包路径.B">
<property name="a" ref="a"/>
</bean>

2.6 - IOC 和 DI 的区别

  • IOC:控制反转,把对象创建交给 spring 进行配置
  • DI:依赖注入,向类里面的属性中设置值
  • 依赖注入不能单独存在,需要在 IOC 基础之上完成操作

2.7 - 注解创建对象

2.7.1 - 注解

第一步:引入约束

1
2
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

第二步:添加到配置文件

1
2
<context:component-scan base-package="包路径"/>
<context:annotation-config/>

- @Component()

用于创建对象,取的名字在括号中赋值给 value

- @Controller()

Component 的衍生,用于 web

- @Service()

Component 的衍生,用于业务层

- @Repository()

Component 的衍生,用于持久层

- @Scope()

功能和配置文件里的 Scope 一样,值在括号中赋值给 value

- @Autowired

自动注入其下的属性

- @Resource()

自动注入属性,在括号中通过 name 指定对应的 bean ,更准确,更常用

- @Aspect

用于增强所在类

- @Before()

前置增强

- @AfterReturning()

后置增强

- @Around()

环绕增强

- @AfterThrowing()

异常增强

- @After()

最终增强

- @Transactional

事务增强

2.7.2 - 使用注解创建对象

1
2
3
4
@Component("a")
public class A {

}

就可以用 getBean("a") 创建对象了

2.7.3 - 使用注解注入属性

设有 A

1
2
3
4
5
6
package 包路径;

public class A{

}

写入配置

1
2
<bean id="自定义id" class="包路径"/>

- 使用 @Autowired 注入

设有 B

1
2
3
4
5
public class B {
@Autowired
A a;
}

在属性上方加上 @Autowired 注解即可

- 使用 @Resource 注入

设有 B

1
2
3
4
5
public class B {
@Resource(name="自定义id")
A a;
}

三、AOP 思想

3.1 - 相关术语

JoinPoint - 连接点

类里面哪些方法可以被增强

PointCut - 切入点

被增强了的方法

Advice - 通知/增强

增强的逻辑,增加一个什么功能

  • 前置增强 – 在切入点之前执行
  • 后置增强 – 在切入点之后执行
  • 异常增强 – 在切入点出现异常时执行
  • 最终增强 – 在后置增强之前执行
  • 环绕增强 – 在切入点之前和之后执行

Aspect - 切面

把增强应用到切入点的过程

Introduction - 引介

在不修改类的代码的前提下,可以在运行期动态的添加一些方法

Target - 目标对象

要增强的类

Weaving - 织入

把增强应用到目标的过程

Proxy - 代理

一个类被 AOP 织入增强后,就产生一个结果代理类

3.2 - AspectJ

用来实现 AOP 操作

  • AspectJ 不是 Spring 的一部分

3.4 - 环境准备

引入约束

1
2
3
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

3.3 - 配置文件实现 AOP

有如下两个类

1
2
3
4
5
6
7
package 包路径;

public class A {
public void front() {
}
}

1
2
3
4
5
6
7
package 包路径;

public class B {
public void met() {
}
}

xml 中配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<aop:config>
// 配置切入点
<aop:pointcut id="切入点名称" expression="execution(* *.*())"/>
// 配置切入面
<aop:aspect ref="增强所在类的 bean-id">
// 配置增强的方法,其中 method 指用哪个类来增强,pointcut-ref 指切入点
<aop:before method="front" pointcut-ref="切入点名称"/>
</aop:aspect>
</aop:config>

- execution(* *.*(..)) 表达式
- 第一个 * 号是返回类型
- 第二个 * 号是切入点所在类的全路径
- 第三个 * 号是切入点

3.5 - 注解方式实现 AOP

添加至 xml 配置文件

1
2
<aop:aspectj-autoproxy/>

有下面两个类

1
2
3
4
5
6
7
8
9
package 包路径;

@Aspect
public class A {
@Before(value="execution(* B的全路径.要增强的方法())")
public void front() {
}
}

1
2
3
4
5
6
7
package 包路径;

public class B {
public void met() {
}
}

四、JDBC Template

4.1 - 环境配置

4.1.1 - 导入 jar 包

  • c3p0
  • mysql-connector-java
  • mchange-commons-java

4.1.2 - 配置连接池

xml 中配置

1
2
3
4
5
6
7
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///表名"/>
<property name="user" value="账号"/>
<property name="password" value="密码"/>
</bean>

4.1.3 - 配置 JDBC Template

xml 中配置,并注入 dataSource

1
2
3
4
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>

4.1.4 - 配置 Dao

新建类,定义 JdbcTemplate,设置 setter 方法

1
2
3
4
5
6
7
8
9
package 包路径;

public class A {
private JdbcTemplate jdbc;
public void setJdbc(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
}

利用 xml 配置文件注入

1
2
3
4
<bean id="自定义" class="包路径.A">
<property name="jdbc" ref="jdbcTemplate"/>
</bean>

4.2 - 增

1
2
3
String sql = "insert into 表名 values(···)";
jdbc.update(sql,···);

4.3 - 删

1
2
3
String sql = "delete from 表名 where 条件";
jdbc.update(sql,···);

4.4 - 查

4.4.1 - 查询某一个返回值

1
2
3
String sql = "select count(*) from 表名";
jdbc.queryFroObject(sql, 希望返回的类型);

4.4.2 - 查询后将一行结果封装到一个对象

1
2
3
String sql = "select * from 表名 where 条件";
结果类 变量 = jdbc.queryForObject(sql, new 映射类, ···);

结果类需自己写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class 映射类 implements RowMapper<结果类> {

public 结果类 mapRow(ResultSet rs, int num) throws SQLException {

结果类 变量 = new 结果类();
变量.属性1 = rs.getString("对应字段1");
···
变量.属性n = rs.getString("对应字段n");

return 变量;

}

}

4.4.2 - 查询后将一行结果封装到一个列表

1
2
3
String sql = "select * from 表名 where 条件";
List<结果类> 变量 = jdbc.query(sql, new 映射类, ···);

结果类需自己写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class 映射类 implements RowMapper<结果类> {

public 结果类 mapRow(ResultSet rs, int num) throws SQLException {

结果类 变量 = new 结果类();
变量.属性1 = rs.getString("对应字段1");
···
变量.属性n = rs.getString("对应字段n");

return 变量;

}

}

4.5 - 改

1
2
3
String sql = "update 表名 set 字段名 = ? where 条件";
jdbc.update(sql,···);

4.6 - 事务管理

4.6.1 - 环境配置

- 引入约束

1
2
3
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

4.6.2 - 配置文件方式

- 创建事务管理器

1
2
3
4
<bean id="管理器名称" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

- 配置事务增强

1
2
3
4
5
6
7
8
<tx:advice id="增强名称" transaction-manager="管理器名称">
<tx:attributes>
<tx:method name="匹配的方法名"/>
</tx:attributes>
</tx:advice>

- 此处的方法是指要进行事务管理的方法

- 配置切面

1
2
3
4
5
6
7
<aop:config>
<aop:pointcut id="切入点名称" expression="execution(返回类型 该方法所在全路径.方法名())"/>
<aop:advisor advice-ref="增强名称" pointcut-ref="切入点名称"/>
</aop:config>

- 此处的方法是指要进行事务管理的方法

4.6.3 - 注解方式

- 配置事务管理器

1
2
3
4
<bean id="管理器名称" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

- 开启事务注解

1
2
<tx:annotation-driven transaction-manager="transactionManager"/>

- 使用

在需要事务管理的类上面加上注解 @Transactional

1
2
3
4
5
@Transactional
public class A {

}