第19章Spring核心之AOP
19.1 AOP概述
AOP:Aspect Oriented Programming,中文翻译为”面向切面编程“。面向切面编程是一种编程范式,它作为OOP面向对象编程的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、权限控制、缓存控制、日志打印等等。AOP采取横向抽取机制,取代了传统纵向继承体系的重复性代码
AOP把软件的功能模块分为两个部分:核心关注点和横切关注点。业务处理的主要功能为核心关注点,而非核心、需要拓展的功能为横切关注点。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点进行分离
使用AOP有诸多好处,如:
1.集中处理某一关注点/横切逻辑
2.可以很方便的添加/删除关注点
3.侵入性少,增强代码可读性及可维护性
19.1.1 了解AOP
AOP的术语
- 1.Join point(连接点)
Spring 官方文档的描述:
A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
程序执行过程中的一个点,如方法的执行或异常的处理。在Spring AOP中,连接点总是表示方法的执行。通俗的讲,连接点即表示类里面可以被增强的方法
- 2.Pointcut(切入点)
Pointcut are expressions that is matched with join points to determine whether advice needs to be executed or not. Pointcut uses different kinds of expressions that are matched with the join points and Spring framework uses the AspectJ pointcut expression language
切入点是与连接点匹配的表达式,用于确定是否需要执行通知。切入点使用与连接点匹配的不同类型的表达式,Spring框架使用AspectJ切入点表达式语言。我们可以将切入点理解为需要被拦截的Join point
- 3.Advice(增强/通知)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知和环绕通知(切面要完成的功能) - 4.Aspect(切面)
Aspect切面表示Pointcut(切入点)和Advice(增强/通知)的结合
19.1.2 AOP的简单实现
- 1.创建web程序,导入cglib-2.2.jar、commons-logging-1.0.4.jar、spring-aop-3.2.0.RELEASE.jar、spring-core-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar、spring-context-3.2.0.RELEASE.jar、spring-context-support-3.2.0.RELEASE.jar、spring-webmvc.jar、spring.jar - 2.创建Target.java类,它是被代理的目标对象,其中有一个execute()方法,它可以专注于自己的智能,此处使用AOP对execute()方法做日子输出
public class Target {
//程序执行的方法
public void execute(String name){
System.out.println("程序开始执行--"+name);
}
}
- 3.创建通知 LoggerExecute.java
public class LoggerExecute implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
before();//执行前置通知
invocation.proceed();
return null;
}
private void before() {
System.out.println("程序开始执行!");
}
}
- 4.创建代理操作类,Manger.java
public class Manger {
//此处使用junit测试
@Test
public void test(){
Target target=new Target(); //创建目标对象
ProxyFactory proxyFactory=new ProxyFactory();
proxyFactory.addAdvice(new LoggerExecute());
proxyFactory.setTarget(target);
Target target2=(Target) proxyFactory.getProxy();
target2.execute("AOP的简单实现");//代理执行execute()方法
}
}
执行后控制台输出
程序开始执行!
程序开始执行--AOP的简单实现
19.2 Spring的切入点
19.2.1 静态切入点与动态切入点
- 静态切入点
静态往往意味着不变,例如一个对象的方法签名是固定不变的,无论在程序的任何位置调用,方法名都不会改变,静态切入点可以为对象的方法签名,例如在某个对象调用了execute()方法时,该方法就可以是静态切入点,静态切入点需要在配置文件时指定,关键配置如下:
<bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="MyAdvisor"/>//指定通知
</property>
<property name="patterns">
<list>
<value>.*getConn*.</value>//指定所有以getConn开头的方法名都是切入点
<value>.*closeConn*.</value>
</list>
</property>
</bean>
- 动态切入点
动态切入点是相对于静态切入点的。
19.2.2 深入静态切入点
19.2.3 深入切入点底层
19.2.4 Spring中其他切入点
19.3 Aspect对AOP的支持
19.3.1 了解Aspect
19.3.2 Spring中的Aspect
19.3.3 DefaultPointcutAdvisor切入点配置器
19.3.4 NameMatchMethodPointcutAdvisor切入点配置
19.4 Spring持久化
19.4.1 DAO模式介绍
1 Spring DAO的概念
什么是DAO
DAO(Data Access Object)是用于访问数据的对象,虽然在大多数情况下将数存在数据库中,但这并不是唯一的选择,也可以将数据存储到文件中或LDAP中。DAO不但屏蔽了数据存储的最终介质的不同,也屏蔽了具体的实现技术的不同。提供DAO层的抽象可以带来一些好处:可以很容易地构造模拟对象,方便单元测试的开展;在使用切面时会有更多的选择,既可以使用JDK动态代理,又可以使用 CGLib动态代理。
Spring DAO的内容
*Spring对多个持久化技术提供了集成支持,包括 Hibernate、 MyBatis、JPA、JDO;
*Spring提供一个简化JDBC API操作的Spring JDBC框架。
*Spring面向DAO制定了一个通用的异常体系,屏蔽具体持久化技术的异常,使业务层和具体的持久化技术实现解耦。
*Spring提供了模板类简化各种持久化技术的使用
19.4.2 Spring的DAO理念
- 1.创建web工程,引入如下jar包commons-logging-1.0.4.jar、mysql-connector-java-5.1.38.jar、spring-aop-3.2.0.RELEASE.jar、spring-web.jar、spring-webmvc.jar、spring.jar
- 2.实体类,Book.java
public class Book {
private int id;
private String name;
private double price;
private int bookCount;
private String author;
...省略setter()和getter()方法
}
- 3.创建接口类
public interface BookDaoImpl {
public void inserBook(Book book);//添加信息
}
- 4.编写实现BookDaoImpl接口的BookDao类并实现方法
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.hwp.spring.bean.Book;
public class BookDao implements BookDaoImpl{
private DataSource dataSource;//注入DataSource
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void inserBook(Book book) {
String name = book.getName();//获取书籍名字
double price = book.getPrice();//获取价格
int bookCount = book.getBookCount();//获取数量
String author = book.getAuthor();//或者作者
Connection connection = null ;//定义Connection
Statement statement = null;//定义Statement
try {
connection = dataSource.getConnection();//获取数据库连接
statement = connection.createStatement();
statement.execute("INSERT INTO book_info (name,price,bookCount,author) "
+ "VALUES('"+ name + "','" + price +"','"+bookCount+"','"+author+ "')");//添加数据的SQL语句
} catch (SQLException e) {
e.printStackTrace();
System.out.println(e.toString());
}
finally {
if(statement != null) {
try {
statement.close();//关闭Statement对象
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(connection != null) {
try {
connection.close();//关闭数据库连接
}
catch(SQLException e) {
e.printStackTrace();
}
}
}
}
}
- 5.配置applicationContext.xml,存放在源码目录下,即src目录下
<?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-2.5.xsd">
<!-- 配置数据源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<!-- 避免乱码 需要这样?characterEncoding=utf-8-->
<value>jdbc:mysql://localhost:3306/book?characterEncoding=utf-8
</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>hwp123456</value>
</property>
</bean>
<!-- 为UserDAO注入数据源 -->
<bean id="bookDAO" class="com.hwp.spring.dao.BookDao">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
</beans>
- 6.编写manger.java操作类
public class Manger {
//使用junit测试
@Test
public void main() {
Resource resource=new ClassPathResource("applicationContext.xml");
BeanFactory beanFactory=new XmlBeanFactory(resource);
Book book=new Book();
book.setAuthor("中文测试");
book.setBookCount(60);
book.setName("我是作者");
book.setPrice(58.0);
BookDao bookDao= (BookDao) beanFactory.getBean("bookDAO");
bookDao.inserBook(book);
System.out.println("添加数据成功");
}
}
运行后,就能往数据库添加一条数据。
19.4.3 事务应用的管理
- 编程式事务管理
- 1.创建web工程,引入如下jar包commons-logging-1.0.4.jar、mysql-connector-java-5.1.38.jar、spring-aop-3.2.0.RELEASE.jar、spring-web.jar、spring-webmvc.jar、spring.jar
- 2.配置applicationContext.xml,存放在源码目录下,即src目录下
<?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-2.5.xsd">
<!-- 配置数据源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/db_database13
</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>111</value>
</property>
</bean>
<!-- 定义TransactionTemplate模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="propagationBehaviorName">
<value>PROPAGATION_REQUIRED</value>
</property>
</bean>
<!-- 定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- 为TransactionExample注入数据源 、事务管理器、TransactionTemplate模板-->
<bean id="transactionExample"
class="com.mr.transaction.TransactionExample">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionTemplate">
<ref bean="transactionTemplate"/>
</property>
</bean>
</beans>
- 3.创建类TransationExample,定义数据添加的方法,在方法中执行两次数据库添加的操作,并用事务对操作进行保护。
public class TransactionExample {
DataSource dataSource;//注入数据源
PlatformTransactionManager transactionManager;//注入事务管理器
TransactionTemplate transactionTemplate;//注入TransactionTemplate模板
...//省略setter()和getter()方法
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public TransactionTemplate getTransactionTemplate() {
return transactionTemplate;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transactionOperation() {
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
Connection conn = DataSourceUtils.getConnection(dataSource);//获得数据库连接
try {
Statement stmt = conn.createStatement();
//执行两次添加方法
stmt.execute("insert into tb_user(name,age,sex) values('小强','26','男')");
int a=0;//制造异常测试事务是否配置成功
a=9/a;
stmt.execute("insert into tb_user(name,age,sex) values('小红','22','女')");
System.out.println("操作执行成功!");
} catch (Exception e) {
transactionManager.rollback(status);//事务回滚
System.out.println("操作执行失败,事务回滚!");
System.out.println("原因:"+e.getMessage());
}
return null;
}
});
}
}
- 4.创建Manger.java,
public class Manger {
public static void main(String[] args) {
Resource resource = new ClassPathResource("applicationContext.xml"); //装载配置文件
BeanFactory factory = new XmlBeanFactory(resource);
TransactionExample transactionExample = (TransactionExample) factory.getBean("transactionExample");//获取UserDAO
transactionExample.transactionOperation();//执行添加方法
}
}
编码方式实现事务管理(代码演示为JDBC事务管理)
Spring实现编程式事务,依赖于2大类,分别是上篇文章提到的PlatformTransactionManager,与模版类TransactionTemplate(推荐使用)。
- 声明式事务管理
声明式事务实现方式主要有2种,一种为通过使用Spring的<tx:advice>定义事务通知与AOP相关配置实现,另为一种通过@Transactional实现事务管理实现
19.4.4 应用JdbcTemplate操作数据库
Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中。
JdbcTemplate位于中spring-jdbc-3.2.0.RELEASE.jar。其全限定命名为org.springframework.jdbc.core.JdbcTemplate。要使用JdbcTemlate还需一个spring-tx-3.2.0.RELEASE.jar这个包包含了一下事务和异常控制
JdbcTemplate主要提供以下五类方法:
- execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
- update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
- query方法及queryForXXX方法:用于执行查询相关语句;
- call方法:用于执行存储过程、函数相关语句。