利用Spring中的高级容器ApplicationContext进行业务解耦


    最近在看一本书<<Spring技术内幕>>,一开始当然看到的是基础容器BeanFactory和高级容器ApplicationContext,首先吸引我眼球的就是ApplicationContext的事件机制,以前的我竟然一点都没了解过。。。因此,自己去捣鼓了一翻,写了一个小demo。
场景:
    假如现在有服务1、服务2、服务3.当服务1处理完业务后需要调用服务2和服务3处理后续的业务。正常的写法就是在服务1中调用服务2和服务3的方法,但是这样子服务1和另外两个服务的耦合性就很高了,而且如果服务2和服务3都是其他小伙伴开发的,那么对方的一小点改动就会造成很大的麻烦。这时候,我们可以利用Spring的ApplicationContext的事件机制来完成,当然了,这是在单体应用中比较好的解决方案。如果在当前非常流行的微服务架构中,ApplicationContext的事件机制就没用了,现在比较流行的解决方案都是利用消息中间件,这里就不多做详细的介绍了,下篇文章将为大家讲解。
背景:
    用户注册后,需要发送短信和发送邮件来通知用户已经注册成功。有三个Service:UserService,MessageService,MailService.
原始写法:

    直接在UserService调用MessageService的发送短信方法和MailService的发送邮件方法。
Controller:

/**
* @author Howinfun
* @desc
* @date 2019/5/13
*/
@RestController
@RequestMapping("/user")
@AllArgsConstructor
public class UserController {
    private UserService userService;

    @PostMapping(value = "/registerUser",consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String registerUser(@RequestBody User user){
        userService.registerUser(user);
        return "注册成功";
    }
}

Services:

 /**
     * @author Howinfun
     * @desc 用户Service
     * @date 2019/5/13
     */
    @Service
    @Slf4j
    @AllArgsConstructor
    public class UserService {

        private MessageService messageService;
        private MailService mailService;

        /**
         * 用户注册
         * @param user
         */
        public void registerUser(User user){
            System.out.println("用户:"+user.getName()+"注册成功");
            applicationContext.publishEvent(new UserEvent(this,user.getName(),user.getPhone(),user.getMail()));

            // 发送短信&邮件
            messageService.sendMessage(user);
            mailService.sendEMail(user);
        }
    }
 /**
     * @author Howinfun
     * @desc 短信Service
     * @date 2019/5/13
     */
    @Service
    @Slf4j
    public class MessageService {

        /**
         * 发送短信
         * @param user
         */
        public void sendMessage(User user){
            System.out.println("给用户"+user.getName()+"发送短信,手机号码为:"+user.getPhone());
        }
    }
/**
     * @author Howinfun
     * @desc 邮件Service
     * @date 2019/5/13
     */
    @Service
    @Slf4j
    public class MailService {

        /**
         * 发送邮件
         * @param user
         */
        public void sendEMail(User user){
            System.out.println("给用户"+user.getName()+"发送邮件,EMail为:"+user.getMail());
        }
    }

控制台打印:

2019-05-14 11:52:41.562  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.UserService              : 用户:howinfun注册成功
2019-05-14 11:52:41.573  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.MessageService           : 给用户howinfun发送短信,手机号码为:12345678900
2019-05-14 11:52:41.573  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.MailService              : 给用户howinfun发送邮件,EMail为:baidu@qq.com

总结:我们可以看到上面的代码,服务之间是直接引入和调用了,耦合性非常的高,如果这时候MessageService和MailService的发送方法多了一个参数,那么UserService的注册方法都必须跟着改代码了。
这不是一件很蛋疼的事情吗?
 

升级写法:这时候,我们可以利用ApplicationContext的事件机制来完成业务的解耦,还互相不影响各服务。原理是这样子的:在UserService的注册方法中,当注册成功后,我们就利用ApplicationContext
去发布一个UserEvent(继承ApplicationEvent类)事件,然后实现ApplicationListener<UserEvent>接口的类就可以捕获到这个用户事件,然后就可以进行后续的处理了。
当然了,以后不管是MessageService还是MailService去修改接口,到Listener去改就ok了,完全不影响UserService里头的注册方法,想当的nice。下面上代码。
1、首先创建UserEvent:

/**
* @author Howinfun
* @desc
* @date 2019/5/13
*/
@Data
public class UserEvent extends ApplicationEvent {
    private String name;
    private String phone;
    private String mail;
    public UserEvent(Object source,String name,String phone,String mail) {
        super(source);
        this.name = name;
        this.phone = phone;
        this.mail = mail;
    }
}

2、然后适当修改UserService的代码,去掉MessageService和MailService的方法调用,添加ApplicationContext的依赖:

/**
* @author Howinfun
* @desc 用户Service
* @date 2019/5/13
*/
@Service
@Slf4j
@AllArgsConstructor
public class UserService {

    private ApplicationContext applicationContext;

    /**
     * 用户注册
     * @param user
     */
    public void registerUser(User user){
        System.out.println("用户:"+user.getName()+"注册成功");
        // 发布事件
        applicationContext.publishEvent(new UserEvent(this,user.getName(),user.getPhone(),user.getMail()));
    }
}

3、最后,新增MessageApplicationListener和MailApplicationListener:

/**
* @author Howinfun
* @desc
* @date 2019/5/13
*/
@Component
public class MessageApplicatinoListener implements ApplicationListener<UserEvent> {

    @Autowired
    private MessageService messageService;

    @Override
    public void onApplicationEvent(UserEvent userEvent) {
        User user = new User();
        BeanUtils.copyProperties(userEvent,user);
        // 发送短信
        messageService.sendMessage(user);
    }
}
/**
* @author Howinfun
* @desc
* @date 2019/5/13
*/
@Component
public class MailApplicatinoListener implements ApplicationListener<UserEvent> {

    @Autowired
    private MailService mailService;

    @Override
    public void onApplicationEvent(UserEvent userEvent) {
        User user = new User();
        BeanUtils.copyProperties(userEvent,user);
        // 发送邮件
        mailService.sendEMail(user);
    }
}

4、当然了,最后调用结果和上面的一样:

2019-05-14 11:52:41.562  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.UserService              : 用户:howinfun注册成功
2019-05-14 11:52:41.573  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.MessageService           : 给用户howinfun发送短信,手机号码为:12345678900
2019-05-14 11:52:41.573  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.MailService              : 给用户howinfun发送邮件,EMail为:baidu@qq.com

5、但是我们可以看到,上面的调用都是用一个线程的,就是说,只有当MessageService和MailService的方法调用结束才会返回信息给前端,但是其实正常的用户注册,我们只需要在注册成功后立刻返回信息给前端,所以我们可以用到异步调用。因为项目是基于Spring Boot的,所以只需要在两个Listener加上@Async注解即可,记得在Application类加上@EnableAsync注解。

下面再看一下控制台的打印:能明显看到发动短信和邮件都是另外的线程!

2019-05-14 11:52:41.562  INFO 8388 --- [nio-8080-exec-1] c.h.t.A.service.UserService              : 用户:howinfun注册成功
2019-05-14 11:52:41.573  INFO 8388 --- [         task-2] c.h.t.A.service.MessageService           : 给用户howinfun发送短信,手机号码为:12345678900
2019-05-14 11:52:41.573  INFO 8388 --- [         task-1] c.h.t.A.service.MailService              : 给用户howinfun发送邮件,EMail为:baidu@qq.com

      文章到此结束,如果还有疑问的小伙伴,可在下面评论~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值