8.4.5 基于注解的 零配置 方式 2. 定义Before增强处理

8.4.5 基于注解的 零配置 方式 2. 定义Before增强处理

在一个切面类(用@Aspect修饰的类)里使用@Before来修饰一个方法时,该方法将作为Before增强处理。使用@Before修饰时,通常需要指定一个value属性值,该属性值指定一个切入点表达式,用于指定该增强处理将被织入哪些切入点。(这个切入点表达式既可以是一个已有的切入点,也可以直接定义切入点表达式)

程序示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\Before
└─src\
├─beans.xml
├─lee\
│ └─BeanTest.java
└─org\
└─crazyit\
└─app\
├─aspect\
│ └─AuthAspect.java
└─service\
├─Hello.java
├─impl\
│ ├─HelloImpl.java
│ └─WorldImpl.java
└─World.java

下面的Java类里使用@Before定义了一个Before增强处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.crazyit.app.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

// 定义一个切面
@Aspect
public class AuthAspect
{
// 匹配org.crazyit.app.service.impl包下所有类的、
// 所有方法的执行作为切入点
@Before("execution(* org.crazyit.app.service.impl.*.*(..))")
public void authority()
{
System.out.println("模拟执行权限检查");
}
}

上面程序使用@Aspect修饰了AuthAspect类,这表明该类是一个切面类,在该切面里定义了一个authority方法—这个方法本来没有任何特殊之处,但因为使用了@Before来标注该方法,这就将该方法转换成了一个Before增强处理
上面程序中使用@Before注解时,直接指定了切入点表达式,指定匹配org.crazyit.app.service.impl包下所有类的所有方法的执行作为切入点。
本应用在org.crazyit.app.service.impl包下定义了两个类:HelloImplWorldImpl,它们与前面介绍AspectJ时所用的两个业务组件类几乎相同(只是增加实现了一个接口),并使用了@Component注解进行修饰。下面是其中HelloImpl类的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.crazyit.app.service.impl;

import org.springframework.stereotype.Component;
import org.crazyit.app.service.*;

@Component("hello")
public class HelloImpl implements Hello
{
// 定义一个deleteUser方法,模拟应用中删除用户的方法
public void deleteUser(Integer id)
{
System.out.println("执行Hello组件的deleteUser删除用户:" + id);
}
// 定义一个addUser()方法,模拟应用中的添加用户的方法
public int addUser(String name, String pass)
{
System.out.println("执行Hello组件的addUser添加用户:" + name);
return 20;
}
}

从上面的HelloImpl类代码来看,它是一个如此”纯净”的Java类,它丝毫不知道将被谁来进行增强,也不知道将被进行怎样的增强——但正因为HelloImpl类的这种”无知”,才是AOP的最大魅力:目标类可以被无限地增强
Spring配置文件中配置自动搜索Bean组件、自动搜索切面类, Spring AOP自动对Bean组件进行增强。下面是Spring配置文件代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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.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组件、自动搜索切面类 -->
<context:component-scan
base-package="org.crazyit.app.service,org.crazyit.app.aspect">
<context:include-filter type="annotation"
expression="org.aspectj.lang.annotation.Aspect" />
</context:component-scan>

<!-- 启动@AspectJ支持 -->
<aop:aspectj-autoproxy />
</beans>

主程序非常简单,通过Spring容器获取helloword这两个Bean,并调用了这两个Bean的业务方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package lee;

import org.springframework.context.*;
import org.springframework.context.support.*;
import org.crazyit.app.service.*;

public class BeanTest
{
public static void main(String[] args)
{
// 创建Spring容器
@SuppressWarnings("resource")
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"beans.xml");
Hello hello = ctx.getBean("hello", Hello.class);
hello.addUser("孙悟空", "7788");
hello.deleteUser(1);
World world = ctx.getBean("world", World.class);
world.bar();
}
}

执行主程序,将在控制台中看到下所示的效果

1
2
3
4
5
6
模拟执行权限检查
执行Hello组件的addUser添加用户:孙悟空
模拟执行权限检查
执行Hello组件的deleteUser删除用户:1
模拟执行权限检查
执行World组件的bar()方法

使用Before增强处理只能在目标方法执行之前织入增强,如果Before增强处理没有特殊处理,目标方法总会自动执行,如果Before处理需要阻止目标方法的执行,可通过抛出一个异常来实现。**Before增强处理执行时,目标方法还未获得执行的机会,所以Before增强处理无法访问目标方法的返回值**。