本篇演示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方式在实际工作场景中较多。