3.6.3 AspectJ
的开发环境
(
1
)
maven
依赖
<?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.it</groupId>
<artifactId>spring3</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springTest2</module>
<module>springAop1</module>
<module>springAopAspectJ</module>
<module>SpringMyBatis</module>
</modules>
<!--指定jdk的版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 单元测试的依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--aspectJ依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
<!--自动生成的生命周期的插件-->
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
(
2
) 引入
AOP
约束
在
AspectJ
实现
AOP
时,要引入
AOP
的约束。配置文件中使用的
AOP
约束中的标签,
均是
AspectJ
框架使用的,而非
Spring
框架本身在实现
AOP
时使用的。
AspectJ
对于
AOP
的实现有注解和配置文件两种方式,常用是注解方式。
3.6.4 AspectJ
基于注解的
AOP
实现
AspectJ
提供了以注解方式对于
AOP
的实现。
(
1
) 实现步骤
A
、
Step1
:定义业务接口与实现类
![](https://i-blog.csdnimg.cn/blog_migrate/7a93c42f89bd6a32659e40896191163c.png)
B、 Step2:定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能
![](https://i-blog.csdnimg.cn/blog_migrate/87d6895aaad00d270ec3e8c406be6efe.png)
C、 Step3:声明目标对象切面类对象
D、Step4:注册 AspectJ 的自动代理
在定义好切面
Aspect
后,需要通知
Spring
容器,让容器生成“目标类
+
切面”的代理
对象。这个代理是由容器自动生成的。只需要在
Spring
配置文件中注册一个基于
aspectj
的
自动代理生成器,其就会自动扫描到
@Aspect
注解,并按通知类型与切入点,将其织入,并
生成代理。
![](https://i-blog.csdnimg.cn/blog_migrate/bab2ef77dd61e959cb6388cd833e56fb.png)
<aop:aspectj-autoproxy/>
的底层是由
AnnotationAwareAspectJAutoProxyCreator
实现的。
从其类名就可看出,是基于
AspectJ
的注解适配自动代理生成器。
其工作原理是,
<aop:aspectj-autoproxy/>
通过扫描找到
@Aspect
定义的切面类,再由切
面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
E
、
Step5
:测试类中使用目标对象的
id
![](https://i-blog.csdnimg.cn/blog_migrate/9b1fce780185c585fa8e8ecf8e72eda4.png)
(
2
)
@Before
前置通知
-
方法有
JoinPoint
参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个
JoinPoint
类型参
数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、
目标对象等。
不光前置通知的方法,可以包含一个
JoinPoint
类型参数,所有的通知方法均可包含该参数。
项目结构
![](https://i-blog.csdnimg.cn/blog_migrate/b0eba9b0da2cdf7dbeebdf05e0866bab.png)
1.创建SomeService接口
package com.it.bao1;
public interface SomeService {
void doSome(String name,Integer age);
}
2.创建SomeServiceImpl实现类
package com.it.bao1;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("=========目标方法doSome()=====");
}
}
3.创建MyAspect类
package com.it.bao1;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
// 切面类中的方法是public方法,方法没有返回值
/**
* @Before:前置通知注解
* 属性:value,是切入点表达式,表示切面功能执行的位置
* 切入点表达式:访问修饰符,方法返回值,完整路径加方法名(里面写方法的参数个数,或方法的类型)
* 在目标方法之前执行,不会影响目标方法的执行
*
*/
/**
* 后置通知定义方法
* 1.方法是public
* 2.方法返回值是void
* 3.方法名称自定义
* 4.方法可以有参数,也可以没有参数。
*/
// @Before(value = "execution(public void com.it.bao1.SomeServiceImpl.doSome(String,Integer))")
// public void myBefore(){
//
// System.out.println("切面类:在目标方法之前输出执行开始的时间:"+new Date());
// }
//指定通知方法中的参数:JoinPoint
@Before(value="execution(public void com.it.bao1.SomeServiceImpl.doSome(String,Integer))")
public void myBefore(JoinPoint joinPoint){
System.out.println("方法的签名:"+joinPoint.getSignature());
System.out.println("方法的名称:"+joinPoint.getSignature().getName());
//获取方法的实参
Object[] args = joinPoint.getArgs();
for (Object arg:args){
System.out.println("参数="+arg);
}
System.out.println("切面类:在目标方法之前输出执行开始的时间:"+new Date());
}
}
4.创建spring配置文件applicationContext.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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!-- 声明目标对象-->
<bean id="someService" class="com.it.bao1.SomeServiceImpl">
</bean>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.it.bao1.MyAspect">
</bean>
<!-- 声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
<aop:aspectj-autoproxy/>
<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象 -->
<!-- 没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
<aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;
import static org.junit.Assert.assertTrue;
import com.it.bao1.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest
{
@Test
public void test1(){
String config="applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) ac.getBean("someService");
//虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
System.out.println("验证该目标对象,是否是代理对象:"+proxy.getClass().getName());
//通过代理的对象执行方法,实现目标方法执行时,增强了功能
proxy.doSome("小明",23);
}
}
项目结果
(3) @AfterReturning 后置通知-注解有 returning 属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回
值。该注解的
returning
属性就是用于指定接收方法返回值的变量名的。所以,被注解为后
置通知的方法,除了可以包含
JoinPoint
参数外,还可以包含用于接收返回值的变量。该变
量最好为
Object
类型,因为目标方法的返回值可能是任何类型。
项目结构
同上
1.创建接口
package com.it.bao2;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name,Integer age);
}
2.创建接口实现类
package com.it.bao2;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("=========目标方法doSome()=====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("===目标方法执行了doOther方法====");
return "abcd";
}
}
3.创建切面类
package com.it.bao2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
// 切面类中的方法是public方法,方法没有返回值
/**
* 后置通知定义方法
* 1.方法是public
* 2.方法返回值是void
* 3.方法名称自定义
* 4.方法是有参数的,参数推荐Object,参数名自定义
*/
/**
* @MyAfterReturning:后置通知
* 属性:1.value切入点表达式
* 2.returning,自定义变量,表示目标方法的返回值的。
* 自定义变量名必须和通知方法的形参名一样。
* 位置:在目标方法上面
* 特点:在目标方法之后执行
*
* @param res
*/
//如果使用JionPoint必须写在参数第一位
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
public void MyAfterReturning(JoinPoint joinPoint,Object res){
System.out.println(joinPoint.getSignature());
System.out.println("后置通知:在目标方法执行之后取到的返回值是:"+res);
}
}
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: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
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!-- 声明目标对象-->
<bean id="someService" class="com.it.bao2.SomeServiceImpl">
</bean>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.it.bao2.MyAspect">
</bean>
<!-- 声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
<aop:aspectj-autoproxy/>
<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象 -->
<!-- 没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
<aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;
import com.it.bao2.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest2
{
@Test
public void test2(){
String config="applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) ac.getBean("someService");
//虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
System.out.println("验证该目标对象,是否是代理对象:"+proxy.getClass().getName());
//通过代理的对象执行方法,实现目标方法执行时,增强了功能
// proxy.doSome("小明",23);
String s=proxy.doOther("小红",22);
}
}
项目结果
![](https://i-blog.csdnimg.cn/blog_migrate/1f6155d307a7f71d52589e2edf4a90e2.png)
(
4
)
@Around
环绕通知
-
增强方法有
ProceedingJoinPoint
参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,
Object
类型。并
且方法可以包含一个
ProceedingJoinPoint
类型的参数。接口
ProceedingJoinPoint
其有一个
proceed()
方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法
的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
接口增加方法:
项目结构
同上
1.创建接口
package com.it.bao3;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name, Integer age);
String doFirst(String name,Integer age);
}
2.创建接口实现类
package com.it.bao3;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("=========目标方法doSome()=====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("===目标方法执行了doOther方法====");
return "abcd";
}
@Override
public String doFirst(String name, Integer age) {
System.out.println("=====业务方法doFirst()=====");
return "doFirst";
}
}
3.创建切面类
package com.it.bao3;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
// 切面类中的方法是public方法,方法没有返回值
/**
* 环绕通知定义方法
* 1.方法是public
* 2.方法必须有返回值,推荐Object
* 3.方法名称自定义
* 4.方法有参数的,参数是固定的:ProceedingJoinPoint
*/
/**
* @Around:环绕通知
* 属性:value切入点表达式
* 特点:
* 1.它是功能最强的通知
* 2.在目标方法的前后都可以增强功能。
* 3.控制目标方法是够能调用执行
* 4.修改原来的目标方法的执行结果,影响最后的执行结果。
*
* 环绕通知=jdk动态代理,invocationHandler接口
* @return
*/
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String arg1=null;
Object[] args = proceedingJoinPoint.getArgs();
if (args!=null && args.length>1){
Object arg = args[0];
arg1 = (String) arg;
System.out.println(arg1);
}
//实现环绕通知
Object result=null;
System.out.println("环绕通知,在目标方法之前,输出时间:"+new Date());
//1.目标方法的调用
if ("李华".equals(arg1)){
result=proceedingJoinPoint.proceed();//Object result=doFirst();
}
System.out.println("环绕通知,在目标方法之后,提交事务:");
//修改目标方法执行结果,会影响最终的返回结果
if (result!=null){
result="Hello AspectJ AOP";
}
//返回目标函数的结果
return result;
}
}
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: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
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!-- 声明目标对象-->
<bean id="someService" class="com.it.bao3.SomeServiceImpl">
</bean>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.it.bao3.MyAspect">
</bean>
<!-- 声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
<aop:aspectj-autoproxy/>
<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象 -->
<!-- 没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
<aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;
import com.it.bao3.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest3
{
@Test
public void test1(){
String config="applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) ac.getBean("someService");
//虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
System.out.println("验证该目标对象,是否是代理对象:"+proxy.getClass().getName());
//通过代理的对象执行方法,实现目标方法执行时,增强了功能
String str = proxy.doFirst("李华", 25);
//返回结果
System.out.println("str=="+str);
}
}
项目结果
![](https://i-blog.csdnimg.cn/blog_migrate/fbc994c8454ab37b19a280d2a01fe693.png)
(5)@AfterThrowing 异常通知-注解中有 throwing 属性
在目标方法抛出异常后执行。该注解的
throwing
属性用于指定所发生的异常类对象。
当然,被注解为异常通知的方法可以包含一个参数
Throwable
,参数名称为
throwing
指定的
名称,表示发生的异常对象。
项目结构
同上
1.创建接口
package com.it.bao4;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name, Integer age);
String doFirst(String name, Integer age);
void doSecond();
}
2.创建接口实现类
package com.it.bao4;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("=========目标方法doSome()=====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("===目标方法执行了doOther方法====");
return "abcd";
}
@Override
public String doFirst(String name, Integer age) {
System.out.println("=====业务方法doFirst()=====");
return "doFirst";
}
@Override
public void doSecond() {
System.out.println("===执行业务方法doSecond()====异常通知"+(10/0));
}
}
3.创建切面类
package com.it.bao4;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
// 切面类中的方法是public方法,方法没有返回值
/**
* 异常通知定义方法
* 1.方法是public
* 2.方法没有返回值。
* 3.方法名称自定义
* 4.方法可以没有参数,如果有参数就是JoinPoint或Exception
*/
/**
* @AfterThrowing:异常通知
* 属性:1.value切入点表达式
* 2、throwing自定义变量,表示目标方法抛出的异常对象。变量名必须和方法的参数名一样。
* 特点:
* 1.在目标方法抛出异常时执行的
* 2.做异常的监控程序,如果有异常发出通知
*
*/
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "e")
public void myAfterThrowing(Exception e){
System.out.println("这是异常通知,在方法发生异常时执行"+e.getMessage());
//发送邮件,短信通知开发人员
}
}
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: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
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!-- 声明目标对象-->
<bean id="someService" class="com.it.bao4.SomeServiceImpl">
</bean>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.it.bao4.MyAspect">
</bean>
<!-- 声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
<aop:aspectj-autoproxy/>
<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象 -->
<!-- 没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
<aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;
import com.it.bao4.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest4 {
@Test
public void test1() {
String config = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) ac.getBean("someService");
//虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
System.out.println("验证该目标对象,是否是代理对象:" + proxy.getClass().getName());
//通过代理的对象执行方法,实现目标方法执行时,增强了功能
proxy.doSecond();
}
}
项目结果
没有异常时
![](https://i-blog.csdnimg.cn/blog_migrate/c54ccf6d5f43e32f8ef31fd8b4dc9502.png)
有异常时
![](https://i-blog.csdnimg.cn/blog_migrate/3446e1c2ee97f696fd82187c3f43e01c.png)
(
6
)
@After
最终通知
无论目标方法是否抛出异常,该增强均会被执行。
项目结构
同上
1.创建接口
package com.it.bao5;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name, Integer age);
String doFirst(String name, Integer age);
void doSecond();
void doThird();
}
2.创建接口实现类
package com.it.bao5;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("=========目标方法doSome()=====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("===目标方法执行了doOther方法====");
return "abcd";
}
@Override
public String doFirst(String name, Integer age) {
System.out.println("=====业务方法doFirst()=====");
return "doFirst";
}
@Override
public void doSecond() {
System.out.println("===执行业务方法doSecond()====异常通知"+(10/0));
}
@Override
public void doThird() {
System.out.println("======执行了doThird()方法========");
}
}
3.创建切面类
package com.it.bao5;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
// 切面类中的方法是public方法,方法没有返回值
/**
* 公共通知定义方法
* 1.方法是public
* 2.方法没有返回值。
* 3.方法名称自定义
* 4.方法可以没有参数,如果有参数就是JoinPoint
*/
/**
* @After:最终通知
* 属性:1.value切入点表达式
* 特点:
* 1、总是会执行。
* 2.是在目标方法执行之后执行的
*
*/
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void muAfter(){
System.out.println("这是执行最终通知,它总是会被执行,不管是否发生异常");
//一般用来做资源清除工作的
}
}
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: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
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!-- 声明目标对象-->
<bean id="someService" class="com.it.bao5.SomeServiceImpl">
</bean>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.it.bao5.MyAspect">
</bean>
<!-- 声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
<aop:aspectj-autoproxy/>
<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象 -->
<!-- 没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
<aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;
import com.it.bao5.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest5 {
@Test
public void test1() {
String config = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) ac.getBean("someService");
//虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
System.out.println("验证该目标对象,是否是代理对象:" + proxy.getClass().getName());
//通过代理的对象执行方法,实现目标方法执行时,增强了功能
proxy.doThird();
}
}
项目结果
![](https://i-blog.csdnimg.cn/blog_migrate/f94595ea653b0f7babbf4369ec98fcdc.png)
(
7
)
@Pointcut
定义切入点
当较多的通知增强方法使用相同的
execution
切入点表达式时,编写、维护均较为麻烦。
AspectJ
提供了
@Pointcut
注解,用于定义
execution
切入点表达式。
其用法是,将
@Pointcut
注解在一个方法之上,以后所有的
execution
的
value
属性值均
可使用该方法名作为切入点。代表的就是
@Pointcut
定义的切入点。这个使用
@Pointcut
注解
的方法一般使用
private
的标识方法,即没有实际作用的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/a49732eefd818823d5282da3f5f18ef1.png)
项目结构
同上
1.创建接口
package com.it.bao6;
public interface SomeService {
void doSome(String name, Integer age);
String doOther(String name, Integer age);
String doFirst(String name, Integer age);
void doSecond();
void doThird();
}
2.创建接口实现类
package com.it.bao6;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("=========目标方法doSome()=====");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("===目标方法执行了doOther方法====");
return "abcd";
}
@Override
public String doFirst(String name, Integer age) {
System.out.println("=====业务方法doFirst()=====");
return "doFirst";
}
@Override
public void doSecond() {
System.out.println("===执行业务方法doSecond()====异常通知"+(10/0));
}
@Override
public void doThird() {
System.out.println("======执行了doThird()方法========");
}
}
3.创建切面类
package com.it.bao6;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
// 切面类中的方法是public方法,方法没有返回值
/**
* @PointCut:定义切入点表达式,如果你的项目中有多个切入点表达式是重复的,
* 那么就可以使用@PointCut
* 属性:value切入点表达式,
* 位置:在自定义的方法上面
* 特点:
*/
@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myPointCut(){}
@After(value = "myPointCut()")
public void muAfter(){
System.out.println("这是执行最终通知,它总是会被执行,不管是否发生异常");
}
@Before(value = "myPointCut()")
public void myBefore(){
System.out.println("这是执行前置通知,它在目标方法之前执行!");
}
}
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: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
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!-- 声明目标对象-->
<bean id="someService" class="com.it.bao6.SomeServiceImpl">
</bean>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.it.bao6.MyAspect">
</bean>
<!-- 声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
<aop:aspectj-autoproxy/>
<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象 -->
<!-- 没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
<aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;
import com.it.bao6.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest6 {
@Test
public void test1() {
String config = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) ac.getBean("someService");
//虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
System.out.println("验证该目标对象,是否是代理对象:" + proxy.getClass().getName());
//通过代理的对象执行方法,实现目标方法执行时,增强了功能
proxy.doThird();
//当目标类没有接口时,使用cglib动态代理,spring框架会自动调用cglib
}
}
项目结果
![](https://i-blog.csdnimg.cn/blog_migrate/7c51a55eac791cec439e9ee4d51fb137.png)