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()