Before 前置处理
在一个切面类里使用@Before来修饰一个方法,该方法将作为Before增强处理。使用@Before修饰时,通常需要指定一个value属性值,该属性指定一个切点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点。
下面定义的java类里使用@Before定义了一个Before增强处理:
// 定义一个切面
@Aspect
public class AuthAspect
{
// 匹配com.owen.app.service.impl包下所有类的
// 所有方法的执行作为切入点
@Before("execution(* com.owen.app.service.impl.*.*(..))")
public void authority()
{
System.out.println("模拟执行权限检查");
}
}
在com.owen.app.service.impl下有两个类分别是HelloImple.java和WorldImpl.java的类。
HelloImple.java
@Component("hello")
public class HelloImpl implements Hello
{
// 定义一个简单方法,模拟应用中的业务逻辑方法
public void foo()
{
System.out.println("执行Hello组件的foo()方法");
}
// 定义一个addUser()方法,模拟应用中的添加用户的方法
public int addUser(String name , String pass)
{
System.out.println("执行Hello组件的addUser添加用户:" + name);
return 20;
}
}
WorldImpl.java
@Component("world")
public class WorldImpl implements World
{
// 定义一个简单方法,模拟应用中的业务逻辑方法
public void bar()
{
System.out.println("执行World组件的bar()方法");
}
}
从上面的两个类来看,他们都是普通的类,它丝毫不知道将被谁进行增强,也不知道将被进行怎样的增强。下面,在Spring配置文件中配置自动搜索Bean组件、自动搜索切面类,Spring AOP自动对Bean组件进行增强。下面是Spring配置文件代码。(如果后序的AOP中无特殊说明,则用到的文件与这一份相同)
<?xml version="1.0" encoding="GBK"?>
<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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 指定自动搜索Bean组件、自动搜索切面类 -->
<context:component-scan base-package="com.owen.app.service
, com.owen.app.aspect">
<context:include-filter type="annotation"
expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<!-- 启动@AspectJ支持 -->
<aop:aspectj-autoproxy/>
</beans>
下面写一个测试类。
public class BeanTest
{
public static void main(String[] args)
{
// 创建Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
Hello hello = ctx.getBean("hello" , Hello.class);
hello.foo();
hello.addUser("owen" , "7788");
World world = ctx.getBean("world" , World.class);
world.bar();
}
}
运行结果如下。
[java]信息:Loading XML bean definitions from class path resource…
[java] 模拟执行权限检查
[java] 执行Hello组件的foo()方法
[java] 模拟执行权限检查
[java] 执行Hello组件的addUser添加用户:owen
[java] 模拟执行权限检查
[java] 执行World组件的bar()方法
总结:
使用Before增强处理只能在目标方法执行之前织入增强,如果Before增强处理没有特殊处理,目标方法总会自动执行,如果Before处需要阻止目标方法的执行,可通过抛出一个异常来实现。Before增强处理执行时,目标方法还未获得执行的机会,所以Before增强处理无法访问目标方法的返回值。
Around 增强处理
@Around
注解用于修饰Around增强处理,Around增强处理是功能比较强大的增强处理,它近似于Before增强处理和AfterReturing增强处理的总结,Around增强处理既可在执行目标方法之前增强动作,也可在执行目标方法之后织入增强的执行。
与Before增强处理、AfterReturning增强处理不同的是,Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标方法的执行。
当定义一个Around增强处理方法时,该方法的第一个形参必须是 ProceedJoinPoint
类型(至少含有一个形参),在增强处理方法体内,调用 ProceedingJoinPoint
参数的 proceed()
方法才会执行目标方法——这就是Around增强处理可以完全控制方法的执行时机、如何执行的关键;如果程序没有调用 ProceedingJoinPoint
参数的 proceed()
方法,则目标方法不会被执行。
Demo Example
服务类:
package com.haan.springdemo.annotation.aop;
public class UserService {
public String sayHello(String name){
System.out.println("UserService.sayHello()执行,"+name + ":hello!");
return name;
}
}
切面类:
package com.haan.springdemo.annotation.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
/**
* @Aspect: 告诉Spring当前类是一个切面类
*/
@Aspect
public class LogAspect {
/**
* 抽取公共的切面
* 使用:
* (1)本类中使用:pointCut()
* (2)其他类中使用:com.haan.springdemo.annotation.aop.LogAspect.pointCut()
*/
@Pointcut(value = "execution(public * com.haan.springdemo.annotation.aop.UserService.*(..))")
public void pointCut(){
}
/**
* 前置通知 @Before 在目标方法之前切入
* value:切入表达式(指定那个方法切入)
*/
@Before("pointCut()")
public void before(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
String signatureName = signature.getName();
System.out.println("测试before...,方法名:"+signatureName);
}
/**
* 后置通知 @After 在目标方法之后切入
* value:切入表达式(指定那个方法切入)
*/
@After("com.haan.springdemo.annotation.aop.LogAspect.pointCut()")
public void after(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
String signatureName = signature.getName();
System.out.println("测试after...,方法名:"+signatureName);
}
/**
* 返回通知 @AfterReturning
* 程序执行无异常,返回结果后切入
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
Signature signature = joinPoint.getSignature();
String signatureName = signature.getName();
System.out.println("测试after returning...,方法名:"+signatureName+",return:"+ (String) result);
}
/**
* 异常通知 @AfterThrowing
* 程序执行出现异常,切入通知
*/
@AfterThrowing(value = "pointCut()",throwing = "exception")
//JoinPoint一定要出现在参数表的第一位
public void afterThrowing(JoinPoint joinPoint,Exception exception){
Signature signature = joinPoint.getSignature();
String signatureName = signature.getName();
System.out.println("测试after throwing...,方法名:"+signatureName);
}
/**
* 环绕通知 @Around
*
*/
@Around("pointCut()")
public void around(ProceedingJoinPoint pj) throws Throwable {
String signatureName = pj.getSignature().getName();
System.out.println("测试around...执行前,方法名:"+ signatureName);
pj.proceed();
System.out.println("测试around...执行后,方法名:"+ signatureName);
}
}
主程序:
package com.haan.springdemo.annotation.aop;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class TestAopConfiguration {
@Bean
public LogAspect logAspect(){
return new LogAspect();
}
@Bean
public UserService userService(){
return new UserService();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestAopConfiguration.class);
UserService userService = context.getBean(UserService.class);
userService.sayHello("libai");
}
}
执行结果:
测试around...执行前,方法名:sayHello
测试before...,方法名:sayHello
UserService.sayHello()执行,libai:hello!
测试after returning...,方法名:sayHello,return:libai
测试after...,方法名:sayHello
测试around...执行后,方法名:sayHello