1.初识Spring

1.1.Apache基金会

Apache软件基金会(Apache Software Fundation)是美国的一个支持Ap ache软件项目(包括Apache Http服务器)的非盈利性组织。Apache基金会有Apache集团性的。我们后面学习的很多技术都由Apache基金会进行维护(Apache基金会开发软件吗?不开发,所有的项目都是别人捐献的或购买的)。

1.2.Spring框架

作者:Rod Johnson(作家、音乐家、数学家)是Spring Framework的鼻祖
Spring框架是一个具有注入依赖和面向切面编程的容器框架,Spring框架也成为胶水框架。
思想:Expert One To One(专家一对一)

1.3.架构图

1.3.1.官网

Spring.io

1.3.2.架构图

1.3.3.优势

  • 便于Web开发中的测试
  • 面向接口编程,简化开发
  • 将不同的框架整合到一起,使得开发变得更加容易

2.Ioc

2.1.概述

Ioc(Inversion Of Control的缩写)被称为控制反转。Ioc是一种思想,Ioc包含了控制和反转两个部分。
所谓控制是指谁控制了对象的创建,而反转则是指角色发生了反转。原来的对象创建者变成了被动接收者。
Ioc是Spring框架的核心思想。

2.2.传统代码

2.2.1.实现

1
2
3
4
5
6
7
8
public class UserDao {
/**
* 新增方法
*/
public void add(){
System.out.println("UserDao.add() is do..........");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Service层
* Service类中add()方法的正确执行需要依赖于Dao对象,而Dao对象的创建是由Service类自身来实现的。
*/
public class UserService {
//实例化UserDao对象
private UserDao dao = new UserDao();

public void add(){
//调用Dao对象的方法
dao.add();
}
}
1
2
3
4
5
6
public class TestSimple {
public static void main(String[] args) {
UserService service = new UserService();
service.add();
}
}

2.2.2.说明

Service中add()方法的正确执行需要依赖于Dao对象的方法,而Dao对象的创建是由Service自身来进行实现的。

2.2.3.代码缺点

  • 代码的依赖性太强(灵活性较差,耦合度太高)

2.3.改进代码

2.3.1.实现

1
2
3
4
5
6
7
8
public class UserDao {
/**
* 新增方法
*/
public void add(){
System.out.println("UserDao.add() is do..........");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 改进代码
* Service类中add()方法的正确执行需要依赖于Dao对象,而Dao对象的创建是由测试类来完成的
*/
public class UserService2 {
//实例化UserDao对象
private UserDao dao;

//构造器(对象由构造器进行传入)
public UserService2(UserDao dao) {
this.dao = dao;
}

public void add(){
//调用Dao对象的方法
dao.add();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 测试改进代码
*/
public class TestNew {
public static void main(String[] args) {
//创建对象
UserDao dao = new UserDao();
//对象通过构造器(set访问器)进行传递
UserService2 service = new UserService2(dao);
service.add();
}
}

2.3.2.说明

Service中add()方法的执行需要依赖于dao对象来完成,而dao对象的创建则是由测试类来完成的,然后通过构造器(或set访问器)进行传递。

2.3.3.优点

  • 依赖性较弱

3.Spring Ioc

3.1.实现步骤

  • 创建项目
  • 添加依赖
  • 添加Spring配置文件
  • 编写代码
  • 修改Spring配置文件
  • 应用Spring注入对象

3.2.实现

3.2.1.创建项目

3.2.2.添加依赖

3.2.3.添加Spring配置文件

(鼠标右键)==》New==》Spring-config

1
2
3
4
5
6
<?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>

3.2.4.编写代码

1
2
3
4
5
6
7
8
/**
* 实体类
*/
public class User {
private String name;
private int age;
….
}

3.2.5.修改Spring配置文件

1
2
3
4
5
6
7
8
9
10
<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="u1" class="edu.shifan.spring.pojo.User">
<property name="name" value="Tom" />
<property name="age" value="20" />
</bean>
</beans>

3.2.6.应用Spring注入对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
//传统方式创建对象
//User u = new User();

//基于Spring的对象创建
//1、获取Context对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
//2、获取对象
User u = (User) ctx.getBean("u1");

//3、使用User对象
System.out.println(u);
}

3.3.Bean标签

3.3.1.作用

定义Spring中的资源(Java对象),使用该标签定义的资源将由Spring容器进行管理。

3.3.2.说明

该标签和new操作符相似,需要依赖于构造器(或Set访问器)来完成对象的实例化。

3.3.3.属性

  • id:bean对象的唯一标识符(名称),其他代码或配置通过id获取bean对象
  • name:bean对象的名称,其他代码或配置通过name获取bean对象
  • class:Java Bean的类型名
  • scope:作用范围(生命周期)
    singleton:创建的对象保存在容器中,对象是单例模式进行创建的(默认值)
    prototype:创建的对象保存在容器中,对象是原型模式进行创建(网络中也有人称为多例模式)

4.对象的创建

4.1.构造器

4.1.1.无参构造

1)代码

1
2
3
4
5
6
<!-- a、无参构造函数-->
<bean id="u1" class="edu.shifan.pojo.User">
<!-- 传递(注入)属性值-->
<property name="name" value="Tom" />
<property name="age" value="20" />
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
//new操作符:用于在实例化对象时,向系统申请内存空间
//User u = new User();
//u = new User();

//1、获取Context对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
//2、通过ctx获取对象
//ctx.getBean()实例化对象
User u = (User) ctx.getBean("u1");
//3、使用对象
System.out.println(u);
}

2)说明

  • 对象的创建时通过无参构造函数进行创建的
  • 对象所需的属性值是通过set访问器进行注入的,所以必须提供set访问器
    3)注意
    在类中如果没有提供无参构造函数将会产生异常

4.1.2.带参构造

1)代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<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">
<!-- 1、构造函数方式-->

<!-- b、带参构造函数-->
<bean id="u2" class="edu.shifan.pojo.User">
<!-- 传递(注入)属性值-->
<constructor-arg name="name" value="Jerry" />
<constructor-arg name="age" value="100" />
</bean>
</beans>
1

2)说明

  • 创建对戏那个时,对象是通过带参构造函数进行实例化的
  • 在类中可以不提供set访问器
    3)注意
    必须按照构造函数的参数及类型传递参数值

4.2.工厂

4.2.1.静态工厂

1)代码

1
2
3
4
5
6
7
8
9
10
11
12
<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">

<!--2、工厂方式 -->
<!-- a、静态工厂方式-->
<bean id="u3" class="edu.shifan.factory.StaticFactory" factory-method="create">
<constructor-arg name="name" value="Mike" />
<constructor-arg name="age" value="70" />
</bean>
</beans>
1

2)说明

  • 需要在bean标签中通过class来配置工厂类,然后通过factory- method属性来指定工厂中的方法
  • 对象参数的传递可以通过构造器或set访问器来实现

4.2.2.动态工厂

1)代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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">
<!-- 1、构造函数方式-->

<!-- 实例化工厂对象-->
<bean id ="fac" class="edu.shifan.factory.InstanceFactory" />
<!-- 使用动态工厂-->
<bean id="u4" factory-bean="fac" factory-method="create">
<constructor-arg name="name" value="Marry" />
<constructor-arg name="age" value="75" />
</bean>
</beans>
1

2)说明

  • 动态工厂方式需要在bean标签中通过factory-bean属性引用已经实例化的工厂对象,然后通过factory-method来指定工厂中的方法
  • 对象参数的传递可以通过构造器或set访问器来进行实现

5.Bean工厂

5.1.相关技术

  • 工厂设计模式
  • 反射技术
  • Dom4j(xml解析技术)
    Ioc是Spring的核心设计思想。

5.2.Bean工厂

  • BeanFactory:接口,接口中定义了获取Bean对象的相关方法,如:getBean()
  • ApplicationContext:BeanFactory、ResourceLoader的子接口,同时具有getBean()和getResource()的能力,通过Application Context可以获取到Bean对象
    Ioc容器启动过程中首先加载配置文件,然后按照注册的顺序加载所有的Bean对象信息,,当用户调用getBean()方法时,Ioc容器会通过反射技术获取到Bean对象。

5.3.Spring容器

  • ApplicatonContext是一个接口,接口中提供了访问Spring容器的相关方法(API)
  • ClassPathXmlApplictionContext是一个接口的实现类,该类中实现了ApplicationContext接口中定义的所有方法
  • BeanFactory接口中定义了操作Bean的相关方法
  • Spring中主要包含了BeanFactory和ApplicationContext两个容器
    区别:
  • ApplicatonContext创建的Bean默认采用立即加载模式,解析配置文件时就创建所有的Bean对象
  • BeanFactory创建Bean对象时采用延迟加载,使用Bean时才对Bean进行创建(实例化)

6.Spring配置文件

6.1.Id

1
2
3
4
5
<!-- 1、id属性:可以唯一的标识出一个对象-->
<bean id="u1" class="edu.shifan.pojo.User">
<property name="name" value="id" />
<property name="age" value="20" />
</bean>

6.2.Name

1
2
3
4
5
<!-- 2、name属性:可以唯一的标识出一个对象,name和id可以同时存在,且可以配置多个name属性-->
<bean id="u2" name="user1,user2,user4,user5" class="edu.shifan.pojo.User">
<property name="name" value="name" />
<property name="age" value="24" />
</bean>

6.3.Alias

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 2、name属性:可以唯一的标识出一个对象,name和id可以同时存在,且可以配置多个name属性-->
<bean id="u2" name="user1,user2,user4,user5" class="edu.shifan.pojo.User">
<property name="name" value="name" />
<property name="age" value="24" />
</bean>

<!--
3、alias属性:简化调用
alias.name:其值可以是bean标签的name或id属性值
alias.alias:配置bean对象对应的(简单)别名
-->
<alias name="user1" alias="u" />

6.4.加载资源文件

1
2
3
4
5
public class DataSource {
private String driver;
private String username;
private String password;
}
1
2
3
driver=com.mysql.dj.jdbc.Driver
user=root
pwd=123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<beans xmlns="http://www.springframework.org/schema/beans"
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">


<!-- 4、加载资源文件-->
<!-- 加载资源文件,需要再beans标签中添加文件的命名空间-->
<context:property-placeholder location="classpath:db.properties" />
<bean id="ds" class="edu.shifan.pojo.DataSource">
<!-- 读取时可以通过${}进行读取-->
<property name="driver" value="${driver}"/>
<property name="username" value="${user}"/>
<property name="password" value="${pwd}"/>
</bean>
</beans>

6.5.导入配置文件

1

1
2
3
4
5
6
7
8
9
10
11
<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">
<!-- 5、import导入-->
<!--
import:在配置文件(将其他配置文件集成到当前配置文件中)中导入其他spring配置文件
注意:如果配置文件在包中,则需要将"."替换成路径标识符"/"
-->
<import resource="classpath:spring-config.xml" />
</beans>

【说明】
在Spring的配置文件中可以引入其他的Spring配置文件,这可能会造成Bean命名冲突(id或name的属性值相同)的问题。如果冲突则加载bean信息时,后面定义的配置会覆盖前面定义的配置,所以最终导致获取的bean对象不是预期的对象

7.DI

7.1.概述

DI(Dependency Injection的缩写)称为依赖注入。Ioc和DI其实是同一回事,只是所处的角度不同而已。

  • 依赖:Bean对象的创建由Ioc容器来进行实现,Bean对象创建时所需的资源依赖于Ioc容器进行传递
  • 注入:Bean对象创建时所需的资源由Ioc容器进行传递,我们将参数传递的行为称为注入
    获得对象的方式发生了反转(由Ioc容器进行资源注入)
  • DI:依赖注入。使用Ioc容器创建对象时所需资源都交给Ioc容器进行管理
  • DI:主要负责将资源注入到使用位置

7.2.资源注入

Spring框架实例化对象时可以注入不同类型的资源,如:常量、对象、二进制等内容。其中最常见的就是常量。

7.2.1.注入常量值

1
2
3
4
5
6
7
8
/**
* 实体类
*/
public class Student {
//常量值
private String name;
private int age;
}
1
2
3
4
5
<!-- 1、注入常量值:常量值的注入通过value属性进行注入-->
<bean id="s1" class="edu.shifan.pojo.Student">
<property name="name" value="Tom" />
<property name="age" value="20" />
</bean>

7.2.2.注入对象

1
2
3
4
public class Address {
private String city; //城市
private String county; //县区
}
1
2
3
4
5
6
7
public class Student {
//常量值
……

//自定义类型属性(Address:类型;addr:对象)
private Address addr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 2、注入对象:注入对象需要通过ref属性进行注入(ref表示引用)-->
<!-- 实例化Address对象-->
<bean id="address" class="edu.shifan.pojo.Address">
<property name="city" value="南阳市" />
<property name="county" value="卧龙区" />
</bean>

<!-- 使用对象-->
<bean id="s2" class="edu.shifan.pojo.Student">
<property name="name" value="Tom" />
<property name="age" value="20" />

<!-- 引用对象-->
<property name="addr" ref="address" />
</bean>

7.2.3.注入集合对象

1
2
3
4
5
6
7
8
9
10
11
public class Student {
//常量值
……

//朋友--字符串数组
private String[] friends;
//爱好--List集合
private List<String* hobbies;
//电话号码--set集合
private Set<String* tels;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- 3、注入集合对象:通过特定的标签进行注入-->
<bean id="s3" class="edu.shifan.pojo.Student">
<property name="name" value="Marry" />
<property name="age" value="20" />

<!--a、注入数组对象-->
<property name="friends">
<array>
<value>Mike</value>
<value>Jack</value>
<value>Tom</value>
</array>
</property>

<!--b、注入List集合对象-->
<property name="hobbies">
<list>
<value>篮球</value>
<value>逛街</value>
<value>游泳</value>
</list>
</property>

<!-- c、注入Set集合对象-->
<property name="tels">
<set>
<value>189001302</value>
<value>156023301</value>
</set>
</property>
</bean>

7.2.4.Null注入

1
2
3
4
5
6
7
public class Student {
//常量值
……

//妻子--可能没有
private String wife="Rose";
}
1
2
3
4
5
6
7
8
<!-- 4、注入null值:null值的注入需要使用null标签-->
<bean id="s4" class="edu.shifan.pojo.Student">
<property name="name" value="Mike" />
<property name="age" value="20" />

<!-- 注入一个null值-->
<property name="wife"><null /></property>
</bean>

7.2.5.注入properties数据

1
2
3
4
5
6
public class Student {
//常量值
……

private Properties prop;
}
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 5、注入properteis值:注入需要使用props标签进-->
<bean id="s5" class="edu.shifan.pojo.Student">
<property name="name" value="Mike" />
<property name="age" value="20" />

<property name="prop">
<props>
<prop key="username">root</prop>
<prop key="pwd">123</prop>
</props>
</property>
</bean>

7.3.P注入与C注入(注入防暑)

7.3.1.P注入

1
2
3
4
5
6
7
8
9
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">

<!-- 6、p注入:p注入其实就是通过set访问器进行注入-->
<bean id ="a1" class="edu.shifan.pojo.Address" p:city="潍坊市" p:county="高新区"/>
</beans>

7.3.2.C注入

1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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">

<!-- 7、c注入:c注入其实就是通过构造器进行注入-->
<bean id ="a2" class="edu.shifan.pojo.Address" c:city="天津市" c:county="天津市"/>
</beans>

8.自动装配

8.1.概述

Spring容器通过配置的方式可以将具有关系的资源注入到Bean对象中,这种能力我们称之为自动装配。

8.2.目的

  • 自动化配置可以减少配置代码

8.3.基础代码

8.3.1.Dao

1
2
3
public interface UserDao {
void add();
}
1
2
3
4
5
6
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("UserDaoImpl.add() is do..........");
}
}

8.3.2.Service

1
2
3
4
5
6
7
public class UserService {
private UserDao userDao;

public void insert(){
userDao.add();
}
}

8.4.自动装配

8.4.1.autowire属性

1)作用
配置自动换的装配方式。
2)取值

  • No(默认值):不使用自动装配
  • byName:按照属性名进行自动装配
  • byType:按照属性类型进行自动装配
  • constructor:通过构造函数进行自动装配

8.4.2.byName自动装配

1)独立方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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="userDao" class="edu.shifan.dao.UserDaoImpl" />


<!--
2、byName自动装配:
按照属性名进行自动装配:要求Bean的Id必须和类中需要注入的属性的名称相一致
-->
<!-- a、独立配置:只对当前bean标签有效-->
<bean id="o2" class="edu.shifan.service.UserService" autowire="byName"/>
</beans>

说明:独立方式只对所在标签有效

2)全局配置(集中配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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" default-autowire="byName">


<bean id="userDao" class="edu.shifan.dao.UserDaoImpl" />



<!--
2、byName自动装配:
按照属性名进行自动装配:要求Bean的Id必须和类中需要注入的属性的名称相一致
-->
<!-- b、集中配置:应用了全局配置-->
<bean id="o3" class="edu.shifan.service.UserService"/>
</beans>

说明:全局配置对当前配置文件中所有的bean标签都有效
3)说明
程序执行过程中,Ioc容器会自动查找Id值和对象的属性名相匹配的所有对象,并通过set访问器将资源注入到对象的属性。
4)注意事项

  • 被注入的Bean标签的Id属性值必须和注入对象的属性名相一致
  • 必须为注入的属性提供set访问器

8.4.3.byType自动装配

1)独立配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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" default-autowire="byName">


<bean id="userDao" class="edu.shifan.dao.UserDaoImpl" />


<!--
3、byType自动装配
按照属性的类型进行自动装配:要求被装配对象属性的类型在配置文件中必须唯一存在
-->
<!-- a、独立配置:只对当前bean标签有效-->
<bean id="o4" class="edu.shifan.service.UserService" autowire="byType"/>

</beans>

说明:按类型进行自动装配时,可以不为bena标签提供id属性,但要求自动装配对象的类型在配置文件中必须唯一
2)集中配置

1
2
3
4
5
6
7
8
9
10
11
12
<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" default-autowire="byType">


<bean id="userDao" class="edu.shifan.dao.UserDaoImpl" />

<!-- b、集中配置:-->
<bean id="o5" class="edu.shifan.service.UserService"/>

</beans>

3)说明
Ioc容器会自动将装配类型与属性类型相同的对象进行注入。
4)注意事项

  • 被应用对象可以省略Id和name属性
  • 使用byType进行自动装配时,相同类型不能重复出现
    5)建议
    尽量避免使用按照类型进行自动装配。

8.4.4.constructor自动装配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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="userDao" class="edu.shifan.dao.UserDaoImpl" />



<!-- 4、constructor自动装配 -->
<bean id="o6" class="edu.shifan.service.UserService" autowire="constructor"/>

</beans>

说明:构造器自动装配时,必须为类提供带参构造函数

9.Ioc与注解

9.1.注解方式实现自动装配

9.1.1.配置文件

1
2
3
4
5
6
7
8
9
10
11
<beans xmlns="http://www.springframework.org/schema/beans"
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">

<!-- 开启注解扫描-->
<context:component-scan base-package="edu.shifan" />
</beans>

9.1.2.Dao

1
2
3
4
5
6
7
8
9
10
11
/**
* 使用@Repository注解标注dao类
* 注解:其实就是一种标记,可以被框架所识别
*/
@Repository
public class UserDao {
//UserDao的方法
public void add(){
System.out.println("UserDao.add()方法被调用了.............");
}
}

9.1.3.Service

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 使用@Service注解进行标记
*/
@Service
public class UserService {
@Autowired
private UserDao userDao;

//调用userDao对象的add方法
public void insert(){
userDao.add();
}
}

9.1.4.Test

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
//1、获取context对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
//2、获取service对象
UserService service = ctx.getBean(UserService.class);

//3、使用service对象
service.insert();
}
}

9.1.5.说明

  • 在进行包扫描时,Spring框架会对配置的包(及其子包)中的所有文件进行扫描
    框架仅对合法的Java文件进行扫描
    扫描过程中仅对能够识别的Spring注解进行识别
  • 扫描结束后会自动被有效注解识别的类型转为Spring对应的资源对象加载到容器中
  • Spring支持混合开发方式(推荐注解方式)

9.2.常用注解

9.2.1.Bean定义

1)注解

  • @Component:顶级注解,都可以进行标注
  • @Repository:标注Dao层类
  • @Service:标注Service层类
  • @Controller:标注控制层类(之前的Servlet)
    2)类型
    类注解
    3)使用对象
    用于标注类
    4)属性值
  • Value:设置bean的id
    5)说明
    @Repository、@Service、@Controller都是@Component的衍生注解。他们的功能都是一样。
    6)示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 使用@Repository注解标注dao类
    * 注解:其实就是一种标记,可以被框架所识别
    */
    @Repository("dao")
    //@Component
    public class UserDao {
    //UserDao的方法
    public void add(){
    System.out.println("UserDao.add()方法被调用了.............");
    }
    }

9.2.2.注入注解

1)常用注解

  • @Autowired
  • @Qulifier
    2)类型
    属性注解
    3)使用对象
    用于标注类的属性。
    4)作用
    对bean的属性进行自动装配(注入)。
    5)示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Service
    //@Component
    public class UserService {
    @Autowired
    //@Qualifier(value="dao")
    private UserDao userDao;

    //调用userDao对象的add方法
    public void insert(){
    userDao.add();
    }
    }
    6)说明
  • 单独使用@Autowire是默认按照类型(byType)进行自动装配。也可以使用@Qualifier进行自动装配,@Qulifier根据bean的Id进行自动装配(byName)

9.2.3.@Resource注解

1)常用注解

  • @Resource
    2)类型
    属性注解
    3)使用对象
    用于标注对象的属性。
    4)属性
  • Name:指定注入bean的id
    5)示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 使用@Service注解进行标记
    */
    @Service
    //@Component
    public class UserService {
    //@Autowired
    //@Qualifier(value="dao")
    @Resource(name="dao")
    private UserDao userDao;

    //调用userDao对象的add方法
    public void insert(){
    userDao.add();
    }
    }
    6)说明
    @Resource默认使用byName方式进行自动装配,byName方式无法得到资源则按照类型(byType)进行自动装配。

10.Aop

10.1.概述

Aop(Aspect Oriented Programing的缩写)称为面向切面编程,在OOP编程中模块化的单元是类(Class),而在Aop中模块化的单元则是切面。切面能够对关注点进行模块化。

  • 面向切面编程是一种编程范式,他主要用于指导开发者如何组织程序结构
  • 面向切面编程不是面向对象编程的替代,而是为了弥补OOP编程的不足。是基于OOP基础上进行横向开发
  • OOP的程序设计主体是类,一切操作围绕对象进行
  • Aop的程序设计关注的是OOP开发中的共性功能,一切操作都是围绕共性功能进行实现的。

10.2.Aop编程

10.2.1.OOP编程

1)需求
编写Dao、Service及Test代码,并依次进行调用。最后在Dao对象的add()方法执行后执行日志功能。
2)实现

3)调用

类之间的调用关系是纵向调用。
4)缺点
所有Dao对象的add()方法执行后都需要去调用公共业务Log的方法。
5)总结

  • 调用为纵向调用方式,代码的关键单元是类。

10.2.2.Aop编程

1)需求
同OOP编程
2)实现

3)调用

类之间是横向调用(横切)
4)优点
提高了代码的复用性。
5)总结
将关注点横切到代码中,关键单元是切面。

10.2.3.意义

  • 提高了代码的复用性
  • 业务代码更加清晰简洁
  • 业务代码的维护更加方便

10.2.4.Aop的作用

  • 允许用户自定义切面来完善OOP编程
  • Aop的核心思想是从各领域入手进行标准化,将所有共性功能进行逐一开发,最终实现程序功能以组合的方式完成业务模块及程序的功能模块

10.2.5.最终目标

程序的开发逐步走向自动化或半自动化,最终实现“插拔式的组件体系”。

10.3.Spring Aop

10.3.1.项目搭建

1)创建项目

2)添加依赖

3)编写公共代码

10.3.2.需求

在Dao对象的方法执行过程中添加日志功能。

10.3.3.传统方式

编写日志类,然后在Service类中进行调用。

10.3.4.通知(增强)

通知一般也称为增强(通过通知可以在功能原有基础上进行一定的提升或扩展)。

  • 前置通知(Before Advice)
  • 后置通知(After Returning Advice)
  • 环绕通知(Interceptor Around Advice)
  • 异常通知(Throw Advice)
  • 自定义通知

10.3.5.切点表达式

1)切点表达式
切点表达式其实就是描述通知的执行具体位置,切点表达式是一个匹配方法执行位置的通配符字符串。
2)语法
Execution(返回值类型 包.类.方法名(参数列表))
3)通配符

  • *:独立的任意字符串,可以独立使用,也可以最为后缀进行使用
  • ..:多个任意字符串,一般用于简化包名及参数名
    4)示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //表示方法的返回值为任意类型
    //表示任意包下任意类的任意方法
    //表示方法的参数为任意参数
    Execution(* *(..))
    Execution(* *..*(..))
    Execution(* *..*.*(..))

    //表示方法的返回值为任意类型
    //表示类属于edu.shifan.service包下任意类
    //方法可以是edu.shifan.service包下任意类中的任意方法
    Execution(* edu.shifan.service.*.*(..))
    Execution(* edu.shifan.service.*.add(..))
    Execution(* edu.shifan.service.UserService.*(..))
    Execution(* edu.shifan.service.UserService.add(..))

    Execution(public void edu.shifan.service.UserSerivce.add(..))

10.3.6.前置通知

1)实现方式
通过实现MethodBeforeAdvice接口实现的前置通知。
2)特点
连接点执行之前执行自定义行为(通知)
3)实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
【通知】
/**
* 日志--公共业务
*/
public class BeforeLog implements MethodBeforeAdvice {
/**
* 前置通知
* @param method:被代理对象的方法
* @param objects:被代理对象方法接收的参数集合
* @param o:被代理对象(目标对象)
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知:" + new Date() + "--" + o.getClass()
.getSimpleName()+"的" + method.getName()+"方法被执行了");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描-->
<context:component-scan base-package="edu.shifan" />

<!-- 1、实例化通知对象(创建切面对象)-->
<!-- a、前置通知 -->
<bean id="log1" class="edu.shifan.log.BeforeLog" />

<!-- 2、配置切点-->
<!--
aop:config
作用:该标签表示配置Aop功能
说明:在spring中可以出现多个Aop配置
-->
<aop:config>
<!--
定义切点
aop:pointcut
属性:
expression:切点表达式,可以定义切点(配置通知执行的位置,配置时可以使用通配符配置多个切点方法)

说明:
在aop:config中可以有多个aop:pointcut配置,且该表达式可以在aop:aspect标签中定义

表达式说明:
结构:方法返回值类型 包名.类名.方法名(参数列表)
通配符:
..:通常表示方法可以有0~n个参数
*:表示任意内容
-->
<aop:pointcut id="pointcut" expression="execution(* edu.shifan.service.*.insert(..))"/>

<!-- 配置切面(通知)-->
<!--
aop:advisor:用于配置通知
advice-ref:配置需要进行使用(引用)的通知
pointcut-ref:配置需要进行使用的切点
-->
<aop:advisor advice-ref="log1" pointcut-ref="pointcut" />
</aop:config>
</beans>

Spring

Spring概述

String框架的核心技术
Spring是由Rod Johnson组织和开发的一个分层的Java SE/EE一站式(full-stack)轻量级开源框架。它最为核心的理念是loC(控制反转)和AOP(面向切面编程),其中,loC是Spring的基础,它支撑着Spring对JavaBean的管理功能;AOP是Spring的重要特性,AOP是通过预编译方式和运行期间动态代理实现程序功能,也就是说可以在不修改源代码的情况下,给程序统─添加功能。

Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以loC( lnverse OfControl:反转控制)和AOP ( Aspect Oriented Programming :面向切面编程)为内核

提供了展现层SpringMVC和持久层Spring JDBCTemplate以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的JavaEE企业应用开源框架.

Spring的体系结构

Spring的下载及目录结构

Spring的入门程序

下面通过一个简单的入门程序演示Spring框架的使用,要求在控制台打印“张三,欢迎来到Spring”实现步骤具体如下。

  • STEP 01
    在IDEA中创建名称为chapter06的Maven项目,然后在pom.xml文件中加载需使用到的Spring四个基础包以及Spring依赖包。

    org.springframework
    spring-expression
    5.2.8.RELEASE

    commons-logging
    commons-logging
    1.2

  • STEP 02
    在chapter06项目的src/main/java目录下中创建com.itheima包,并在该包下创建名为HelloSpring的类。在HelloSpring类中定义userName属性和show()方法。
    package com.itheima;
    public class HelloSpring {private String userName;
    public void setUserName(String userName){this.userName=userName;}
    public void show() {
    System.out.printIn(userName+”:欢迎来到Spring”);}
    }

  • STEP 03
    在chapter06项目的src/main/resources目录下新建applicationContext.xml文件作为HelloSpring类的配置文件,并在该配置文件中创建id为helloSpring的Bean。

  • STEP 04
    在chapter06项目的com.itheima文件夹下创建测试类TestHelloSpring,在main()方法中初始化Spring容器并加载applicationContext.xml配置文件,通过Spring容器获取HelloSpring类的helloSpring实例,调用HelloSpring类中的show)方法在控制台输出信息。
    public class TestHelloSpring {
    public static void main(String[] args){
    /初始化spring容器,加载applicationContext.xml配置ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);/通过容器获取配置中helloSpring的实例
    HelloSpring helloSpring=
    (HelloSpring)applicationContext.getBean(“helloSpring”);helloSpring.show();//调用方法}
    }

  • STEP 05
    在IDEA中启动测试类TestHelloSpring,控制台会输出结果。

控制反转的概念

依赖注入的概念

什么是依赖注入
依赖注入(Dependency Inject,缩写DI)就是由loC容器在运行期间动态地将某种依赖资源注入对象之中。例如,将对象B注入(赋值)给对象A的成员变量。依赖注入的基本思想是:明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行。

依赖注入和控制反转的比较
依赖注入(DI)和控制反转(loC)是从不同角度来描述了同一件事情。依赖注入是从应用程序的角度描述,即应用程序依赖loC容器创建并注入它所需要的外部资源;而控制反转是从loC容器的角度描述,即loC容器控制应用程序,由loC容器反向地向应用程序注入应用程序所需要的外部资源。这里所说的外部资源可以是外部实例对象,也可以是外部文件对象等。

依赖注入的类型–构造方法注入

依赖注入的实现方式
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中。依赖注入通常有两种实现方式,一种是构造方法注入,另一种是属性setter方法注入。这两种实现方式具体介绍如下。

  1. 构造方法注入
    构造方法注入是指Spring容器调用构造方法注入被依赖的实例,构造方法可以是有参的或者是无参的。Spring在读取配置信息后,会通过反射方式调用实例的构造方法,如果是有参构造方法,可以在构造方法中传入所需的参数值,最后创建类对象。
    下面通过案例演示构造方法注入的实现,具体步骤如下。
  • STEP 01
    编写用户类:在项目chapter06的com.itheima包下新建User1类,在User1类中定义id、name和password三个属性。
    public class User1 {
    private int id; private Strilg name; private String password;public User1(int id, String name, String password){
    this.id=id;this.name=name;
    this.password=password; }
    public String toString(){
    return “id=”+id+”,name=”+name+”,password=”+password;
    }
    }
  • STEP 02
    获取Bean的配置信息:在chapter06项目的src文件夹下创建applicationContext-User.xml文件,在该文件中添加User1类的配置信息。

bean id=”user1” class=”com.itheima.User1”>


元素
一个元素表示构造方法的一个参数,且定义时不区分顺序,只需要通过元素的name属性指定参数即可。元素还提供了type属性类指定参数的类型,避免字符串和基本数据类型的混淆。

  • STEP 03
    编写测试类:在项目chapter06的com.itheima包下创建测试类TestUser1。
    public class TestUser1 {
    public static void main(String[] args)throws Exception{//加载applicationContext.xml配置
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext-User.xml”);
    //获取配置中的User1实例
    User1 user1,applicationContext.getBean(“user1”, User1.class);System.out.println(user1);
    }
    }

依赖注入的类型–属性setter方法注入

属性setter方法注入
属性setter方法注入是Spring最主流的注入方法,这种注入方法简单、直观,它是在被注入的类中声明一个setter方法,通过setter方法的参数注入对应的值。

下面通过案例演示属性setter方法注入的实现,具体步骤如下。

  • STEP 01
    编写用户类:在项目chapter06的com.itheima包下新建User2类,在User2类中定义id、name和password三个属性。
    public class User2 {
    private int id; private String name;private String password;
    省略getter/setter方法public String toStringo{
    return “id=”+id+”,name=”+name+ “,password=”+password;
    }
    }

  • STEP 02
    获取Bean的配置信息:在chapter06项目的src文件夹下创建applicationContext-User2.xml文件,并在该文件的bean元素中添加User2类的配置信息。
    bean id=”user2” class=”com.itheima.User2”>

  • STEP 03
    编写测试类:在项目chapter06的com.itheima包下创建测试类TestUser2。
    public class TestUser2{
    public static void main(String[]args)throws Exception {//加载applicationContext.xml配置
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(“applicationContext-User2.xml”);//获取配置中的User2实例
    User2 user2 = applicationContext.getBean(“user2”, User2.class);System.out.printIn(user2);
    }
    }

依赖注入的应用

下面以属性setter方法注入为例,实现一个简单的登录验证。具体实现步骤如下所示。

  • STEP 01
    (1)编写DAO层:在项目chapter06的com.itheima包下新建dao包,在dao包下创建接口UserDao.java,在UserDao.java接口中添加方法login),用于实现登录功能。
    public interface UserDao {
    public boolean login(String name,String password);}

(2)编写DAO层的实现类:在com.itheima.dao包下创建impl包,在impl包下创建UserDao接口的实现类UserDaolmpl,在UserDaolmpl类中实现login()方法。
public class UserDaolmpl implements UserDao {
@Override
public boolean login(String name, String password) {if (name.equals(“张三”)&&password.equals( “123”)){
return true;
}
return false;}
}

  • STEP 02
    (1)编写Service层:在项目chapter06的com.itheima包下新建service包,在service包下创建接口UserService.java,在接口中添加方法login().
    public interface UserService {
    public boolean login(String name,String password);}

(2)编写Service层实现类:在com.itheima.service包下创建impl包
在impl包下
创建UserService接口的实现类UserServicelmpl,在UserServicelmpl类中实现login()方法。
public class UserServicelmpl implements UserService {UserDao userDao;
public void setUserDao(UserDaouserDao){ this.userDao=userDao; }
@Override
public boolean login(String name, String password) {return userDao.login(name,password);}
}

  • STEP 03
    编写applicationContext.xml配置文件:使用元素添加创建的UserDaolmpl类和UserServicelmpl类的实例,并配置其相关属性。


  • STEP 04
    编写测试类:在com.itheima包中新建测试类TestSpring。
    public class TestSpring {
    public static void main(String[] args){//加载applicationContext.xml配置
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“ applicationContext.xml”);
    UserService userService=(UserService)//获取配置中的UserService实例applicationContext.getBean(“userService”);
    boolean flag =userService.login(“张三”,”123””);if (flag) { System.out.printIn(“登录成功”);
    }else { System.out.printIn(“登录失败”);}
    }
    }

BeanFactory

BeanFactory接口实例的语法格式
Spring提供了几个BeanFactory接口的实现类,其中最常用的是XmIBeanFactory,它可以读取XML文件并根据XML文件中的配置信息生成BeanFactory接口的实例,BeanFactory接口的实例用于管理Bean。XmlBeanFactory类读取XML文件生成BeanFactory接口实例的具体语法格式如下。
BeanFactory beanFactory=new XmlBeanFactory
(new FileSystemResource(“D:/bean.xml”));

ApplicationContext接口

ApplicationContext接口的特点
ApplicationContext接口建立在BeanFactory接口的基础之上,它丰富了BeanFactory接口的特性,例如,添加了对国际化、资源访问、事件传播等方面的支持。
ApplicationContext接口可以为单例的Bean实行预初始化,并根据元素执行setter方法,单例的Bean可以直接使用,提升了程序获取Bean实例的性能。

Bean的配置

Spring容器所支持的配置文件格式
Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常用的是XML格式的配置文件。XML是标准的数据传输和存储格式,方便查看和操作数据。在Spring中,XML配置文件的根元素是, 元素包含子元素,每个
子元素可以定义一个Bean,通过元素将Bean注册到Spring容器中。

普通的Bean通常只需定义id(或者name)和class两个属性

构造方法实例化

下面通过一个案例演示Spring容器如何通过构造方法实例化Bean。
在IDEA中创建一个名为chapter07的Maven项目,然后在项目的pom.xml文件中配置需使用

  • STEP 01
    到的Spring四个基础包和Spring的依赖包。
    <-这里只展示了一个依赖包x-> org.springframeworkspring-expression5.2.8.RELEASE

在chapter07项目的src/main/java目录下,创建一个名称为com.itheima的包,在该包中创

  • STEP 02
    建Bean1类。
    package com.itheima;public class Bean1
    public Bean1){
    System.out.println(“这是Bean1”);
    }

在chapter07项目的src/main/resources目录下新建applicationBean1.xml作为Bean1类的

  • STEP 03
    配置文件,在该配置文件中定义一个id为bean1的Bean,并通过class属性指定其对应的实现类为Bean1。

在chapter07项目的com.itheima包中创建测试类Bean1Test,在main()方法中通过加载

  • STEP 04
    applicationBean1.xml配置文件初始化Spring容器,再通过Spring容器生成Bean1类的实例bean1,用来测试构造方法是否能实例化Bean1。
    public class Bean1Test {
    public static void main(String[] args){//加载applicationBean1.xml配置
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationBean1.xml”);通过容器获取配置中bean1的实例
    Bean1 bean=(Bean1) applicationContext.getBean(“bean1”);System.out.print(bean);}
    }

  • STEP 05 在IDEA中启动Bean1Test类,控制台会输出结果。

静态工厂实例化

下面通过一个案例演示如何使用静态工厂方式实例化Bean。
在chapter07项目的com.itheima包中创建Bean2类,该类与Bean1类一样,只定义一个构造方法,不需额外添加任何方法。

  • STEP 01
    package com.itheima;
    public class Bean2 {public Bean2(){
    System.out.println(“这是Bean2”);}
    }

在com.itheima包中,创建一个MyBean2Factory类,在该类中定义一个静态方法

  • STEP 02
    createBean(),用于创建Bean的实例。createBean()方法返回Bean2实例。
    package com.itheima;
    public class MyBean2Factory {
    //使用MyBean2Factory类的工厂创建Bean2实例public static Bean2 createBean(){
    return new Bean2();
    }
    }

在chapter07项目的src/main/resources目录下新建applicationBean2.xml文件,作为

  • STEP 03
    MyBean2Factory类的配置文件。 <bean id=”bean2”
    class=”com.itheima.MyBean2Factory”factory-method=”createBean”/>

在com.itheima包中,创建一个测试类Bean2Test,用于测试使用静态工厂方式是否能实例化Bean。

  • STEP 04
    public class Bean2Test {
    public static void main(String[] args){
    llApplicationContext在加载配置文件时,对Bean进行实例化ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext(“ applicationBean2.xml”);System.out.printIn(applicationContext.getBean(“ bean2”));}
    }

实例工厂实例化

下面通过一个案例演示如何使用实例工厂方式实例化Bean.在chapter07项目的com.itheima包中创建Bean3类,该类与Bean1一样,不需添加任何方法。

  • STEP 01
    package com.itheima;public class Bean3 {public Bean3(){
    System.out.println(“这是Bean3”);}
    }

在com.itheima包中,创建一个MyBean3Factory类,在该类中定义无参构造方法,并定义createBean()方法用于创建Bean3对象。

  • STEP 02
    package com.itheima;
    public class MyBean3Factory {public MyBean3Factory0 {
    System.out.printIn(“bean3工厂实例化中”);
    public Bean3 createBean() //创建Bean3实例的方法return new Bean3();}
    }

在chapter07项目的src/main/resources目录下新建applicationBean3.xml文件,作为MyBean3Factory类的配置文件。

  • STEP 03

在com.itheima包中,创建一个测试类BeanTest3,用于测试使用实例化工厂方式是否能实例化Bean。

  • STEP 04
    public class Bean3Test {
    public static void main(String[] args){
    ll ApplicationContext在加载配置文件时,对Bean进行实例化ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext(“applicationBean3.xml”);System.out.printIn(applicationContext.getBean(“bean3”));}
    }

singleton作用域


下面将通过案例的方式演示Spring容器中singleton作用域的使用。将id为bean1的作用域设置为singleton。

  • STEP 01

在chapter07项目的com.itheima包中创建测试类scopeTest,在main()方法中通过加载

  • STEP 02
    applicationBean1.xml配置文件初始化Spring容器,通过Spring容器获取Bean1类的两个实例,判断两个实例是否为同一个。
    public class scopeTest{
    public static void main(String[] args){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationBean1.xml”);Bean1 bean1_1=(Bean1) applicationContext.getBean(“bean1”);Bean1 bean1_2=(Bean1) applicationContext.getBean(“bean1”);System.out.print(bean1_1==bean1_2);}
    }

  • STEP 03 在IDEA中启动scopeTest类,控制台会输出运行结果。

prototype作用域

prototype作用域的使用
在7.4.1节的基础上修改配置文件,将id为bean1的作用域设置为prototype。

基于XML的装配

两种基于XML的装配方式
在基于XML的装配就是读取XML配置文件中的信息完成依赖注入,Spring容器提供了两种基于XML的装配方式,属性setter方法注入和构造方法注入。下面分别对这两种装配方式进行介绍。

a.属性setter方法注入
属性setter方法注入要求一个Bean必须满足以下两点要求。
(1) Bean类必须提供一个默认的无参构造方法。
(2) Bean类必须为需要注入的属性提供对应的setter方法。
b.构造方法注入
使用构造方法注入时,在配置文件里,需要使用元素的子元素来定义构造方法的参数,例如,可以使用其value属性(或子元素)来设置该参数的值。

基于注解的装配


下面通过案例演示如何使用注解来装配Bean,具体实现步骤如下。
导入依赖:在项目chapter07的pom.xml文件中导入spring-aop-5.2.8.RELEASE.jar依赖包,导入代码如下所示。

  • STEP 01 org.springframeworkspring-aop 5.2.8.RELEASE

XML配置文件:在chapter07项目的src\main\resources目录下创建

  • STEP 02
    applicationContext.xml,在该文件中引入Context约束并启动Bean的自动扫描功能。

    <context:component-scan base-package=”com.itheima” />

定义实体类:在项目chapter07的com.itheima包下新建entity包,在entity包下创建User实体类。

  • STEP 03
    @Component(“user”)
    @Scope(“singleton”)
    public class User {
    @Value(“1”)
    private int id;
    @Value(“张三”)
    private String name;
    @Value(“123”)
    private String password;
    省略getter/setter方法和toString()方法}

定义dao层:在chapter07项目的com.itheima包下创建dao包,在dao包下创建UserDao接口作为数据访问层接口,并在UserDao接口中声明save()方法,用于查询User实体的对象信息。

  • STEP 04
    package com.itheima.dao;public interface UserDao {
    public void save();
    }

实现dao层: chapter07项目的com.itheima.dao包下创建UserDaolmpl作为UserDao的实现类,并在UserDaolmpl类中实现UserDao接口中的save()方法。

  • STEP 05
    @Repository(“userDao”)
    public class UserDaolmpl implements UserDao {
    public void save(){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    User user=(User) applicationContext.getBean(“user”);
    System.out.println(user);
    System.out.println(“执行UserDaolmpl.save()”);}
    }

定义service层:在chapter07项目的com.itheima包下新建service包,在service包下创建UserService接口作为业务逻辑层接口,并在UserService接口中定义save()方法。

  • STEP 06
    package com.itheima.service;
    public interface UserService {
    public void save();
    }

实现service层:在chapter07项目的com.itheima.service包下创建UserServicelmpl作为

  • STEP 07
    UserService的实现类,并在UserServicelmpl类中实现UserService接口中的save()方法。
    @Service(“userService”)
    public class UserServicelmpl implements UserService {
    //使用@Resource注解注入UserDao
    Resource(name=”userDao”)
    private UserDao userDao;
    public void save(){
    this.userDao.save();
    System.out.println(“执行UserServicelmpl.save()”);}
    }

定义controller层: 在chapter07项目的com.itheima的包下新建controller包,在该包下创建UserController类作为控制层。

  • STEP 08
    @Controller
    public class UserController {
    //使用@Resource注解注入UserService
    @Resource(name=”userService”)
    private UserService userService;public void save(){
    this.userService.save();
    System.out.println(“执行UserController.save()”);}
    }

定义测试类:在chapter07项目的com.itheima的包下创建测试类AnnotationTest,在该类中编写测试代码,通过Spring容器加载配置文件并获取UserController实例,然后调用实例中的save()方法。

  • STEP 09
    public class AnnotationTest {
    public static void main(String[] args){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    UserController usercontroller=(UserController)
    applicationContext.getBean(“userController”);
    usercontroller.save();}
    }

自动装配

Bean的生命周期

Bean在不同作用域内的生命周期
Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中,Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中,Spring容器可以管理Bean的生命周期,控制着Bean的创建、初始化和销毁。在prototype作用域中,Spring容器只负责创建Bean实例,不会管理其生命周期。

监控时间节点的方式
监控两个节点的方式有两种,一种是使用XML配置文件,一种是使用注解。
Spring容器提供了@PostConstruct用于监控Bean对象初始化节点,
提供了@PreDestroy用于监控Bean对象销毁节点。下面通过案例演示这两个注解的使用。

在chapter07项目的com.itheima包下创建Student类,在类中定义id和name字段,并使用@PostConstruct指定初始化方法,使用@PreDestroy指定Bean销毁前的方法。

  • STEP 01
    Component(“student”)
    public class Student {@Value(“1”)
    private String id;@Value(“张三”)
    private String name;/省略getter/setter方法,以及toString()方法postConstruct
    public void init(){System.out.printIn(“Bean的初始化完成,调用init()方法”);}@PreDestroy
    public void destroy(){System.out.printIn(“Bean销毁前调用destroy()方法”);}
    }

在chapter07项目的src\main\resources目录下创建applicationStudent.xml,在该文件中引入Context约束并启动Bean的自动扫描功能。

在chapter07项目的com.itheima的包下创建测试类StudentTest,在该类中编写测试代码,通过Spring容器加载配置文件并获取Student实例。

  • STEP 03
    public class StudentTest {
    public static void main(Stringargs){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationStudent.xml”);Student student=(Student)applicationContext.getBean(“student”);System.out.printIn(student);
    //销毁Spring容器中的所有Bean
    AbstractApplicationContext ac=(AbstractApplicationContext)
    applicationContext;
    ac.registerShutdownHook();}
    }

Spring AOP概述

AOP概述
AOP的全称是Aspect Oriented Programming,即面向切面编程。和OOP不同,AOP主张将程序中相同的业务逻辑进行横向隔离,并将重复的业务逻辑抽取到一个独立的模块中,以达到提高程序可重用性和开发效率的目的。
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。

Spring AOP术语

AOP术语
AOP并不是一个新的概念,AOP中涉及很多术语,如切面、连接点、切入点、通知/增强处理、目标对象、织入、代理和引介等,下面针对AOP的常用术语进行简单介绍。
切面(Aspect)
切面是指关注点形成的类(关注点是指类中重复的代码),通常是指封装的、用于横向插入系统的功能类(如事务管理、日志记录等)。在实际开发中,该类被Spring容器识别为切面,需要在配置文件中通过元素指定。

JDK动态代理

Spring AOP的默认代理方式
默认情况下,Spring AOP使用JDK动态代理,JDK动态代理是通过
java.lang.reflect.Proxy类实现的,可以调用Proxy类的newProxyInstance()方法创建代理对象。JDK动态代理可以实现无侵入式的代码扩展,并且可以在不修改源代码的情况下,增强某些方法。

接下来,通过一个案例演示Spring中JDK动态代理的实现过程,案例具体实现步骤如下。

  • STEP 01
    在IDEA中创建一个名为chapter08的Maven项目,然后在项目的pom.xml文件中加载需使用到的Spring基础包和Spring的依赖包。

在项目的src/main/java目录下,创建一个com.itheima.demo01包,在该包下创建接口UserDao,在UserDao接口中编写添加和删除的方法。

  • STEP 02
    package com.itheima.demo01;
    .. . .十.
    public interface ‘UserDao {public void addUser();public void deleteUser();}

在com.itheima.demo1包中,创建UserDao接口的实现类UserDaolmpl,分别实现接口中的方法。

  • STEP 03
    package com.itheima.demo01;
    public class UserDaolmpl implements UserDao {
    public void addUser() {
    System.out.println(“添加用户”);}public void deleteUser() {
    System.out.println(“删除用户”);}
    }

在com.itheima.demo01包下创建切面类MyAspect,在该类中定义一个模拟权限检查的方法和一个模拟日志记录的方法,这两个方法就是切面中的通知。

  • STEP 04
    package com.itheima.demo01;
    public class MyAspect {//切面类:存在多个通知Advice(增强的方法)public void check_Permissions(){
    System.out.println(“模拟检查权限…”);
    }
    public void log(){
    System.out.println(“模拟记录日志..”);}
    }

在com.itheima.demo01包下创建代理类MyProxy,该类需要实现InvocationHandler接口设置代理类的调用处理程序。在代理类中,通过newProxyInstance()生成代理方法。

  • STEP 05
    public class MyProxy implements lnvocationHandler {
    private UserDao userDao;
    public Object createProxy(UserDao userDao) {
    this.userDao = userDao;
    ClassLoader classLoader = MyProxy.class.getClassLoader(); l/1.类加载器
    Classclasses = userDao.getClass().getInterfaces();// 2.被代理对象实现的所有接口
    return Proxy.newProxyInstance(classLoader,classes,this);// 3.返回代理对象

/所有动态代理类的方法调用,都会交由invoke()方法去处理。这里省略invoke()方法}
}

newProxyInstance()方法的3个参数
第1个参数是classLoader,表示当前类的类加载器。
第2个参数是classes,表示被代理对象实现的所有接口。
第3个参数是this,表示代理类JdkProxy本身。

在com.itheima.demo01包中,创建测试类JDKTest。在该类中的main()方法中创建代理对象jdkProxy和目标对象userDao,然后从代理对象jdkProxy中获得对目标对象userDao增强后的对象userDao1,最后调用userDao1对象中的添加和删除方法。

  • STEP 06
    public class JDKTest {
    public static void main(String[] args) {
    MyProxy jdkProxy = new MyProxy;//创建代理对象UserDao userDao = new UserDaolmplO;/创建目标对象//从代理对象中获取增强后的目标对象
    UserDao userDao1 = (UserDao)jdkProxycreateProxy(userDao);/执行方法
    userDao1.addUser();userDao1.deleteUser();}
    }

CGLib动态代理

JDK与CGLib动态代理的比较
JDK动态代理存在缺陷,它只能为接口创建代理对象,当需要为类创建代理对象时,就需要使用CGLib (Code Generation Library)动态代理,CGLib动态代理不要求目标类实现接口,它采用底层的字节码技术,通过继承的方式动态创建代理对象。Spring的核心包已经集成了CGLib所需要的包,所以开发中不需要另外导入JAR包。

接下来通过一个案例演示CGLib动态代理的实现过程,具体步骤如下。
在chapter08项目的src/main/java目录下创建一个com.itheima.demo02包,在该包下创建目标类UserDao,在该类中编写添加用户和删除用户的方法。

  • STEP 01
    package com.itheima.demo02;
    public class UserDao {
    public void addUser(){
    System.out.println(“添加用户”);}public void deleteUser(){
    System.out.println(“删除用户”);}
    }

在com.itheima.demo02包下创建代理类CglibProxy,该代理类需要实现MethodInterceptor接口用于设置代理类的调用处理程序,并实现接口中的intercept()方法。
STEP 02
public class CglibProxy implements MethodInterceptor {
public Object createProxy(Object target){//代理方法
Enhancer enhancer = new Enhancer();//创建一个动态类对象
enhancer.setSuperclass(target.getClass());//确定需要增强的类,设置其父类enhancer.setCallback(this);//添加回调函数
return enhancer.create();//返回创建的代理类
// intercept()方法省略}
}

在com.itheima.demo02包中创建测试类CglibTest,在main()方法中首先创建代理对象cglibProxy和目标对象userDao,然后从代理对象cglibProxy中获得增强后的目标对象userDao1,最后调用userDao1对象的添加和删除方法。

  • STEP 03
    public class CglibTest {
    public static void main(String[] args) {
    CglibProxy cglibProxy = new CglibProxy();//创建代理对象UserDao userDao = new UserDao();//创建目标对象
    /获取增强后的目标对象
    UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);//执行方法
    userDao1.addUser();userDao1.deleteUser();}
    }

基于XML的AOP实现

使用AOP代理对象的好处
因为Spring AOP中的代理对象由loC容器自动生成,所以开发者无须过多关注代理对象生成的过程,只需选择连接点、创建切面、定义切点并在XML文件中添加配置信息即可。Spring提供了一系列配置Spring AOP的XML元素。

Aop配置:在 1.切面 中配置 2.切入点和3.增强的 4.关系

  1. 配置切面
    在Spring的配置文件中,配置切面使用的是aop:aspect元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,因此,在使用aop:aspect元素之前,要在配置文件中先定义一个普通的Spring Bean。Spring Bean定义完成后,通过aop:aspect元素的ref属性即可引用该Bean。配置aop:aspect元素时,通常会指定id和ref两个属性。

aop:aspect元素的id属性和ref属性的描述
属性名称 描述
id 用于定义该切面的唯一标识
ref 用于引用普通的Spring Bean

  1. 配置切入点
    在Spring的配置文件中,切入点是通过aop:pointcut元素来定义的。当
    aop:pointcut元素作为aop:config元素的子元素定义时,表示该切入点是全局的,它可被多个切面共享;当aop:pointcut元素作为aop:aspect元素的子元素时,表示该切入点只对当前切面有效。定义aop:pointcut元素时,通常会指定id、expression属性。

Spring AOP切入点表达式的基本格式
execution(modifiers-pattern ret-type-patterndeclaring-type-pattern?
name-patternbaram-patterr throws-pattern?)

  1. 配置通知
    在Spring的配置文件中,使用aop:aspect元素配置了5种常用通知,分别为前置通知、后置通知、环绕通知、返回通知和异常通知。
  • STEP 01
    接下来通过一个案例演示如何在Spring中使用XML实现Spring AOP,具体实现步骤如下。
    在chapter08项目的pom.xml文件中导入AspectJ框架的相关JAR包。 org.aspectjaspectjrt1.9.1 org.aspectjaspectjweaver1.9.6

在chapter08项目的src/main/java目录下创建一个com.itheima.demo03包,在该包下创建接口UserDao,并在该接口中编写添加、删除、修改和查询的方法。

  • STEP 02
    package com.itheima.demo03;
    public interface UserDao {
    public void insert();
    public void delete();
    public void update();
    public void select();
    }

在com.itheima.demo03包下创建UserDao接口的实现类UserDaolmpl,实现UserDao接口中的方法。

  • STEP 03
    public class UserDaolmpl implements UserDao{public void insert() {
    System.out.println(“添加用户信息”);}public void delete(){
    System.out.println(“删除用户信息”);}public void update() {
    System.out.println(“更新用户信息”);}public void select() {
    System.out.println(“”查询用户信息”);}
    }

在com.itheima.demo03包下创建XmlAdvice类,用于定义通知。

  • STEP 04

public class XmlAdvice {
//前置通知
public void before(JoinPoint joinPoint){
System.out.print(“这是前置通知!”);
System.out.print(“目标类是: “+joinPoint.getTarget());System.out.println(“,被织入增强处理的目标方法为:”+
joinPoint.getSignature().getName();
}
/其他通知省略:返回通知、环绕通知、异常通知、后置通知}

在chapter08项目的resource文件夹下创建applicationContext.xml文件,在该文件中引入AOP命名空间,使用元素添加Spring AOP的配置信息。

  • STEP 05 <aop:aspect ref =”xmlAdvice”>
    <aop:before method=”before” pointcut-ref=”pointcut” /><aop:after-returning method=”afterReturning” pointcut-ref=”pointcut” /><aop:around method=”around” pointcut-ref=”pointcut” />–指定环绕方式–><aop:after-throwing method=”afterException”pointcut-ref=”pointcut” /><aop:after method=”after” pointcut-ref=”pointcut” />

在com.itheima.demo03包中创建测试类TestXml。

  • STEP O6
    public class TestXml{
    public static void main(String[] args){ApplicationContext context=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    UserDao userDao=context.getBean(“userDao”,UserDao.class);
    userDao.delete();
    System.out.printIn();
    userDao.insert();
    System.out.printIn();
    userDao.select();
    System.out.printIn();
    userDao.update();}
    }

基于注解的AOP实现

下面通过一个案例演示基于注解的AOP的实现,案例具体实现步骤如下。
在chapter08项目的src/main/java目录下创建一个com.itheima.demo04包,在该包下创建AnnoAdvice类,用于定义通知。

  • STEP 01
    @Aspect
    public class AnnoAdvice {/*
    @Pointcut(“execution( * com.itheima.demo03.UserDaolmpl.(..))”)Before(“ poincut0”)
    @AfterReturning(“poincut()”)
    @Around(“poincut()”)
    @AfterThrowing(“poincut()”)
    @After(“poincut()”)
    使用以上注解分别定义切点、前置通知、返回通知、环绕通知、异常通知、后置通知
    /}

在chapter08项目的resource文件夹下创建applicationContext-Anno.xml文件,在该文件中引入AOP命名空间,使用元素添加Spring AOP的配置信息。

JdbcTemplate概述

数据库用于处理持久化业务产生的数据,应用程序在运行过程中经常要操作数据库。一般情况下,数据库的操作由持久层来实现。作为扩展性较强的一站式开发框架,Spring也提供了持久层Spring JDBC功能,Spring JDBC可以管理数据库连接资源,简化传统JDBC的操作,进而提升程序数据库操作的效率。本章将对Spring JDBC相关知识进行详细讲解。

JDBCTemplate作用
针对数据库操作,Spring框架提供了JdbcTemplate类,JdbcTemplate是一个模板类,Spring JDBC中的更高层次的抽象类均在JdbcTemplate模板类的基础上创建。
JdbcTemplate类提供了操作数据库的基本方法,包括添加、删除、查询和更新。在操作数据库时,JdbcTemplate类简化了传统JDBC中的复杂步骤,这可以让开发人员将更多精力投入到业务逻辑中。

抽象类JdbcAccessor的属性
JdbcTemplate类继承自抽象类JdbcAccessor,同时实现了JdbcOperations接口。抽象类JdbcAccessor提供了一些访问数据库时使用的公共属性,具体如下:
. DataSource: DataSource主要功能是获取数据库连接。在具体的数据操作中,它还提供对数据库连接的缓冲池和分布式事务的支持。
. SQLExceptionTranslator: SQLExceptionTranslator是一个接口,它负责对SQLException异常进行转译工作。

Spring JDBC的配置

Spring对数据库的操作都封装在core、dataSource、object和support这4个包,使用Spring JDBC,就要对这些包进行配置。在Spring中,JDBC的配置是在配置文件applicationContext.xml中完成的。

  • STEP 01
    配置数据源:包括数据库驱动、连接数据库url.
    连接数据库用户名、连接数据库密码。


  • STEP 02
    配置JDBC模板:必须使用默认数据源。

  • STEP 03
    配置注入类

dataSource属性值的设定要求
在dataSource的4个属性中,需要根据数据库类型或者系统配置设置相应的属性值。例如,如果数据库类型不同,需要更改驱动名称;如果数据库不在本地,则需要将地址中的localhost替换成相应的主机IlP;默认情况下,数据库端口号可以省略,但如果修改过MySQL数据库的端口号,则需要加上修改后的端口号。此外,连接数据库的用户名和密码需要与数据库创建时设置的用户名和密码保持一致。

excute()方法

execute()方法用于执行SQL语句,其语法格式如下:
jdTemplate.execute(“SQL语句”);

下面以创建数据表的SQL语句为例,来演示excute()方法的使用,具体步骤如下。

  • STEP 01
    创建数据库:在MySQL中,创建一个名为spring的数据库。

创建项目并引入依赖:在IDEA中创建一个名为chapter09的Maven项目,然后在pom.xml文件中加载使用到的Spring 基础包、Spring依赖包、MySQL数据库的驱动JAR包、Spring JDBC的JAR包和Spring事务处理的JAR包。

  • STEP 02
    <!-这里只展示了其中一个JAR包–> mysql mysql-connector-java5.1.47 runtime

编写配置文件:在chapter09项目的src/main/resources目录下,创建配置文件applicationContext.xml,在该文件中配置数据源Bean和JDBC模板Bean,并将数据源注入到JDBC模板中。

  • STEP 03

编写测试类:在src/main/java目录下创建一个com.itheima包,在该包中创建测试类TestJdbcTemplate,在该类的main()方法中获取JdbcTemplate实例,然后调用execute()方法执行创建数据表的SQL语句。

  • STEP 04
    public class TestdbcTemplate {
    public static void main(Stringl args){
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    JdbcTemplate jdTemplate =
    (JdbcTemplate) applicationContext.getBean(“jdbcTemplate”);
    jdTemplate.execute(“create table account(“+
    “id int primary key auto_increment,” +“username varchar(50),”+
    “balance double)”);
    System.out.println(“账户表account创建成功!”);}
    }

update()方法

下面通过一个案例演示update()方法的使用,该案例要求添加、更新、删除用户账户。案例具体实现步骤如下所示。
编写实体类:在chapter09项目的com.itheima包中,创建Account类,在该类中定义属性,以及其对应的getter/setter方法。

  • STEP 01
    public class Account {
    private Integer id;
    /账户id
    private String username;
    //用户名
    private Double balance;
    账户余额
    /省略gettter/setter方法
    public String toString0 {
    return “Account [id=” +id + “””
  • “username=” + username + “, balance=” + balance + “]”;}
    }

编写Dao层接口:在com.itheima包中,创建接口AccountDao,并在接口中定义添加、更新和删除账户的方法。

  • STEP 02
    public interface AccountDao {
    //添加
    public int addAccount(Account account);更新
    public int updateAccount(Account account);//删除
    public int deleteAccount(int id);
    }

实现Dao层接口:在com.itheima包中,创建AccountDao接口的实现类AccountDaolmpl,并在类中实现添加、更新和删除账户的方法。

  • STEP 03
    public class AccountDaolmpl implements AccountDao {
    定义JdbcTemplate属性,此处省略setter方法
    private JdbcTemplate jdbcTemplate;
    这里只展示(添加账户)的操作
    public int addAccount(Account account) {
    String sql = “insert into account(username,balance) value(?,?)”;Object[] obj = new Object[]/定义数组来存放SQL语句中的参数
    account.getUsername(), account.getBalance()
    };
    //执行添加操作,返回的是受SQL语句影响的记录条数
    return this.jdbcTemplate.update(sql, obj);
    }

编写配置文件:在applicationContext.xml中,定义一个id为accountDao的Bean用于将jdbcTemplate注入到accountDao实例中。

  • STEP 04

测试添加功能:在com.itheima包中创建测试类TestAddAccount,该类主要用于添加用户账户信息。

  • STEP 05
    public class TestAddAcount {
    public static void main(String[] args){
    ApplicationContext applicationContext =new
    ClassPathXmlApplicationContext(“applicationContext.xml”);AccountDao accountDao =
    (AccountDao) applicationContext.getBean(“accountDao”);Account account = new Account();
    account.setUsername(“tom”); account.setBalance(1000.00);int num = accountDao.addAccount(account);
    if (num > 0) { System.out.println(“成功插入了” + num +”条数据!”);} else { System.out.printIn(“插入操作执行失败!“);}
    }
    }

  • STEP 06
    查看执行第5步后的运行结果:在IDEA中启动TestAddAccount类,控制台会输出结果。此时再次查询spring数据库中的account表。

query()方法


了解了JdbcTemplate类中几个常用的query()方法后,接下来通过一个具体的案例演示query()方法的使用,案例实现步骤如下。插入数据:向数据表account中插入几条数据。

  • STEP 01
    uSE ‘spring;
    DROP TABLE IF EXISTSaccount;CREATE TABLE account( id int(11) NOT NULL AUTO_INCREMENT,'username' varchar(50)DEFAULT NULL,balance double DEFAULT NULL,
    PRIMARY KEY (id)
    )ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;insert into ‘account`(id ; ‘username , balance ) values
    (1;’zhangsan’,100),(3,’lisi’ ,500),(4,’wangwu’,300);

编写查询方法:在前面的AccountDao接口中,声明findAccountByld()方法,通过id查询单个账户信息;声明findAllAccount()方法,用于查询所有账户信息。

  • STEP 02
    //通过id查询
    public Account findAccountByld(int id);查询所有帐户b
    public List findAllAccount();

实现查询方法:在前面的的AccountDaolmpl类中,实现AccountDao接口中的findAccountByld()方法和findAllAccount()方法,并调用query()方法分别进行查询。

  • STEP 03
    /这里只展示了其中一个方法,通过id查询单个账户信息public Account findAccountByld(int id){
    //定义SQL语句
    String sql = “select * from account where id = ?”;/创建一个新的BeanPropertyRowMapper对象RowMapper rowMapper
    new BeanPropertyRowMapper(Account.class);/将id绑定到SQL语句中,通过RowMapper返回Object类型的单行记录return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
    (1Ee2 , 1239)ercrr
    }

测试条件查询:在com.itheima包中创建测试类FindAccountByldTest,用于测试条件查询。
STEP 04
public class FindAccountByldTest {public static void main(String[] args){//加载配置文件
ApplicationContext applicationContext =new
ClassPathXmlApplicationContext(“applicationContext.xml”);//获取AccountDao实例
AccountDao accountDao =
(AccountDao) applicationContext.getBean(“accountDao”);Account account = accountDao.findAccountByld(1);
System.out.println(account); }
}

测试查询所有用户信息:在com.itheima包中创建测试类FindAllAccountTest,用于查询所有用户账户信息。

  • STEP 05

public class FindAllAccountTest {
public static void main(String[]args) {//加载配置文件
ApplicationContext applicationContext =new
ClassPathXmlApplicationContext(“applicationContext.xml”);/获取AccountDao实例
AccountDao accountDao =
(AccountDao) applicationContext.getBean(“accountDao”);List°account = accountDao.findAllAccount();//执行方法for (Account act : account){//循环输出集合中的对象
System.out.println(act); }
}
}

事务管理的核心接口

spring-tx-5.2.8.RELEAS依赖包的3个接口
PlatformTransactionManager接口:可以根据属性管理事务。
TransactionDefinition接口:用于定义事务的属性。
TransactionStatus接口:用于界定事务的状态。



事务的传播行为
事务的传播行为是指处于不同事务中的方法在相互调用时,方法执行期间,事务的维护情况。例如,当一个事务的方法B调用另一个事务的方法A时,可以规定A方法继续在B方法所属的现有事务中运行,也可以规定A方法开启一个新事务,在新事务中运行,B方法所属的现有事务先挂起,等A方法的新事务执行完毕后再恢复。

事务的超时时间
事务的超时时间是指事务执行的时间界限,超过这个时间界限,事务将会回滚。TransactionDefinition接口提供了TIMEOUT_DEFAULT常量定义事务的超时时间。

事务是否只读
当事务为只读时,该事务不修改任何数据,只读事务有助于提升性能,如果在只读事务中修改数据,会引发异常。
TransactionDefinition接口中除了提供事务的隔离级别、事务的传播行为、事务的超时时间和是否为只读事务的常量外,还提供了一系列方法来获取事务的属性。

事务管理的方式

Spring中的事务管理的两种方式
Spring中的事务管理分为两种方式,一种是传统的编程式事务管理,另一种是声明式事务管理。

  • 编程式事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
  • 声明式事务管理:通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码植入到业务目标类中。

基于XML方式的声明式事务

如何实现XML方式的声明式事务
基于XML方式的声明式事务管理是通过在配置文件中配置事务规则的相关声明来实现的。在使用XML文件配置声明式事务管理时,首先要引入tx命名空间,在引入tx命名空间之后,可以使用tx:advice元素来配置事务管理的通知,进而通过Spring AOP实现事务管理。
配置tx:advice元素时,通常需要指定id和transaction-manager属性,其中,id属性是配置文件中的唯一标识,transaction-manager属性用于指定事务管理器。除此之外,tx:advice元素还包含子元素<tx: attributes>,tx:attributes元素可配置多个
tx:method子元素,tx:method子元素主要用于配置事务的属性。

接下来通过一个案例演示如何通过XML方式实现Spring的声明式事务管理。本案例以9.2小节的项目代码和数据表为基础,编写一个模拟银行转账的程序,要求在转账时通过Spring对事务进行控制。案例具体实现步骤如下。

  • STEP 01
    导入依赖:在chapter09项目的pom.xml文件中加入aspectjweaver依赖包和aopalliance依赖包作为实现切面所需的依赖包。 org.aspectjaspectjweaver1.9.6 runtime aopallianceaopalliance1.0

定义Dao层方法:在com.itheima包的AccountDao接口中声明转账方法transfer()。

  • STEP 02
    /转账
    public void transter(String ouo’b
    String inUser,
    Double money);

实现Dao层方法:在com.itheima包的AccountDaolmpl实现类中实现AccountDao接口中的transfer()方法。

  • STEP 03
    //转账inUser:收款人; outUser:汇款人; money:收款金额public void transfer(String outUser, String inUser, Double money){/收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update(“update account set balance = balance +? “
  • “where username = ?”,money, inUser);
    /模拟系统运行时的突发性问题
    int i = 1/0;
    汇款时,汇款用户的余额=现有余额-所汇金额
    thisjdbcTemplate.update(“update account set balance = balance-? “
  • “where username = ?”,money, outUser);

修改配置文件:修改chapter09项目的配置文件applicationContext.xml,添加命名空间等相关配置代码。

测试系统:在chapter09项目的com.itheima包中创建测试类TransactionTest。

  • STEP 05
    public class TransactionTest {
    public static void main(String[] args) {ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext(“applicationContext.xml”);获取AccountDao实例
    AccountDao accountDao =
    (AccountDao)applicationContext.getBean(“accountDao”);/调用实例中的转账方法
    accountDao.transfer(“lisi”, “zhangsan”,100.0);//输出提示信息
    System.out.println(“转账成功!”);}
    }

  • 未使用事物管理的缺陷
    由XML方式实现声明式事务管理的案例可知,zhangsan的账户余额增加了100,而lisi的账户确没有任何变化,这样的情况显然是不合理的。这就是没有事务管理,系统无法保证数据的安全性与一致性,下面使用事务管理解决该问题。

基于注解方式的声明式服务

接下来对上一小节的案例进行修改,以注解方式来实现项目中的事务管理,具体实现步骤如下。
创建配置文件:在chapter09项目的src/main/resources目录下,创建配置文件applicationContext-annotation.xml,在该文件中声明事务管理器等配置信息。

  • STEP 01
    <!-前四步省略–> <tx:annotation-driven transaction-manager=”transactionManager” />

修改Dao层实现类:在AccountDaolmpl类的transfer()方法上添加事务注解Transactional。

  • STEP 02
    @Transactional(propagation = Propagation.REQUIRED,
    isolation = lsolation.DEFAULT, readOnly = false)
    public void trassfer(String outUser, String inUser,Double money) {//收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update(“update account set balance = balance +? “
  • “where username = ?” ,money, inUser);
    /模拟系统运行时的突发性问题
    int i = 1/0;
    /汇款时,汇款用户的余额=现有余额-所汇金额
    this.jdbcTemplate.update(“update account set balance = balance-? “
    +”where username = ?”,money, outUser);
    }

编写测试类:在chapter09项目的com.itheima包中创建测试类AnnotationTest。

  • STEP 03
    public class AnnotationTest {
    public static void main(Stringl[] args){
    ApplicationContext applicationContext =newClassPathXmlApplicationContext(“applicationContext-annotation.xml”);
    /获取AccountDap实例AccountDao accountDao =
    (AccountDao)applicationContext.getBean(“accountDao”);/调用实例中的转账方法
    accountDao.transfer(“lisi”, “zhangsan”,100.0);//输出提示信息
    System.out.println(“转账成功!”);}
    }

案例:实现用户登录

  • 思路分析
    根据学生管理系统及其登录要求,可以分析案例的实现步骤如下。
    1)为了存储学生信息,需要创建一个数据库。
    2)为了程序连接数据库并完成对数据的增删改查操作,需要在XML配置文件中配置数据库连接和事务等信息。
    3)在Dao层实现查询用户信息的方法。
    4)在Controller层处理业务逻辑,如判断用户输入的用户名与密码是否正确。

  • STEP 01
    创建数据库:在MySQL中的spring数据库中创建一个名为student的表。

编写实体类:在chapter09项目的com.itheima包中创建entity包,在 Student类,在该类中定义id、username、password和course属性,应的getter/setter方法。

  • STEP 02
    public class Student {
    //学生ID
    private Integer id;//学生姓名
    private String username;//学生密码
    private String password;//学生班级
    private String course;/省略getter/setter方法
    }

编写配置文件:在chapter09项目的resources文件夹下,创建配置文件applicationContext-student.xml,在该文件中配置id为dataSource的Bean和id为jdbcTemplate的JDBC模板Bean,并将数据源注入到JDBC

  • STEP 03

实现Dao层方法:在com.itheima.dao包下创建lmpl包,在该包下创疑StudentDaolmpl实现类,在StudentDaolmpl类中实现StudenfindAllStudent()方法。

  • STEP 05

public class StudentDaolmpl implements StudentDao {
/声明JdbcTemplate属性,省略了setter方法
private JdbcTemplate jdbcTemplate;
public List findAllStudent() {String sql = “select * from student”;RowMapper rowMapper =
new BeanPropertyRowMapper(Student.class);
/执行静态的SQL查询,并通过RowMapper返回结果
return this.jdbcTemplate.query(sql, rowMapper);l}
}

编写Controller层:在com.itheima包中创建controller包,在contrc

  • STEP 06
    StudentController类,用于实现用户登录操作。
    public class StudentController {
    public static void main(String[] args) {Scanner sca=new Scanner(System.in);String name=sca.nextLine();
    ApplicationContext applicationContext =new
    ClassPathXmlApplicationContext( “ applicationContext-student.xml”);/获取AccourRtDao实例
    StudentDao studentDao =
    (StudentDao) applicationContext.getBean(“studentDao”);List student = studentDao.findAllStudent();
    l/ for循环输出集合中的对象,此处省略}
    }