Spring-AOP面向切面编程注解版
AOP切面技术是基于动态代理技术实现的,底层是反射技术,对应用中出现的一些公共代码做横向抽取,放到切面中
专业术语
连接点: JoinPoint 目标对象中可以被增强的方法 基本上所有非必须增强的方法增强的方法都可以是连接点
目标对象: Target 需要被增强的类对象
切入点: PointCut 目标对象中必须要增强的方法称之为切入点
织入: Wearing 将通知应用到连接点的过程称之为织入
代理:: Proxy 生成代理对象 如由Spring创建UserService类对象.通过动态代理技术生成代理对象
切面: 切入点 + 通知 = 切面
AOP快速入门
1.准备UserService接口
// 业务层接口
public interface UserService {
// 配置addUser
void addUser();
// 配置updateUser
void updateUser();
// 配置deleteUser
void deleteUserById(int id);
// 配置queryObject
List<Object> queryObject();
// 配置queryTotal
int queryTotal();
// 配置queryFuzzyLike
List<Object> queryFuzzyLike(String name);
// 配置queryObjectById
Object queryObjectById(int id);
2.准备UserServiceImpl并且交给Spring管理
/**
* @author: admin
* @date: 2021/3/5
*
* 业务层接口实现类
*/
@Service("userService")//表示交给Spring内存
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加用户信息。。。");
int i = 1 / 0;// 算术异常
}
@Override
public void updateUser() {
System.out.println("用户信息已经发生更改。。。");
}
@Override
public void deleteUserById(int id) {
System.out.println("已经根据指定的"+id+"值删除指定的对象。。。");
}
@Override
public List<Object> queryObject() {
return null;
}
@Override
public int queryTotal() {
return 0;
}
@Override
public List<Object> queryFuzzyLike(String name) {
return null;
}
@Override
public Object queryObjectById(int id) {
return null;
}
}
**- 准备Spring的相关jar包
-
除了Spring配置的常规jar包,还需要配置:
- aopalliance
- aspectJweaver
- spring-aspects
-
准备Spring的配置文件 xml
-
把UserServiceImpl类通过方式配置到Spring的核心文件中applicationContext.xml**
pom.xml文件
<packaging>jar</packaging>
<!--Spring的核心包+Spring-jdbc+数据库驱动jar包+连接池jar包(C3P0连接池)+spring-test测试包+JUnit-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.2</version>
</dependency>
<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.13.1</version>
<scope>test</scope>
</dependency>
<!--添加aopalliance坐标依赖-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--添加aspectJWeaver坐标依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
<!--添加spring-aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
-
配置通知(目标对象中需要增强的内容)
交给Spring管理
-
配置切入点
在Spring的主配置文件中配置切入点
-
通过切入点和通知配置切面 切面 = 切入点 + 通知
借助于aop:aspect
-
测试 采用Sping整合JUnit进行测试
配置主配置文件
<!--把UserServiceImpl类对象配置到Spring容器中-->
<!--<bean id="userService" class="com.zhiyou100.service.impl.UserServiceImpl"></bean>-->
<!--组件扫描-->
<context:component-scan base-package="com.zhiyou100"/>
<!--把通知MyAdvice类对象配置到Spring容器中-->
<bean id="myAdvice" class="com.zhiyou100.advice.MyAdvice"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切入点-->
<!-- 要增强的方法 public void addUser() 方法名=包名+类名+方法名 -->
<aop:pointcut id="pct" expression="execution(* com.zhiyou100.service..*.*(..))"/>
<!--
expression 切入点表达式的改造过程:目的是为了下面的通知通配所有的切入点
public void com.zhiyou100.service.impl.UserServiceImpl.addUser()
// 扩展UserServiceImpl其他的切入点方法
// .. 代表通配方法中的所有参数 * 代表该类中的所有的方法
public void com.zhiyou100.service.impl.UserServiceImpl.*(..)
// 扩展业务包下面的其他类
public void com.zhiyou100.service.impl.*.*(..)
// 扩展业务包下面类以及子包中的类 .. 代表service当前包下面的类以及service包的子包中类
public void com.zhiyou100.service..*.*(..) 切入点表达式到此为止 * com.zhiyou100.service..*.*(..)
//
public void com.zhiyou100..*.*(..)
//
public void *..*.*(..)
// 第一个* 匹配方法中的所有返回值
public * *..*.*(..)
// 权限修饰符可以表达式中省略不写
* *..*.*(..)
-->
<!--配置切面 = 切入点 + 通知 -->
<aop:aspect ref="myAdvice">
<!--配置通知的目标方法 关联哪一个切入点
实质上就是把before方法中的通知内容和addUser()切入点方法做一个绑定 形成切面
-->
<aop:before method="before" pointcut-ref="pct"></aop:before>
<!--后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="pct"/>
<!--异常拦截通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pct"/>
<!--环绕通知-->
<!--<aop:around method="around" pointcut-ref="pct"/>-->
<!--最终通知-->
<aop:after method="after" pointcut-ref="pct"/>
</aop:aspect>
</aop:config>
myadvice代码
public class MyAdvice {
// 通知的内容需要往方法存放
public void before(){
System.out.println("前置通知。。。已经连接上数据库。。。。");
// addUser方法有aop加持之后
// 前置通知。。。已经连接上数据库。。。。
// 添加用户信息。。。
}
/**
* 前置通知 before()
* 后置通知 afterReturning() 如果切入点有异常信息,则会中断
* 异常拦截通知 afterThrowing() 如果切入点中有异常信息,则会植入该方法定义的增强的内容
* 环绕通知 around() 前置通知+后置通知+异常拦截通知
* 最终通知 after() 他不管切入点中是否有异常信息,都会执行该方法定义的增强的内容。
*/
// 后置通知
public void afterReturning() {
System.out.println("后置通知。。。。。可以进行资源释放。。。");
}
// 异常拦截通知
public void afterThrowing() {
System.out.println("异常拦截通知。。。。。此时切入点中有异常信息。。。。");
}
// 环绕通知
public void around(ProceedingJoinPoint joinPoint){
try {
System.out.println("方法执行前加载信息。。。。。");// 前置通知
joinPoint.proceed(); // Method invoke()
System.out.println("方法执行后释放信息。。。。");// 后置通知
} catch (Throwable throwable) {
System.out.println("异常发生时。。。。执行异常处理。。。。。");// 异常拦截通知
throwable.printStackTrace();
}
}
// 最终通知
public void after() {
System.out.println("不管方法执行中是否异常。。。我都要释放资源。。。");
}
}