您的位置:首页 > 编程语言 > Java开发

使用spring事件驱动机制,实现权限信息的自动收集存库

2017-08-28 13:43 821 查看
最近在做权限管理,希望能够在容器初始化后,自动的将所有Controller上的url进行收集,并存放到权限表中,从而代替人工配置的方式。实现思路如下:         

                    spring事件驱动机制+注解来实现。

spring事件驱动机制

spring在容器初始化之后,会触发ContextRefreshedEvent等事件,只要实现了ApplicationListener就可以捕获这个事件,这个时候,我们就可以做很多事情了,比如权限信息的提取,加载缓存等等,所以,我们的思路也是基于此的。

1、自定义注解

[java] view
plain copy

import java.lang.annotation.ElementType;  

import java.lang.annotation.Retention;  

import java.lang.annotation.RetentionPolicy;  

import java.lang.annotation.Target;  

  

/** 

 * 权限注解,后台会在容器中所有的类都加载完毕之后,通过扫描该注解,将系统中所有url对应的权限信息存到数据库中, 

 * 减少人为配置 

 * 注意:之所以加该注解而不直接用RequestMapping原因如下: 

 * 1、经测试,classpath下其他的框架中也可能存在使用RequestMapping的情况,这样就会出错,例如org.springframework.boot.autoconfigure.web.BasicErrorController类 

 * 2、RequestMapping没有其他的需要的信息,用起来也不灵活 

 * @author chhliu 

 * 

 */  

@Retention(RetentionPolicy.RUNTIME)  

@Target({ElementType.METHOD, ElementType.TYPE})  

public @interface PermissionPath {  

    // 权限路径,需要和RequestMapping里面的路径一致  

    public String path() default "";  

    // 当前Controller对应的模块名称  

    public String moduleName() default "";  

    // 当前Url对应的操作  

    public Operation operation();  

}  

[java] view
plain copy

/** 

 * controller中url对应的操作权限,目前限定的操作为4种 

 * @author chhliu 

 */  

public enum Operation {  

    VIEW, UPDATE, ADD, DELETE  

}  

2、实现ApplicationListener

[java] view
plain copy

import java.lang.reflect.Method;  

import java.util.Map;  

import java.util.Map.Entry;  

  

import org.springframework.context.ApplicationContext;  

import org.springframework.context.ApplicationListener;  

import org.springframework.context.event.ContextRefreshedEvent;  

import org.springframework.core.annotation.AnnotationUtils;  

import org.springframework.stereotype.Component;  

  

import com.chhliu.srd.rdcloud.annotation.PermissionPath;  

  

/** 

 * spring的事件驱动机制 

 * 实现ApplicationListener后,容器会在初始化所有的bean之后,触发refresh事件 

 * 所以在该方法中,可以实现对系统中所有Controller中url的提取 

 * @author chhliu 

 * 

 */  

@Component  

public class ExtractPermissionInformationContext implements ApplicationListener<ContextRefreshedEvent> {  

  

    /** 

     * refresh事件 

     */  

    @Override  

    public void onApplicationEvent(ContextRefreshedEvent event) {  

          

        System.out.println("===============开始提取权限信息================");  

        System.out.println(event.getTimestamp());  

        // 通过event获取到spring 的ApplicationContext上下文  

        ApplicationContext applicationContext = event.getApplicationContext();  

          

        // 获取到所有Bean上标记有RequestMapping注解的Bean  

        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(PermissionPath.class);  

        // 判空  

        if(null != beansWithAnnotation && !beansWithAnnotation.isEmpty()){  

            for(Entry<String, Object> entry:beansWithAnnotation.entrySet()){  

                // 获取带指定注解的类  

                Class<? extends Object> clz = entry.getValue().getClass();  

                System.out.println("className:"+clz.getName());  

                // 通过类获取到类上注解的信息  

                PermissionPath rm = AnnotationUtils.findAnnotation(clz, PermissionPath.class);  

                System.out.println("class requestMapping:"+rm.path());  

                  

                // 获取该类下的所有方法  

                Method[] methods = clz.getMethods();  

                for(Method m:methods){  

                    // 如果该方法上标记了对应的注解,则是我们需要提取权限信息的方法  

                    if(m.isAnnotationPresent(PermissionPath.class)){  

                        // 获取方法上注解的信息  

                        PermissionPath ma = m.getAnnotation(PermissionPath.class);  

                        System.out.println("method requestMapping:"+ma.path());  

                    }  

                }  

            }  

        }  

        System.out.println("===============开始提取权限信息================");  

        System.out.println(event.getTimestamp());  

    }  

}  

测试结果如下:

[java] view
plain copy

===============开始提取权限信息================  

1503631242290  

className:com.chhliu.srd.rdcloud.contractmanager.contractimport.controller.ContractImportController  

class requestMapping:/api/contractimport  

method requestMapping:updateMilston  

method requestMapping:queryDepartMent  

method requestMapping:queryByCondition  

method requestMapping:/fileUpload  

通过上面的几个步骤,就基本实现了对权限信息的自动提取工作。那我们更近一步,spring的事件驱动机制是怎么工作的了,下面我们来看一个简单的示例。

spring事件驱动流程:spring的事件传播机制 是基于观察者模式(Observer)实现的,它可以将 Spring Bean 的改变定义为事件 ApplicationEvent,通过 ApplicationListener 监听 ApplicationEvent
事件,一旦Spring Bean 使用 ApplicationContext.publishEvent( ApplicationEvent event )发布事件后,Spring 容器会通知注册在容器中的所有 ApplicationListener 接口的实现类,最后 ApplicationListener 接口实现类判断是否响应刚发布出来的 ApplicationEvent 事件。

从上面的流程原理这段描述中,我们可以发现几个关键类:1、ApplicationEvent,2、ApplicationListener

所以,在使用中,会有如下几个步骤:

1、建立事件类,即我们对外要发布的事件

2、建立监听类,监听第一步中发布的事件

3、发布事件

下面我们就通过一个简单的示例来逐步实现,示例需求:用户在注册成功之后,发布通知事件,通知采取短信和邮件两种通知方式,当事件监听器监听到发布的事件之后,通知用户注册成功。

建立事件类:通过继承ApplicationEvent类来实现,示例代码如下:

[java] view
plain copy

package com.chhliu.application;  

  

import org.springframework.context.ApplicationEvent;  

  

public class SendEvent extends ApplicationEvent {  

      

    // 邮件或者是短信主题  

    private String title;  

      

    // 邮件或者是短信发送人  

    private String sender;  

      

    // 邮件或短信接收人  

    private String receiver;  

      

    // 邮件或短信内容  

    private String message;  

    /** 

     *  

     */  

    private static final long serialVersionUID = 1L;  

  

    public SendEvent(String source) {  

        super(source);  

    }  

      

    public SendEvent(String source, String title, String sender, String receiver, String message){  

        super(source);  

        this.title = title;  

        this.sender = sender;  

        this.receiver = receiver;  

        this.message = message;  

    }  

}  

建立监听类,并监听事件,这里我们创建两个监听类,分别是短信通知监听和邮件通知监听。

短信通知监听示例代码如下:

[java] view
plain copy

package com.chhliu.application;  

  

import org.springframework.context.ApplicationListener;  

import org.springframework.stereotype.Component;  

  

@Component  

public class MessageSendListener implements ApplicationListener<SendEvent> {  

  

    @Override  

    public void onApplicationEvent(SendEvent event) {  

        System.out.println(event.getSource()+":注册成功,发送短信通知!");  

          

        System.out.println("发送短信到:"+event.getReceiver()+" 发送人:"+event.getSender()+" 短信内容:"+event.getMessage());  

    }  

  

}  

邮件通知监听示例代码如下:

[java] view
plain copy

package com.chhliu.application;  

  

import org.springframework.context.ApplicationListener;  

import org.springframework.stereotype.Component;  

  

@Component  

public class MailSendListener implements ApplicationListener<SendEvent> {  

  

    @Override  

    public void onApplicationEvent(SendEvent event) {  

        System.out.println(event.getSource()+":注册成功,发送邮件通知!");  

          

        System.out.println("发送邮件到:"+event.getReceiver()+" 发送人:"+event.getSender()+" 邮件内容:"+event.getMessage());  

    }  

  

}  

发布事件:经过上面的几个步骤,事件源和事件监听就做好了,剩下的就是发布事件了,spring中通过实现ApplicationContextAware类来发布事件,示例代码如下:

[java] view
plain copy

package com.chhliu.application;  

  

import org.springframework.beans.BeansException;  

import org.springframework.context.ApplicationContext;  

import org.springframework.context.ApplicationContextAware;  

import org.springframework.stereotype.Service;  

  

@Service  

public class Registration implements ApplicationContextAware {  

  

    private ApplicationContext applicationContext;  

      

      

    @Override  

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  

        this.applicationContext = applicationContext;  

    }  

      

    public void register(final String username, final String password){  

        System.out.println("调用后台注册服务!");  

        System.out.println("注册成功,发送邮件和短信通知!");  

          

        // 创建待发布的事件  

        SendEvent se = new SendEvent(username, "注册会员成功通知", "chhliu", username, "恭喜您,注册我司会员成功,从今天开始,您将享受我司5星级服务!");  

        // 发布事件  

        this.applicationContext.publishEvent(se);  

    }  

}  

测试代码如下:

[java] view
plain copy

package com.chhliu;  

  

import org.junit.Test;  

import org.junit.runner.RunWith;  

import org.springframework.beans.factory.annotation.Autowired;  

import org.springframework.boot.test.context.SpringBootTest;  

import org.springframework.test.context.junit4.SpringRunner;  

  

import com.chhliu.application.Registration;  

  

@RunWith(SpringRunner.class)  

@SpringBootTest  

public class SpringStudyApplicationTests {  

      

    @Autowired  

    private Registration regis;  

      

    @Test  

    public void contextLoads() {  

        regis.register("xyh", "123456");  

          

    }  

  

}  

测试结果如下:

[java] view
plain copy

调用后台注册服务!  

注册成功,发送邮件和短信通知!  

xyh:注册成功,发送邮件通知!  

发送邮件到:xyh 发送人:chhliu 邮件内容:恭喜您,注册我司会员成功,从今天开始,您将享受我司5星级服务!  

xyh:注册成功,发送短信通知!  

发送短信到:xyh 发送人:chhliu 短信内容:恭喜您,注册我司会员成功,从今天开始,您将享受我司5星级服务!
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: