前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring MVC使用AOP拦截Controller

Spring MVC使用AOP拦截Controller

作者头像
幽鸿
发布2020-04-02 15:26:49
2.2K1
发布2020-04-02 15:26:49
举报

接着上篇文章《SpringMVC快速使用AOP》继续,如果我们需要对Controller进行切面编程,加上注解后,会发现我们的LogAspect竟然无法拦截到Controller层,仔细查找原因后,发现我们的代码并无过错。但是,我们确实有拦截Controller层的需要,比如日志记载,比如权限控制等等。有的时候,我们不止需要拦截Controller,还需要获取到HttpServletRequest,那么该如何解决这个问题呢?

     其实并不是什么Spring的Controller层已经被AnnotationMethodHandlerAdapter给拦截了,真正的原因是:我在配置该Demo项目的时候采用了applicationContext.xml和spring-servlet.xml两个配置文件,其中值得一提的是:spring-servlet.xml配置文件可以直接丢在web文件夹下,而不用在web.xml中配置,我亲自试过有效。但是为了方便文件管理,还是和applicationContext.xml一起放在resource路径下哦。

     我们必须先明白这两个配置文件在SpringMVC中的作用,applicationContext.xml会在ContextLoaderListenerclass被初始化时加载,Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在 ServletContext中,keyWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。而spring-servlet.xml可以配置多个,它代表每个 DispatcherServlet有一个自己的上下文对象(WebApplicationContext),称为子上下文(子容器),子上下文可以访问父上下文中的内容,但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。

     那么问题来了,当spring在加载父容器的时候就会去找切入点,但是这个时候切入的controller是在子容器中的,父容器是无法访问子容器,所以就拦截不到。所以只需将上文配置的丢到spring-servlet.xml子配置文件中去即可。注意这里了,这是SpringMVC web项目案例,如果是测Server服务,还是放在applicationContext.xml中哦。

     这里,对Aspect代码进行更新,贴出最简代码:

代码语言:javascript
复制
@Aspect
public class LogAspect {
	
	private final static Logger logger=Logger.getLogger(LogAspect.class);
	
	@Autowired
	private LogService logService;
	
	//Controller层切点  
    @Pointcut("@annotation(com.annotation.LogAnno)")  
    public void controllerAspect() {  
    } 
    
    @Before("controllerAspect()")  
    public void doBefore(JoinPoint joinPoint) {  
  
    	try {
        	HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
            HttpSession session = request.getSession();  
            //读取session中的用户  
            User user = (User) session.getAttribute("user");  
    		LogEntity logEntity=new LogEntity();
    		logEntity.setCdate(new Date());
    		
    		String ip="";
    		if (null==request.getHeader("x-forwarded-for")) { 
    			
    			ip=request.getRemoteAddr(); 
    	    }else{
    	    	ip=request.getHeader("x-forwarded-for"); 
    	    }
    	    
    	    logEntity.setIp(ip);
    		
    		if(null==user){
    			logEntity.setUser_id(0);
    		}else{
    			logEntity.setUser_id(user.getId());
    		}
    		
    		String operation=getControllerMethodDescription(joinPoint);
    		logEntity.setOperation(operation);
    		
    		logService.insertLog(logEntity);
    		logger.info("-----------访问切面------------"+JsonUtil.parseObjectToJSON(logEntity));
		} catch (Exception e) {
			logger.error("记录日志异常:{}",e);			
		}
    }   
    
	/**
	 * @Title: getUserIp 
	 * @Description: 获取用户的访问IP 
	 * @param @param request
	 * @param @return    设定文件 
	 * @return String    返回类型 
	 * @throws
	 */
	protected String getUserIp(HttpServletRequest request){
		
		logger.error("获取IP信息");
		if (null==request.getHeader("x-forwarded-for")) { 
			
	        return request.getRemoteAddr(); 
	    } else{
	    	
		    return request.getHeader("x-forwarded-for");
	    }
  
	}
	
	/** 
     * 获取注解中对方法的描述信息 用于Controller层注解 
     * 
     * @param joinPoint 切点 
     * @return 方法描述 
     * @throws Exception 
     */  
    public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {  
        String targetName = joinPoint.getTarget().getClass().getName();  
        String methodName = joinPoint.getSignature().getName();  
        Object[] arguments = joinPoint.getArgs();  
        Class targetClass = Class.forName(targetName);  
        Method[] methods = targetClass.getMethods();  
        String description = "";  
        for (Method method : methods) {  
            if (method.getName().equals(methodName)) {  
                Class[] clazzs = method.getParameterTypes();  
                if (clazzs.length == arguments.length) {  
                    description = method.getAnnotation(LogAnno.class).operation();
                    break;  
                }  
            }  
        }  
        return description;  
    }  
}

     代码至此,AOP功能基本完成。只要我们在需要拦截的方法前加上我们自定义注解,即可拦截该方法,并将日志计入数据库。非常方便,也不用如何改动之前代码。

     建议大家多阅读官网:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html,只不过这些细节问题,官网不会提及。代码写的很费劲,主要是这个注解必须每次重新发布,累。不知道是本人eclipse原因,还是其他,在此记上一笔。欢迎各位大神指正。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档