AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
所谓的核心业务,好比登录,增长数据,删除数据都叫核心业务
所谓的周边功能,好比性能统计,日志,事务管理等等 java
周边功能在Spring的面向切面编程AOP思想里,即被定义为切面 spring
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发
而后把切面和核心业务功能 "编织" 在一块儿,这就叫AOPexpress
注意:Spring会根据是否实现了接口自动切换JDK动态代理和CGLib动态代理
1.若是是使用Jdk动态代理实现Spring AOP,Spring容器的getBean方法得到的对象是不能转型成该Bean定义的Class类型。
假设有Service接口和实现该接口的ServiceImpl类,使用Jdk动态代理实现Spring AOP,getBean得到的对象是Jdk动态代理生成的代理类的对象,这个代理类只是实现了Service接口,而没有继承ServiceImpl。 使用CGLib动态代理不会有这个问题,由于CGLib动态代理生成的代理类是继承咱们的目标类的,而不是实现目标接口。编程
2.若是使用CGLib动态代理实现Spring AOP,经过Spring容器的getBean方法得到的对象不能直接引用目标类的公有属性,读取或者修改公有属性。
假设Service类没有实现任何接口,使用Spring容器的getBean方法时转型成Service类的对象service,但咱们不能调用service.type来得到或者修改type属性。
这是由于使用CGLib动态代理实现的Spring AOP,调用service.type是引用CGLib代理类对象的属性,而不是目标对象的type属性。app
总之:若是实现了接口,getBean要强转成接口,若是没有实现接口,则能够转成实现类。框架
首先了解一下几种注解的做用:
@Aspect 将某个类声明成一个切面函数
@Pointcut("切入点表达式") 声明一个切点,咱们可利用方法签名来编写切入点表达式。最典型的切入点表达式是根据方法的签名来匹配各类方法:性能
@Before("切入点表达式或已经声明的切入点")前置通知方法。 前置通知方法在目标方法开始以前执行。spa
@AfterReturning("")返回前执行代理
@AfterThrowing("") 抛出异常前执行
@After("") 后置通知。方法执行完后必定会执行。相似finaly
@Around("") 环绕通知。被拦截(被切入)方法的调用由环绕通知决定。切面方法须要传入一个ProceedingJointPoint对象,该对象用于启用被拦截方法。实例以下:
public Object log(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("start log:" + joinPoint.getSignature().getName());//前置 Object object = joinPoint.proceed();//启用被拦截的核心业务方法 System.out.println("end log:" + joinPoint.getSignature().getName());//后置 return object; }
单个Aspect状况:
多个Aspect状况:
为了便于理解,还能够参考以下的图:
将切面想象成同心圆,@Order(n)中n越小圆越大,越先执行。
而先执行的后退出。
示例程序:
aspect:
package com.myspring.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component//这里务必记得要把aspect也加入到容器里 public class Log{ //实际中尽可能不要复用切入点表达式,而是声明一个切入点方法 @After("execution(* com.myspring.imple..*(..))") public void after() { System.out.println("after"); } @Around("execution(* com.myspring.imple..*(..))") public Object log(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("start log:" + joinPoint.getSignature().getName()); Object object = joinPoint.proceed(); System.out.println("end log:" + joinPoint.getSignature().getName()); return object; } @Before( "execution(* com.myspring.imple..*(..))") public void before() { System.out.println("before"); } }
service:
package com.myspring.imple; import org.springframework.stereotype.Component; @Component("s") public class ServiceImpl { public void save() { System.out.println("save run"); } }
applicationContext.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" <!--添加aop命名空间--> xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.myspring.imple"/> <context:component-scan base-package="com.myspring.aspect"/> <!-- spring调用了aspectj——一个面向切面的框架的自动代理 --> <aop:aspectj-autoproxy/> </beans>
test:
package com.myspring.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.myspring.imple.ServiceImpl; public class test { public static void main(String[] args) { // TODO 自动生成的方法存根 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //若是核心业务实现了接口则为JDK动态代理,须要强转为接口,不然为CGLib动态代理,强转为实现类 ServiceImpl imp1 = (ServiceImpl)context.getBean("s"); imp1.save(); } }
applicationContext.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" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!--首先将核心功能和周边功能的Bean加载到容器里--> <bean id="logAspect" class="com.how2java.aspect.LoggerAspect"></bean> <bean id="service" class="com.how2java.service.ProductService"></bean> <aop:config> <!-- 声明一个切入点 --> <aop:pointcut expression="execution(* com.how2java.service.ProductService.*(..))" id="logPointcut"/> <!-- 声明切面 --> <aop:aspect ref="logAspect" id="log" > <!-- 设定aspect中advice的方法与绑定的切入点 --> <!--使用已经定义好的pointcut--> <aop:around method="around" pointcut-ref="logPointcut"/> <aop:before method="before" pointcut-ref="logPointcut"/> <!--使用切入点表达式定义Pointcut--> <aop:after method="after" pointcut="execution(* com.how2java.service.ProductService.*(..))"/> </aop:aspect> </aop:config> </beans>
名字太多记不住?咱们来理一遍。
首先加载两个bean:
做为切面的bean——logAspect以及做为核心功能的bean——service。对应注解模式的@Component
而后开始配置aop:
拦截住核心功能的一个方法(声明一个pointcut),名字叫logPointcut,对应注解@Pointcut
接着将做为切面的bean——logAspect声明为一个切面,名字是log,对应注解@Aspect
如今切入点和切面都有了,最后将他们编织在一块儿:
在切面中设置advice类型和周边功能方法,而且与相应的切入点——logPointcut绑定,对应注解@Before(切入点表达式或已定义的切入点)等
ps:Pointcut定义在Aspect外则全部Aspect均可以捕获,定义在某个Aspect里则只有该Aspect内的Advice能够捕获
当咱们使用别人写好的周边功能与咱们写的核心业务编织时,应当使用配置文件方式;当咱们独立完成而且封装整个系统时才考虑使用注解方式。所以推荐使用配置文件方式。