Spring学习笔记
Spring
1.Sring快速入门
1.导入maven项目依赖
<!-- 导入Spring核心配置文件-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
2.编写Dao接口和实现类
//User接口
public interface UserDao {
public void save();
}
//UserDao实现类
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save方法执行了");
}
}
3.创建Spring核心配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rzVDtQ44-1626493289505)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210713222010458.png)]
4.在Spring核心配置文件中配置实现类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--绑定实现类的接口-->
<!-- id( 标识)随便取 但是调用的时候 也是调用对应的id class就是对应的类 -->
<bean id="userDao" class="com.itheima.dao.UserDaoImpl"/>
</beans>
5.使用Spring的API获得Beans示例(测试)
public class UserDaoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDao.class); //这里的getBean就是对应的id
userDao.save();
}
}
2.Spring配置文件详解
1.bean文件标签基本配置
用于配置对象交由Spring 来创建。
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
基本属性:
**id:Bean实例在Spring容器中的唯一标识**
**class:Bean的全限定名称**
2.bean标签的范围
scope:指对象的作用范围,取值如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pAaTp2xI-1626493289507)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210713222946827.png)]
1.范围测试
1.singleton
<bean id="userDao" class="com.itheima.dao.UserDaoImpl" scope="singleton"/>
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2.prototype
<!-- id( 标识)随便取 但是调用的时候 也是调用对应的id class就是对应的类 -->
<bean id="userDao" class="com.itheima.dao.UserDaoImpl" scope="prototype"/>
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
3.bean的生命周期属性
1.UserDaoImpl 定义方法
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
// 重写无参数的构造
public UserDaoImpl() {
System.out.println("UserDaoImpl 对象创建了");
}
// 创建开始方法创建的方法
public void init() {
System.out.println("init 方法执行了");
}
// 创建销毁方法
public void destroy() {
System.out.println("destroy方法执行了");
}
2.配置文件属性
<!-- id( 标识)随便取 但是调用的时候 也是调用对应的id class就是对应的类 -->
<bean id="userDao" class="com.itheima.dao.UserDaoImpl" scope="prototype" init-method="init" destroy-method="destroy"/>
3.测试
4.bean 实例化的三种方式
1.无参构造
<!-- id( 标识)随便取 但是调用的时候 也是调用对应的id class就是对应的类 -->
<bean id="userDao" class="com.itheima.dao.UserDaoImpl"/>
2.工厂静态方法
public class StaticFactoryBean {
public static UserDao createUserDao(){
return new UserDaoImpl();
}
}
<bean id="userDao" class="com.itheima.factory.StaticFactoryBean"
factory-method="createUserDao" />
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDao.class); //这里的getBean就是对应的id
System.out.println(userDao);
}
3.工厂实例方法实例化
public class DynamicFactoryBean {
public UserDao createUserDao(){
return new UserDaoImpl();
}
}
<bean id="factoryBean" class="com.itheima.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="factoryBean" factory-method="createUserDao"/>
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDao.class); //这里的getBean就是对应的id
System.out.println(userDao);
}
5.Bean的依赖注入
1.创建 UserService,UserServiceImpl 内部在调用 UserDao的save() 方法
public void save() {
// 内部去调用UserDao的save方法
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDao.class);
//这里的getBean就是对应的id
userDao.save();
}
}
2.测试
3.简化(将UserService托管给Spring)
<!-- 这个是将UserDao托管给Spring-->
<bean id="userDao" class="com.itheima.dao.UserDaoImpl"/>
<!-- 将UserService也托管给Spring-->
<bean id="userService" class="com.itheima.service.UserServiceImpl"/>
4测试
@Test
public void userServicetest(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);//这里的getBean就是对应的id
service.save();
}
5.DI依赖注入概念
-
依赖注入(
Dependency
Injection):它是 Spring 框架核心 IOC 的具体实现。 - 在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
- IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
- 那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
- 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取
解决的方法:使用set方法或者构造方法解决问题以及弊端(将userDao通过set方法注入到servicez 中)
6.使用set方法将Dao层方法植入到Service
这里想要植入 就必须要有Dao的set方法
1.在UserServiceImpl中添加setUserDao方法
public class UserServiceImpl implements UserService {
// 2. 引入UserDao
private UserDao userDao;
//设置set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
// 使用set方法以后 直接调用dao层的代码
userDao.save();
}
}
2.配置Spring容器调用set方法进行注入(设置对应的property)
<bean id="userDao" class="com.itheima.dao.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.UserServiceImpl">
<!-- 这里的属性的名称就是Dao在Service中注入的set方法后面的名称首字母变小写(方法名称)
而这个ref表示你将哪一个dao注入给了service 这个是对应的dao的id-->
<property name="userDao" ref="userDao"/>
</bean>
3.测试
@Test
public void userServicetest(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);//这里的getBean就是对应的id
service.save();
}
7.使用构造方法注入
1.在Service创建Dao的有参构造
// 2. 引入UserDao
private UserDao userDao;
//创建dao的有参构造
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl() {
}
2.配置Spring容器调用set方法进行注入(设置对应的property)
<bean id="userDao" class="com.itheima.dao.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.UserServiceImpl">
<!-- 这里的构造的name是构造方法的参数名称 ref表示对应注入的dao的id-->
<constructor-arg name="userDao" ref="userDao"/>
</bean>
3.测试
@Test
public void userServicetest(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);//这里的getBean就是对应的id
service.save();
}
8.Bean的依赖注入的数据类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8ClFkLp-1626493289514)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210714003353205.png)]
1.普通类型的注入
-
数据类型声明(就是在是dao的实现类里面)
// 普通数据的注入 使用set方法 private String username; private int age; public void setUsername(String username) { this.username = username; } public void setAge(int age) { this.age = age; }
2.Spring配置文件设置
<bean id="userDao" class="com.itheima.dao.UserDaoImpl"> <!-- 普通属性值的注入 当注入普通属性值的时候 只需要value--> <property name="username" value="任柏庆"/> <property name="age" value="20"/> </bean>
3.测试
public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = context.getBean("userDao", UserDao.class); //这里的getBean就是对应的id System.out.println(userDao); }
2.集合类型注入(List)
1.数据类型声明(就是在是dao的实现类里面)
// 集合类型 以及配置文件类型的注入
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
public void setStrList(List<String> strList) {
this.strList = strList;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
2.Spring配置文件设置
<!-- List集合类型注入-->
<property name="strList">
<list>
<value>大数据</value>
<value>软件</value>
<value>大数据</value>
</list>
</property>
3.Map类型注入
1.数据类型声明(就是在是dao的实现类里面)
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
public void setStrList(List<String> strList) {
this.strList = strList;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
2.Spring配置文件设置
这里特别说明 由于 这里由于Map中的后面的值是User类型(需要创建一个User,同时带有一个set方法,里面的属性也是需要注册到容器中的
User类注册
<bean id="user1" class="com.itheima.pojo.User">
<property name="addr" value="重庆市"/>
<property name="name" value="大数据"/>
</bean>
Map注册
<!-- Map类型-->
<property name="userMap">
<map>
<!-- 这里由于Map的key是String 所以使用key 而value则是User(引用的 则使用value-ref)-->
<!--又因为User需要在容器中才能引用 所以user需要注册 key 是String 后面的user1是引用的-->
<entry key="u1" value-ref="user1"></entry>
</map>
</property>
3.Properties类型注入
1.数据类型声明(就是在是dao的实现类里面)
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
public void setStrList(List<String> strList) {
this.strList = strList;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
2.Spring配置文件设置
<!-- Pro 配置文件类型 -->
<property name="properties">
<props>
<prop key="k1">llll</prop>
<prop key="k2">222</prop>
</props>
</property>
9.引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
<?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">
<import resource="classpath:applicationContext.xml"/>
</beans>
4.Spring相关API
1.ApplicationContext的继承体系
1)ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种
2)FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3)AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
2.getBean()方法使用
其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。
当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2 = applicationContext.getBean(UserService.class);
5.Spring配置数据源
1.数据源(连接池)作用
1.数据源(连接池)是提高程序性能如出现的
2.事先实例化数据源,初始化部分连接资源
3.使用连接资源时从数据源中获取
4.使用完毕后将连接资源归还给数据源
2.数据开发的步骤
①导入数据源的坐标和数据库驱动坐标
<!-- C3P0连接池 -->
<dependencies>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- Druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
</dependencies>
②创建数据源对象
③设置数据源的基本连接数据
④使用数据源获取连接资源和归还连接资源
⑤测试
1.c3p0数据源
// c3p0 数据源测试
@Test
public void testData() throws Exception {
//创建数据源
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 设置连接参数
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssmbuild");
dataSource.setUser("root");
dataSource.setPassword("root");
//获得连接对象
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
2.druid数据源
// druid 数据源测试
@Test
public void testDruid() throws Exception {
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/ssmbuild");
dataSource.setUsername("root");
dataSource.setPassword("root");
//获得连接对象
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
3.抽取jdbc…properties 配置连接池
1.jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild
jdbc.username=root
jdbc.password=root
2.测试
@Test
public void template() throws Exception {
// 加载类路径下的jdbc.properties 这个方法会自动加载resources下的 properties 只用写文件名即可
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(rb.getString("jdbc.driver"));
dataSource.setJdbcUrl(rb.getString("jdbc.url"));
dataSource.setUser(rb.getString("jdbc.username"));
dataSource.setPassword(rb.getString("jdbc.password"));
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
}
4.使用Spring配置数据源
1.导入项目依赖context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
2.applicationContext.xml配置数据源(c3p0)
<?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">
<!-- 配置数据源 这个是使用c3p0数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssmbuild"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
</beans
3.applicationContext.xml配置数据源(druid)
<!-- 使用的的druid 数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssmbuild"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
4.测试
// 使用Spring自动配置数据源
@Test
public void template1() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource source = context.getBean("dataSource", DataSource.class);
Connection connection = source.getConnection();
System.out.println(connection);
connection.close();
}
}
5.引入外部的properties
1.导入Spring context约束
命名空间:xmlns:context="http://www.springframework.org/schema/context"
约束路径:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2.修改applicationContext.xml配置文件
1.导入配置文件的依赖
<!-- 这里的属性就是这个属性 而后面的参数则是使用对应的配置文件的位置-->
<context:property-placeholder location="classpath:jdbc.properties"/>
2.配置bean:
<!-- 使用的的druid 数据源 这里的value 使用${}取值 里面的值对应的是配置文件的名称-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
3.测试
// 使用Spring自动配置数据源
@Test
public void template1() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource source = context.getBean("dataSource", DataSource.class);
Connection connection = source.getConnection();
System.out.println(connection);
connection.close();
}
6.Spring使用注解开发
1.Spring原始注解
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |
@Resource | 相当于@[email protected],按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注Bean的作用范围 |
Spring原始注解主要是替代的配置
注意:使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
1.将userDao的(application)配置剪切到对应的实现类上( 使用@Component注解替换)
// <!-- 配置dao-->
// <bean id="userDao" class="com.study.dao.impl.UserDaoImpl"/>
@Component
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save 执行了");
}
}
2.将userService的applicationContext.xml剪切到对应的的实现类上
// <!-- 配置service-->
// <bean id="userService" class="com.study.service.impl.UserServiceImpl">
//
// </bean>
@Component("userService")
public class UserServiceImpl implements UserService {
// <property name="userDao" ref="userDao"/>
@Autowired // 自动注入
@Qualifier("userDao")//表示被引用的数据(userdao)的id 名称
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
System.out.println("init方法执行了");
}
}
3.在application添加注解扫描包支持
<!-- 使用注解开发 需要开启注解的支持 扫描对应的包-->
<context:component-scan base-package="com.study"/>
4.小结
使用注解开发 就是简化了流程 开启注解支持—对应需要扫描的包—到对应的实现类上添加
1.@Component组件 里面的默认值就相当于bean的id
2.当需要使用引用类型时:
@Autowired // 自动注入
@Qualifier(“userDao”)//表示被引用的数据(userdao)的id 名称
3.简化开发 为了使Component更加的明确化,不同的层架采用不同的注解
4.当采用 @Autowired 和@Qualifier注解开发时 set方法可以省略不写
@Autowired:可以单独使用 :只能用于同一个类型的bean只有一个的时候
@[email protected]:相当于全限定名 通过id来寻找了Qualifier()里面为对应的注入的id值
@@Resource(name="")等同于[email protected] 通过id来寻找
[email protected]进行字符串的注入
@Value("大数据")
String name;
@Autowired // 自动注入
@Qualifier("userDao")//表示被引用的数据(userdao)的id 名称
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save()
{
userDao.save();
System.out.println(name);
}
}
注意:可以使用对应的String(普通类型)获取配置文件的内容
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild
jdbc.username=root
jdbc.password=root
<!-- 加入外部配置文件的依赖-->
<context:property-placeholder location="classpath:jdbc.properties"/>
@Value("${jdbc.password}")
String password; // 动态获取配置文件的值
2.Spring新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
**1.非自定义的Bean的配置:<bean>**
**2.加载properties文件的配置:<context:property-placeholder>**
**3.组件扫描的配置:<context:component-scan>**
4.引入其他文件:
1.新注解的类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5igxOjpn-1626493289516)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210714201813997.png)]
2.新注解的使用
1.编写SpringConfiguration类和使用import导入到Configuration类的分类
*/
@Configuration //标志这是一个使用注解开发的配置类
// 使用注解开启扫描包
//<!-- 使用注解开发 需要开启注解的支持 扫描对应的包-->
//<context:component-scan base-package="com.study"/>
@ComponentScan("com.study")
// <!-- 加入外部配置文件的依赖-->
// <context:property-placeholder location="classpath:jdbc.properties"/>
//使用import 导入数据源 这是一个数组 可以添加多个
@Import(DataConfiguration.class)
public class SpringConfiguration {
}
@Configuration
@PropertySource("classpath:jdbc.properties") //这里面的值就是location里面的值
public class DataConfiguration {
@Value("${jdbc.driver}")
String driver;
@Value("{jdbc.url}")
String url;
@Value("${jdbc.username}")
String username;
@Value("${jdbc.password}")
String password;
// 定义数据源的导入
@Bean("dataSource")
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
2.测试
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService service = context.getBean("userService", UserService.class);
service.save();
3.Spring集成Junit 测试
1.步骤
①导入spring集成Junit的坐标
②使用@Runwith注解替换原来的运行期
③使用@ContextConfiguration指定配置文件或配置类
④使用@Autowired注入需要测试的对象
⑤创建测试方法进行测试
2.代码实现
1.导入spring集成Junit的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
2.使用@Runwith注解替换原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)// 通过什么方式来测
3.使用@ContextConfiguration指定配置文件或配置类
@ContextConfiguration("classpath:applicationContext.xml") //告诉Spring 配置文件在哪里
//@ContextConfiguration(classes = {SpringConfiguration.class})// 使用注解配置
4.使用@Autowired注入需要测试的对象
@Autowired
private UserService userService;//需要测的类和接口
5.创建测试方法进行测试
@Test
public void test() throws SQLException {
userService.save();
System.out.println(dataSource.getConnection());
}
3.SpringAOP
1.AOP简介
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2.AOP 优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
3.AOP 的底层实现
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
4.AOP 的动态代理技术
常用的动态代理技术
JDK 代理 : 基于接口的动态代理技术
cglib 代理:基于父类的动态代理技术
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dRBUErwj-1626493289517)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210714224641553.png)]
5.JDK动态代理技术
①目标类接口
public interface TargetInterface {
public void save();
}
②目标类
public class Target implements TargetInterface {
public void save() {
System.out.println("Target running....");
}
}
③动态代理类
Target target = new Target(); //创建目标对象
//创建代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass()
.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("前置增强代码...");
Object invoke = method.invoke(target, args);
System.out.println("后置增强代码...");
return invoke;
}
}
);
④测试
// 测试,当调用接口的任何方法时,代理对象的代码都无序修改
proxy.method();
6.Cjlib动态代理技术
①目标类
public class Target {
public void method() {
System.out.println("Target running....");
}
}
②动态代理代码
Target target = new Target(); //创建目标对象
Enhancer enhancer = new Enhancer(); //创建增强器
enhancer.setSuperclass(Target.class); //设置父类
enhancer.setCallback(new MethodInterceptor() { //设置回调
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("前置代码增强....");
Object invoke = method.invoke(target, objects);
System.out.println("后置代码增强....");
return invoke;
}
});
Target proxy = (Target) enhancer.create(); //创建代理对象
③调用代理对象的方法测试
//测试,当调用接口的任何方法时,代理对象的代码都无序修改
proxy.method();
7 AOP 相关概念
-
Target(目标对象):代理的目标对象
-
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点**(可以被增强的方法)**
-
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义(真正被增强的方法) 切入点为连接点的一部分
-
Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知(就是增强的方法)
-
Aspect(切面):是切入点和通知(引介)的结合(切点+通知)
-
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入(将老方法和需要加入的方法相结合)
8.AOP 开发明确的事项
1)需要编写的内容
-
编写核心业务代码(目标类的目标方法)
-
编写切面类,切面类中有通知(增强功能方法)
-
在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
2)AOP 技术实现的内容
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
3)AOP 底层使用哪种代理方式
在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。(jdk和cjlib)
4)小结
-
aop:面向切面编程
-
aop底层实现:基于JDK的动态代理 和 基于Cglib的动态代理
-
aop的重点概念:
Pointcut(切入点):被增强的方法 Advice(通知/ 增强):封装增强业务逻辑的方法 Aspect(切面):切点+通知 Weaving(织入):将切点与通知结合的过程
-
开发明确事项:
谁是切点(切点表达式配置) 谁是通知(切面类中的增强方法) 将切点和通知进行织入配置
9.使用AOP 开发
1.快速入门
①导入 AOP 相关坐标
<!--导入spring的context坐标,context依赖aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--spring 集成Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
②创建目标接口和目标类(内部有切点)
public interface TargetInterface {
public void method();
}
public class Target implements TargetInterface {
@Override
public void method() {
System.out.println("Target running....");
}
}
③创建切面类(内部有增强方法)
public class MyAspect {
//前置增强方法
public void before(){
System.out.println("前置代码增强.....");
}
}
④将目标类和切面类的对象创建权交给 spring
<!--配置目标类-->
<bean id="target" class="com.itheima.aop.Target"></bean>
<!--配置切面类-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>
⑤在 applicationContext.xml 中配置织入关系
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
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
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<aop:config>
<!--引用myAspect的Bean为切面对象-->
<aop:aspect ref="myAspect">
<!--配置Target的method方法执行时要进行myAspect的before方法前置增强-->
<aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.method())"></aop:before>
</aop:aspect>
</aop:config>
⑥测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
2.XMl配置文件详解
1) 切点表达式的写法
表达式语法:
execution([修饰符(可写可不写)] 返回值类型 包名.类名.方法名(参数))
-
访问修饰符可以省略
-
返回值类型、包名、类名、方法名可以使用星号 代表任意*
-
包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
-
参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
例如:
execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)-->
<aop:config>
<!--声明切面 这个相当于告诉spring 切面是哪一个 id-->
<aop:aspect ref="myAspect">
<!-- 切面=切点+通知 method表示 切面中增强方法的名字 后面的切点 表示需要在目标对象的哪一个方法里面增强-->
<!-- <aop:before method="before" pointcut="execution(public void com.study.aop.Target1.save())"/>-->
<aop:before method="before" pointcut="execution(* com.study.aop.*.*())"/>
</aop:aspect>
</aop:config>
2) 通知的类型
通知的配置语法:
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
3) 切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)-->
<aop:config>
<!--声明切面 这个相当于告诉spring 切面是哪一个 id-->
<aop:aspect ref="myAspect">
<!-- 切面=切点+通知 method表示 切面中增强方法的名字 后面的切点 表示需要在目标对象的哪一个方法里面增强-->
<!-- <aop:before method="before" pointcut="execution(public void com.study.aop.Target1.save())"/>-->
<!-- 切点表达式的抽取 将pointcut抽取配置 后面调用的时候 就使用 point-cut-ref -->
<aop:pointcut id="myPointcut" expression="execution(* com.study.aop.*.*(..))"/>
<aop:before method="before" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
4)小结
- aop织入的配置
<aop:config>
<aop:aspect ref=“切面类”>
<aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>
</aop:aspect>
</aop:config>
-
通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知
-
切点表达式的写法
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
3.使用注解进行AOP开发
1 快速入门
基于注解的aop开发步骤:
①创建目标接口和目标类(内部有切点)
public interface TargetInterface {
public void method();
}
public class Target implements TargetInterface {
@Override
public void method() {
System.out.println("Target running....");
}
}
②创建切面类(内部有增强方法)
public class MyAspect {
//前置增强方法
public void before(){
System.out.println("前置代码增强.....");
}
}
③将目标类和切面类的对象创建权交给 spring
@Component("target")
public class Target implements TargetInterface {
@Override
public void method() {
System.out.println("Target running....");
}
}
@Component("myAspect")
public class MyAspect {
public void before(){
System.out.println("前置代码增强.....");
}
}
④在切面类中使用注解配置织入关系
@Component("myAspect")
@Aspect
public class MyAspect {
@Before("execution(* com.study.aop.*.*(..))")
public void before(){
System.out.println("前置代码增强.....");
}
}
⑤在配置文件中开启组件扫描和 AOP 的自动代理
<!--组件扫描-->
<context:component-scan base-package="com.itheima.aop"/>
<!--aop的自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
⑥测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.method();
}
}
2.注解配置 AOP 详解
1) 注解通知的类型
通知的配置语法:@通知注解(“切点表达式")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-156Sl16a-1626493289520)(F:\主流框架\3-1 就业课(2.1)]-Spring\第3节 AOP简介\03-Spring的AOP\笔记\img\图片7.png)
2) 切点表达式的抽取
同 xml配置
aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在在增强注解中进行引用。具体如下:
@Component("myAspect")
@Aspect
public class MyAspect {
@Before("MyAspect.myPoint()")
public void before(){
System.out.println("前置代码增强.....");
}
@Pointcut("execution(* com.itheima.aop.*.*(..))")
public void myPoint(){
}
}
3.小结
- 注解aop开发步骤
①使用@Aspect标注切面类
②使用@Bean通知注解标注通知方法
**③在配置文件中配置aop自动代理aop:aspectj-autoproxy/**
- 通知注解类型
4.SpringJDBCtemplate的使用
1.JdbcTemplate基本使用
JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等
2.JdbcTemplate基本使用-开发步骤
①导入spring-jdbc和spring-tx坐标
②创建数据库表和实体
③创建JdbcTemplate对象
④执行数据库操作
3.JdbcTemplate基本使用-快速入门
①导入spring-jdbc和spring-tx坐标
<name>itheima_spring_jdbc Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
②创建数据库表和实体
/*
Navicat Premium Data Transfer
Source Server : DataBase
Source Server Type : MySQL
Source Server Version : 50605
Source Host : localhost:3306
Source Schema : test
Target Server Type : MySQL
Target Server Version : 50605
File Encoding : 65001
Date: 15/07/2021 21:24:36
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`money` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('lucy', 5000);
INSERT INTO `account` VALUES ('tom', 5000);
SET FOREIGN_KEY_CHECKS = 1;
③创建JdbcTemplate对象
// 创建数据源
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("root");
// 设置JdbcTemplate模板
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置数据源 知道数据库和数据源在哪
jdbcTemplate.setDataSource(dataSource);
④执行数据库操作
// 执行相关操作
int tom = jdbcTemplate.update("insert into account values (?,?)", "lucy", 5000);
System.out.println(tom);
4.JdbcTemplate基本使用-sprin*生模板对象
1.applicationContext.xml编写
<!-- 数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- jdbc对象模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
2.执行操作
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate dataSource = context.getBean(JdbcTemplate.class);
int lucy = dataSource.update("insert into account values (?,?)", "zhangsan", 5000);
System.out.println(lucy);
5.抽取jdbc.properties简化代码
1.jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
2.applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1.导入外部配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--2. 数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 3. jdbc对象模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
3.测试
@Test //测试Sprin*生JDbc产生模板对象
public void test2() throws PropertyVetoException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate dataSource = context.getBean(JdbcTemplate.class);
int lucy = dataSource.update("insert into account values (?,?)", "lisi", 5000);
System.out.println(lucy);
}
}
特别提示(这是一套代码 数据库相关的)
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1.导入外部配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--2. 数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 3. jdbc对象模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
4.更新及删除操作(集成Spring-junit)
RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
@Autowired
private JdbcTemplate jdbcTemplate;
// 更新操作
@Test
public void testUpdate(){
int tom = jdbcTemplate.update("update account set money =? where name=?", 10000, "tom");
System.out.println(tom);
}
// 删除操作
@Test
public void testDelete() {
int tom = jdbcTemplate.update("delete from account where name=?","tom");
System.out.println(tom);
}
@Test //查询所有操作
public void testQuery() {
List<Account> list = jdbcTemplate.query("select * from account ", new BeanPropertyRowMapper<Account>(Account.class));
for (Account account : list) {
System.out.println(account.toString());
}
}
@Test //查询一个
public void testQueryOne() {
List<Account> list = (List<Account>) jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), "zhangsan");
for (Account account : list) {
System.out.println(list);
}
}
@Test //聚合查询
public void testQueryCounts(){
Long aLong = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(aLong);
}
}
5.小结
①导入spring-jdbc和spring-tx坐标
②创建数据库表和实体
③创建JdbcTemplate对象
JdbcTemplate jdbcTemplate = newJdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
④执行数据库操作
更新操作:
jdbcTemplate.update (sql,params)
查询操作:
jdbcTemplate.query (sql,Mapper,params)
jdbcTemplate.queryForObject(sql,Mapper,params)
5.事务控制
1.编程式事务控制相关对象
1.PlatformTransactionManager
PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8DKiybcg-1626493289522)(F:\主流框架\3-1 就业课(2.1)]-Spring\第4节 JdbcTemplate基本使用\04-Spring JdbcTemplate&声明式事务\笔记\img\2.png)
注意:
PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager
2.TransactionDefinition
TransactionDefinition 是事务的定义信息对象,里面有如下方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7RLRbFT1-1626493289522)(F:\主流框架\3-1 就业课(2.1)]-Spring\第4节 JdbcTemplate基本使用\04-Spring JdbcTemplate&声明式事务\笔记\img\3.png)
1. 事务隔离级别
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。
-
ISOLATION_DEFAULT
-
ISOLATION_READ_UNCOMMITTED
-
ISOLATION_READ_COMMITTED
-
ISOLATION_REPEATABLE_READ
-
ISOLATION_SERIALIZABLE
2. 事务传播行为
-
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
-
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
-
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
-
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
-
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
-
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
-
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
-
超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
-
是否只读:建议查询时设置为只读
3. TransactionStatus(事务的定义对象)
TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。
4.小结
编程式事务控制三大对象
-
PlatformTransactionManager
-
TransactionDefinition
-
TransactionStatus
2.基于 XML 的声明式事务控制
1.概念
Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。
声明式事务处理的作用
-
事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
-
在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便
注意:Spring 声明式事务控制底层就是AOP。
2.声明式事务控制的实现
声明式事务控制明确事项:
-
谁是切点?
-
谁是通知?
-
配置切面?
1引入命名空间tx
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
2.配置事务增强
<!--.配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 设置JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 注册Dao层-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 注册Service层 这个就是切点 内部的方法就是切点-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!-- 这里引用了dao-->
<property name="accountDao" ref="accountDao"/>
</bean>
<!--通知是事务的增强-->
<!-- 1.在这之前 需要配置平台事务管理器 平台事务管理器需要映入数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2. 通知 事务的增强-->
<tx:advice id="tx-advice" transaction-manager="transactionManager">
<!-- 设置事务的属性信息 -->
<tx:attributes>
<!-- 一个事务配置所有的 所有的方法-->
<tx:method name="transfer" isolation="REPEATABLE_READ" timeout="-1" propagation="REQUIRED" read-only="false"/>
<tx:method name="*" propagation="REQUIRED" timeout="-1" isolation="DEFAULT"/>
<tx:method name="updtate*"/>
</tx:attributes>
</tx:advice>
3.配置事务 AOP 织入
<!-- 3. 配置事务的aop的炽入-->
<aop:config>
<!-- 专门用于事务-->
<aop:advisor advice-ref="tx-advice" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>
4.测试
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = app.getBean(AccountService.class);
accountService.transfer("zhangsan","lucy",500);
}
5.小结
切点方法的事务参数的配置
<!--事务增强配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
其中,tx:method 代表切点方法的事务参数的配置,例如:
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
-
name:切点方法名称
-
isolation:事务的隔离级别
-
propogation:事务的传播行为
-
timeout:超时时间
-
read-only:是否只读
3.基于注解的声明式事务
1.在对应的实现类上加上注解
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
}
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
}
}
@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
int i = 1/0;
accountDao.in(inMan,money);
}
}
2.编写 applicationContext.xml
<!--.配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 设置JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 设置平台事务管理器-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--事务的注解驱动-->
<tx:annotation-driven/>
:attributes>
</tx:advice>
其中,<tx:method> 代表切点方法的事务参数的配置,例如:
```xml
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
-
name:切点方法名称
-
isolation:事务的隔离级别
-
propogation:事务的传播行为
-
timeout:超时时间
-
read-only:是否只读
3.基于注解的声明式事务
1.在对应的实现类上加上注解
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
}
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
}
}
@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
int i = 1/0;
accountDao.in(inMan,money);
}
}
2.编写 applicationContext.xml
<!--.配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 设置JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 设置平台事务管理器-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--事务的注解驱动-->
<tx:annotation-driven/>