AOP

        面向切面编程(AOP是Aspect Oriented Program的首字母缩写),我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。

AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。


过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符。

        过滤器是在Servlet规范里面定义的接口javax.servlet.Filter。Filter接口中定义了三个方法,init(FilterConfig filterConfig)、destroy()和doFilter(HttpRequest request, HttpServletResponse response, FilterChain chain)。

       init()方法会在Filter初始化后进行调用,在init()方法里面我们可以通过FilterConfig访问到初始化参数(getInitParameter()或getInitParameters())、ServletContext(getServletContext)和当前Filter部署的名称(getFilterName())等信息。destroy()方法将在Filter被销毁之前调用。而doFilter()方法则是真正进行过滤处理的方法,在doFilter()方法内部,我们可以过滤请求的request和返回的response,同时我们还可以利用FilterChain把当前的request和response传递给下一个过滤器或Servlet进行处理。


拦截器,是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作,是面向切面编码AOP思想的一种具体实现。在Struts2中定义的接口是Interceptor接口或者AbstractInterceptor接口,在Struts2中自定义一个拦截器需要三步:

1 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。

2 在strutx.xml中注册上一步中定义的拦截器。

3 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。

Interceptor接口声明了三个方法:

public interface Interceptor extends Serializable {

    void destroy();

    void init();

    String intercept(ActionInvocation invocation) throws Exception;

}

init方法在拦截器类被创建之后,在对Action镜像拦截之前调用,相当于一个post-constructor方法,使用这个方法可以给拦截器类做必要的初始话操作。Destroy方法在拦截器被垃圾回收之前调用,用来回收init方法初始化的资源。

 intercept(ActionInvocation invocation)是拦截器的主要拦截方法,如果需要调用后续的Action或者拦截器,只需要在该方法中调用invocation.invoke()方法即可,在该方法调用的前后可以插入Action调用前后拦截器需要做的方法。如果不需要调用后续的方法,则返回一个String类型的对象即可,例如Action.SUCCESS。

另外AbstractInterceptor提供了一个简单的Interceptor的实现,这个实现为:

public abstract class AbstractInterceptor implements Interceptor {

     public void init() { }

     public void destroy() {}

    public abstract String intercept(ActionInvocation invocation) throws Exception;

}

在不需要编写init和destroy方法的时候,只需要从AbstractInterceptor继承而来,实现intercept方法即可。

在Struts中使用拦截器的详细内容可参考http://www.blogjava.net/i369/articles/162407.html

除了在Struts2中使用拦截器外,在spring中定义的拦截器接口是HandlerInterceptor或者HandlerInterceptorAdapter

package org.springframework.web.servlet;
public interface HandlerInterceptor {

  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throwsException;
  void postHandle(
      HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;     
  void afterCompletion(
      HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}

拦截器与过滤器的区别 :

拦截器是基于java的反射机制的,而过滤器是基于函数回调。 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次 执行顺序 :过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。个人认为过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。


--------------------------------------------------分割线,于201707补充--------------------------------------------------

面向切面编程AOP是一种思想,在编程中的三种技术:Filter(过滤器)、Interceptor(拦截器)和Spring AOP注解AspectJ库 ;
Spring的拦截器HandlerInterceptor与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。
不同的是:
使用范围不同:Filter是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。
规范不同:Filter是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的。
使用的资源不同:同其他的代码块一样,拦截器也是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IoC注入到拦截器即可;而Filter则不能。
深度不同:Filter在只在Servlet前后起作用,而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,要优先使用拦截器。
像记录每次请求所消耗的时间一般也是用拦截器或AOP来实现,而Filter中只有一个方法,很难去实现类似功能。

/*
*
* 1.Filter只有如下三个方法,把握三个的运行时机;
* 2.由于Filter不属于Spring管理,因此Filter的类成员变量不能用Spring的注解@AutoWire引用Spring管理的Bean;
* 3.依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。
* 使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据.
* 比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等 
* 关于过滤器的一些用法可以参考:
* 继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数:https://www.zifangsky.cn/677.html
* 在SpringMVC中使用过滤器(Filter)过滤容易引发XSS的危险字符:https://www.zifangsky.cn/683.html
*
*/
public class LogFilter implements Filter {
    private final Logger        logger      = LoggerFactory.getLogger(getClass());


    @Override
    public void init(FilterConfig arg0) throws ServletException {
        logger.info("##init LogFilter");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
                                                                                           ServletException {
        
    }

    @Override
    public void destroy() {

    }
}

/*
* 1.HandlerInterceptor只有如下三个方法,把握三个的运行时机;
* 2.依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用.
  因此可以使用spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些   比如直接访问静态资源的请求则没办法进行拦截处理。
  关于拦截器的一些用法可以参考:
    SpringMVC中使用拦截器(interceptor)拦截CSRF攻击(修):https://www.zifangsky.cn/671.html
    SpringMVC中使用Interceptor+cookie实现在一定天数之内自动登录:https://www.zifangsky.cn/700.html
*
*/
public class LoginHandlerInterceptor implements HandlerInterceptor {
    private final Logger       logger      = LoggerFactory.getLogger(getClass());
    @Autowired
    private UserUtils          userUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
     //preHander();在调用目标方法前调用,同时如果自定义拦截器的preHandler()返回false的情况下,还是会去调用拦截器的afterCompletion() 方法。                        
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    //在调用目标方法后,渲染视图前调用postHandler()方法。拦截器栈preHandler()和postHandler()方法调用形成了一个 U 形。 
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                Exception ex) throws Exception {
    //整个请求处理完毕后的回调方法,即在视图渲染完毕时回调afterCompletion()方法进行的处理,如果发生了异常也会调用该方法,异常信息通过参数Exception传入。
    }
}

/**
 * 1.只需要加上@Aspect注解即可,Spring AOP的方法中有五个注解函数,分别注意各自的执行时机和参数;
 * 2.如下函数所示的切入点@Pointcut往往是一个包,当执行该包下面的所有函数的时候会被拦截,
 * 从这个层面来讲,AOP控制的粒度是基于函数层面,而拦截器和过滤器则是基于请求,因此AOP的粒度比拦截器和过滤器要细;
 *
 */
@Component
@Aspect
public class LogAspect {
    private final Logger        LOGGER          = LoggerFactory.getLogger("webLog");

      
    //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
    @Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
    public void aspect(){   }
    
    /*
     * 配置前置通知,在方法执行之前执行,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint切入点对象,可以没有该参数
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("before " + joinPoint);
        }
    }
    
    //配置后置通知,在方法执行之后执行,(注意的是方法中抛异常了仍然会执行该函数),使用在方法aspect()上注册的切入点
    @After("aspect()")
    public void after(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("after " + joinPoint);
        }
    }
    
    //配置环绕通知,围绕着方法执行,使用在方法aspect()上注册的切入点
    @Around("aspect()")
    public void around(JoinPoint joinPoint){
        long start = System.currentTimeMillis();//方法执行之前执行
        try {
            //执行切入点的方法,在切入点之前打印开始时间,在切入点之后打印执行后的时间;
            ((ProceedingJoinPoint) joinPoint).proceed();//执行方法,通过参数JoinPoint能获取到执行函数和参数
            long end = System.currentTimeMillis();//方法执行之后执行
            if(log.isInfoEnabled()){
                log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
            }
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            if(log.isInfoEnabled()){
                log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
    }
    
    //配置后置返回通知,在方法返回结果之后执行,若抛出异常则不执行该函数,使用在方法aspect()上注册的切入点;
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("afterReturn " + joinPoint);
        }
    }
    
    //配置抛出异常后通知,在方法抛出异常之后,使用在方法aspect()上注册的切入点
    @AfterThrowing(pointcut="aspect()", throwing="ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex){
        if(log.isInfoEnabled()){
            log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
        }
    }
}


Filter是基于servlet的,在servlet前后进行拦截执行,我们知道SpringMVC的核心就是一个servlet,所以对Filter而言的话会在DispatcherServlet前后执行,而Interceptor是基于方法的,会在Handler中的方法之后进行拦截执行。从这一点我们也能看出先会执行Filter,之后才去执行Interceptor,最后执行Handler中的业务方法,可参考《Java过滤器,SpringMVC拦截器之间的一顺序点关系》。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值