AOP
面向切面(儿)编程: 面向程序的横截面(儿)编程,将软件横向切开,在原有软件不变的情况下扩展横向的功能。
AOP:
如何没有AOP将会是这样的:
Hello World
AOP的演示案例:
步骤:
-
导入包:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjtools</artifactId> <version>1.8.0</version> </dependency>
-
开发了 AOP Bean 组件
@Component //将DemoAspect交给Spring管理 @Aspect //声明当前Bean组件是一个切面组件 public class DemoAspect { /** * @Before("bean(userService)") 注解的意义: * 在userService bean的全部方法之前执行test 方法, * 这个注解声明会被Spring 自动处理,并且执行 */ @Before("bean(userService)") public void test(){ System.out.println("Hello World!"); } }
-
配置:
<!-- spring-aop.xml --> <context:component-scan base-package="cn.tedu.store.aspect" /> <!-- 让 aspectj 带来的 注解生效--> <aop:aspectj-autoproxy/>
通知
用于声明 AOP 方法在目标业务方法执行时机。
常用的有5种通知:
@Before
@After
@AfterThrowing
@AfterReturning
@Around
执行原理如下:
@Before
在目标方法之前执行, 案例:
/**
* @Before("bean(userService)") 注解的意义:
* 在userService bean的全部方法之前执行test 方法,
* 这个注解声明会被Spring 自动处理,并且执行
* Before 之前
*/
@Before("bean(userService)")
public void test(){
System.out.println("Hello World!");
}
@After
在目标方法执行之后执行,无论目标业务方法是否出现异常@After修饰的方法都会执行:
/**
* 在 userService 的全部方法之后(After)执行
*/
@After("bean(userService)")
public void test2(){
System.out.println("Hello @After!");
}
@AfterReturning
在目标方法正常执行之后,如果没有异常发生,则执行@AfterReturning修饰的方法:
/**
* 在目标方法没有异常情况下执行
*/
@AfterReturning("bean(userService)")
public void test3(){
System.out.println("Hello @AfterReturning");
}
@AfterThrowing
在目标方法执行出现异常以后会执行 @AfterThrowing 修饰的方法。
/**
* 在目标方法有异常情况下执行
*/
@AfterThrowing("bean(userService)")
public void test4(){
System.out.println("Hello @AfterThrowing");
}
@Around 通知
这个是一个万能通知,可以替代其他几个通知,但是使用繁琐:
案例:
/**
* Around 通知: 对应的AOP方法:
* 1. 必须有 参数 ProceedingJoinPoint
* 2. 必须有返回值 Object
* 3. 必须抛出异常 Throwable
* @param jp
* @return
* @throws Throwable
*/
@Around("bean(userService)")
public Object test5(ProceedingJoinPoint jp)
throws Throwable{
//Proceeding 进行,处理
//Join 连接
//Point 点
// 处理过程的连接点
System.out.println("Around Before");
//jp.proceed() 调用了目标业务方法,其返回值
//就是业务方法返回的业务处理结果
Object obj = jp.proceed();
//jp 对象中包含被调用目标方法的全部信息
//其中 getSignature 返回方法的签名,包括:
//方法和方法的参数类型列表
Signature method= jp.getSignature();
System.out.println(method);
System.out.println("Around After:"+obj);
return obj;
}
切入点表达式PointCut
切入点用于声明对那些 类,对象,方法进行AOP切入
Bean组件切入点
语法:
@Before("bean(userService)") //切入到userService的全部方法
@Before("bean(userService) || bean(dictService)")
案例:
/**
* Bean组件切入点,对两个Bean组件进行切入
* 如下程序将在 userService 和 dictService
* 全部方法之前执行 test() 方法
*/
//@Before("bean(userService) || bean(dictService)")
public void test(){
System.out.println("Point Cut Test!");
}
/**
* 切入点表达式 bean(*Service) 将会拦截全部的
* 业务层方法,每个业务层方法之前都会执行 test1()
* 方法。使用这种方式进行切入时候,建议有统一的
* 命名规范。
*/
//@Before("bean(*Service)")
public void test1(){
System.out.println("Point Cut Test1!");
}
类切入点
按照具体的类名,切到类的全部方法
语法:
//切入到类 UserServiceImpl 中声明的全部的方法
@Before("within(cn.tedu.store.service.UserServiceImpl)")
@Before("within(cn.tedu.store.service.*ServiceImpl)")
@Before("within(cn.tedu.store..*Impl)")
案例:
/**
* 类的切入点:如下切入点表达式将test2()方法切入到
* UserServiceImpl 的全部方法之前执行。
*/
@Before("within(cn.tedu.store.service.UserServiceImpl)")
public void test2(){
System.out.println("Point Cut Test2!");
}
@Before("within(cn.tedu.store.service.*ServiceImpl)")
public void test3(){
System.out.println("Point Cut Test3!");
}
方法切入点
语法:
//execution 执行
@Before("execution(修饰词 类名.方法名(参数类型))")
@Before("execution(* cn.tedu.store.service.UserService.login(..))")
@Before("execution(* cn.tedu.store.service.*Service.get*(..))")
@Before("execution(* cn.tedu.store..*Service.get*(..))")
案例:
/**
* 方法切入点:只切入到login方法
*/
@Before("execution(* cn.tedu.store.service.*Service.login(..))")
public void test4(){
System.out.println("Point Cut Test4!");
}
测试业务层的所有方法的性能
方案:
@Component
@Aspect
public class TestAspect {
@Around("bean(*Service)")
public Object test(ProceedingJoinPoint jp)
throws Throwable{
try {
long t1 = System.currentTimeMillis();
Object val=jp.proceed();
long t2 = System.currentTimeMillis();
Signature m = jp.getSignature();
System.out.println((t2-t1)+":"+m);
return val;
} catch (Throwable e) {
//继续抛出业务异常
throw e;
}
}
}
AOP 的工作原理
Spting 底层是 AspectJ 再底层利用了 Java的反射和动态代理。其中反射用于解析注解,动态代理用于生成代理对象。
Spring AOP 的底层代理方式有两种,一种是基于Java 动态代理对象,一种是基于 CGlib 的动态代理。 当被代理对象有接口时候优先使用 JAVA 动态代理,如果被代理对象没有接口时候,会自动使用CGLIB。 推荐使用 JAVA 动态代理.