1.spring概述
1.1.spring的两大核心
- IOC(Inverse Of Control):反转控制
- AOP(Aspect Oriented Programming):面向切面编程
2.spring基于XML的IOC环境搭建
2.1.环境搭建
-
spring的pom.xml配置
<?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"> <modelVersion>4.0.0</modelVersion> <groupId>com.zzx</groupId> <artifactId>spring</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies> </project>
-
在resources目录下新建bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--此处是将两个对象放入spring容器中--> <bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl"/> <bean id="accountDao" class="com.zzx.dao.impl.AccountDaoImpl"/> </beans>
-
代码测试
//获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //根据id获取Bean对象 AccountService as = (AccountService) ac.getBean("accountService"); //此处的getBean()方法在对象名后面还传入了对象的字节码文件,此时无需再强转了 AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);
2.2.核心容器的两个接口引发出的问题:
-
ApplicationContext: 单例对象适用 采用此接口
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
-
BeanFactory: 多例对象使用
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
2.3.spring对bean的管理细节
2.3.1.创建bean的三种方式
-
使用默认构造函数创建:bean标签内只有id和class属性,没有其他属性和标签时采用的就是默认构造函数,此时如果类中没有默认构造函数,则对象会无法创建
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl"/>
-
使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="com.zzx.factory.InstanceFactory"/> <!--下面的factory-bean会找到对应的id的bean标签内的factory工厂,并调用其getAccountService()方法来创建并返回对象,存入spring容器--> <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"/>
-
使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
<bean id="accountService" class="com.zzx.factory.StaticFactory" factory-method="getAccountService"/>
2.3.2.bean对象的作用范围
bean标签的scope属性,作用是指定bean的作用范围,有如下几个取值:(常用的就是单例和多例)
- singleton:默认值,表示单例
- prototype:多例的
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于web应用的集群环境的会话范围,当不是集群环境时,就是session
2.3.3.bean对象的生命周期
-
单例对象**(生命周期与容器相同)**
-
出生:当容器创建时对象就出生了
-
活着:只要容器还在,对象就一直活着
-
死亡:容器销毁,对象就死亡了
-
-
多例对象
- 出生:当我们使用对象时,spring容器才为我们创建该对象
- 活着:对象只要被使用就一直活着
- 死亡:当对象长时间不用,而且没有其他的对象引用时,由java的垃圾回收器回收
3.spring中的依赖注入(DI:Dependency Injection)
3.1.DI的概述
IOC的作用是降低程序之间的耦合(其实就是降低了程序之间的依赖关系),依赖关系的管理都交由spring来维护。在当前类需要使用到其他类的对象,我们只需要在spring配置文件中说明依赖关系的维护,就称之为依赖注入。
3.2.可以注入的数据
3.3.注入的方式
3.3.1.使用构造函数注入
- bean.xml文件的配置
<!--构造函数注入:
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值 常用的
=============以上三个用于指定给构造函数中哪个参数赋值===============================
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="泰斯特"/>
<constructor-arg name="age" value="18"/>
<!--此处的birthday是使用ref属性引用了下面定义的bean,ref属性的值与配置的bean的id值一致-->
<constructor-arg name="birthday" ref="now"/>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"/>
- AccountServiceImpl实现类
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
//利用构造方法注入
public AccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("service中的saveAccount方法执行了。。。" + name + "," + age + "," + birthday);
}
}
- 代码测试
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取Bean对象
AccountService as = (AccountService) ac.getBean("accountService");
as.saveAccount();
//输出结果:service中的saveAccount方法执行了。。。泰斯特,18,Tue Dec 17 09:53:19 CST 2019
3.3.2.使用set方法注入
- bean.xml文件的配置
<!-- set方法注入 更常用的方式
涉及的标签:property
出现的位置:bean标签的内部
标签的属性
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
-->
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl">
<!--此处property标签内name属性的值为set方法的名称而不是成员变量的名称-->
<property name="name" value="TEST" />
<property name="age" value="21"/>
<property name="birthday" ref="now"/>
</bean>
- AccountServiceImpl实现类
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
//只需提供set方法
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("service中的saveAccount方法执行了。。。" + name + "," + age + "," + birthday);
}
}
- 代码测试
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取Bean对象
AccountService as = (AccountService) ac.getBean("accountService");
as.saveAccount();
//输出结果:service中的saveAccount方法执行了。。。TEST,21,Tue Dec 17 10:12:43 CST 2019
3.3.3.复杂类型的注入
- bean.xml的配置
<!-- 复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:
list array set
用于个Map结构集合注入的标签:
map props
结构相同,标签可以互换
-->
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl">
<property name="myStrs">
<!--此处property内的子标签可以互换-->
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="mySet">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="myMap">
<props>
<prop key="testC">ccc</prop>
<prop key="testD">ddd</prop>
</props>
</property>
<property name="myProps">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
</bean>
- AccountServiceImpl实现类
public class AccountServiceImpl implements AccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
public void saveAccount() {
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(myProps);
}
}
- 代码测试
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取Bean对象
AccountService as = (AccountService) ac.getBean("accountService");
as.saveAccount();
/*
输出结果:
[AAA, BBB, CCC]
[AAA, BBB, CCC]
[AAA, BBB, CCC]
{testD=ddd, testC=ccc}
{testA=aaa, testB=BBB}
*/
3.3.4.使用注解注入
4.常用IOC注解
4.1.按照作用分类
- 使用注解得先在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">
<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为
context名称空间和约束中-->
<context:component-scan base-package="com.zzx"/>
</beans>
- 曾经XML的配置:
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl"
scope="" init-method="" destroy-method="">
<property name="" value="" | ref=""/>
</bean>
4.1.1.用于创建对象
他们的作用就和在XML配置文件中编写一个标签实现的功能是一样的
- @Component:
-
作用:用于把当前类对象存入spring容器中
-
属性value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
- @Controller:一般用在表现层
- @Service:一般用在业务层
- @Repository:一般用在持久层
以上三个注解他们的作用和属性与Component是一模一样。
他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
4.1.2.用于注入数据的
他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的
- @Autowired:
- 作用:自动按照类型注入。
只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中有多个类型匹配时:则通过变量名称来匹配要注入哪个对象
-
出现位置:可以是变量上,也可以是方法上
-
细节:在使用注解注入时,set方法就不是必须的了。
- @Qualifier:(解决使用Autowired注入时,Ioc容器中有多个类型匹配)
-
作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以
-
属性value:用于指定注入bean的id。
- @Resource(相当于Autowired和Qualifier的结合)
-
作用:直接按照bean的id注入。它可以独立使用
-
属性name:用于指定bean的id。
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过XML来实现。
- @Value:
-
作用:用于注入基本类型和String类型的数据
-
属性:
value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
SpEL的写法:${表达式}
4.1.3.用于改变作用范围的
他们的作用就和在bean标签中使用scope属性实现的功能是一样的
@Scope
- 作用:用于指定bean的作用范围
- 属性value:指定范围的取值。常用取值:singleton prototype,默认单例
4.1.4.和生命周期相关
他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
@PreDestroy
- 作用:用于指定销毁方法
@PostConstruct
- 作用:用于指定初始化方法
5.spring基于xml配置实现单表的crud
- pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzx</groupId>
<artifactId>spring_withoutxml</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
- bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置Service -->
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"/>
</bean>
<!--配置Dao对象-->
<bean id="accountDao" class="com.zzx.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="runner" ref="runner"/>
</bean>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--注入数据源-->
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///zzx"/>
<property name="user" value="root"/>
<property name="password" value="123"/>
</bean>
</beans>
6.spring基于注解实现单表的crud(没有xml)
- 新增配置类:SpringConfiguration,该类的作用等同于bean.xml
- 几个新注解:
- @Configuration
-
作用:指定当前类是一个配置类
-
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
- @ComponentScan
-
作用:用于通过注解指定spring在创建容器时要扫描的包
-
属性value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
我们使用此注解就等同于在xml中配置了:
<context:component-scan base-package="com.zzx">
- @Bean
- 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
-
属性name:用于指定bean的id。当不写时,默认值是当前方法的名称
-
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
查找的方式和Autowired注解的作用是一样的
- @Import
- 作用:用于导入其他的配置类
- 属性value:用于指定其他配置类的字节码。
- 当我们使用Import的注解之后,有Import注解的类就是父配置类,而导入的都是子配置类
- @PropertySource
- 作用:用于指定properties文件的位置
- 属性value:指定文件的名称和路径。
- 关键字:classpath,表示类路径下
SpringConfiguration.class
//@Configuration //说明该类是一个配置类,但该注解不是必须的,当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写
@ComponentScan(basePackages = "com.zzx") //配置扫描的包路径
@Import(JdbcConfig.class) //导入jdbc的子配置类
@PropertySource("classpath:jdbcConfig.properties") //加了classpath关键字以后代表该路径是类路径
public class SpringConfiguration {
}
jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///zzx
jdbc.username=root
jdbc.password=123
JdbcConfiguration.class
//由于在SpringConfiguration.class中使用了@Import注解,在此处就无需再加@Configuration注解了
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name = "runner") //将QueryRunner对象创建之后存入spring的ioc容器中
@Scope("prototype") //说明创建的该对象是多例的
//此处参数上的注解Qualifer的值为下面配置的dataSource的name属性的值
public QueryRunner createQueryRunner(@Qualifer("dataSource") DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource") //连接数据库的配置
public DataSource createDataSource() {
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
测试类:
@Test
public void testFindAll() {
//ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//使用注解时应该使用AnnotationConfigApplicationContext这个对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
AccountDao accountDao = context.getBean("accountDao", AccountDao.class);
List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
7.AOP
7.1AOP概述
AOP全称是:Aspect Oriented Programmer ,即面向切面编程
7.2.AOP的作用及优势
-
作用:在程序运行期间,不修改源码对已有方法进行增强
-
优势:
减少重复代码
提高开发效率
维护方便
7.3.AOP相关术语
-
Joinpoint(连接点):连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
-
Pointcut(切入点):切入点指的是我们要对哪些Joinpoint进行拦截的定义,即被增强的方法就是切入
由此可知:所有的切入点都是连接点,但并不是所有的连接点都是切入点,只有被增强过的连接点才是切入点
-
Advice(通知/增强):拦截到 Joinpoint 之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
-
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。(了解)
-
Target(目标对象):代理的目标对象。
-
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
-
Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
-
Aspect(切面):是切入点和通知(引介)的结合
7.4.spring基于xml实现AOP
需要引入Spring和aop的相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
bean.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: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">
<!--spring中基于XML的AOP配置步骤
1、把通知Bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的Id。
4、在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
public void com.zzx.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void com.zzx.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* com.zzx.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
* *.*.*.*.AccountServiceImpl.saveAccount())
包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
* *..*.*()
参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
全通配写法:
* *..*.*(..)
实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法
* com.zzx.service.impl.*.*(..)
-->
<!-- 配置srping的Ioc,把service对象配置进来-->
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl"/>
<!-- 配置Logger类 -->
<bean id="logger" class="com.zzx.utils.Logger"/>
<!--配置AOP-->
<aop:config>
<!--配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<aop:before method="printLog" pointcut="execution(* com.zzx.service.impl.*.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
7.5.四种常用通知类型
<aop:config>
<!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
此标签写在aop:aspect标签内部只能当前切面使用。
它还可以写在aop:aspect外面,此时就变成了所有切面可用
-->
<aop:pointcut id="pt1" expression="execution(* com.zzx.service.impl.*.*(..))"/>
<!--配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置前置通知:在切入点方法执行之前执行
<aop:before method="beforePrintLog" pointcut-ref="pt1" />-->
<!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"/>-->
<!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"/>-->
<!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
<!-- 配置环绕通知 详细的注释请看Logger类中-->
<aop:around method="aroundPringLog" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
7.6.spring基于注解实现AOP
- bean.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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.zzx"/>
<!-- 配置spring开启注解AOP的支持 -->
<aop:aspectj-autoproxy/>
</beans>
- logger通知类
@Component("logger")
@Aspect//表示当前类是一个切面类
public class Logger {
@Pointcut("execution(* com.zzx.service.impl.*.*(..))") //切入点表达式
private void pt1(){}
//前置通知,注意:引用切入点时一定要加括号,即写成pt1会报错
@Before("pt1()")
public void beforePrintLog(){
System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
}
//后置通知
@AfterReturning("pt1()")
public void afterReturningPrintLog(){
System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
}
//异常通知
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
}
//最终通知
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
}
@Around("pt1()")
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
}
8.spring基于XML的声明式事务控制
- bean.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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置业务层-->
<bean id="accountService" class="com.zzx.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.zzx.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///zzx"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!-- spring中基于XML的声明式事务控制配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性
是在事务的通知tx:advice标签的内部
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务的属性
isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
<!-- 配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.zzx.service.impl.*.*(..))"/>
<!--建立切入点表达式和事务通知的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
</beans>
9.spring基于注解的声明式事务控制
bean.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.zzx"/>
<!-- 配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置数据源-->
<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/eesy"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
<!-- spring中基于注解 的声明式事务控制配置步骤
1、配置事务管理器
2、开启spring对注解事务的支持
3、在需要事务支持的地方使用@Transactional注解
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>