在这可能是最手把手的dubbo框架入门中,写了一个Dubbo服务化入门的例子。dubbo-provider
模块是服务提供方,其中包含了服务实现,启动时会作为服务的提供方注册到本地机器的注册中心(例子中用的是本地启动的zookeeper);dubbo-consumer
是服务调用方,其启动时会远程调用注册在注册中心上的服务;dubbo-api
包含了提供服务的接口,在调用服务时,需要引入这个module作为依赖。上一个例子中,已经能够成功实现了通过注册中心的远程接口调用演示。
在Spring注入(IOC)和AOP实例教程中,我们写了好多切面的演示例子。在例子中,通过springaop切面和aspectj切面,都可以正确拦截本地服务的执行。
这里要介绍的是通过aspectj切面,来拦截dubbo服务的执行,并修改服务调用的参数。该例子,在这可能是最手把手的dubbo框架入门中介绍的例子之上做介绍(更具体的说,这里的例子所用的module仍然建在前面的project中)。为了演示,这里建立一个和前面例子中的dubbo-consumer
module类似的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服务确实被切面拦截,环绕切面对服务调用参数的修改也生效了。
最后,工程的结构如下图: