AspectJ 开发
① AOP 操作准备 — AspectJ
Spring框架一般都是基于
AspectJ
实现AOP操作
☞ What is AspectJ?
- AspectJ 不是 Spring 组成部分,属于独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作
☞ How to use it?
- 基于AspectJ实现AOP操作的方式:
- 基于
xml配置文件
方式 - 基于
注解
方式
- 基于
☞ FirstStep~
- 在项目工程里面
引入 AOP 相关依赖
☞ Simple Learning
☛ 切入点表达式
1.作用:明确对哪个类里面的哪个方法进行增强
2.语法结构:execution( [权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))
举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))
② AspectJ 基于注解方式操作实现AOP
1、创建类,在类里面定义方法
package AspectJ.anno;
import org.springframework.stereotype.Component;
/**
* 被增强的类
*/
public class User {
public void add(){
System.out.println("add......");
}
}
2、创建增强类(编写增强逻辑)
package AspectJ.anno;
/**
* 增强的类
*/
public class UserProxy {
// 前置通知
public void before(){
System.out.println("before......");
}
}
3、进行通知的配置
(1)在 spring 配置文件中,开启注解扫描
<?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: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/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">
<!-- 开启组件扫描 -->
<context:component-scan base-package="AspectJ.anno"></context:component-scan>
</beans>
(2)使用注解创建 User 和 UserProxy 对象
package AspectJ.anno;
import org.springframework.stereotype.Component;
/**
* 被增强的类
*/
@Component // 添加注解创建对象
public class User {
public void add(){
System.out.println("add......");
}
}
package AspectJ.anno;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 增强的类
*/
@Component // 添加注解创建对象
public class UserProxy {
// 前置通知
public void before(){
System.out.println("before......");
}
}
(3)在增强类上面添加注解 @Aspect
package AspectJ.anno;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 增强的类
*/
@Component // 添加注解创建对象
@Aspect // 生成代理对象
public class UserProxy {
// 前置通知
public void before(){
System.out.println("before......");
}
}
(4)在 spring 配置文件中开启生成代理对象
<?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: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/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">
<!-- 开启组件扫描 -->
<context:component-scan base-package="AspectJ.anno"></context:component-scan>
<!-- 开启AspectJ生成代理对象 ===> 只要类上面添加了AspectJ注解的,就会自动生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
4、配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
package AspectJ.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 增强的类
*/
@Component // 添加注解创建对象
@Aspect // 生成代理对象
public class UserProxy {
// 前置通知
// 添加前置通知注解 --- 指定切入点表达式(明确要被增强的方法)
@Before(value = "execution(* AspectJ.anno.User.add(..))")
public void before() {
System.out.println("before......");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* AspectJ.anno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* AspectJ.anno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* AspectJ.anno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "execution(* AspectJ.anno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
注意:Around环绕通知是在被增强方法执行前、后都执行,所以被增强的方法在这之间执行,需要利用proceedingJoinPoint.proceed();
来完成!!!
(2)测试
public class test {
@Test
public void test_anno(){
try {
// 加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("AspectJ/bean_anno.xml");
// 获取对象
User user = context.getBean("user", User.class);
// 条用add方法
user.add();
} catch (Exception e){
e.printStackTrace();
}
}
}
通过结果我们可以理清这五种通知的执行顺序。注意After、AfterReturing两种通知的区别,After
是在被增强的方法执行后就立刻执行,AfterReturning
是在返回值之后立刻执行。
还有AfterThrowing
是在被增强的方法有异常抛出之后立即执行。可以看到在add方法中抛出异常之后,AfterThrowing执行,并且之后的所有通知均被阻断。
返回顶部
5、相同的切入点抽取
比如上述的五种通知中我们使用的通知切入点表达式完全一样,针对这种情况,我们可以对其进行抽取。在类中定义一个方法(方法名称自定义),然后只需要在该方法上添加@Pointcut注解,value属性填写公共切入点表达式,然后在需要用到的注释value属性值中填写该方法名称即可
。
/**
* 增强的类
*/
@Component // 添加注解创建对象
@Aspect // 生成代理对象
public class UserProxy {
// 定义公共切入点抽取方法
@Pointcut(value = "execution(* AspectJ.anno.User.add(..))")
public void same_point(){
}
// 前置通知
// 添加前置通知注解 --- 指定切入点表达式(明确要被增强的方法)
@Before(value = "same_point()")
public void before() {
System.out.println("before......");
}
//后置通知(返回通知)
@AfterReturning(value = "same_point()")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "same_point()")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "same_point()")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "same_point()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
结果保持不变~
6、有多个增强类多同一个方法进行增强,设置增强类优先级
假设StudentProxy
类也同时对User
类中的add()
方法进行增强:
- 方法:在增强类上面添加注解
@Order(数字类型值)
,数字类型值越小优先级越高
package AspectJ.anno;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(value = 1)
public class StudentProxy {
@Pointcut(value = "execution(* AspectJ.anno.User.add(..))")
public void point(){
}
@Before(value = "point()")
public void before(){
System.out.println("Student Before ,........");
}
//最终通知
@After(value = "point()")
public void after() {
System.out.println("Student After.........");
}
//后置通知(返回通知)
@AfterReturning(value = "point()")
public void afterReturning() {
System.out.println("Student afterReturning.........");
}
}
通过结果我们可以看出每种通知的先后执行顺序都是相对于单个增强类的内部顺序而言的
。当有多个类增强一个方法时,只有当级别大的(优先级低的)执行完毕后,级别小的(优先级高的)才会继续执行。
③ AspectJ 基于xml配置文件方式操作实现AOP
1.创建两个类,增强类和被增强类,创建方法
package AspectJ.xml;
/**
* 被增强的类
*/
public class People {
public void eating(){
System.out.println("eating......");
}
}
package AspectJ.xml;
/**
* 增强类
*/
public class PeopleProxy {
public void before(){
System.out.println("before.......");
}
}
2.在Spring配置文件中创建两个类对象
<?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: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/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">
<!-- 创建配置对象 -->
<bean id="people" class="AspectJ.xml.People"></bean>
<bean id="peopleProxy" class="AspectJ.xml.PeopleProxy"></bean>
</beans>
3.在Spring配置文件中共配置切入点
- 使用
aop:config
标签进行aop增强; - 之前是通过注解的方式配置切入点,在xml配置文件中,采用
aop:pointcut
标签来实现,其属性expression
中填入切入点表达式; - 具体的增强方法在配置前首先要通过
aop:aspect
标签实现切面的配置(将通知应用到切入点的过程 — 将增强逻辑代码写入到需要增强的方法的过程),然后在切面中通过各种通知实现增强)。
<?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: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/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">
<!-- 1.创建配置对象 -->
<bean id="people" class="AspectJ.xml.People"></bean>
<bean id="peopleProxy" class="AspectJ.xml.PeopleProxy"></bean>
<!-- 2.配置aop增强 -->
<aop:config>
<!-- 2.1 配置切入点 -->
<aop:pointcut id="p" expression="execution(* AspectJ.xml.People.eating(..))"/>
<!-- 2.2 配置切面 -->
<aop:aspect ref="peopleProxy">
<!-- 2.2.1 增强具体的作用方法 -->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
</beans>
4.测试
@Test
public void test_xml(){
try {
// 加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("AspectJ/bean_xml.xml");
// 获取对象
People people = context.getBean("people", People.class);
// 条用add方法
people.eating();
} catch (Exception e){
e.printStackTrace();
}
}
通过测试运行,可以看出通过xml方式,AspectJ实现了AOP操作~