目录
一、AOP概念的引入
1、编写入门案例,完成AOP概念的引入
创建Maven项目,创建对应的包,在pom.xml中引入对应的jar包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--有单元测试的环境,Spring5版本,Junit4.12版本-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--连接池-->
<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>8.0.28</version>
</dependency>
<!--Spring整合Junit测试的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--SpringAspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
导入TxUtils类,进行事务管理
package com.qcby.utils;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 事务的工具类
*/
public class TxUtils {
private static DruidDataSource ds = null;
// 使用ThreadLocal存储当前线程中的Connection对象
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
// 在静态代码块中创建数据库连接池
static {
try {
// 通过代码创建C3P0数据库连接池
ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC&useSSL=false");
ds.setUsername("root");
ds.setPassword("lrh123456");
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* @Method: getConnection
* @Description: 从数据源中获取数据库连接
* @Anthor:
* @return Connection
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn == null) {
// 从数据源中获取数据库连接
conn = getDataSource().getConnection();
// 将conn绑定到当前线程
threadLocal.set(conn);
}
return conn;
}
/**
* @Method: startTransaction
* @Description: 开启事务
* @Anthor:
*
*/
public static void startTransaction() {
try {
Connection conn = threadLocal.get();
if (conn == null) {
conn = getConnection();
// 把 conn绑定到当前线程上
threadLocal.set(conn);
}
// 开启事务
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: rollback
* @Description:回滚事务
* @Anthor:
*/
public static void rollback() {
try {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn != null) {
// 回滚事务
conn.rollback();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: commit
* @Description:提交事务
* @Anthor:
*/
public static void commit() {
try {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn != null) {
// 提交事务
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: close
* @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)
* @Anthor:
*
*/
public static void close() {
try {
// 从当前线程中获取Connection
Connection conn = threadLocal.get();
if (conn != null) {
conn.close();
// 解除当前线程上绑定conn
threadLocal.remove();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Method: getDataSource
* @Description: 获取数据源
* @Anthor:
* @return DataSource
*/
public static DataSource getDataSource() {
// 从数据源中获取数据库连接
return ds;
}
}
AccountServiceImpl代码如下
package com.qcby.service.impl;
import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
import java.sql.SQLException;
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao){
this.accountDao = accountDao;
}
public void saveAll(Account account1, Account account2) throws SQLException {
//保存1账号
accountDao.save(account1);
//模拟异常
// int a = 1/0;
//保存2账号
accountDao.save(account2);
}
/**
* 保存2个
* @param account1
* @param account2
*/
// @Override
// public void saveAll(Account account1,Account account2){
// try{
// //开启事务
// TxUtils.startTransaction();
// //保存1账号
// accountDao.save(account1);
// //模拟异常
// int a = 1/0;
// //保存2账号
// accountDao.save(account2);
// //提交事务/回滚事务
// TxUtils.commit();
// }catch (Exception e){
// //打印异常信息
// e.printStackTrace();
// //回滚事务
// TxUtils.rollback();
// }finally {
// //关闭资源
// TxUtils.close();
// }
// }
}
对应的接口代码AccountService
package com.qcby.service;
import com.qcby.model.Account;
import java.sql.SQLException;
public interface AccountService {
public void saveAll(Account account1,Account account2) throws SQLException;
}
AccountDaoImpl代码如下
package com.qcby.dao.impl;
import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.utils.TxUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class AccountDaoImpl implements AccountDao{
/**
*保存
* @param account
*/
@Override
public void save(Account account) throws SQLException {
System.out.println("持久层:保存账户...");
//把数据储存到数据库中
//先获取到链接
Connection conn = TxUtils.getConnection();
//编写sql语句
String sql = "insert into account values (null,?,?)";
//预编译SQL语句
PreparedStatement stmt = conn.prepareStatement(sql);
//设置值
stmt.setString(1,account.getName());
stmt.setDouble(2,account.getMoney());
//执行操作
stmt.executeUpdate();
//关闭资源,conn不能关闭
stmt.close();
}
}
对应的接口代码AccountDao
package com.qcby.dao;
import com.qcby.model.Account;
import java.sql.SQLException;
public interface AccountDao {
public void save(Account account) throws SQLException;
}
手写一个代理对象JdkPoxy
package com.qcby.utils;
import com.qcby.service.AccountService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkPoxy{
public static Object getPoxy(final AccountService accountService){
Object proxy = Proxy.newProxyInstance(JdkPoxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
TxUtils.startTransaction();
result = method.invoke(accountService, args);
TxUtils.commit();
} catch (Exception e) {
e.printStackTrace();
TxUtils.rollback();
} finally {
TxUtils.close();
}
return result;
}
});
return proxy;
}
}
测试方法
package com.qcby.test;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
import com.qcby.utils.JdkPoxy;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;
public class Demo1 {
@Test
public void run() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) ac.getBean("accountService");
Account account1 = new Account();
account1.setName("喜羊羊");
account1.setMoney(1000d);
Account account2 = new Account();
account2.setName("美羊羊");
account2.setMoney(1000d);
// accountService.saveAll(account1,account2);
AccountService poxy = (AccountService) JdkPoxy.getPoxy(accountService);
poxy.saveAll(account1,account2);
}
}
这样就写完了入门案例
二、AOP相关的概念
1.AOP的概述
AOP技术:面向切面编程
AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到 框架中,必须遵守AOP联盟的规范
通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要 内容,是函数式编程的一种衍生范型
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间 的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP:面向切面编程.(思想.---解决OOP遇到一些问题)
AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务 管理、安全检查、缓存)
为什么要学习AOP,可以在不修改源代码的前提下,对程序进行增强!!
2. AOP的优势
运行期间,不修改源代码的情况下对已有的方法进行增强
优势 1. 减少重复的代码 2. 提供开发的效率 3. 维护方便
3. AOP的底层原理
JDK 的动态代理技术 1、为接口创建代理类的字节码文件 2、使用ClassLoader 将字节码文件加载到JVM 3、创建代理类实例对象,执行对象的目标方法
cglib 代理技术
三、Spring的AOP技术-配置文件方式
1. AOP相关的术语
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的 是方法,因为spring只支持方法类型的连接点
Pointcut(切入点)-- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知. 通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象)-- 代理的目标对象
Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程
Proxy(代理)--一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)--是切入点和通知的结合,以后咱们自己来编写和配置的
2.AOP配置文件方式的入门
创建maven项目,坐标依赖
pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<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>
</dependency>
<!--AOP联盟-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--SpringAspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
创建Spring的配置文件,引入具体的AOP的schema约束
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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/beans
http://www.springframework.org/schema/beans/spring-be
ans.xsd
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">
</beans>
创建包结构,编写具体的接口和实现类
UserServiceImpl代码
package com.qcby.demo2;
public class UserServiceImpl implements UserService{
public void save() {
// int a = 10/0;
System.out.println("业务层:保存用户...");
}
}
对应接口UserService
package com.qcby.demo2;
public interface UserService {
public void save();
}
将目标类配置到Spring中
<bean id="userService" class="com.qcby.demo2.UserServiceImpl"/>
定义切面类MyXmlAspect
package com.qcby.demo2;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 自定义切面类 = 切入点(表达式)+通知(增强代码)
*/
public class MyXmlAspect {
/**
* 通知
*/
public void log(){
//发送手机短信
//发送邮件/记录日志/事务管理
System.out.println("增强的方法执行了...");
}
/**
* 环绕通知
* 问题:目标对象的方法没有执行,需要手动执行目标对象的方法
*/
public void aroundLog(ProceedingJoinPoint point){
try{
System.out.println("环绕方法一...");
//让目标对象的方法去执行
point.proceed();
System.out.println("环绕方法二...");
}catch (Throwable throwable){
throwable.printStackTrace();
//有异常时输出
System.out.println("环绕方法三....");
}finally {
System.out.println("环绕方法四...");
}
}
}
在配置文件中定义切面类
<!--定义切面类-->
<bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>
在配置文件中完成aop的配置
<!--配置Aop增强-->
<aop:config>
<!--配置切面 = 切入点 + 通知组成-->
<aop:aspect ref="myXmlAspect">
<!--前置通知(aop:before):UserServiceImpl的save方法执行前,会增强-->
<!--<aop:before method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--后置通知(after-returning):UserServiceImpl的save方法执行后,会增强-->
<!--<aop:after-returning method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--最终通知(after):UserServiceImpl的save方法执行成功或者失败,会进行增强-->
<!--<aop:after method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--异常通知:只有在UserServiceImpl的save方法执行失败,进行通知,成功是不会通知-->
<!--<aop:after-throwing method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--环绕通知(around):UserServiceImpl的save方法执行前后,都会进行增强,目标对象的方法需要手动执行。-->
<aop:around method="aroundLog" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
测试
package com.qcby.test;
import com.qcby.demo2.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo2 {
@Test
public void run1(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = context.getBean("userService",UserService.class);
userService.save();
}
}
3.切入点的表达式
再配置切入点的时候,需要定义表达式,具体展开如下:
<!--前置通知:UserServiceImpl的save方法执行前,会增强-->
<!--<aop:before method="log" pointcut="execution(public void cn.tx.service.UserServiceImpl.sava())" />-->
<!--<aop:after-returning method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.sava*(..))" />-->
<!--<aop:after-throwing method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.sava*(..))" />-->
<!--
切入点的表达式
execution() 固定的写法
public 是可以省略不写的
方法的返回值 int String 通用的写法,可以编写 * 不能省略不写的
包名+类名 不能省略不写的,编写 * UserServiceImpl AccountServiceImpl
方法名称 save() 可以写 *
参数列表 (..) 表示任意类型和个数的参数
比较通用的表达式:execution(public * com.qcby.*.*ServiceImpl.*(..))
<aop:before method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />
上面的意思就是增强com.qcby包下面的所有包里面的后缀是ServiceImpl的所有实现类中的方法名为save里面的任意类型和个数的参数
-->
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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.qcby.demo2.UserServiceImpl"/>
<!--定义切面类-->
<bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>
<!--配置Aop增强-->
<aop:config>
<!--配置切面 = 切入点 + 通知组成-->
<aop:aspect ref="myXmlAspect">
<!--前置通知(aop:before):UserServiceImpl的save方法执行前,会增强-->
<!--<aop:before method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--后置通知(after-returning):UserServiceImpl的save方法执行后,会增强-->
<!--<aop:after-returning method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--最终通知(after):UserServiceImpl的save方法执行成功或者失败,会进行增强-->
<!--<aop:after method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--异常通知:只有在UserServiceImpl的save方法执行失败,进行通知,成功是不会通知-->
<!--<aop:after-throwing method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--环绕通知(around):UserServiceImpl的save方法执行前后,都会进行增强,目标对象的方法需要手动执行。-->
<aop:around method="aroundLog" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
<!--前置通知:UserServiceImpl的save方法执行前,会增强-->
<!--<aop:before method="log" pointcut="execution(public void cn.tx.service.UserServiceImpl.sava())" />-->
<!--<aop:after-returning method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.sava*(..))" />-->
<!--<aop:after-throwing method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.sava*(..))" />-->
<!--
切入点的表达式
execution() 固定的写法
public 是可以省略不写的
方法的返回值 int String 通用的写法,可以编写 * 不能省略不写的
包名+类名 不能省略不写的,编写 * UserServiceImpl AccountServiceImpl
方法名称 save() 可以写 *
参数列表 (..) 表示任意类型和个数的参数
比较通用的表达式:execution(public * com.qcby.*.*ServiceImpl.*(..))
<aop:before method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />
上面的意思就是增强com.qcby包下面的所有包里面的后缀是ServiceImpl的所有实现类中的方法名为save里面的任意类型和个数的参数
-->
</beans>
4.AOP的通知类型
1.前置通知目标方法执行前,进行增强。
2.最终通知目标方法执行成功或者失败,进行增强。
3.后置通知目标方法执行成功后,进行增强。
4.异常通知目标方法执行失败后,进行增强。
5.环绕通知目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.qcby.demo2.UserServiceImpl"/>
<!--定义切面类-->
<bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>
<!--配置Aop增强-->
<aop:config>
<!--配置切面 = 切入点 + 通知组成-->
<aop:aspect ref="myXmlAspect">
<!--前置通知(aop:before):UserServiceImpl的save方法执行前,会增强-->
<!--<aop:before method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--后置通知(after-returning):UserServiceImpl的save方法执行后,会增强-->
<!--<aop:after-returning method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--最终通知(after):UserServiceImpl的save方法执行成功或者失败,会进行增强-->
<!--<aop:after method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--异常通知:只有在UserServiceImpl的save方法执行失败,进行通知,成功是不会通知-->
<!--<aop:after-throwing method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>-->
<!--环绕通知(around):UserServiceImpl的save方法执行前后,都会进行增强,目标对象的方法需要手动执行。-->
<aop:around method="aroundLog" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
<!--前置通知:UserServiceImpl的save方法执行前,会增强-->
<!--<aop:before method="log" pointcut="execution(public void cn.tx.service.UserServiceImpl.sava())" />-->
<!--<aop:after-returning method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.sava*(..))" />-->
<!--<aop:after-throwing method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.sava*(..))" />-->
<!--
切入点的表达式
execution() 固定的写法
public 是可以省略不写的
方法的返回值 int String 通用的写法,可以编写 * 不能省略不写的
包名+类名 不能省略不写的,编写 * UserServiceImpl AccountServiceImpl
方法名称 save() 可以写 *
参数列表 (..) 表示任意类型和个数的参数
比较通用的表达式:execution(public * com.qcby.*.*ServiceImpl.*(..))
<aop:before method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />
上面的意思就是增强com.qcby包下面的所有包里面的后缀是ServiceImpl的所有实现类中的方法名为save里面的任意类型和个数的参数
-->
</beans>