spring之AOP

一、AOP的概念

    AOP(Aspect Oriented Programming),即面向切面编程,是面向对象编程的的有力补充。面向对象编程关注的主要是业务处理,与之关系不大的部分是切面关注点。他们经常发生在核心业务的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心业务和切面关注点分离开来。主要是利用动态代理来实现AOP的。

aop中几个重要的概念

1、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象,用于编写多个advice;

2、连接点(joinpoint)

程序执行过程中明确的点,如方法的调用,在Spring中连接点指的就是被拦截到的方法;

3、切入点(pointcut)

对连接点的定义

4、增强处理(Advice)

aop在特定切入点执行的增强处理,有:before、after、around等

5、织入(weave)

将切面应用到目标对象并创建代理对象的过程

二、基于注解方式的实现

1、jar包的引入

  • spring-aop-3.2.5.RELEASE.jar
  • aopalliance.jar
  • aspectjweaver.jar
  • aspectjrt.jar

2、bean,xml中引入aop命名空间

xmlns:aop="http://www.springframework.org/schema/aop

3、aop注解扫描配置

<aop:aspectj-autoproxy/>

4、注解介绍

1、@Aspect:定义当前类为切面类

2、@Before 前置通知:在目标方法之前执行

3、@After 后置通知:在目标方法之后执行(出不出现异常始终执行)

4、@AfterReturning 在目标方法完成之后运行(出现异常不执行)

5、@AfterThrowing 出现异常时执行

6、@Around 既可以在目标完成之前织入,也可以在目标完成之后织入

7、@PointCut  定义一个切入点

注:1、切入点表达式使用规则

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

//有“?”号的部分表示可省略的
//modifers-pattern表示修饰符如public、protected等,
//ret-type-pattern表示方法返回类型,
//declaring-type-pattern代表特定的类,
//name-pattern代表方法名称,
//param-pattern表示参数,
//throws-pattern表示抛出的异常。
//在切入点表达式中,可以使用*来代表任意字符,用..来表示任意个参数。

如:

@Before("execution(* com.pjf.spring.dao.*.*(..))")

2、pointcut运用

@Pointcut("execution(* com.pjf.spring.dao.*.*(..))")
//这里需要定义一个空函数,也就是切入点名myPointcut
    public void myPointcut(){};
    
    @Before("myPointcut()")
    public void before() {

        System.out.println("program beginning");
    }

5、代码实现

包括hotelDao接口和实现,Hotel实体类,service类、aop类和测试类

1、HotelDao接口和实现

import com.pjf.spring.po.Hotel;

public interface HotelDAO {
    public void operate(Hotel hotel);
}
import org.springframework.stereotype.Component;
import com.pjf.spring.po.Hotel;
@Component(
"hotelAdd") public class HotelDAOAddImpl implements HotelDAO { public void operate(Hotel hotel) { System.out.println(hotel + "saved!"); }; }

2、Hotel实体类

public class Hotel {
    private int id;
    private String hotelName;public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getHotelName() {
        return hotelName;
    }
    public void setHotelName(String hotelName) {
        this.hotelName = hotelName;
    }
    @Override
    public String toString(){
        return hotelName;
    }   
}

3、service类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.pjf.spring.dao.HotelDAO;
import com.pjf.spring.po.Hotel;

@Component
public class HotelService {       
    private HotelDAO hotelDAO;   
    public HotelDAO getHotelDAO() {
        return hotelDAO;
    }    
    @Autowired
    public void setHotelDAO(@Qualifier("hotelAdd") HotelDAO hotelDAO) {
        this.hotelDAO = hotelDAO;
    }
    public void operateHotel(Hotel hotel) {
        hotelDAO.operate(hotel);
    }

}

4、aop类(打印日志)

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class log {
    // 切入点定义
    @Pointcut("execution(* com.pjf.spring.dao.*.*(..))")
    public void myPointcut() {
    };

    // 前置通知 : 在执行目标方法之前执行
    @Before("myPointcut()")
    public void before() {
        System.out.println("开始");
    }
// @After 后置通知:在目标方法之后执行(出不出现异常始终执行)
    @After("myPointcut()")
    public void after() {
        System.out.println("结束");
    }

    //@AfterReturning 在目标方法完成之后运行(出现异常不执行)
    @AfterReturning("myPointcut()")
    public void afterReturning() {
        System.out.println("afterReturning()");
    }

    //@AfterThrowing 出现异常时执行
    @AfterThrowing("myPointcut()")
    public void afterThrowing() {
        System.out.println("afterThrowing()");
    }

    // @Around 既可以在目标完成之前织入,也可以在目标完成之后织入
    @Around("myPointcut()")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前....");
        pjp.proceed(); // 执行目标方法
        System.out.println("环绕后....");
    }
}

5、测试类

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.pjf.spring.po.Hotel;
public class HotelServiceSpringTest { @Test public void test() { Hotel hotel =new Hotel(); hotel.setId(1001); hotel.setHotelName("南京金陵饭店"); ApplicationContext ctx= new ClassPathXmlApplicationContext("springBeans.xml"); HotelService hotelService = ctx.getBean("hotelService",HotelService.class);
hotelService.operateHotel(hotel); } }

6、结果

环绕前....
开始
南京金陵饭店saved!
环绕后....
结束
afterReturning()

可以看到最先执行的是around,然后是before,再是around、after、afterReturnning的顺序,但是结果并没有afterThrowing(),这是因为我没在程序HotelDAOAddImpl类的operate方法中抛异常 。

注:如果程序中我没有编写hotelDao接口,直接编写实现类hotelDaoAddimpl,这时候会怎么样(这时候就需要jar包帮我们实现接口,需要导入cglibjar包)有兴趣可以实验下。

三、基于xml方式的实现

1、jar包的引入

     同注解方式一样

2、bean,xml中引入aop命名空间

     同注解方式一样

3、代码实现

包括hotelDao接口和实现,Hotel实体类,service类、aop类和测试类

1、HotelDao接口和实现、Hotel实体类,service类、和测试类

    同注解方式一样

2、aop类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class log {public void before() {
        System.out.println("开始");
    }public void after() {
        System.out.println("结束");
    }public void afterReturning() {
        System.out.println("afterReturning()");
    }public void afterThrowing() {
        System.out.println("afterThrowing()");
    }public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前....");
        pjp.proceed(); // 执行目标方法
        System.out.println("环绕后....");
    }
}

3、springBeans文件配置

不需要死记,需要用的时候copy下,然后修改就可以了

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <!-- spring注解扫描 -->
    <context:component-scan base-package="com.tuniu.spring.*" />
    <!-- 切面类 -->
    <bean id="log" class="com.tuniu.spring.aop.log"></bean>

    <aop:config>
        <!-- 切入点表达式 -->
        <aop:pointcut expression="execution(* com.tuniu.spring.dao.*.*(..))"
            id="daoImplLog" />
        <!-- 增强处理 -->
        <aop:aspect id="ImplLog" ref="log">
            <aop:before method="before" pointcut-ref="daoImplLog" />
            <aop:after method="after" pointcut-ref="daoImplLog" />
            <aop:after-returning method="afterReturning"
                pointcut-ref="daoImplLog" />
            <aop:after-throwing method="afterThrowing"
                pointcut-ref="daoImplLog" />
            <aop:around method="around" pointcut-ref="daoImplLog" />
        </aop:aspect>
    </aop:config>

</beans>

4、结果

开始
环绕前....
南京金陵饭店saved!
环绕后....
afterReturning()
结束

一般情况下都是用注解方式,但是在某些特殊情况下,如在没有源码的情况下,使用xml配置,当然大部分情况下我们都是有源码的,所以基本上推荐使用注解方式。

 

 四:aop和annotation的结合

 代码和上面注解的一样

1、新增annotation类

package com.pjf.spring.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OrdLink {
    
    String value();
}

2、新增aop类

package com.pjf.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogManager {

    /*切点为@annotation OrdLink注解*/
    @Before("@annotation(ordLink)")
    public void ordLinkLog(OrdLink ordLink) {
        System.out.println(ordLink.value());
    }
}

3、修改HotelDAOAddImpl 类

package com.pjf.spring.dao;

import org.springframework.stereotype.Component;
import com.pjf.spring.aop.OrdLink;
import com.pjf.spring.po.Hotel;

@Component("hotelAdd")
public class HotelDAOAddImpl implements HotelDAO {
    
    @OrdLink("添加酒店")    
    public void operate(Hotel hotel) {
        System.out.println(hotel + "saved!");
    };
}

测试结果:

环绕前....
开始
添加酒店
南京金陵饭店坐落在鼓楼区汉中路2号saved!
环绕后....
结束
afterReturning()

 

 

 

 
posted @ 2017-10-10 14:34  梦天幻  阅读(377)  评论(0编辑  收藏  举报