本篇演示AOP的Hello World示例,三种方式,aspectJ类,注解方式,spring AOP。
示例演示在MessageCommunicator的deliver方法执行之前插入一段代码,这里是Authenticator类的authenticate方法,插入的这段代码不是重点,可以直接替换为简单代码,例如System.out.println("come from aop")。
1、aspectJ类
实现的步骤如下:
第一步:编写任意业务模块的类,编写任意方法。原著中为MessageCommunicator类,它只是将方法的参数打印在控制台上。创建对象,运行deliver方法,它只是简单的把参数打印在控制台。
public class MessageCommunicator {
/**
*
* @Title: deliver
* @Description:方法非常简单,将message打印到控制台上
* @param message
*/
public void deliver(String message) {
System.out.println(message);
}
/**
*
* @Title: deliver
* @Description 方法非常简单,将Person的名称和message打印在控制台上
* @param person
* @param message
*/
public void deliver(String person, String message) {
System.out.println(person + ":" + message);
}
}
第二步:公共业务模块的代码,这段代码让用户输入两次,两次输入一致即通过,如果为了方便,可以修改authenticate(),输出”check something”。
public class Authenticator {
private ThreadLocal<String> authenticatedUser =
new ThreadLocal<String>();
public void authenticate() {
if (isAuthenticated()) {
return;
}
String[] userNamePassword = getUserNamePassword();
if (!userNamePassword[0].equals(userNamePassword[1])) {
System.out.println("User/password didn't match");
}
authenticatedUser.set(userNamePassword[0]);
}
public boolean isAuthenticated() {
return authenticatedUser.get() != null;
}
public String[] getUserNamePassword() {
boolean usePrintln = Boolean.getBoolean("ant.run");
String[] userNamePassword = new String[2];
BufferedReader in =
new BufferedReader(new InputStreamReader(System.in));
try {
if (usePrintln) {
System.out.println("Username: ");
} else {
System.out.print("Username: ");
}
userNamePassword[0] = in.readLine().trim();
if (usePrintln) {
System.out.println("Password: ");
} else {
System.out.print("Password: ");
}
userNamePassword[1] = in.readLine().trim();
} catch (IOException ex) {
// ignore... will return array of null strings
}
return userNamePassword;
}
}
第三步:编写AspectJ,这是重点。它将业务模块和公共模块关联起来。
public aspect SecurityAspectJ {
// 继承Java的语法,可以自定义变量
private Authenticator authenticator = new Authenticator();
/**
*
* @Title: secureAccess
* @Description:定义pointcut,只能在AspectJ中定义,在本例为MessageCommunicator的deliver方法
* 格式 pointcut name : join point,本示例中三个部分
* 1.pointcut:关键字
* 2.secureAccess: pointcut的名称,不是必须的
* 3.execution(): 是join point的一种,表示在方法执行之前。在第四章节中将介绍
* 4. * :* 表示不限包名,
* 5 MessageCommunicator.deliver(..)表示MessageCommunicator的deliver方法,
* 6. .. 表示不限制方法的参数个数和类型
*/
pointcut secureAccess() : execution(* MessageCommunicator.deliver(..));
/**
*
* @Title: before
* @Description:定义Advice
* 格式 when : pointcut name { // 代码,通常包含公共模块的代码 }
* 1. when:定义AspectJ代码与业务代码的运行时机,有三种类型,before(之前),after(之后),around(环绕)
* 2. pointcut name:这里引用上述的secureAccess
* 3. AspectJ代码:通常包含公共模块的代码,在后续章节中会学到很多的特定上下文。
*/
before() : secureAccess(){
System.out.println("Checking and authenticating user");
authenticator.authenticate();
}
}
上述的AspectJ包含pointcut,advice。它的格式,以及每一项的含义都包含在方法注释中。上述的Advice只是其中一种Crosscutting element(切面点),在第二小节会具体介绍切面点。
2、注解语法
AspectJ还有另外一种语法,是在普通Java类上添加AOP相关注解。代码如下:
/**
*
* @File Name: Security.java
* @Description: SecurityAspectJ的
* @version 1.0
* @since JDK 1.8
*/
@Aspect
public class Security {
// 负责校验功能的类
private Authenticator authenticator = new Authenticator();
@Pointcut("execution (* ch2.MessageCommunicator.deliver(..))")
public void secureAccess() {
}
@Before("secureAccess()")
public void secure() {
System.out.println("Checking and authenticating user");
authenticator.authenticate();
}
}
与直接编写AspectJ,主要有以下三点区别:
- AspectJ:从public aspect name的语法改变为在@AspectJ注解。
- Pointcut:有以下三个变化
-
- Pointcut关键字改变为@Pointcut注解
- Pointcut的名称从自定义名称改变为Java类方法的名称,在上述示例中为secureAccess方法,它方法体是空的。
- Pointcut中join point这部分的内容没有改变,结构从跟在name的冒号变为@Pointcut注解的value属性。
3.Advice:有以下两个变化
-
- Advice中的时机before改变为@Before注解
- Pointcut的name改变为@Before注解的value属性。
3、SpringAOP方式
使用spring方式实现相同功能的步骤如下:
- 编写业务代码,公共模块代码,编写Aspect,这些都不变
- 将业务类,公共类,Aspect类注入到容器中。
- 在配置文件中引入aop schema,添加<aop:aspectj-autoproxy>配置。
配置如下:
<aop:aspectj-autoproxy/> <!-- 配置公共业务模块 --> <bean id="coreConcern" class="ch2.MessageCommunicator"/> <!-- 配置Aspect --> <bean id="securityAspect" class="ch2.Security"/>
注解方式,SpringAOP方式在实际工作场景中较多。