- 一 项目概述
- 1 设计思想
- 2 项目结构
- 二 注解使用
- 1 环境配置
- 1.1 注册中心
- 1.2 调用方式
- 2 添加注解
- 2.1 @Email
- 2.2 @SMS
- 2.3 @Message
- 3. 属性配置
- 3.0 type
- 3.1 value
- 3.2 feignName
- 3.3 feignIP
- 3.4 msMethod
- 3.5 sendConfig
- 3.6 callWay
- 3 实战案例
- 4 注意事项
- 4.1 mybatis-plus 版本
- 4.2 AOP代理生成-循环依赖
- 三 注解开发
- 1 项目结构
- 2 执行流程
- 2.1 触发器: AOP
- 2.2 调度器: Dispatcher
- 2.2 映射器: Mapping
- 2.3 适配器: Adapter
- 2.4 处理器: Handler
- 2.5 解析器: Resolve
- 四 后续开发
- 1 写表日志
- 2 邮件附件
- 3 定时+异步调用
- 4 集成消息队列
- 5 配置调用
- 6 增删改查开发
- 后记
1 环境配置
1.1 注册中心
- Eureka
(1) pom.xml 依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-client
(2) 依赖管理器
org.springframework.cloud spring-cloud-dependencies Hoxton.SR1 pom import
(3) application.properties
spring.application.name=Message-ann eureka.client.service-url.defaultZone=http://localhost:8000/eureka/ spring.main.allow-bean-definition-overriding=true
- Nacos
(1) pom.xml 依赖
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 2.2.3.RELEASE
(2) 依赖管理器
org.springframework.cloud spring-cloud-dependencies Hoxton.SR1 pom import
(3) application.properties
spring.application.name=Message-ann spring.main.allow-bean-definition-overriding=true # discovery spring.cloud.nacos.discovery.server-addr=10.22.50.72:31504 #spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.cloud.nacos.discovery.enabled=true spring.cloud.nacos.discovery.namespace=a5447a9d-28e9-41c2-b5eb-8f795b17bc8f spring.cloud.nacos.discovery.group=DEV
(4) 启动类上
@ComponentScan(basePackages={"com.boss.**","cn.**","com.sunline.ms.**"})
@EnableFeignClients(basePackages = "com.sunline.ms.feign.**")
@EnableDiscoveryClient
@RefreshScope
1.2 调用方式
-
Ribbon
-
Openfeign
2 添加注解org.springframework.cloud spring-cloud-starter-openfeign
注解体系:
2.1 @Email- 发送邮件
@Email
public SmsDto addEmail(SmsDto smsVo) {
//处理....
return smsDto;
}
2.2 @SMS
- 发送短信
@SMS
public SmsDto addSMS(SmsDto smsVo) {
//处理....
return smsDto;
}
2.3 @Message
- 配置发送类型
@Message(type = MessageType.Email)
private SmsDto b(){
}
3. 属性配置
属性体系:
3.0 type-
发送邮件
-
发送短信
@Message(type = MessageType.Email)
@Message(type = MessageType.SMS)
3.1 value
- 调用服务名
@Message(value = "boss-sms")3.2 feignName
- 调用服务名
@Message(feignName = "boss-sms")3.3 feignIP
- 调用服务URL: IP+端口+路径
@Message(feignIP = "127.0.0.1:8080/123")3.4 msMethod
- 增删改查
@Message(msMethod = MSMethod.ADD_MS)
@Message(msMethod = MSMethod.DELETE_MS)
@Message(msMethod = MSMethod.UPDATE_MS)
@Message(msMethod = MSMethod.SELECT_MS)
3.5 sendConfig
- 异步
- 定时
- 异步+定时
@Message(sendConfig = SendConfig.ASYNC)
@Message(sendConfig = SendConfig.SCHEDULING)
@Message(sendConfig = SendConfig.ASYNC_SCHEDULING)
3.6 callWay
- OpenFeign
- Ribbon+RestTemplate
@Message(callWay = CallWay.OpenFeign)
@Message(callWay = CallWay.Ribbon_Rest)
3 实战案例
架构调整-通知org2100025-文档
(1) controller
@ApiOperation(value = "架构调整通知", notes = "架构调整通知")
@PostMapping("/org/org210025")
public ResponseResult mailArchitectAdjNoticeController(@RequestBody FlowArchitectAdjNoticeVo flowArchitectAdjNoticeVo) {
flowArchitectADJNoticeService.mailSend(flowArchitectAdjNoticeVo.getDocNo());
return result();
}
(2) service
- 版本1
@Override
@Email
public SmsDto mailSend(String docNo) {
//查询内容-->set
SmsDto smsDto = new SmsDto();
FlowArchitectAdjNotice one = this.getOne(new QueryWrapper().lambda().eq(FlowArchitectAdjNotice::getDocNo, docNo));
smsDto.setSubject(one.getTitle());
smsDto.setMessageType("email");
smsDto.setSourceService("ERP");
ArrayList strings = new ArrayList<>();
List flowArchitectAdjNoticeEmplyList = flowArchitectADJNoticeEmplyService.list(new QueryWrapper().lambda().eq(FlowArchitectAdjNoticeEmply::getDocNo, docNo));
for (FlowArchitectAdjNoticeEmply flowArchitectAdjNoticeEmply : flowArchitectAdjNoticeEmplyList) {
Emplybase i = emplybaseService.getOne(new QueryWrapper().lambda().eq(Emplybase::getEmplyNo, flowArchitectAdjNoticeEmply.getEmplyNo()));
strings.add(i.getCorpMail());
}
smsDto.setMailReceiver(strings.toArray(new String[strings.size()]));
String url = serverFileHost+":"+serverFilePort;
StringBuffer buffer = new StringBuffer();
List commonFileRelationshipList = commonFileRelationshipService.list(new QueryWrapper().lambda().eq(CommonFileRelationship::getBusinessValue1, docNo));
for (CommonFileRelationship commonFileRelationship : commonFileRelationshipList) {
SysFileInfo byId = sysFileInfoService.getById(commonFileRelationship.getSysFileInfoId());
buffer.append(url);
buffer.append(byId.getFilePath()+byId.getFileName()+byId.getFileSuffix());
buffer.append(",");
}
smsDto.setContent(one.getNoticeContent()+"n"+"n"+buffer);
return smsDto;
}
- 版本2
@Autowired FlowArchitectADJNoticeService flowArchitectADJNoticeService;
@Override
public void mailSend(String docNo) {
//查询内容-->set
SmsDto smsDto = new SmsDto();
FlowArchitectAdjNotice one = this.getOne(new QueryWrapper().lambda().eq(FlowArchitectAdjNotice::getDocNo, docNo));
smsDto.setSubject(one.getTitle());
smsDto.setMessageType("email");
smsDto.setSourceService("ERP");
ArrayList strings = new ArrayList<>();
List flowArchitectAdjNoticeEmplyList = flowArchitectADJNoticeEmplyService.list(new QueryWrapper().lambda().eq(FlowArchitectAdjNoticeEmply::getDocNo, docNo));
for (FlowArchitectAdjNoticeEmply flowArchitectAdjNoticeEmply : flowArchitectAdjNoticeEmplyList) {
Emplybase i = emplybaseService.getOne(new QueryWrapper().lambda().eq(Emplybase::getEmplyNo, flowArchitectAdjNoticeEmply.getEmplyNo()));
strings.add(i.getCorpMail());
}
smsDto.setMailReceiver(strings.toArray(new String[strings.size()]));
String url = serverFileHost+":"+serverFilePort;
StringBuffer buffer = new StringBuffer();
List commonFileRelationshipList = commonFileRelationshipService.list(new QueryWrapper().lambda().eq(CommonFileRelationship::getBusinessValue1, docNo));
for (CommonFileRelationship commonFileRelationship : commonFileRelationshipList) {
SysFileInfo byId = sysFileInfoService.getById(commonFileRelationship.getSysFileInfoId());
buffer.append(url);
buffer.append(byId.getFilePath()+byId.getFileName()+byId.getFileSuffix());
buffer.append(",");
}
smsDto.setContent(one.getNoticeContent()+"n"+"n"+buffer);
//调发送方法
Object o = flowArchitectADJNoticeService.messageSend(smsDto);
}
@Email
public SmsDto messageSend(SmsDto smsDto) {
//抽取~公共处理/最后处理
return smsDto;
}
4 注意事项
4.1 mybatis-plus 版本
版本: 3.4.3–>3.4.1
4.2 AOP代理生成-循环依赖- AOP原理
调用代理对象的增强方法
-
JDK代理与CGLib代理
-
this调用无效
@Override
public void mailSend(String docNo) {
//业务逻辑
...
//调发送方法
Object o = this.messageSend(smsDto);
}
@Override
public void mailSend(String docNo) {
//业务逻辑
...
//调发送方法
Object o = flowArchitectADJNoticeService.messageSend(smsDto);
}
- 获取代理对象
this不是代理对象,需要在spring容器重新获取
(1) 循环依赖
用Autowired 注入自身的实例
@Autowired FlowArchitectADJNoticeService flowArchitectADJNoticeService;
(2) ApplicationContext获取增强后的实例引用
@Autowired private ApplicationContext applicationContext;
applicationContext.getBean(FlowArchitectADJNoticeService.class);
(3) AopContext获取增强后的实例引用
(FlowArchitectADJNoticeService)AopContext.currentProxy();
@EnableAspectJAutoProxy(exposeProxy = true) //启动类三 注解开发
1 项目结构 2 执行流程开发顺序
注册触发器
@Pointcut("@annotation(com.sunline.ms.annotation.AEmail.Email)")
public void pointcut() {
}
2.2 调度器: Dispatcher
链式调用: 映射器–>适配器–>处理器–>解析器
//链式调用
@Around(value = "@annotation(com.sunline.ms.annotation.AEmail.Email)")
public Object aroundEmail(ProceedingJoinPoint joinPoint){
try {
EmailConfigStream emailConfigStream = new EmailConfigStream();
emailConfigStream.setType(MessageType.Email);
EmailServerBean emailServerBean = new EmailServerBean(null,emailConfigStream,joinPoint);
//配置读取-->参数校验-->调度执行-->参数封装-->解析返回
emailServerBean.handlerMappingAOP().handlerAdapterAOP().HandlerAOP().resolverAOP();
//返回!=null正常
return emailServerBean.getSmsDto();
}catch (Throwable throwable){
throwable.printStackTrace();
return null;
}
}
2.2 映射器: Mapping
注解配置映射–>ConfigBean
public void readConfig(baseServerBean baseServerBean) {
//获取参数
Method method = this.getConfig(baseServerBean.getJoinPoint());
//注入参数
if (method!=null)
this.setConfig(baseServerBean.getbaseConfigStream(),method);
}
private Method getConfig(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
Class>[] argTypes = new Class[joinPoint.getArgs().length];
for (int i = 0; i < args.length; i++) {
argTypes[i] = args[i].getClass();
}
Method method = null;
try {
method = joinPoint.getTarget().getClass()
.getMethod(joinPoint.getSignature().getName(), argTypes);
return method;
} catch (NoSuchMethodException e) {
e.printStackTrace();
return null;
} catch (SecurityException e) {
e.printStackTrace();
return null;
}
}
private void setConfig(baseConfigStream baseConfigStream, Method method) {
if (baseConfigStream.getType().equals(MessageType.Email)){
Email email= method.getAnnotation(Email.class);
baseConfigStream.setValue(email.value());
baseConfigStream.setType(email.type());
baseConfigStream.setSendConfig(email.sendConfig());
baseConfigStream.setCallWay(email.callWay());
baseConfigStream.setFeignName(email.feignName());
baseConfigStream.setFeignIP(email.feignIP());
baseConfigStream.setMsMethod(email.msMethod());
}else if(baseConfigStream.getType().equals(MessageType.SMS)){
SMS sms = method.getAnnotation(SMS.class);
baseConfigStream.setValue(sms.value());
baseConfigStream.setType(sms.type());
baseConfigStream.setSendConfig(sms.sendConfig());
baseConfigStream.setCallWay(sms.callWay());
baseConfigStream.setFeignName(sms.feignName());
baseConfigStream.setFeignIP(sms.feignIP());
baseConfigStream.setMsMethod(sms.msMethod());
}else {
Message message = method.getAnnotation(Message.class);
baseConfigStream.setValue(message.value());
baseConfigStream.setType(message.type());
baseConfigStream.setSendConfig(message.sendConfig());
baseConfigStream.setCallWay(message.callWay());
baseConfigStream.setFeignName(message.feignName());
baseConfigStream.setFeignIP(message.feignIP());
baseConfigStream.setMsMethod(message.msMethod());
}
}
2.3 适配器: Adapter
反射注入ConfigBean配置
public void paramReflect(baseServerBean baseServerBean) {
try {
//反射改变FeignName:-value
changeFeignName(baseServerBean.getbaseConfigStream().getFeignName());
//反射改变FeignIp
//changeFeignIp(baseServerBean.getbaseConfigStream().getFeignIP());
//反射改变调用方式:
//changeFeignCallWay(baseServerBean.getbaseConfigStream().getCallWay());
}catch (Exception e){
e.printStackTrace();
}
}
private void changeFeignName(String feignName) throws NoSuchFieldException, IllegalAccessException {
Field nameField = FeignClientConfig.class.getDeclaredField("NAME");
nameField.setAccessible(true);
//去掉final
Field modifiers = nameField.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL);
//填写配置
nameField.set(null,feignName);
//加上final
modifiers.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL);
//查看结果
System.out.println("------------------------");
System.out.println(FeignClientConfig.NAME);
System.out.println("------------------------");
}
private void changeFeignCallWay(CallWay callWay) {
}
private void changeFeignIp(String feignIP) {
}
2.4 处理器: Handler
调用OpenFeign接口
public SmsDto executeMethod(baseServerBean baseServerBean) throws Throwable {
//-----------------------
//根据ConfigStream选择执行
//-----------------------
//执行方法
Object o=baseServerBean.getJoinPoint().proceed();//目标方法
//根据ConfigStream.method+sendConfig判断
MSMethod msMethod = baseServerBean.getbaseConfigStream().getMsMethod();
CommonSmsFeign commonSmsFeign = SpringUtils.getBean(CommonSmsFeign.class);
switch (baseServerBean.getbaseConfigStream().getMsMethod()) {
case ADD_MS: {
System.out.println("ADD_MS");
if (o instanceof SmsDto) commonSmsFeign.addMs((SmsDto) o);
break;
}
case UPDATE_MS: {
System.out.println("UPDATE_MS");
break;
}
case DELETE_MS: {
System.out.println("DELETE_MS");
break;
}
case SELECT_MS: {
System.out.println("SELECT_MS");
break;
}
default: {
}
}
return (SmsDto) o;
}
2.5 解析器: Resolve
处理返回结果
public void returnDto(baseServerBean baseServerBean) {
}
四 后续开发 1 写表日志
- 方案一
- 方案二
3 定时+异步调用DTO加附件
调用者业务
- 例子
@Scheduled(cron = "0 30 0 * * ? *")
@Async
@Email
public SmsDto messageSend(SmsDto smsDto) {
return smsDto;
}
4 集成消息队列
5 配置调用异步、解耦、消峰
- openfigname+ip+多例
问题1 : 暴力反射无效?
问题2 :Openfeign单例–>多例加载
- ribbon调用方式配置调用
后记@增删改查Email
@增删改查SMS
注解模块再抽象–>开发注解框架



