文章预览:
一.Spring的bean与Ioc
bean标签
<!--
每一个实现类就对应一个bean标签
id属性: 对象的唯一标识,根据这个唯一标识,就可以从核心容器中获取对象
class属性: 对象所属的实现类的全限定名
scope属性: 对象的范围
1. singleton 单例(默认)
2. prototype 多例
lazy-init: 配置懒加载,核心容器创建的时候是否创建出该类对象
init-method: 配置类的对象初始化的时候,要调用哪个方法
destroy-method: 配置这个类的对象销毁的时候,要调用哪个方法
单例模式下(默认没有开启懒加载),由核心容器进行管理的对象什么时候创建什么时候销毁?
1. 核心容器创建的时候,会创建出它所配置的所有类的对象
2. 核心容器销毁的时候,它里面的对象才会被销毁
多例模式下,由spring管理的对象什么时候创建什么时候销毁
1. 当核心容器调用getBean(id)的时候,创建对象
2. 垃圾回收机制才能销毁这个对象
-->
<bean class="com.wjs.service.impl.UserServiceImpl"
id="userService"
scope="prototype" lazy-init="false"
init-method="init"
destroy-method="destroy"></bean>
</beans>
1. id或者name属性
用于标识bean , 其实id 和 name都必须具备唯一标识 ,两种用哪一种都可以。但是一定要唯一、 一般开发中使用id来声明.
-
class属性: 用来配置要实现化类的全限定名
-
scope属性: 用来描述bean的作用范围
singleton: 默认值,单例模式。spring创建bean对象时会以单例方式创建。(默认)
prototype: 多例模式。spring创建bean对象时会以多例模式创建。
request: 针对Web应用。spring创建对象时,会将此对象存储到request作用域。(不用管)
session: 针对Web应用。spring创建对象时,会将此对象存储到session作用域。(不用管)
-
init-method属性:spring为bean初始化提供的回调方法
-
destroy-method属性:spring为bean销毁时提供的回调方法. 销毁方法针对的都是单例bean , 如果想销毁bean , 可以关闭工厂
2.bean的作用范围和生命周期
-
单例对象: scope=“singleton”,一个应用只有一个对象的实例。它的作用范围就是整个引用。
- 核心容器创建的时候,会创建出它所配置的所有类的对象
- 核心容器销毁的时候,它里面的对象才会被销毁
-
多例对象: scope=“prototype”,每次访问对象时,都会重新创建对象实例。
- 当核心容器调用getBean(id)的时候,创建对象
- 垃圾回收机制才能销毁这个对象
Spring工厂(了解)
1.ApplicationContext接口的三种实现类
- ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件
- FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
- AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
2.BeanFactory和ApplicationContext的区别(了解)
-
ApplicationContext 是现在使用的工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
-
XmlBeanFactory是老版本使用的工厂,目前已经被废弃【了解】
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
3. 两者的区别(了解):
-
ApplicationContext加载方式是框架启动时就开始创建所有单例的bean,存到了容器里面
-
非懒加载: 在核心容器创建的时候,创建出所有的bean对象,存到核心容器中
-
懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中
-
BeanFactory加载方式是用到bean时再加载(目前已经被废弃)
- 懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中
-
ApplicationContext是BeanFactory的子接口,子接口的功能肯定比父接口更强大
4.新旧工厂
- 新工厂: 在工厂初始化的话的时候, 就会创建配置的所有的单例bean,存到Spring容器里面
- 旧工厂(已经废弃了): 等用到对象的时候, 再创建
实例化Bean的三种方式(了解)
1. 无参构造方法方式
<bean class="com.wjs.service.impl.UserServiceImpl"
id="userService"></bean>
2. 静态工厂方式
需要额外提供工厂类 , 工厂方法是静态方法
<bean id="dataSource" class="com.wjs.service.utils.DruidUtil"
factory-method="getDataSource"></bean>
3. 实例工厂实例化的方法
配置实例工厂实例化Bean
第一步: 配置实例工厂对象
第二步: 配置bean对象
factory-bean 表示要调用哪个实例工厂对象的方法
factory-method 表示要调用实例工厂对象的哪个方法
-->
<bean id="druidUtil" class="com.wjs.service.utils.DruidUtil"></bean>
<bean id="dataSource" factory-bean="druidUtil" factory-method="getDataSource"></bean>
二.Spring的DI
1.概念
依赖注入全称是 dependency Injection 翻译过来是依赖注入.其实就是如果spring核心容器管理的某一个类中存在属性,需要spring核心容器在创建该类实例的时候,顺便给这个对象里面的属性进行赋值。 这就是依赖注入。
现在, Bean的创建交给Spring了, 需要在xml里面进行注册
我们交给Spring创建的Bean里面可能有一些属性(字段), Spring帮我创建的同时也把Bean的一些属性(字段)给赋值, 这个赋值就是注入.
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">
<!--
它里面的userDao属性要进行赋值
-->
<bean id="userService" class="com.wjs.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
<bean id="userDao" class="com.wjs.dao.impl.UserDaoImpl"></bean>
</beans>
3.set方法注入(注入)
<?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">
<!--
它里面的UserService属性要进行赋值
第一种:使用有参构造进行属性的注入,使用<constructor-arg>标签注入
第二种:使用set方法进行属性的注入
第三种:p命名空间的方式注入
要注入的属性类型:
1. 对象(spring核心容器中的)类型: ref="对象的id"
2. 简单类型: String、int、Integer: value="要注入的数据"
-->
<bean id="userController" class="com.wjs.controller.UserController">
<!--set方法注入-->
<property name="userService" ref="userService"></property>
</bean>
<!--
它里面的userDao属性要进行赋值
-->
<bean id="userService" class="com.wjs.service.impl.UserServiceImpl">
<!--set方法注入-->
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.wjs.dao.impl.UserDaoImpl"></bean>
</beans>
P名称空间注入[了解] 其实底层也是调用set方法,只是写法有点不同
4 p名称空间注入的方式
-
提供属性的set方法
-
在spring.xml引入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
<?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" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
- 使用
<!--
它里面的userDao属性要进行赋值
-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:userDao-ref="userDao">
<!--构造函数方式注入-->
<!--<constructor-arg name="userDao" ref="userDao"></constructor-arg>-->
<!--set方法注入-->
<!--<property name="userDao" ref="userDao"></property>-->
</bean>
<bean id="userDao" class="com.wjs.dao.impl.UserDaoImpl"
p:age="28"
p:username="奥巴马">
</bean>
三.Spring的注解
一.使用注解创建对象
1.注解开发与配置开发区别
学习基于注解的 IoC 配置,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
2.包扫描(第一步)
在beans标签内部,使用context:component-scan ,让spring扫描该基础包下的所有子包注解
<context:component-scan base-package="com.wjs"></context:component-scan>
在applicationContext.xml 开启包扫描
3.@Component
作用:
把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
4. @Controller @Service @Repository
web里面的三层结构中的所有类,在spring里面都称之为 Component (组件) , 但是它也提供了更详细的注解来针对不同的层级声明 。
三个衍生注解如下:
@Controller :修饰WEB层类 --->web | SpringMVC
@Service :修饰业务层类 --->service
@Repository :修饰DAO层类 --->dao
在需要spring创建对象的类上面使用注解 @Component(“us”) 即可.Spring看类上是否有该注解,如果有该注解,生成这个类的实例。如果不指定value值, 那么默认的id值就是类名的名字, 第一个字母小写.
5. 用于改变作用范围的@Scope
@Scope
singleton: 单例(默认)
prototype:多例
@Scope注解用来描述类的作用范围的,默认值singleton。如同xml中bean标签的属性scope <bean scope=""/>
.如果配置成多例的使用prototype。
@Scope("prototype")
@Component("accountService")
public class AccountServiceImpl implements AccountService {}
6.和生命周期相关的【了解】
-
初始化和销毁回调方法对应的注解
@PostConstrut:如同xml中bean标签的属性init-method
<bean init-method=""/>
,用来设置spring框架初始化此类实例时调用的初始化方法,标注在此类的初始化方法上 @PreDestroy:如同xml中bean标签的属性destroy-method
<bean destroy-method=""/>
,用来设置spring框架销毁此类实例时调用的销毁方法,标注在此类的销毁方法上注意:这两个注解都是配在方法上的
二.使用注解注入属性
1.@Value : 注入简单类型的数据
-
作用:
注入基本数据类型和 String 类型数据的
-
属性:
value:用于指定值 , 可以通过表达式动态获得内容再赋值
-
实例:
@Value("奥巴马")
private String
2.@Autowired
-
作用:
自动按照类型注入。当使用注解注入属性时, set 方法可以省略。它只能注入其他 bean 类型。
如果只有一个实现类, 可以自动注入成功
如果有两个或者两个以上的实现类, 找到变量名一致的id对象给注入进去, 如果找不到,就报错
-
实例:
@Component("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
//当有多个AccountDao实现类时候, @Autowired会在在Spring容器里面找id为accountDao的对象注入,找不到就报错
private AccountDao accountDao;
@Override
public void save() {
System.out.println("AccountServiceImpl---save()");
accountDao.save();
}
}
3.@Qualifier
-
作用
在自动按照类型注入(@Autowired)的基础之上,再按照Bean的id注入。
它在给字段注入时不能独立使用,必须和@Autowire一起使用;
但是给方法参数注入时,可以独立使用。
-
属性
value:指定bean的id。
-
实例
package com.wjs.controller;
import com.wjs.service.UserService;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
/**
* 包名:com.wjs.controller
*
* 注入对象类型的属性:
* 1. Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
* 如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
* 如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id
*/
@Controller
public class UserController {
@Autowired
@Qualifier("userServiceImplAnother")
private UserService userService;
public String getName(){
//调用业务层的getName()方法
return userService.getName();
}
}
4.@Resource
如果上面一个接口有多种实现,那么现在需要指定找具体的某一个实现,那么可以使用@Resource
package com.wjs.controller;
import com.wjs.service.UserService;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
/**
* 包名:com.wjs.controller
*
* 注入对象类型的属性:
* 1. Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
* 如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
* 如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id
*
* 2. Resource: 能够进行自动装配以及手动装配
*/
@Controller
public class UserController {
@Resource(name = "userServiceImpl")
private UserService userService;
public String getName(){
//调用业务层的getName()方法
return userService.getName();
}
}
二.小结
1.XML与注解的优劣势
- xml
- 优点: 方便维护, 改xml文件
- 缺点: 相对注解而言, 麻烦一点
- 注解
- 优点: 开发简洁方便
- 缺点: 维护没有xml那么方便, 需要改源码
2.混合开发特点
IOC: 自己写的类使用注解进行IOC,非自己写的类使用配置文件进行IOC
DI: 如果一个类是使用注解进行IOC的,那么它的属性使用注解进行注入;如果一个类是使用配置文件进行IOC,那么它的属性使用配置文件进行注入
四.spring整合mybatis(非常重要)
1.步骤
- 引入mybatis以及整合的相关依赖
- 在spring的IOC容器中创建DataSource对象(可以使用spring内置的DataSource)
- 在spring的IOC容器中创建SqlSessionFactoryBean对象,并且可以指定要配置别名的包或者是加载核心配置文件
- 在spring的IOC容器中创建MapperScannerConfigurer对象,用于扫描Dao接口创建代理对象
- 通过注解的方式注入Dao代理对象
2.实现步骤
2.1. 引入依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!--spring-->
<spring.version>5.0.2.RELEASE</spring.version>
<!--日志打印框架-->
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<!--mysql-->
<mysql.version>5.1.6</mysql.version>
<!--mybatis-->
<mybatis.version>3.5.3</mybatis.version>
</properties>
<dependencies>
<!--引入依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<!--mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--
mybatis整合spring的依赖
-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.2. spring.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">
<!--1. 包扫描-->
<context:component-scan base-package="com.wjs"/>
<!--
2. spring整合mybatis
-->
<!--
1. 配置jdbc的环境:
1.1 配置dataSource(既可以使用spring内置的DataSource,又可以使用第三方的DataSource)
1.2 配置事务管理者(现在不做),后面笔记声明式事务的时候有
2. 将SqlSessionFactoryBean 对象进行IOC配置到spring的核心容器中,并且将dataSource注入进去
3. 扫描dao包,创建出dao的代理对象,交给spring的核心容器管理
各种dao代理对象的id,就是接口名首字母改小写
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="root"></property>
<property name="password" value=""></property>
<property name="url" value="jdbc:mysql:///数据库?characterEncoding=utf8"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--加载mybatis的主配置文件-->
<!--<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>-->
<!--别名配置的包扫描-->
<property name="typeAliasesPackage" value="com.wjs.pojo"></property>
</bean>
<bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.wjs.dao"></property>
</bean>
</beans>
3.纯注解改造增删改查(了解)
3.1实现
3.2 @Configuration
-
作用:
用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。
获取容器时需要使用AnnotationApplicationContext(类.class)。
-
属性:
value:用于指定配置类的字节码
-
示例代码:
@Configuration
public class SpringConfig {
}
3.3 @ComponentScan
-
作用:
用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的:
<context:component-scan base-package="com.wjs"/>
是一样的。 -
属性:
basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。
-
示例代码:
@Configuration
@ComponentScan(basePackages = "com.wjs")
public class SpringConfig {
}
3.4@Bean
-
作用:
该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。
-
属性:
name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。
-
示例代码:
@Configuration
@ComponentScan(basePackages = "com.wjs")
public class SpringConfig {
@Bean
public DataSource getDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUsername("root");
dataSource.setPassword("");
dataSource.setUrl("jdbc:mysql:///数据库?characterEncoding=utf8");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//设置dataSource属性
sqlSessionFactoryBean.setDataSource(dataSource);
//设置typeAliasesPackage
sqlSessionFactoryBean.setTypeAliasesPackage("com.wjs.pojo");
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.wjs.dao");
return mapperScannerConfigurer;
}
}
3.5通过注解获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
4.Spring整合junit(了解)
4.1目标
在测试类中,每个测试方法都有以下两行代码:
ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);
AccountService as= ac.getBean("accountService",AccountService.**class**);
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
4.2分析
早前我们测试业务逻辑,一般都使用Junit 框架来测试, 其实在spring框架里面它也做出来了自己的一套测试逻辑, 里面是对junit进行了整合包装。让我们在执行spring的单元测试上面,可以少写一些代码.
- 导入Spring整合单元测试的坐标
- 在测试类上面添加注解(指定运行的环境, 加载配置文件或者配置类)
4.3实现
-
导入spring整合Junit的坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency>
-
在测试类上面标记注解
package com.itheima.test; import com.wjs.controller.AccountController; import com.wjs.pojo.Account; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.sql.SQLException; import java.util.List; /** * 包名:com.wjs.test * * 直接在测试用例中,注入要使用的对象AccountController * 1. 我们自己不创建核心容器,那么我们就应该交给别人(Junit)去创建核心容器 * 1. 引入spring整合Junit的依赖 spring-test * 2. 保证Junit的版本是4.12以及以上的版本 * 3. 在单元测试类上添加RunWith注解 * @RunWith(SpringJUnit4ClassRunner.class) * 4. 指定有Junit创建核心容器的时候,要加载的配置文件/配置类 * @ContextConfiguration(locations = "classpath:applicationContext.xml") 混合开发 * @ContextConfiguration(classes = 配置类名.class) 纯注解开发 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class TestSpring { @Autowired private AccountController accountController; @Test public void testFindAll() throws SQLException { List<Account> accountList = accountController.findAll(); System.out.println(accountList); } }
4.3小结
- 添加Spring整合单元测试的坐标
- 在测试类上面添加注解
- @RunWith(SpringJUnit4ClassRunner.class) 指定运行的环境
- @ContextConfiguration() 加载配置文件或者配置类的
5.混合开发方式补充
5.1知识点-import标签
使用import标签导入外部的xml配置文件
实现
<import resource="classpath:spring-mybatis.xml"/>
知识点-引入外部properties文件
引入外部的properties属性文件
实现
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--1. 创建DataSource对象-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
五.AOP
1.基本名词解释
连接点: 所有可以被增强的方法。
切入点: 已经在项目中真正配置的方法。(例如一个项目有增删改查,但是只对增加方法进行配置增强)
通知: 用于增强切入点的方法。
通知有----->1.前置通知,2.后置通知,3.环绕通知,4.异常通知,5.最终通知。
切面: 存放通知的类。
execution表达式: expression是切入点表达式,它的作用是用一个表达式描述切入点
如图:
第一个 * 表示所有返回值。如果写void,表达找到所有返回值是void的方法。
第二个表示找到一个类。
第三个 .* 表达所有方法名
第四个 (…) 表达方法里面可以有参数或则没有参数都可以,有参数可以是任意类型。(*)则表示方法里面必须有参数。这里的方法都是切入点的方法
2.Aop几个通知的介绍
2.1 后置通知
<aop:after-returning method="" pointcut-ref="" returning=""/>
如果配置的方法中形参有值则,在所有切入点的方法中,只有有返回值的方法才能被用到后置通知,否则都可以用到后置通知。
2.2 Aop异常通知
切入点出现异常时候执行的操作。常用的是把异常信息写入到本地文件中,但是后来被很多的日志工具代替了。
切入点:
public void error(Throwable errorMsg){
//使用流将异常信息写入到本地磁盘文件中
System.out.println("将"+errorMsg.getMessage()+"写入到磁盘");//此处省略了写入到磁盘操作,相当于日志打印
}
异常配置:
<aop:after-throwing method="error" pointcut-ref="p1" throwing="errorMsg"/>
2.3 最终通知
在执行所有方法之后,无论是否出现异常,均执行的代码片段。
切面类中代码如下:
public void finalM(){
System.out.println("hi");
}
配置如下:
<aop:after method="finalM" pointcut-ref="p1"/>
2.4环绕通知
环绕通知是在通知方法的中间进行执行。需要在方法中传入ProceedingJoinPoint形式参数,然后.proceed执行切入点
切入点:
@Override
public String query() {
try {
Thread.sleep(3000);
System.out.println("执行查询...");
return "张三";
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
通知
配置切入点
<aop:pointcut id="p2" expression="execution(String com.wjs.service.impl.UserServiceImpl.query())"/>
配置通知:
<!--配置环绕通知-->
<aop:around method="totalTime" pointcut-ref="p2"/>
3.Aop注解配置
3.1 在spring.xml中加入
<aop:aspectj-autoproxy/>
3.2 在切面中加入注解,并在其中配置切入点以及通知。
以下面的代码为例
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(* com.wjs.service.impl.UserServiceImpl.*(..))")
public void p1(){
}
@Pointcut("execution(String com.wjs.service.impl.UserServiceImpl.query()))")
public void p2(){
}
@Before("p1()")
public void checkPermission(){
System.out.println("进行权限校验...");
}
@AfterReturning(value = "p1()",returning = "reback")
public void afterMehod(String reback){
System.out.println("后置方法执行");
}
public void getBack(){
System.out.println("执行完毕,返回值为...");
}
public void error(Throwable errorMsg){
//使用流将异常信息写入到本地磁盘文件中
System.out.println("将"+errorMsg.getMessage()+"写入到磁盘");//此处省略了写入到磁盘操作,相当于日志打印
}
@After("p1()")
public void finalM(){
System.out.println("hi");
}
//环绕通知,统计代码执行时间
@Around("p2()")
public Object totalTime(ProceedingJoinPoint joinPoint){
// 1. 获取当前时间
long startTime = System.currentTimeMillis();
// 2.执行切入点
try {
Object proceed = joinPoint.proceed();
// 3.获取执行完切入点之后的时间
long endTime = System.currentTimeMillis();
System.out.println("方法的总执行时间是" + (endTime - startTime));
return proceed+"sb";
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
3.3 删除配置文件中的Aop配置信息
六. Spring中的JdbcTemplate 【了解】
<!-- 配置HikariCP数据库连接池 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="jdbcUrl" value="jdbc:mysql:///spring_jdbc" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
七. Spring中的事务
1.编程式(通过代码)(硬编码)事务【了解】
- 持久层不同的框架有不同的事务控制方案:
-
- 原始的JDBC:
- 1.1 开启事务: connection.setAutoCommit(false);
- 1.2 提交事务: connection.commit();
- 1.3 回滚事务: connection.rollback();
-
- DBUtils框架: 和原始的JDBC是一样的
-
- mybatis框架: 底层也是使用的JDBC的事务
- 3.1 开启事务: sqlSessionFactory.openSession(); 开启事务
- 3.2 提交事务: sqlSession.commit();
- 3.3 回滚事务: sqlSession.rollback();
-
- 只要某个持久层框架和spring整合了,那么事务相关的操作就都交给spring控制
- 4.1 编程式事务(了解)
- 4.2 声明式事务(重点掌握)
1.创建事务管理者
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
2.给事务管理者注入dataSource
transactionManager .setDataSource(dataSource);
3.创建事务模板
TransactionTemplate transactionTemplate = new TransactionTemplate();
4.给事务模板配置事务管理者
transactionTemplate.setTransactionManager(transactionManager);
6.事务模板执行方法,需要被事务执行的方法放入
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
//1. 转出账户扣款
accountDao.updateAccount(new Account(null,fromName,-money));
//出现异常
int num = 10/0;
//2. 转入账户收款
accountDao.updateAccount(new Account(null,toName,money));
return null;
}
});
}
上面这种配置方式把代码写在业务层不利于业务代码的书写
2.Spring声明式事务-xml配置方式【重点】
Spring的声明式事务的作用是: 无论spring集成什么dao框架,事务代码都不需要我们再编写了
声明式的事务管理的思想就是AOP的思想。面向切面的方式完成事务的管理。声明式事务有两种,xml配置方式和注解方式.
2.1 步骤
- 注册事务管理器, 配置数据源
- 配置事务建议(事务规则: 事务隔离级别, 遇到什么异常回滚,是否只读…)
- 配置AOP
- 配置切入点
- 配置切面
- 配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
-
配置事务建议(事务的规则)
<!--配置事务规则--> <tx:advice id="transferAdvice" transaction-manager="transactionManager"> <!--配置事务的属性--> <tx:attributes> <!-- 哪个方法需要使用什么样的事务配置 rollback-for="java.lang.Exception" 什么时候回滚:遇到所有的Exception都会回滚 no-rollback-for="java.lang.NullPointerException" 什么时候不回滚:遇到NullPointerException不回滚 timeout="-1" 事务的超时时间,默认不超时 read-only="false" 是否是只读事务,默认不是只读事务,只读事务只会针对于查询方法,如果是增删改一定不能设置为只读 isolation="" 事务的隔离级别: 目的是为了防止不同事务之间相互影响 1. Read Uncommitted 读取到未提交的事务,在这种隔离级别下,可能发生脏读、不可重复读、幻读 2. Read Committed(Oracle的默认隔离级别) 读取到已提交的事务,在这种隔离级别下,不可能发生脏读,但是可能发生不可重复读和幻读 3. Repeatable Read(mysql的默认隔离级别) 可重复读,在这种隔离级别下不会发生脏读、不可重复读,但是有可能发生幻读 4. Serializable 串行化的,在这种隔离级别下不会发生脏读、不可重复读、幻读 propagation="" 事务的传播行为 - PROPAGATION_REQUIRED:默认值,也是最常用的场景. 如果当前没有事务,就新建一个事务, 如果已经存在一个事务中,加入到这个事务中。 - PROPAGATION_SUPPORTS: 如果当前没有事务,就以非事务方式执行。 如果已经存在一个事务中,加入到这个事务中。 - PROPAGATION_MANDATORY 如果当前没有有事务,就抛出异常; 如果已经存在一个事务中,加入到这个事务中。 保证不在同一个事务里: - PROPAGATION_REQUIRES_NEW 如果当前有事务,把当前事务挂起,创建新的事务但独自执行 - PROPAGATION_NOT_SUPPORTED 如果当前存在事务,就把当前事务挂起。不创建事务 - PROPAGATION_NEVER 如果当前存在事务,抛出异常 --> <tx:method name="transfer" rollback-for="java.lang.Exception"/> </tx:attributes> </tx:advice>
-
配置事务的AOP
<aop:config> <!--声明切入点--> <aop:pointcut id="pt1" expression="execution(* com.wjs.service.impl.AccountServiceImpl.transfer(..))"/> <!--绑定切入点和事务--> <aop:advisor advice-ref="transferAdvice" pointcut-ref="pt1"></aop:advisor> </aop:config>
3 事务的传播行为
3.1事务的传播行为的作用
我们一般都是将事务设置在Service层,那么当我们调用Service层的一个方法的时候, 它能够保证我们的这个方法中,执行的所有的对数据库的更新操作保持在一个事务中, 在事务层里面调用的这些方法要么全部成功,要么全部失败。
如果你的Service层的这个方法中,除了调用了Dao层的方法之外, 还调用了本类的其他的Service方法,那么在调用其他的Service方法的时候, 我必须保证两个service处在同一个事务中,确保事物的一致性。
事务的传播特性就是解决这个问题的。
3.2 事务的传播行为的取值
保证在同一个事务里面:
-
PROPAGATION_REQUIRED:默认值,也是最常用的场景.
如果当前没有事务,就新建一个事务,
如果已经存在一个事务中,加入到这个事务中。 -
PROPAGATION_SUPPORTS:
如果当前没有事务,就以非事务方式执行。
如果已经存在一个事务中,加入到这个事务中。
-
PROPAGATION_MANDATORY
如果当前没有有事务,就抛出异常;
如果已经存在一个事务中,加入到这个事务中。
保证不在同一个事物里:
-
PROPAGATION_REQUIRES_NEW
如果当前有事务,把当前事务挂起,创建新的事务但独自执行
-
PROPAGATION_NOT_SUPPORTED
如果当前存在事务,就把当前事务挂起。不创建事务
-
PROPAGATION_NEVER
如果当前存在事务,抛出异常
4 spring声明式事务-注解方式【重点】
4.1 步骤
-
注册事务管理器
-
开启事务注解支持
-
在业务类上面添加@Transactional
-
在applicationContext里面打开注解驱动
<?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" xmlns:tx="http://www.springframework.org/schema/tx" 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"> <import resource="classpath:application-mybatis.xml"></import> <!--配置申明式事务--> <!-- 注解方式配置声明式事务 1. 配置事务管理者 2. 加载事务注解驱动 3. 在代码中使用Transactional注解来标注,哪个方法需要使用事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
-
在业务逻辑类上面使用注解
@Transactional : 如果在类上声明,那么标记着该类中的所有方法都使用事务管理。也可以作用于方法上, 那么这就表示只有具体的方法才会使用事务。
4.2两种方式比较
- xml方式
- 优点: 只需要配一次, 全局生效, 容易维护
- 缺点: 相对注解而言 要麻烦一点
- 注解方式
- 优点: 配置简单
- 缺点: 写一个业务类就配置一次, 事务细节不好处理, 不好维护