AOP:面向切面
AOP: 面向切面编程,是一种编程思想,是对面向对象的一种补充.
:面向对象是静态角度考虑程序结构而面向切面是动态的考虑程序运行过程.
:面向切面编程是将交叉业务封装成切面,利用AOP容器功能将切面织入到主业务逻辑
Spring AOP是Spring框架中的一部分,但可以作为一个独立的模块单独存在。Spring AOP实现AOP技术从本质上来讲,是利用了JDK提供的动态代理技术。而从实际的实现方式来看,则是利用了IoC(Inversion of Control,反转模式)机制,同时采用了AOP联盟(AOP Alliance)的通用AOP接口。首先,Spring AOP通过xml配置文件配置了pointcut,并利用Interceptor(拦截机)作为设定的触发条件。Interceptor是由用户自定义的,它相当于是AOP中的advice,但该Interceptor需要实现AOP联盟的通用AOP接口,例如org.aopalliance.intercept.MethodInterceptor。最后定义一个Spring AOP ProxyFactory用于加载执行AOP组件,并利用IoC机制将advice注入到接口以及实现类中。
通知(advice): Spring 提供的一种切面,功能简单,只能讲切面织入到目标类的所有目标方法中而无法织入到指定目标方法中
/*AOP 小Demo*/ /*业务接口*/ public interface IService { public void doSome(); public void doOther(); } /*业务实现类*/ public class ServiceImpl implements IService{ @Override public void doSome() { System.out.println("doSome 业务方法!"); } @Override public void doOther() { System.out.println("doOther 业务方法"); } } /*通知类: 前置通知*/ public class MyMethodAdvice implements MethodBeforeAdvice{ public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("before method ......"); } } /*测试类*/ public class MyTest { @Test public void test01(){ String config = "com/xiehe/dao/applicationSpring.xml"; //1.获取容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); //2.从容器中获取代理对象 IService service= (IService) context.getBean("myServiceProxy"); service.doSome(); service.doOther(); } /*spring 配置文件: applicationSpring.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目标对象 --> <bean id="myService" class="com.xiehe.dao.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao.MyMethodAdvice"></bean> <!-- 配置代理: 此处使用了jdk 动态代理 --> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象 --> <property name="target" ref="myService" /> <!-- 接口 --> <property name="interfaces" value="com.xiehe.dao.IService" /> <!-- 切面 --> <property name="interceptorNames" value="myMethodAdvice" /> </bean> </beans>
顾问(advisor):Spring 提供的另一种切面,可以完成复杂的切面织入功能(顾问 + 切入点)
在上例的spring 配置文件中配置顾问
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目标对象 --> <bean id="myService" class="com.xiehe.dao8.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao8.MyMethodAdvice"></bean> <bean id="myMethodAdvice2" class="com.xiehe.dao8.MyMethodAdvice2"></bean> <!-- 配置顾问(通知 + 切入点):名称匹配 --> <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 通知--> <property name="advice" ref="myMethodAdvice" /> <!-- 切入点--> <property name="mappedNames" value="doSome,doOther"/> </bean> <!-- 配置代理 : CGLIB --> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interfaces" value="com.xiehe.dao8.IService" /> <!-- 配置顾问-- > <property name="interceptorNames" value="myAdvisor"/> <!-- 指定优化的值为true: 强制使用CGLIB --> <property name="optimize" value="true"/> </bean> </beans>
上例中,代理工厂bean(ProxyFactoryBean)一次只能为一个目标对象代理,Spring中提供自动代理来为所有的目标自动生成代理:
自动代理生成器均继承于bean 后处理器,BeanPostProcessor
1.DefaultAdvisorAutoProxyCreator (默认advisor自动代理生成器)
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目标对象 --> <bean id="myService" class="com.xiehe.dao10.ServiceImpl"></bean> <bean id="myService2" class="com.xiehe.dao10.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao10.MyMethodAdvice"></bean> <bean id="myMethodAdvice2" class="com.xiehe.dao10.MyMethodAdvice2"></bean> <!-- 配置顾问:名称匹配 --> <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="myMethodAdvice" /> <property name="mappedNames" value="doOther,doSome"/> </bean> <!-- 配置代理 --> <!-- 默认advisor配置自动代理 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> </beans>
默认advisor自动代理生成器 会给每一个目标对象生成代理,灵活性较差
BeanNameAutoProxyCreator(Bean名称自动代理生成器):可以指定给某一个或多个目标对象生成代理,灵活性好
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目标对象 --> <bean id="myService" class="com.xiehe.dao10.ServiceImpl"></bean> <bean id="myService2" class="com.xiehe.dao10.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao10.MyMethodAdvice"></bean> <bean id="myMethodAdvice2" class="com.xiehe.dao10.MyMethodAdvice2"></bean> <!-- 配置顾问:名称匹配 --> <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="myMethodAdvice" /> <property name="mappedNames" value="doOther,doSome"/> </bean> <!-- 配置代理 --> <!-- 名称自动代理生成器 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="myService2"/> <property name="interceptorNames" value="myMethodAdvice,myMethodAdvice2"/> </bean> </beans>
由于 AspectJ 是 Java 语言语法和语义的扩展,所以它提供了自己的一套处理方面的关键字。除了包含字段和方法之外,AspectJ 的方面声明还包含pointcut和advice成员。示例中的pointcut使用了修饰符(modifier)和通配符(wildcard)模式来表达“所有公共方法”。对帐户的访问,由 pointcut 参数提供。advice使用这个参数,而pointcut则用 this(account) 把它绑定。这样做的效果,就是捕获了正在执行的方法所隶属的Account对象。否则,advice的主体与方法的主体相似。advice可以包含认证代码,或者就像在这个示例中一样,可以调用其他方法。
AspectJ 基于注解的AOP 实现
/*上例中接口和实现类不变*/ /*自定义java类,使用@Aspect 注解表示这是一个切面类*/ @Aspect public class MyAspect { /*在切面类中,为每一个方法指定切入点*/ @Before(value="execution(* com.xiehe.dao11.IService.doSome(..))") public void beforeSome(){ System.out.println("前置增强!"); } @AfterReturning("execution(* com.xiehe.dao11.IService.doSome(..))") public void afterReturn(){ System.out.println("后置增强!"); } } /*spring配置文件*/ <?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> <!-- 配置目标bean --> <bean id="myService" class="com.xiehe.dao11.ServiceImpl"/> <!-- 配置切面 --> <bean id="myAspect" class="com.xiehe.dao11.MyAspect"/> <!-- 配置aspectj自动代理 --> <aop:aspectj-autoproxy/> </beans>
AspectJ 基于xml的AOP 实现
/*在定义类中,不在需要指定切点和切面注解*/ public class MyAspect { public void beforeSome(JoinPoint jp){ System.out.println("前置增强!"); } public void afterReturn(Object result){ System.out.println("后置增强 执行结果:" +result); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("换绕前:"); Object result =pjp.proceed(); System.out.println("环绕后:"); return result; } public void afterThrows(Throwable ex){ System.out.println("异常通知:"+ex.getMessage()); } public void after(){ System.out.println("最终方法!"); } public void myAspect(){} } /*Spring配置文件:applicationSpring.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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> <!-- 配置目标bean --> <bean id="myService" class="com.xiehe.dao13.ServiceImpl"/> <!-- 配置切面 --> <bean id="myAspect" class="com.xiehe.dao13.MyAspect"/> <!-- 配置AOP --> <aop:config> <!-- 定义切入点 --> <aop:pointcut expression="execution(* *..IService.doSome(..))" id="doSomePointcut"/> <aop:pointcut expression="execution(* com.xiehe.dao13.IService.doOther(..))" id="doOtherPointcut"/> <aop:pointcut expression="execution(* com.xiehe.dao13.IService.doThrid(..))" id="doThridPointcut"/> <!-- 定义切面 --> <aop:aspect ref="myAspect"> <aop:before method="beforeSome" pointcut-ref="doSomePointcut"/> <aop:after-returning method="afterReturn" pointcut-ref="doOtherPointcut" returning="result"/> <aop:around method="around(org.aspectj.lang.ProceedingJoinPoint)" pointcut-ref="doThridPointcut"/> </aop:aspect> </aop:config> </beans>