文章目录
Spring5学习笔记
[!TIP]
笔记总结自哔哩哔哩狂神说
Spring5
。
1. 简介
1.1 介绍
[!NOTE]
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Spring是分层的java SE/EE应用的full-stack的轻量级开源框架,以IOC(Inverse of control:控制反转) 和 AOP(Aspect Oriented Programming: 面向切面编程)为内核,提供了展现层spring MVC和持久层 Spring JDBCTemplate以及业务层事务管理等企业级应用技术,能整合各类第三方框架和类库,是一个技术大杂烩。
- 官网: http://spring.io/
- GitHub : https://github.com/spring-projects
1.2 发展历程
[!NOTE]
- 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架
- 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版
1.3 理念
[!NOTE]
使现有技术更加实用,更加简单。
1.4 优点
- 开源免费的容器框架
- 轻量级的非入侵式框架
- 优秀的事务支持
[!NOTE]
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
1.4 组成
1.4.1 Spring Core
[!NOTE]
核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)将应用程序的配置和依赖性规范与实际的应用程序代码分开。
1.4.2 Spring Context
[!NOTE]
Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
1.4.3 Spring AOP
[!NOTE]
通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
1.4.4 Spring DAO
[!NOTE]
JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
1.4.5 Spring ORM
[!NOTE]
Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
1.4.6 Spring Web
[!NOTE]
Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
1.4.7 Spring Web MVC
[!NOTE]
MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
1.5 扩展
- 现代化的java开发,就是基于Spring的开发
- Spring Boot
- 快速开发脚手架
- Spring Cloud
- 多个Spring Boot
2. 控制反转
2.1 原型
2.1.1 UserDao
package com.dingwen.stu.spr.dao;
/**
* 用户 dao
*
* @author dingwen
* @date 2021/10/05
*/
public interface UserDao {
/**
* 通过ID查询用户信息
*
* @param id id
*/
void selectById(String id);
}
2.1.2 UserDaoMysqlImpl
package com.dingwen.stu.spr.dao.impl;
import com.dingwen.stu.spr.dao.UserDao;
/**
* 用户 dao impl
* mysql 实现
*
* @author dingwen
* @date 2021/10/05
*/
public class UserDaoMysqlImpl implements UserDao {
@Override
public void selectById(String id) {
System.out.println("dao 通过用户ID查询用户信息,mysql实现");
}
}
2.1.3 UserDaoOracleImpl
package com.dingwen.stu.spr.dao.impl;
import com.dingwen.stu.spr.dao.UserDao;
/**
* 用户 dao impl
* mysql 实现
*
* @author dingwen
* @date 2021/10/05
*/
public class UserDaoOracleImpl implements UserDao {
@Override
public void selectById(String id) {
System.out.println("dao 通过用户ID查询用户信息,Oracle实现");
}
}
2.1.4 UserService
package com.dingwen.stu.spr.service;
/**
* 用户服务
*
* @author dingwen
* @date 2021/10/05
*/
public interface UserService {
/**
* 通过ID查询用户信息
*
* @param id id
*/
void selectById(String id);
}
2.1.5 UserServiceImpl
2.1.5.1 实现方式一
package com.dingwen.stu.spr.service.impl;
import com.dingwen.stu.spr.dao.UserDao;
import com.dingwen.stu.spr.service.UserService;
/**
* 用户服务impl
*
* @author dingwen
* @date 2021/10/05
*/
public class UserServiceImpl implements UserService {
/**
* 用户 dao
*/
private UserDao userDao = new UserDaoMysqlImpl();
private UserDao userDao = new UserDaoOracleImpl();
@Override
public void selectById(String id) {
userDao.selectById(id);
}
}
[!NOTE]
用户的需求会影响我们原来的代码,我们需要根据用户的需求去修改源代码,如果程序代码量十分大,修改一次的成本代价十分昂贵。
2.1.5.2 实现方式二
package com.dingwen.stu.spr.service.impl;
import com.dingwen.stu.spr.dao.UserDao;
import com.dingwen.stu.spr.service.UserService;
/**
* 用户服务impl
*
* @author dingwen
* @date 2021/10/05
*/
public class UserServiceImpl implements UserService {
/**
* 用户 dao
*/
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void selectById(String id) {
userDao.selectById(id);
}
}
[!NOTE]
之前,程序是主动创建对象,控制权在程序员手上,使用了set注入之后,程序不再有主动性,而是变成了被动的接受对象。这种思想从本质上实现了问题,程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务的实现上,这是IOC的原型。
2.2 本质
[!NOTE]
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3. Hello Spring
3.1 依赖
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.17.RELEASE</version>
</dependency>
3.2 Hello
实体
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
3.3 Spring
配置文件
[!TIP]
文件名称随意。
<?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就是java对象 , 由Spring创建和管理
类型 变量名 = new 类型();
Hello hello = new Hello();
bean = 对象 new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个变量
-->
<bean id="hello" class="nuc.ss.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
3.4 测试
public class MyTest {
public static void main(String[] args) {
// 获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中管理了,我们要使用,直接去里面取出来就可以
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello);
}
}
3.5 总结
[!NOTE]
- 程序创建对象改为由
Spring
来创建对象- 反转:程序本身不创建对象 , 而变成被动的接收对象
- 依赖注入
4. 配置
4.1 别名配置
[!NOTE]
可以添加多个别名,通过别名也能获取到该对象。
<alias name="userServiceImpl" alias="userService"></alias>
<alias name="userServiceImpl" alias="test"></alias>
4.2 Bean配置
[!NOTE]
- id:bean的唯一标识符,也就是相当于我们学的对象名
- class:bean 对象所对应的权限定名:包名 + 类型
- name: 也是别名,而且name更高级,可以起多个别名,通过逗号空格分号等分割
<bean id="userDaoOracleImpl" class="com.dingwen.stu.spr.dao.impl.UserDaoOracleImpl" name="userDaoOracleImpl,t2 t3">
<property name="userDao" ref="userDaoOracleImpl"></property>
</bean>
4.3 import
[!NOTE]
团队的合作通过import来实现 ,可以将多个配置文件,导入合并为一个。
<import resource="beans2.xml"></import>
5. 依赖注入
[!NOTE]
- 依赖:指Bean对象的创建依赖于容器
- 注入: 指Bean对象所依赖的资源 , 由容器来设置和装配
5.1 构造器注入
[!NOTE]
默认使用无参构造。
5.1.1 使用无参构造创建对象
<!--无参构造器-->
<bean id="user" class="nuc.ss.pojo.User">
<!--set 方法必须-->
<property name="name" value="dingwen"/>
</bean>
5.1.2 使用有参构造创建对象
private String name;
private int age;
public UserDaoOracleImpl(String name, int age) {
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("UserDaoOracleImpl constructor");
}
5.1.2.1 下标赋值
<bean id="userDaoOracleImpl" class="com.dingwen.stu.spr.dao.impl.UserDaoOracleImpl">
<constructor-arg index="0" value="dingwen"></constructor-arg>
<constructor-arg index="1" value="23"></constructor-arg>
</bean>
5.1.2.2 参数类型赋值
<bean id="userDaoOracleImpl" class="com.dingwen.stu.spr.dao.impl.UserDaoOracleImpl">
<constructor-arg type="java.lang.String" value="dingwen"></constructor-arg>
<constructor-arg type="int" value="23"></constructor-arg>
</bean>
[!NOTE]
只适用于同一类型参数只有一个多情况下使用。
5.1.2.3 参数名称赋值
<bean id="userDaoOracleImpl" class="com.dingwen.stu.spr.dao.impl.UserDaoOracleImpl">
<constructor-arg name="name" value="dingwen"></constructor-arg>
<constructor-arg name="age" value="23"></constructor-arg>
</bean>
5.2 set()
方式注入
5.2.1 实体准备
5.2.1.1 Address
package com.dingwen.stu.spr.di.entity;
/**
* 地址
*
* @author dingwen
* @date 2021/10/05
*/
public class Address {
/**
* 地址
*/
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
5.2.1.2 Student
package com.dingwen.stu.spr.di.entity;
import java.util.*;
/**
* 学生
*
* @author dingwen
* @date 2021/10/05
*/
public class Student {
/**
* 名字
*/
private String name;
/**
* 家庭地址
*/
private Address address;
/**
* 书
*/
private String[] books;
/**
* 爱好
*/
private List<String> hobbies;
/**
* 银行卡
*/
private Map<String,String> cards;
/**
* 游戏
*/
private Set<String> games;
/**
* 是否结婚
*/
private boolean isWife;
/**
* 信息
*/
private Properties info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getCards() {
return cards;
}
public void setCards(Map<String, String> cards) {
this.cards = cards;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public boolean isWife() {
return isWife;
}
public void setWife(boolean wife) {
isWife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.toString() +
", books=" + Arrays.toString(books) +
", hobbies=" + hobbies +
", cards=" + cards +
", games=" + games +
", isWife=" + isWife +
", info=" + info +
'}';
}
}
5.2.2 配置
<?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="address" class="com.dingwen.stu.spr.di.entity.Address">
<property name="address" value="昆明"></property>
</bean>
<bean id="student" class="com.dingwen.stu.spr.di.entity.Student">
<!--普通值注入-->
<property name="name" value="dingwen"/>
<!--对象引用注入-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>Thinking in Java</value>
<value>C语言程序设计</value>
<value>数据结构</value>
</array>
</property>
<!--list 注入-->
<property name="hobbies">
<list>
<value>看电影</value>
<value>听歌</value>
<value>干酒</value>
</list>
</property>
<!--map注入-->
<property name="cards">
<map>
<entry key="招商银行" value="8888888"></entry>
<entry key="农业银行" value="6666666"></entry>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!--null 注入-->
<property name="developer">
<null/>
</property>
<!--Properties 注入-->
<property name="info">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
5.2.3 测试
import com.dingwen.stu.spr.di.entity.Address;
import com.dingwen.stu.spr.di.entity.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DITest {
@Test
public void test01(){
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("beans.xml");
Address address = applicationContext.getBean("address", Address.class);
System.out.println("address = " + address);
Student student = applicationContext.getBean("student", Student.class);
System.out.println("student = " + student);
}
}
5.2.4 结果
ideVersion5 -junit4 DITest,test01
address = Address{address='昆明'}
student = Student{name='dingwen', address=Address{address='昆明'}, books=[Thinking in Java, C语言程序设计, 数据结构], hobbies=[看电影, 听歌, 干酒], cards={招商银行=8888888, 农业银行=6666666}, games=[LOL, COC, BOB], isWife=false, developer='null', info={password=123456, username=root}}
Process finished with exit code 0
5.3 扩展方式注入
[!NOTE]
官网解释。
5.3.1 C
命名空间注入
5.3.1.1 引入约束
xmlns:c="http://www.springframework.org/schema/c"
5.3.1.2 使用
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--c命名空间可以通过构造器注入 construct-args-->
<bean id="userDaoOracleImpl" class="com.dingwen.stu.spr.dao.impl.UserDaoOracleImpl" c:name="dingwen" c:age="24"></bean>
</beans>
[!DANGER]
通过构造器注入。
5.3.2 P
命名空间注入
5.3.2.1 引入约束
xmlns:p="http://www.springframework.org/schema/p"
5.3.2.2 使用
<?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">
<!--p命名空间可以直接注入属性的值 property-->
<bean id="userDaoOracleImpl" class="com.dingwen.stu.spr.dao.impl.UserDaoOracleImpl" p:name="dingwen" p:age="24"></bean>
</beans>
[!DANGER]
通过set()方式注入,需无参构造方法。
5.4 Bean
的作用域
[!NOTE]
创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。Spring Framework支持五种作用域,分别阐述如下表。
类别 | 说明 |
---|---|
singleton | 在Spring IOC 容器中只存在一个Bean实例,以单例模式存在,默认为单例模式 |
prototype | 原型模式,每次从容器中获取Bean时,都返回一个新的实例,想当与new Object() |
request | 每次Http请求都会创建一个Bean,只适用于WebApplicationContext环境 |
session | 同一个session环境共享一个Bean,只适用于WebApplicationContext环境 |
globalSession | 一般用作Portlet应用环境,只适用于WebApplicationContext环境 |
5.4.1 singleton
[!DANGER]
单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
5.4.2 prototype
[!DANGER]
原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
<!--或者-->
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
5.4.3 request
[!GDNGER]
每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="loginAction" class=com.foo.LoginAction" scope="request"/>
5.4.4 session
[!DANGER]
在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
5.4.5 global session
[!DANGER]
表示在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="user" class="com.foo.Preferences "scope="globalSession"/>
[!TIP]
global session作用域类似于标准的HTTP Session作用域,不过仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。
6. Bean的自动装配
6.1 简介
[!NOTE]
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文自动寻找,并自动给bean装配属性
6.2 三种自动装配的方式
[!NOTE]
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配bean
6.3 测试环境准备
6.3.1 Cat
package com.dingwen.stu.spr.auto.entity;
/**
* 猫
*
* @author dingwen
* @date 2021/10/05
*/
public class Cat {
public void shut(){
System.out.println("喵喵~");
}
}
6.3.2 Dog
package com.dingwen.stu.spr.auto.entity;
/**
* @author dingwen
* @date 2021/10/05
*/
public class Dog {
public void shut(){
System.out.println("旺旺~");
}
}
6.3.3 People
package com.dingwen.stu.spr.auto.entity;
/**
* 人
*
* @author dingwen
* @date 2021/10/05
*/
public class People {
/**
* 名字
*/
private String name;
/**
* 猫
*/
private Cat cat;
/**
* 狗
*/
private Dog dog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
6.4 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">
<bean id="cat" class="com.dingwen.stu.spr.auto.entity.Cat"></bean>
<bean id="dog" class="com.dingwen.stu.spr.auto.entity.Dog"></bean>
<!--
byName:会自动在容器上下文中套接,和自己对象的set方法后面的值相对应的bean id
当前容器中符合自动装配的Bean的Id必须和依赖对象中的属性set方法后面的值对应
-->
<bean id="people" class="com.dingwen.stu.spr.auto.entity.People" autowire="byName">
<property name="name" value="dingwen"></property>
</bean>
</beans>
6.5 byType
<?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="cat1" class="com.dingwen.stu.spr.auto.entity.Cat"></bean>
<bean id="dog2" class="com.dingwen.stu.spr.auto.entity.Dog"></bean>
<!--
byType:会自动在容器上下文中套接,和自己对象属性类型相同的bean id
当前容器环境中依赖对象注入的类型必须唯一
-->
<bean id="people" class="com.dingwen.stu.spr.auto.entity.People" autowire="byType">
<property name="name" value="dingwen"></property>
</bean>
</beans>
6.6 总结
[!NOTE]
- byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
6.7 使用注解实现自动装配
6.7.1 开启注解
[!NOTE]
- 导入约束:context支持
- 配置注解的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
6.7.2 @Autowired
[!NOTE]
- 需要导入 spring-aop的包
- 直接在属性上使用即可,也可以在set方式上使用
- 使用Autowired我们可以不用编写set方法,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName和类型byType
<!--正常1(byName)-->
<bean id="cat" class="com.dingwen.entity.Cat"/>
<bean id="dog" class="com.dingwen.entity.Dog"/>
<bean id="people" class="com.dingwen.People"/>
<!--正常2(byType)-->
<bean id="cat111" class="com.dingwen.entity.Cat"/>
<bean id="dog222" class="com.dingwen.entity.Dog"/>
<bean id="people" class="com.dingwen.entity.People"/>
<!--正常3(混合使用,先类型后名字)-->
<bean id="cat11" class="com.dingwen.entity.Cat"/>
<bean id="cat" class="com.dingwen.entity.Cat"/>
<bean id="dog222" class="com.dingwen.entity.Dog"/>
<bean id="people" class="com.dingwen.entity.People"/>
<!--不正常(多种类型且名字也不匹配,报错-->
<bean id="cat11" class="com.dingwen.entity.Cat"/>
<bean id="cat111" class="com.dingwen.entity.Cat"/>
<bean id="dog22" class="com.dingwen.entity.Dog"/>
<bean id="dog222" class="com.dingwen.entity.Dog"/>
<bean id="people" class="com.dingwen.entity.People"/>
package com.dingwen.stu.spr.auto.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
/**
* 人
*
* @author dingwen
* @date 2021/10/05
*/
public class People {
/**
* 名字
*/
private String name;
/**
* 猫
* @Autowired 先基于byType匹配,当同一类型Bean有多个时且byName不能完成匹配时
*,需要配合使用@Qualifier指定Bean Id.
*/
@Autowired(required = false)如果显示的定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Qualifier(value = "cat1")
private Cat cat;
/**
* 狗
* @Resource 先基于byName匹配,当name与依赖对象set方法对应无法匹配时使用byType匹配,
*. 若都不能则需要通过name指定Bean Id.
*/
@Resource(name = "dog1")
private Dog dog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
[!NOTE]
- @Autowired默认通过bytype的方式实现,如果有多个类型,则通过byname实现,如果两个都找不到则报错
- 发生冲突解决:@Autowired配合@Qualifier(value = “Bean Id”)使用
- @Resource默认通过byname的方式实现,如果找不到名字,则通过bytype实现,如果两个都找不到则错
- 发生冲突解决:@Resource直接使用@Resource(name = “Bean Id”)
7. 使用注解开发
[!DANGER]
- 保证导入AOP包
- 开启注解扫描
- 开启注解支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置注解扫描-->
<context:component-scan base-package="com.dingwen.stu.spr.auto"/>
<!--开启注解支持-->
<context:annotation-config/>
</beans>
7.1 @Component
[!NOTE]
注入Bean,等价于:
<bean id="user" class="com.dingwen.entity.User"/>
7.1.1 衍生注解
[!NOTE]
四个衍生注解功能时一致的,都是注入一个Bean。
7.1.1.1 @Repository
[!NOTE]
用于数据访问层。
7.1.1.2 @Service
[!NOTE]
用于服务层。
7.1.1.3@Controller
[!NOTE]
用于试图控制层。
7.2 自动装配
7.2.1 @Autoried
[!NOTE]
默认通过bytype的方式实现,如果有多个类型,则通过byname实现,如果两个都找不到则报错。配合@Qualifier(value = “Bean Id”)解决冲突。
7.2.2 @Resource
[!NOTE]
默认通过byname的方式实现,如果找不到名字,则通过bytype实现,如果两个都找不到则错。配合name属性指定Bean Id 解决冲突。
8. 使用java的方式配置Spring
8.1 配置文件
//这个也被Spring容器托管,注册到容器里,因为他本来就是一个@Component,
// @Component代表这是一个配置类,就和我们之前看的beans.xml是一样的
// @Import 合并其他配置类
@Configuration
@ComponentScan("com.dingwen")
@Import(MyConfig2.class)
public class MyConfig {
//注册一个bean,就相当于我们之前写的一个bean标签,
//这个方法的名字就相当于bean标签的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User getUser() {
return new User();//就是要返回注入到bean的对象!
}
}
8.2 测试
@Test
public void test1() {
// 如果完全使用了配置类的方式去做,我们就只能通过AnnotationConfig
//上下文来获取容器,通过配置类的class对象加载!
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User getUser = context.getBean("getUser", User.class);
System.out.println(getUser.getName());
}
9. 代理模式
[!NOTE]
代理模式是AOP的底层,
9.1 分类
[!NOTE]
- 静态代理
- 动态代理
9.2 角色分析
[!NOTE]
抽象角色:接口、抽象类、类
- 真实角色:被代理的角色
- 代理角色:代理真实角色
- 客户角色:访问代理对象的人
9.3 静态代理
9.3.1 抽象租房接口
package com.dingwen.pro.demo01;
/**
* 租房抽象借口
*
* @author dingwen
* @date 2021/10/06
*/
public interface Rent {
/**
* 租房
*/
void rent();
}
9.3.2 抽象角色(房东)
package com.dingwen.pro.demo01;
/**
* 房东
*
* @author dingwen
* @date 2021/10/06
*/
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
9.3.3 代理角色(中介)
package com.dingwen.pro.demo01;
/**
* 中介
*
* @author dingwen
* @date 2021/10/06
*/
public class Intermediary implements Rent{
/**
* 房东
*/
private Host host;
public void setHost(Host host) {
this.host = host;
}
@Override
public void rent() {
// 中介带看房子
see();
host.rent();
sign();
// 签订合同
}
public void see(){
System.out.println("看房");
}
public void sign(){
System.out.println("签合同");
}
}
9.3.4 测试
package com.dingwen.pro.demo01;
/**
* 客户端
*
* @author dingwen
* @date 2021/10/06
*/
public class Client {
public static void main(String[] args) {
Host host = new Host();
Intermediary intermediary = new Intermediary();
intermediary.setHost(host);
intermediary.rent();
}
}
看房房东要出租房子签合同
9.3.5 总结
[!NOTE]
- 优点
- 使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 缺点
- 一个真实角色就会产生一个代理角色
- 代码量会翻倍-开发效率会变低
9.3.6 加深理解
[!NOTE]
在不改变原有代码的情况下,增加日志记录的功能。
我们在不改变原来的代码的情况下,实现对原有功能的增强,这是AOP中最核心的思想
9.3.6.1 抽象接口
package com.dingwen.pro.demo02;
/**
* 用户服务
*
* @author dingwen
* @date 2021/10/06
*/
public interface UserService {
/**
* 添加
*/
void add();
/**
* 删除
*/
void remove();
/**
* 通过id查询
*/
void findById();
/**
* 修改
*/
void modify();
}
9.3.6.2 真实角色
package com.dingwen.pro.demo02;
/**
* 用户服务impl
*
* @author dingwen
* @date 2021/10/06
*/
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add");
}
@Override
public void remove() {
System.out.println("remove");
}
@Override
public void findById() {
System.out.println("findById");
}
@Override
public void modify() {
System.out.println("modify");
}
}
9.3.6.3 代理角色
package com.dingwen.pro.demo02;
/**
* 用户服务impl代理
*
* @author dingwen
* @date 2021/10/06
*/
public class UserServiceImplProxy implements UserService {
/**
* 用户服务
*/
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void add() {
userService.add();
log("add");
}
@Override
public void remove() {
userService.remove();
log("remove");
}
@Override
public void findById() {
userService.findById();
log("findById");
}
@Override
public void modify() {
userService.modify();
log("modify");
}
public void log(String msg){
System.out.println("日志: "+ msg);
}
}
9.3.6.3 测试
package com.dingwen.pro.demo02;
/**
* 客户端
*
* @author dingwen
* @date 2021/10/06
*/
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceImplProxy proxy = new UserServiceImplProxy();
proxy.setUserService(userService);
proxy.add();
proxy.findById();
proxy.remove();
proxy.modify();
}
}
// 结果
add
日志: add
findById
日志: findById
remove
日志: remove
modify
日志: modify
9.4 动态代理
9.4.1 简介
[!NOTE]
- 动态代理的代理类是动态生成的,不是 我们直接写好的
- 动态代理分类
- 基于接口:JDK动态代理
- 基于类:cglib
- java字节码实现:javasist(日本)
- 核心类
- Proxy:生成代理对象
- InvocationHandler:调用处理程序
9.4.2 优势
- 使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可【核心】
9.4.3 代码实现
9.4.3.1 抽象接口
package com.dingwen.pro.demo03;
/**
* 租房抽象借口
*
* @author dingwen
* @date 2021/10/06
*/
public interface Rent {
/**
* 租房
*/
void rent();
}
9.4.3.2 真实角色
package com.dingwen.pro.demo03;
/**
* 房东
*
* @author dingwen
* @date 2021/10/06
*/
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
9.4.3.3 事件处理程序
package com.dingwen.pro.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理调用处理程序
*
* @author dingwen
* @date 2021/10/06
*/
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 抽象接口
*/
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
/**
* 获得代理对象
*
* @return {@link Object}
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader()
,rent.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
see();
Object result = method.invoke(rent, args);
sign();
return result;
}
public void see(){
System.out.println("看房");
}
public void sign(){
System.out.println("签合同");
}
}
9.4.5 总结
package com.dingwen.pro.demo04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理调用处理程序
*
* @author dingwen
* @date 2021/10/06
*/
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* 获得代理对象
*
* @return {@link Object}
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
log(method.getName());
return result;
}
public void log(String msg){
System.out.println("日志:"+ msg);
}
}
10. AOP
10.1简介
[!NOTE]
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
10.2 在Spring中的应用
10.2.1 基本概念
[!NOTE]
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化的特殊对象。即,它是一个类
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法
- 目标(Target):被通知对象,目标对象
- 代理(Proxy):向目标对象应用通知之后创建的对象,代理对象
- 切入点(PointCut):切面通知 执行的 “地点”的定义,method
- 连接点(JointPoint):与切入点匹配的执行点,invoke
10.2.2 结合日志案例再分析
10.2.3 通知类型
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.aopalliance.intercept.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
10.2.4 案例使用
[!NOTE]
使用AOP织入需要导入
aspectjweaver
依赖包。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
10.2.4.5 准备业务类
// 抽象接口
package com.dingwen.stu.spr.aop;
/**
* 用户服务
*
* @author dingwen
* @date 2021/10/06
*/
public interface UserService {
/**
* 添加
*/
void add();
/**
* 删除
*/
void remove();
/**
* 通过id查询
*/
void findById();
/**
* 修改
*/
void modify();
}
// 实现类
package com.dingwen.stu.spr.aop;
/**
* 用户服务impl
*
* @author dingwen
* @date 2021/10/06
*/
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add");
}
@Override
public void remove() {
System.out.println("remove");
}
@Override
public void findById() {
System.out.println("findById");
}
@Override
public void modify() {
System.out.println("modify");
}
}
10.2.4.2 使用Spring API
10.2.4.2.1 代码
// 后置通知测试
package com.dingwen.stu.spr.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* 后置通知测试
*
* @author dingwen
* @date 2021/10/07
*/
public class AfterTest implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
System.out.println("后置通知执行, 类" + target.getClass().getName() + "的" + method.getName() + "方法。返回值为" + returnValue);
}
}
// 前置通知测试
package com.dingwen.stu.spr.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* 前置通知测试
*
* @author dingwen
* @date 2021/10/07
*/
public class BeforeTest implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println("前置通知在"+target.getClass().getName()+ "的"+ method.getName()+ "之前执行");
}
}
10.2.4.2.1 配置
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.dingwen.stu.spr.aop.UserServiceImpl"/>
<bean id="before" class="com.dingwen.stu.spr.advice.BeforeTest"/>
<bean id="after" class="com.dingwen.stu.spr.advice.AfterTest"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<!--权限 包 类 方法 (参数类型)-->
<aop:pointcut id="pointcut" expression="execution(* com.dingwen.stu.spr.aop.*.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
</aop:config>
</beans>
10.2.4.2.3 测试
import com.dingwen.stu.spr.aop.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add();
}
}
//结果
前置通知在com.dingwen.stu.spr.aop.UserServiceImpl的add之前执行
add
后置通知执行, 类com.dingwen.stu.spr.aop.UserServiceImpl的add方法。返回值为null
10.2.4.3 自定义实现
10.2.4.3.1 自定义切面
package com.dingwen.stu.spr.diy;
/**
* 自定义通知切入类
*
* @author dingwen
* @date 2021/10/07
*/
public class DiyPointCut {
/**
* 前
*/
public void before(){
System.out.println("自定义前置通知");
}
/**
* 后
*/
public void after(){
System.out.println("自定义后置通知");
}
}
10.2.4.3.2 配置
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.dingwen.stu.spr.aop.UserServiceImpl"/>
<bean id="diyPointCut" class="com.dingwen.stu.spr.diy.DiyPointCut"></bean>
<!--自定义切面-->
<aop:config>
<aop:aspect ref="diyPointCut">
<aop:pointcut id="point" expression="execution(* com.dingwen.stu.spr.aop.*.*(..))"/>
<aop:after method="after" pointcut-ref="point"></aop:after>
<aop:before method="before" pointcut-ref="point"></aop:before>
</aop:aspect>
</aop:config>
</beans>
10.2.4.4 注解实现
10.2.4.4.1 注解实现
package com.dingwen.stu.spr.ann;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 基于注解实现切面
*
* @author dingwen
* @date 2021/10/07
*/
@Aspect
//@EnableAspectJAutoProxy
public class AnnotationPointCut {
@Before("execution(* com.dingwen.stu.spr.aop.*.*(..))")
public void before(){
System.out.println("自定义注解实现,方法之前之前通知执行");
}
@After("execution(* com.dingwen.stu.spr.aop.*.*(..))")
public void after(){
System.out.println("自定义注解实现,方法执行之后执行");
}
@Around("execution(* com.dingwen.stu.spr.aop.*.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("自定义注解实现,环绕通知执行");
// 获取方法签名
Signature signature = proceedingJoinPoint.getSignature();
// 方法执行
proceedingJoinPoint.proceed();
System.out.println("自定义注解实现,环绕通知执行");
}
}
10.3.4.4.2 配置
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.dingwen.stu.spr.aop.UserServiceImpl"/>
<bean id="annotationPointCut" class="com.dingwen.stu.spr.ann.AnnotationPointCut"></bean>
<!--开启注解支持
proxy-target-class 默认值为false 动态代理基于JDK实现
true 表示是cglib实现
-->
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
</beans>
10.2.5 After & AfterRunning
[!DANGER]
都是后置通知,区别在于是否有返回值。返回值为切入方法本身的返回值。
11. 整合Mybatis
11.1 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Spring-Study</artifactId>
<groupId>nuc.ss</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!--Spring操作数据库的话,还需要一个spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!--代码织入需导入的包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
<!--解决XML没有打包问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
11.3 准备实体
package com.dingwen.entity;
@Data
public class User {
private int id;
private String name;
private String pwd;
}
11.4 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="nuc.ss.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="nuc.ss.dao"/>
</mappers>
</configuration>
11.5 接口
public interface UserMapper {
public List<User> selectUser();
}
11.6 mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dingwen.dao.UserMapper">
<select id="selectUser" resultType="User">
select * from user
</select>
</mapper>
11.7 测试
@Test
public void selectUser() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user: userList){
System.out.println(user);
}
sqlSession.close();
}
11.8 整合(Mybatis-Spring)
11.8.1 依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
11.8.2 引入配置文件
<?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>
11.8.3 配置数据源
<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
11.8.4 sqlSessionFactory
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:nuc/ss/dao/*.xml"/>
</bean>
11.8.5 sqlSessionTemplate
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入,没有set注入,只能使用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
11.8.6 impl
public class UserDaoImpl implements UserMapper {
//sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
// 或者
public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
}
<bean id="userDao" class="com.dingwen.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
// 测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = (UserMapper) context.getBean("userDao");
List<User> user = mapper.selectUser();
System.out.println(user);
}
11.8.7 整合完成mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--也可以在Spring中配置完成-->
<typeAliases>
<package name="com.dingwen.entity"/>
</typeAliases>
</configuration>
12. 申明式事务
12.1 含义
[!NOTE]
把一组业务当成一个业务来做;要么都成功,要么都失败。
12.2 ACID
[!NOTE]
- 原子性(atomicity):事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
- 一致性(consistency):一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
- 隔离性(isolation):多个业务可能操作同一个资源,防止数据损坏
- 持久性(durability):事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中
12.3 Spring中的事务管理
12.3.1 声明式事务
[!NOTE]
- 一般情况下比编程式事务好用
- 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理
- 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理
12.3.2 编程式事务
[!NOTE]
- 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
- 必须在每个事务操作业务逻辑中包含额外的事务管理代码
12.3.3 配置使用
12.3.3.1 头文件约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
12.3.3.3.2 事务管理器
[!NOTE]
- 无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的
- 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法
12.3.3.3.3 JDBC事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
12.3.3.3.4 配置事务的通知
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
12.3.3.3.5 织入事务
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* nuc.ss.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
12.3.4 完整配置案例
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--DataSource:使用spring的数据源替换mybatis的配置 c3p0 dbcp druid
我们这里使用spring提供的jdbc
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:nuc/ss/mapper/*.xml"/>
</bean>
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--<constructor-arg ref="dataSource"/>-->
<property name="dataSource" ref="dataSource" />
</bean>
<!--结合AOP实现事务的引织入-->
<!--配置事务通知:-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务织入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* nuc.ss.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
12.4 事务的传播特性
[!NOTE]
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为。
12.4.1 propagation_requierd
[!NOTE]
如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
12.4.2 propagation_supports
[!NOTE]
支持当前事务,如果没有当前事务,就以非事务方法执行。
12.4.3 propagation_required_new
[!NOTE]
新建事务,如果当前存在事务,把当前事务挂起。
12.4.4 propagation_not_supported
[!NOTE]
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
12.4.5 propagation_neve
[!NOTE]
以非事务方式执行操作,如果当前事务存在则抛出异常。
12.4.6 propagation_mandatory
[!NOTE]
使用当前事务,如果没有当前事务,就抛出异常。
12.4.7 propagation_nested
[!NOTE]
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。