在这可能是最手把手的 dubbo 框架入门中, 写了一个 Dubbo 服务化入门的例子. dubbo-provider 模块是服务提供方, 其中包含了服务实现, 启动时会作为服务的提供方注册到本地机器的注册中心(例子中用的是本地启动的 zookeeper);dubbo-consumer 是服务调用方, 其启动时会远程调用注册在注册中心上的服务; dubbo-API 包含了提供服务的接口, 在调用服务时, 需要引入这个 module 作为依赖. 上一个例子中, 已经能够成功实现了通过注册中心的远程接口调用演示.
在 Spring 注入 (IoC) 和 AOP 实例教程中, 我们写了好多切面的演示例子. 在例子中, 通过 springaop 切面和 aspectj 切面, 都可以正确拦截本地服务的执行.
这里要介绍的是通过 aspectj 切面, 来拦截 dubbo 服务的执行, 并修改服务调用的参数. 该例子, 在这可能是最手把手的 dubbo 框架入门中介绍的例子之上做介绍(更具体的说, 这里的例子所用的 module 仍然建在前面的 project 中). 为了演示, 这里建立一个和前面例子中的 dubbo-consumermodule 类似的 module, 也是基于 maven,groupid 写 com.spacecat.dubbo,Artifactid 写 dubbo-consumer-aspect.
pom.xml 文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.spacecat.dubbo</groupId>
- <artifactId>dubbo-consumer-aspect</artifactId>
- <version>1.0-SNAPSHOT</version>
- <dependencies>
- <!-- Spring Core -->
- <!-- http://mvnrepository.com/artifact/org.springframework/spring-core -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>4.1.4.RELEASE</version>
- </dependency>
- <!-- Spring Context -->
- <!-- http://mvnrepository.com/artifact/org.springframework/spring-context -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>4.1.4.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>dubbo</artifactId>
- <version>2.5.3</version>
- <exclusions>
- <exclusion>
- <groupId>org.springframework</groupId>
- <artifactId>spring</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.spacecat.dubbo</groupId>
- <artifactId>dubbo-API</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.apache.zookeeper</groupId>
- <artifactId>zookeeper</artifactId>
- <version>3.4.10</version>
- </dependency>
- <dependency>
- <groupId>com.GitHub.sgroschupf</groupId>
- <artifactId>zkclient</artifactId>
- <version>0.1</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aspects</artifactId>
- <version>5.1.8.RELEASE</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.7.4</version>
- </dependency>
- </dependencies>
- </project>
写一个类, 来调用前面 dubbo-provider 提供的服务, 实际上, 这个文件和之前例子中的一样.
- com.dubbo.consumer.UserServiceCaller:
- package com.dubbo.consumer;
- import com.dubbo.API.UserService;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * Created by chengxia on 2019/5/5.
- */
- public class UserServiceCaller {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("dubbo-consumer.xml");
- UserService service = (UserService) context.getBean("userService");
- System.out.print("Got service bean:");
- System.out.println(service);
- System.out.println("RPC call output:");
- System.out.println(service.sayHi("Kobe"));
- }
- }
写一个 aspectj 切面实现类.
- com.spring.aspect.MyAspect:
- package com.spring.aspect;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- /**
- * Created by chengxia on 2019/8/2.
- */
- @Aspect // 表明当前 POJO 类是一个切面
- public class MyAspect {
- // 前置通知
- @Before("execution(* com.dubbo.api..*.*(..))")
- public void myBefore(){
- System.out.println("执行前置通知方法");
- }
- // 后置通知
- @AfterReturning("execution(* com.dubbo.api..*.*(..))")
- public void myAfterReturning(){
- System.out.println("执行后置通知");
- }
- // 环绕通知
- @Around("execution(* com.dubbo.api..*.*(..))")
- public Object around(ProceedingJoinPoint pjp) throws Throwable{
- System.out.println("环绕通知: 目标方法执行之前");
- Object[] args = pjp.getArgs(); // 获取被切函数 参数
- args[0] = "Duncan";
- Object res = pjp.proceed(args);
- System.out.println("环绕通知: 目标方法执行之后" + args[0]);
- if(res != null){
- res = ((String)res).toUpperCase();
- }
- return res;
- }
- }
在这个类中实现了前置通知, 后置通知以及环绕通知, 在环绕通知的实现中, 拦截并修改了服务调用参数的值.
最后, 在配置文件 dubbo-consumer.xml 中配置要调用的服务, 以及配置切面实现类, 如下.
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
- <dubbo:application name="dubbo-consumer"/>
- <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
- <dubbo:consumer check="false"/>
- <!-- 导入 dubbo 配置, dubbo-api 模块打包后自带的配置文件 -->
- <import resource="classpath*:user-references.xml"/>
- <!-- 注册切面 -->
- <bean id="myAspect" class="com.spring.aspect.MyAspect"/>
- <!-- AspectJ 的自动代理 -->
- <aop:aspectj-autoproxy/>
- </beans>
这时候, 通过如下命令启动本地的 zookeeper:
$ zkServer status ZooKeeper JMX enabled by default Using config: /usr/local/etc/zookeeper/zoo.cfg Error contacting service. It is probably not running. $ zkServer start ZooKeeper JMX enabled by default Using config: /usr/local/etc/zookeeper/zoo.cfg Starting zookeeper ... STARTED $ zkServer status ZooKeeper JMX enabled by default Using config: /usr/local/etc/zookeeper/zoo.cfg Mode: standalone localhost:~ chengxia$
启动并运行 dubbo-provider 模块, 然后, 运行 dubbo-consumer-aspect 中的 com.dubbo.consumer.UserServiceCaller, 最后, 控制台输出如下.
Got service bean:com.alibaba.dubbo.common.bytecode.proxy0@383dc82c RPC call output:
环绕通知: 目标方法执行之前
执行前置通知方法
环绕通知: 目标方法执行之后 Duncan
执行后置通知
HELLO DUNCAN! Process finished with exit code 0
可以看到 dubbo 服务确实被切面拦截, 环绕切面对服务调用参数的修改也生效了.
最后, 工程的结构如下图:
aspectj 切面在 dubbo 服务中的应用
参考资料
Spring 注入 (IoC) 和 AOP 实例教程
这可能是最手把手的 dubbo 框架入门
来源: http://www.jianshu.com/p/fddf152c7167