对于 AOP 这种编程思想,很多框架都进行了实现。 Spring 就是其中之一, 可以完成面向切面编程。AspectJ这个框架也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring2.0版本中将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中,不过织入仍然还是使用的spring的aop完成。 在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
基于xml实现AOP的方式添加依赖:
org.aspectj
aspectjweaver
1.8.13
org.springframework
spring-aspects
5.0.4.RELEASE
代码示例:
创建UserService接口:
public interface UserService {
void addUser();
void selectUserById(int id) throws Exception;
int updateUser();
void deleteUser();
void selectUser();
}
创建UserService接口的实现类:
import com.monkey1024.service.UserService;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("执行service中的addUser方法");
}
@Override
public void selectUser() {
System.out.println("执行service中的selectUser方法");
}
@Override
public void selectUserById(int id) throws Exception{
System.out.println("执行service中的selectUserById方法");
if (id == 0){
throw new Exception();
}
}
@Override
public int updateUser() {
System.out.println("执行service中的updateUser方法");
return 1024;
}
@Override
public void deleteUser() {
System.out.println("执行service中的deleteUser方法");
}
}
创建切面类MyAspect:
package com.monkey1024.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void before() {
System.out.println("========前置通知========");
}
public void after() {
System.out.println("========最终通知========:");
}
public void afterThrowing(Exception e) {
System.out.println("========异常通知========:" + e);
}
public void afterReturning(int result) {
System.out.println("========后置通知========:" + result);
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("========环绕通知:前========:");
Object proceed = pjp.proceed();
System.out.println("========环绕通知:后========:");
return proceed;
}
}
spring的配置文件:
创建测试方法:
@Test
public void testDI() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.addUser();
System.out.println("#####################");
userService.updateUser();
System.out.println("#####################");
try {
userService.selectUserById(0);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("#####################");
userService.selectUser();
System.out.println("#####################");
userService.deleteUser();
}
配置文件中,除了要定义目标类与切面的 Bean 外,最主要的是在 aop:config
中进行aop 的配置。而该标签的底层,会根据其子标签的配置,生成自动代理。
通过其子标签aop:pointcut
定义切入点,该标签有两个属性,id 与 expression。分别用于指定该切入点的名称及切入点的值。expression 的值为 execution 表达式。
aop:aspect
的 ref 属性用于指定使用哪个切面。aop:aspect
的子标签是各种不同的通知类型。不同的通知所包含的属性是不同的,但也有共同的属性。 method:指定该通知使用的切面中的哪个方法。 pointcut-ref:指定该通知要织入的切入点。 AspectJ 通知的 XML 标签如下:
:前置通知
: 后置通知
:环绕通知
:异常通知
:最终通知
AspectJ 的切入点表达式
AspectJ 除了提供了六种通知外,还定义了专门的表达式用于指定切入点。表达式的原型是:
execution (
[modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern] 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型
)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。
举例:
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为: IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为: IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参 数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。
execution(* joke(String,*)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型,如 joke(String s1,String s2)和 joke(String s1,double d2)都是,但 joke(String s1,double d2,String s3)不是。
execution(* joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第 一个参数为 String,后面可以有任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(Strings1,double d2,String s3)都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
基于注解的AOP的实现方式
使用注解首先要确保你的项目中有下面两个依赖:
org.aspectj
aspectjweaver
1.8.13
org.springframework
spring-aspects
5.0.4.RELEASE
在之前定义的切面类上面加上下面两个注解:
@Aspect
@Component
其中@Aspect表示当前类为切面类。
之后在切面类中的方法上面添加响应的通知注解:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Before("execution(* *..UserServiceImpl.addUser())")
public void before() {
System.out.println("========前置通知========");
}
@After("execution(* *..UserServiceImpl.selectUser())")
public void after() {
System.out.println("========最终通知========:");
}
@AfterThrowing(value = "execution(* *..UserServiceImpl.selectUserById(..))" ,throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("========异常通知========:" + e);
}
@AfterReturning(value = "execution(* *..UserServiceImpl.updateUser())",returning = "result")
public void afterReturning(int result) {
System.out.println("========后置通知========:" + result);
}
@Around(value = "execution(* *..UserServiceImpl.deleteUser())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("========环绕通知:前========:");
Object proceed = pjp.proceed();
System.out.println("========环绕通知:后========:");
return proceed;
}
}
接下来需要在spring配置文件中添加下面两个标签:
然后使用之前的测试方法测试即可。
http://www.monkey1024.com/framework/1430
pom.xml
4.0.0
com.monkey1024
02_spring
0.0.1-SNAPSHOT
war
UTF-8
org.springframework
spring-context
5.0.4.RELEASE
org.aspectj
aspectjweaver
1.8.13
org.springframework
spring-aspects
5.0.4.RELEASE
junit
junit
3.8.1
test
02_spring
org.apache.maven.plugins
maven-compiler-plugin
1.8
1.8
UTF-8
spring-aop.xml
MyAspect.java
package com.monkey1024.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/*
* 切面
*/
@Aspect
@Component
public class MyAspect {
@Before("execution(* *..UserServiceImpl.addUser()")
public void before(){
System.out.println("===============前置通知================");
}
@AfterReturning(value="execution(* *..UserServiceImpl.updateUser())",returning="result")
public void afterReturning(int result){
System.out.println("==================后置通知===================="+result);
}
@After(value="execution(* *..UserServiceImpl.selectUser()")
public void after(){
System.out.println("==================最终通知====================");
}
@AfterThrowing(value="execution(* *..UserServiceImpl.selectUserById(..))",throwing="e")
public void afterThrowing(Exception e){
System.out.println("==================异常通知===================="+ e);
}
@Around("execution(* *..UserServiceImpl.deleteUser())")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("========环绕通知:前==============");
Object proceed = pjp.proceed();
System.out.println("========环绕通知:后==============");
return proceed;
}
}
UserService.java
package com.monkey1024.service;
public interface UserService {
void addUser();
void selectUserById(int id) throws Exception;
int updateUser();
void deleteUser();
void selectUser();
}
UserServiceImpl.java
package com.monkey1024.service.impl;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import com.monkey1024.dao.UserDao;
import com.monkey1024.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
public void addUser(){
System.out.println("执行addUser方法");
}
public void selectUserById(int id) throws Exception{
System.out.println("执行selectUserById方法");
if(id==0){
throw new Exception();
}
}
public int updateUser(){
System.out.println("执行updateUser方法");
return 1024;
}
public void deleteUser(){
System.out.println("执行deleteUser方法");
}
public void selectUser(){
System.out.println("执行selectUser方法");
}
}
Test01.java
package com.monkey1024.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.monkey1024.service.UserService;
public class Test01 {
@Test
public void testAOP(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
UserService userService = (UserService) context.getBean("userService");
// 前置通知在切入点执行之前执行
// userService.addUser();
// 后置通知在切入点执行之后执行
// userService.updateUser();
// 最终通知切入点执行后执行
// userService.selectUser();
//环绕通知在切入点执行之前和之后都可以执行
userService.deleteUser();
}
}