美文网首页
Spring/AOP

Spring/AOP

作者: 米刀灵 | 来源:发表于2016-10-26 17:22 被阅读30次

    切面编程,其实就是动态代理,使用反射动态的构造一个新的Class,新的Class包含旧的Class,可以在旧的Class执行前后和中间插入代码。
    基于XML Schema的AOP:
    AOP代理就是AOP框架通过代理模式创建的对象,Spring AOP默认首先使用JDK动态代理来代理目标对象,如果目标对象没有实现任何接口将使用CGLIB代理,如果需要强制使用CGLIB代理,对于Schema风格配置切面使用如下方式来指定使用CGLIB代理:

        <aop:config proxy-target-class="true">  
        </aop:config>  
    

    例子:

        //目标接口
        package cn.javass.spring.chapter6.service;  
        public interface IHelloWorldService {  
            public void sayHello();  
        }  
    
        //目标接口实现
        package cn.javass.spring.chapter6.service.impl;  
        import cn.javass.spring.chapter6.service.IHelloWorldService;  
        public class HelloWorldService implements IHelloWorldService {  
            @Override  
            public void sayHello() {  
                System.out.println("============Hello World!");  
            }  
        }  
    
        //切面支持类
        package cn.javass.spring.chapter6.aop;  
        public class HelloWorldAspect {  
            //前置通知  
            public void beforeAdvice() {  
                System.out.println("===========before advice");  
            }  
            //后置最终通知  
            public void afterFinallyAdvice() {  
                System.out.println("===========after finally advice");  
            }  
        } 
    
        //在XML中进行配置
        <?xml version="1.0" encoding="UTF-8"?>  
        <beans  xmlns="http://www.springframework.org/schema/beans"  
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
                xmlns:aop="http://www.springframework.org/schema/aop"  
                xsi:schemaLocation="  
                   http://www.springframework.org/schema/beans  
                   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                   http://www.springframework.org/schema/aop  
                   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  
        </beans>  
    
        <bean id="helloWorldService"  class="cn.javass.spring.chapter6.service.impl.HelloWorldService"/>  
    
        <bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/>  
        <aop:config>  
        <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/>  
            <aop:aspect ref="aspect">  
                <aop:before pointcut-ref="pointcut"                    method="beforeAdvice"/>  
                <aop:after  pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/>  
            </aop:aspect>  
        </aop:config>  
    
        //测试
        package cn.javass.spring.chapter6;  
        import org.junit.Test;  
        import org.springframework.context.ApplicationContext;  
        import org.springframework.context.support.ClassPathXmlApplicationContext;  
        import cn.javass.spring.chapter6.service.IHelloWorldService;  
        import cn.javass.spring.chapter6.service.IPayService;  
        public class AopTest {  
            @Test  
            public void testHelloworld() {  
                ApplicationContext ctx =  new ClassPathXmlApplicationContext("chapter6/helloworld.xml");  
                IHelloWorldService helloworldService =  
                ctx.getBean("helloWorldService", IHelloWorldService.class);  
                helloworldService.sayHello();  
            }  
        }  
    

    其他后置通知,后置返回通知,后置异常通知,环绕通知等参考:http://jinnianshilongnian.iteye.com/blog/1418598

    基于@AspectJ的AOP:
    在Spring和SpringMVC的配置文件中启用切面,注意Spring和SpringMVC分别起用
    Spring配置文件 applicationContext.xml(JDK本身提供的DynamicProxy):

    <beans .....>
           <aop:aspectj-autoproxy/>
    </beans>
    

    SpringMVC配置文件 spring-mvc.xml(由CGLib提供):

    <beans .....>
          <aop:aspectj-autoproxy proxy-target-class="true"/>
    </beans>
    

    正如开篇提到,所谓切面编程就是动态代理,那么动态代理有两种方式:

    • JDK本身提供的DynamicProxy。JDK的动态代理是针对接口的,某个Class首先要实现某个接口,可以对接口方法进行代理。
    • CGLib提供的。CGLib是针对具体的Class的,不需要你的Object去implement某个接口。在SpringMVC配置中使用cglib进行代理,这里注意如果你的pom需要有cglib的依赖。

    选择哪种代理:
    一般在Service切入AOP,使用JDK本身的动态代理,即<aop:aspectj-autoproxy/>。因为按照习惯我们编写的Service都会继承接口。
    在Controller切入AOP,使用CGLib,即<aop:aspectj-autoproxy proxy-target-class="true"/>。Controller一般不继承接口。

    下面以在Service层前后打印日志为例:
    servie层代码:

    package com.tengj.demo.service
    public interface UserService {
        public void sayHello(String name);
    }
    

    servie实现类代码:

    package com.tengj.demo.service.impl;
    @Service("userService")
    public class UserServiceImpl implements UserService{
        @Override
        public void sayHello(String name) {
            System.out.println("hello,"+name);
        }
    }
    

    切点:

    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component //注入依赖
    @Aspect //该注解标示该类为切面类
    public class LogAspect {
        @Pointcut("execution(* com.tengj.demo.service.impl.UserServiceImpl.*(..))")
        public void logAop(){}
    
        @Before("logAop() && args(name)")
        public void logBefore(String name){
            System.out.println(name+"前置通知Before");
        }
    
        /**
         * 定义返回值的型参名称为r,并传入切面
         */
        @AfterReturning(value = "logAop()", returning = "r")
        public void logAfterReturning(JoinPoint joinPoint, Object r) {
            log.info("方法调用结束 返回值:{}",r.toString());
        }
    
        @After("logAop() && args(name)")
        public void logAfter(String name){
            System.out.println(name+"后置通知After");
        }
    
        /**
         * 定义抛出类的型参名称为ex,并传入切面
         */
        @AfterThrowing(value = "logAop()", throwing = "ex")
        public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
            String message = (ex.getMessage() == null)?ex.getClass().getName():ex.getMessage();
            log.warn("方法抛出异常: {} ",message);
        }
    }
    

    上面方法index()就是我们定义的切点,表示在哪里切入AOP:


    后边的Before和After就很好理解,就是以下方法在动态代理的方法前还是之后执行,除了@Before和@After还有@Around和@AfterThrowing、@AfterReturning等。
    @Pointcut注解是为了定义切面内重用的切点,也就是说把公共的东西抽出来,定义了任意的方法名称logAop,这样下面用到的各种类型通知就只要写成例如:@Before("logAop() && args(name)")。也可以直接在其余注解中使用表达式例如:@Before(value = “execution(public*com.company.service.impl..*.*(..))”)
    @Before("logAop() && args(name)")这里多出来个&& args(name)
    ,这个是用来传递参数的,定义要跟Service里的方法sayHello参数名称一样就可以。

    applicationContext配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
           http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-4.1.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd"
           default-lazy-init="true">
        <context:component-scan base-package="com.tengj.demo"/>
        <mvc:resources location="/WEB-INF/pages/" mapping="/pages/**"/>
        <!-- 默认的注解映射的支持 -->
        <mvc:annotation-driven/>
        <!--启用AspectJ自动代理-->
        ***<aop:aspectj-autoproxy/>***
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/pages/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    </beans>
    

    参考:
    http://www.jianshu.com/p/d35e46f27187
    http://alanli7991.github.io/2016/10/21/%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B%E4%B8%89AspectJ%E4%B8%8EShiro%E4%B8%8D%E5%85%BC%E5%AE%B9%E5%92%8CSpring%E4%BA%8C%E6%AC%A1%E4%BB%A3%E7%90%86%E9%94%99%E8%AF%AF%E5%88%86%E6%9E%90/

    相关文章

      网友评论

          本文标题:Spring/AOP

          本文链接:https://www.haomeiwen.com/subject/pubduttx.html