使用spring事件驱动机制,实现权限信息的自动收集存库
2017-08-25 12:15
591 查看
最近在做权限管理,希望能够在容器初始化后,自动的将所有Controller上的url进行收集,并存放到权限表中,从而代替人工配置的方式。实现思路如下:
spring事件驱动机制+注解来实现。
spring事件驱动机制
spring在容器初始化之后,会触发ContextRefreshedEvent等事件,只要实现了ApplicationListener就可以捕获这个事件,这个时候,我们就可以做很多事情了,比如权限信息的提取,加载缓存等等,所以,我们的思路也是基于此的。
1、自定义注解
2、实现ApplicationListener
测试结果如下:
通过上面的几个步骤,就基本实现了对权限信息的自动提取工作。那我们更近一步,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类来实现,示例代码如下:
建立监听类,并监听事件,这里我们创建两个监听类,分别是短信通知监听和邮件通知监听。
短信通知监听示例代码如下:
邮件通知监听示例代码如下:
发布事件:经过上面的几个步骤,事件源和事件监听就做好了,剩下的就是发布事件了,spring中通过实现ApplicationContextAware类来发布事件,示例代码如下:
测试代码如下:
测试结果如下:
从测试结果来看,需求基本上实现了。
扩展:在spring中,也可以通过实现InitializingBean来实现类似的功能,InitializingBean也是在容器初始化之后才会启动,但他们仍然有区别,InitializingBean的方式要先于spring的事件机制,如果我们的需求是要在所有的类都初始化之后,再做一些事情的话,就不能用InitializingBean了。
在平时的框架中,用到了大量的事件驱动机制,比如springmvc中加载所有的Controller,当有请求的时候,对Controller进行拦截,然后进行Controller的匹配以及分发等,spring cloud中的config热部署配置文件等
最后,我们来稍微解析下源码。
spring事件驱动相关的包都在spring-context的org.springframework.context.event包以及org.springframework.context(接口的相关定义)下,截图如下:
![](http://img.blog.csdn.net/20170825153622383?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1Y2h1YW5ob25nMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20170825153635831?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1Y2h1YW5ob25nMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这里重点说下ApplicationContextAware这个接口:通过该接口,可以很方便的获得spring的ApplicationContext上下文,并通过ApplicationContext获取到spring容器中定义的Bean,该接口只定义了一个方法,如下:
spring会自动的将ApplicationContext注入到ApplicationContextAware接口的实现类中。
spring事件驱动机制+注解来实现。
spring事件驱动机制
spring在容器初始化之后,会触发ContextRefreshedEvent等事件,只要实现了ApplicationListener就可以捕获这个事件,这个时候,我们就可以做很多事情了,比如权限信息的提取,加载缓存等等,所以,我们的思路也是基于此的。
1、自定义注解
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(); }
/** * controller中url对应的操作权限,目前限定的操作为4种 * @author chhliu */ public enum Operation { VIEW, UPDATE, ADD, DELETE }
2、实现ApplicationListener
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上标记有PermissionPath注解的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()); } }
测试结果如下:
===============开始提取权限信息================ 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类来实现,示例代码如下:
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; } }
建立监听类,并监听事件,这里我们创建两个监听类,分别是短信通知监听和邮件通知监听。
短信通知监听示例代码如下:
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()); } }
邮件通知监听示例代码如下:
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类来发布事件,示例代码如下:
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); } }
测试代码如下:
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"); } }
测试结果如下:
调用后台注册服务! 注册成功,发送邮件和短信通知! xyh:注册成功,发送邮件通知! 发送邮件到:xyh 发送人:chhliu 邮件内容:恭喜您,注册我司会员成功,从今天开始,您将享受我司5星级服务! xyh:注册成功,发送短信通知! 发送短信到:xyh 发送人:chhliu 短信内容:恭喜您,注册我司会员成功,从今天开始,您将享受我司5星级服务!
从测试结果来看,需求基本上实现了。
扩展:在spring中,也可以通过实现InitializingBean来实现类似的功能,InitializingBean也是在容器初始化之后才会启动,但他们仍然有区别,InitializingBean的方式要先于spring的事件机制,如果我们的需求是要在所有的类都初始化之后,再做一些事情的话,就不能用InitializingBean了。
在平时的框架中,用到了大量的事件驱动机制,比如springmvc中加载所有的Controller,当有请求的时候,对Controller进行拦截,然后进行Controller的匹配以及分发等,spring cloud中的config热部署配置文件等
最后,我们来稍微解析下源码。
spring事件驱动相关的包都在spring-context的org.springframework.context.event包以及org.springframework.context(接口的相关定义)下,截图如下:
这里重点说下ApplicationContextAware这个接口:通过该接口,可以很方便的获得spring的ApplicationContext上下文,并通过ApplicationContext获取到spring容器中定义的Bean,该接口只定义了一个方法,如下:
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
spring会自动的将ApplicationContext注入到ApplicationContextAware接口的实现类中。
相关文章推荐
- 使用spring事件驱动机制,实现权限信息的自动收集存库
- 使用spring事件驱动机制,实现权限信息的自动收集存库
- 使用spring事件驱动机制,实现权限信息的自动收集存库
- 使用spring注解 自动装配以及自动扫描机制 实现零xml配置的前提
- 使用spring注解 自动装配以及自动扫描机制 实现零xml配置的前提
- 使用spring注解 自动装配以及自动扫描机制 实现零xml配置的前提
- Spring学习-webcontex的自动配置和事件传播机制及使用
- 使用Spring事件机制实现异步的方法
- 使用spring注解 自动装配以及自动扫描机制 实现零xml配置的前提
- 使用Spring 的 AOP 实现权限管理
- 自定义CancelEventArgs类,封装事件参数信息,实现e.Cancle=true取消机制。
- 使用Spring3 实现用户登录以及权限认证
- 使用BeanNameAutoProxyCreator实现spring的自动代理
- Spring中的IOC(四):IOC中其他接口的使用及Spring的事件处理机制(监听机制)
- 基于Spring实现seam形式的事件机制
- Spring学习笔记 使用annotation配置实现Bean的auto-wiring (自动绑定)
- 使用spring的ProxyFactoryBean来实现权限控制
- 使用spring的aop实现权限拦截后出现依赖注入为空的问题
- 使用vs2010的Coded UI Test实现数据驱动的用户自动登录测试
- flex4 + spring + blazeds , 使用anonation(注解)机制,利用push技术的实现例子和过程。