美文网首页
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