如果项目不能使用Java5.0, 那么就无法使用基于@AspectJ注解的切面。 但是Spring提供了基于Schema配置的方法,它完全可以替代基于@AspectJ注解声明切面的方式。
基于@AspectJ注解的切面,本质上是将切点、增强类型的信息使用注解描述,我们将这两个信息转移到Schema的xml配置文件中,只是形式变了,本质还是相同的。
使用基于Schema的切面定义后,切点、增强类型的注解信息从切面类中剥离出来,原来的切面类也就蜕变为真正意义上的POJO了
代码已托管到Github—> https://github.com/yangshangwei/SpringMaster
首先来配置一个基于Schema的切面,它使用aop命名空间。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:config proxy-target-class="true">
<aop:aspect ref="adviceMethods">
<aop:before pointcut="target(com.xgj.aop.spring.advisor.schema.demo.BussinessOne) and execution(* program(..))"
method="crossCutting"/>
aop:aspect>
aop:config>
<bean id="bussinessOne" class="com.xgj.aop.spring.advisor.schema.demo.BussinessOne"/>
<bean id="bussinessTwo" class="com.xgj.aop.spring.advisor.schema.demo.BussinessTwo"/>
<bean id="adviceMethods" class="com.xgj.aop.spring.advisor.schema.demo.AdviceMethods"/>
beans>
解析:
使用一个
元素标签定义切面,其内部可以定义多个增强。
在
元素中可以定义多个切面。
通过
声明了一个前置增强,并通过pointcut属性定义切点表达式,切点表达式的语法和@AspectJ中所用的语法完全相同,由于&&在XML中使用不便,所以一般用and操作符代替。
通过method属性指定增强的方法,该方法应该是adviceMethods Bean中的方法。
AdviceMethods是增强方法所在的类,它是一个普通的java类,没有任何特殊的地方,如下
package com.xgj.aop.spring.advisor.schema.demo;
/**
*
*
* @ClassName: AdviceMethods
*
* @Description: 增强方法所在的Bean
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午4:38:25
*/
public class AdviceMethods {
/**
*
*
* @Title: crossCutting
*
* @Description: 改方法通过配置被用作增强方法
*
*
* @return: void
*/
public void crossCutting() {
System.out.println("crossCutting executed");
}
}
业务类
package com.xgj.aop.spring.advisor.schema.demo;
/**
*
*
* @ClassName: BussinessOne
*
* @Description: 普通POJO
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午4:37:56
*/
public class BussinessOne {
public void program() {
System.out.println("BussinessOne program executed");
}
public void fixBug() {
System.out.println("BussinessOne fixBug executed");
}
}
package com.xgj.aop.spring.advisor.schema.demo;
/**
*
*
* @ClassName: BussinessTwo
*
* @Description: 普通POJO
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午4:38:10
*/
public class BussinessTwo {
public void program() {
System.out.println("BussinessOne program executed");
}
public void fixBug() {
System.out.println("BussinessOne fixBug executed");
}
}
测试类
package com.xgj.aop.spring.advisor.schema.demo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConfigBySchemaTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/aop/spring/advisor/schema/demo/conf-bySchema.xml");
BussinessOne bussinessOne = ctx.getBean("bussinessOne",
BussinessOne.class);
BussinessTwo bussinessTwo = ctx.getBean("bussinessTwo",
BussinessTwo.class);
// 根据配置文件中的切点表达式
// target(com.xgj.aop.spring.advisor.schema.demo.BussinessOne)and
// execution(* program(..)) ,只有
// bussinessOne.program()符合条件
bussinessOne.program();
bussinessOne.fixBug();
bussinessTwo.program();
bussinessTwo.fixBug();
}
}
运行结果
2017-09-15 22:01:42,550 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61ee30d2: startup date [Fri Sep 15 22:01:42 BOT 2017]; root of context hierarchy
2017-09-15 22:01:42,704 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/schema/demo/conf-bySchema.xml]
crossCutting executed
BussinessOne program executed
BussinessOne fixBug executed
BussinessOne program executed
BussinessOne fixBug executed
可见,切面被正确的实施到目标Bean中。
上面的例子中通过pointcut属性声明的切点时匿名切点,它不能被其他增强或其他切面引用。Spring提供了命名切点的配置方式。
<aop:config proxy-target-class="true">
<aop:pointcut id="bussinessOneProgram" expression="target(com.xgj.aop.spring.advisor.schema.namePointcut.BussinessOne) and execution(* program(..))" />
<aop:aspect ref="adviceMethods">
<aop:before pointcut-ref="bussinessOneProgram" method="crossCutting"/>
aop:aspect>
aop:config>
定义了一个切点,并通过id属性进行命名.
一样,除了引介增强外,其他任意增强类型都拥有pointcut、pointcut-ref和method这3个属性。当然了,
如果位于
元素中,则该命名切点只能被当前
内定义的元素访问到。 为了能让整个
元素中定义的所有增强访问,必须在
元素下定义切点,如上所示。
如果有在
元素下直接定义
,则必须保证
在
之前定义。 在
元素下还可以定义
,三者的顺序要求为:首先是
,然后是
,最后是
基于Schema定义的切面和基于@AspectJ定义的切面内容基本一致,只是在表现形式上存在差异罢了。
下面通过实例来演示
业务类
package com.xgj.aop.spring.advisor.schema.advices;
public class BussinessSvc {
public void dealBussinessBefore() {
System.out.println("dealBussinessBefore executed");
}
public int dealWorkNumberForAfterReturring() {
System.out.println("dealWorkNumberForAfterReturring executed");
return 10;
}
public void dealWorkForAround(String workName) {
System.out.println("dealWorkForAround executed");
}
public void dealBussinessForAfterThorowing(String bussinessName) {
System.out.println("dealBussinessForAfterThorowing executed");
// just a demo code ,in fact it's not cautious
if (bussinessName != null && "bug".equals(bussinessName))
throw new IllegalArgumentException("iae Exception");
else
throw new RuntimeException("re Exception");
}
public void dealWorkForAfter() {
System.out.println("dealWorkForAfter executed");
}
}
切面增强逻辑
package com.xgj.aop.spring.advisor.schema.advices;
import org.aspectj.lang.ProceedingJoinPoint;
public class AdviceMethods {
/**
*
*
* @Title: before
*
* @Description: 前置增强对应的方法
*
* @param name
*
* @return: void
*/
public void beforeMethod() {
System.out.println("--Before CrossCuttingCode--");
}
/**
*
*
* @Title: afterReturning
*
* @Description: 后置增强对应方法 ,配置文件中的returing属性必须和增强方法的入参名称一致
*
* @param retVal
*
* @return: void
*/
public void afterReturningMethod(int retVal) {
System.out.println("----afterReturning() begin----");
System.out.println("returnValue:" + retVal);
System.out.println("----afterReturning() end----");
}
/**
*
*
* @Title: aroundMethod
*
* @Description: 环绕增强对应方法
*
* @param pjp
*
* @return: void
* @throws Throwable
*/
public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("----aroundMethod() begin----");
System.out.println("args[0]:" + pjp.getArgs()[0]);
// 执行目标类的目标方法
pjp.proceed();
System.out.println("----aroundMethod() end----");
}
/**
*
*
* @Title: afterThrowingMethod
*
* @Description: 抛出异常增强
*
* @param iae
*
* @return: void
*/
public void afterThrowingMethod(IllegalArgumentException iae) {
System.out.println("----afterThrowingMethod()----");
System.out.println("exception msg:" + iae.getMessage());
System.out.println("----afterThrowingMethod()----");
}
/**
*
*
* @Title: afterMethod
*
* @Description: final增强
*
*
* @return: void
*/
public void afterMethod() {
System.out.println("----afterMethod()----");
}
}
为了演示引介增强引入的几个接口和实现类
package com.xgj.aop.spring.advisor.schema.advices;
/**
*
*
* @ClassName: InterfaceOne
*
* @Description: 演示引介增强用
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:14:18
*/
public interface InterfaceOne {
void dealAnotherWork();
}
package com.xgj.aop.spring.advisor.schema.advices;
/**
*
*
* @ClassName: InterfaceOneImpl
*
* @Description: 演示引介增强用
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:14:33
*/
public class InterfaceOneImpl implements InterfaceOne {
@Override
public void dealAnotherWork() {
System.out.println("InterfaceOneImpl dealAnotherWork executed ");
}
}
package com.xgj.aop.spring.advisor.schema.advices;
/**
*
*
* @ClassName: IntfBussiness
*
* @Description: 演示引介增强用
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:14:41
*/
public interface IntfBussiness {
void fixBug();
}
package com.xgj.aop.spring.advisor.schema.advices;
/**
*
*
* @ClassName: IntfBussinessImpl
*
* @Description: 演示引介增强用
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:14:49
*/
public class IntfBussinessImpl implements IntfBussiness {
@Override
public void fixBug() {
System.out.println("IntfBussinessImpl fixBug executed");
}
}
配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" 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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* dealBussinessBefore(..))" id="before" />
<aop:pointcut expression="execution(* dealWorkNumberForAfterReturring(..))" id="afterReturnning" />
<aop:pointcut expression="execution(* dealWorkForAround(..)) and within(com.xgj.aop.spring.advisor.schema.advices.BussinessSvc)" id="around" />
<aop:pointcut expression="within(com.xgj.aop.spring.advisor.schema.advices.BussinessSvc) and execution(* dealBussinessForAfterThorowing(..))" id="afterThrowing"/>
<aop:pointcut expression="execution(* dealWorkForAfter(..))" id="after"/>
<aop:aspect ref="adviceMethods">
<aop:before pointcut-ref="before" method="beforeMethod" />
<aop:after-returning pointcut-ref="afterReturnning"
method="afterReturningMethod" returning="retVal" />
<aop:around pointcut-ref="around" method="aroundMethod"/>
<aop:after-throwing pointcut-ref="afterThrowing" method="afterThrowingMethod" throwing="iae"/>
<aop:after pointcut-ref="after" method="afterMethod"/>
<aop:declare-parents
types-matching="com.xgj.aop.spring.advisor.schema.advices.IntfBussiness+"
implement-interface="com.xgj.aop.spring.advisor.schema.advices.InterfaceOne"
default-impl="com.xgj.aop.spring.advisor.schema.advices.InterfaceOneImpl"/>
aop:aspect>
aop:config>
<bean id="bussinessSvc" class="com.xgj.aop.spring.advisor.schema.advices.BussinessSvc" />
<bean id="intfBussinessImpl" class="com.xgj.aop.spring.advisor.schema.advices.IntfBussinessImpl">bean>
<bean id="adviceMethods" class="com.xgj.aop.spring.advisor.schema.advices.AdviceMethods" />
beans>
测试类
package com.xgj.aop.spring.advisor.schema.advices;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DifferentAdviceTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/aop/spring/advisor/schema/advices/conf-advices.xml");
BussinessSvc bussinessSvc = ctx.getBean("bussinessSvc",
BussinessSvc.class);
// 方法执行前织入前置增强
bussinessSvc.dealBussinessBefore();
// 方法执行后织入后置增强
bussinessSvc.dealWorkNumberForAfterReturring();
// 方法执行时织入环绕增强
bussinessSvc.dealWorkForAround("fixBug");
// 方法执行时出现特定异常时织入异常抛出增强
bussinessSvc.dealWorkForAfter();
// 方法执行后,不管异常与否都执行的增强
bussinessSvc.dealWorkForAfter();
// 引介 --强制类型转换成功,说明也实现了另外的接口
IntfBussinessImpl intfBussinessImpl = ctx.getBean("intfBussinessImpl",
IntfBussinessImpl.class);
((InterfaceOne) intfBussinessImpl).dealAnotherWork();
}
}
运行结果
2017-09-15 22:37:49,383 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c24cb3: startup date [Fri Sep 15 22:37:49 BOT 2017]; root of context hierarchy
2017-09-15 22:37:49,485 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/schema/advices/conf-advices.xml]
--Before CrossCuttingCode--
dealBussinessBefore executed
dealWorkNumberForAfterReturring executed
----afterReturning() begin----
returnValue:10
----afterReturning() end----
----aroundMethod() begin----
args[0]:fixBug
dealWorkForAround executed
----aroundMethod() end----
dealWorkForAfter executed
----afterMethod()----
dealWorkForAfter executed
----afterMethod()----
InterfaceOneImpl dealAnotherWork executed
重点说一下引介增强 : 通过
配置引介增强, 引介增强和其他类型的增强有所不同,它没有method、pointcut和point-ref属性
引介增强通过types-matching以及AspectJ切点表达式语法指定哪些bean需要引介接口的实现, implement-interface要引介实现的接口 , default-impl 引介默认的实现类
值的注意的是,虽然
么有method属性指定增强方法所在的Bean,但是
中的ref属性依然要指定一个增强Bean。
基于Schema配置的增强方法绑定连接点信息和基于@AspectJ绑定连接点信息所使用的方式没有区别。
1)所有增强类型对应的方法第一个入参都可以声明为JoinPoint(环绕增强可声明为ProceedingJoinPoint)访问连接点信息;
2)
(后置增强)可以通过returning属性绑定连接点方法的返回值,
(抛出异常增强)可以通过throwing属性绑定连接点方法所抛出的异常;
3)所有增强类型都可以通过可绑定参数的切点函数绑定连接点方法的入参。
前两种已经阐述过了,下面我们来看第三种绑定参数的方法
业务类
package com.xgj.aop.spring.advisor.schema.bindParameter;
import org.springframework.stereotype.Component;
/**
*
*
* @ClassName: BussinessBindParam
*
* @Description: 使用注解定义的Bean
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:25:23
*/
@Component
public class BussinessBindParam {
public void program(String name, int number) {
System.out.println("BussinessBindParam program execute");
System.out.println("program:" + name + ", number:" + number);
}
public void fixbug(String name, int number, double salary) {
System.out.println("BussinessBindParam fixBug executed");
System.out.println("program:" + name + ", number:" + number + ",salary"
+ salary);
}
}
增强切面
package com.xgj.aop.spring.advisor.schema.bindParameter;
import org.springframework.stereotype.Component;
/**
*
*
* @ClassName: AdviceMethodsBindParam
*
* @Description: 使用注解定义的Bean,同时也是切面类
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:25:13
*/
@Component
public class AdviceMethodsBindParam {
/**
*
*
* @Title: crossCutting
*
* @Description: 改方法通过配置被用作增强方法
*
*
* @return: void
*/
public void crossCutting(String name, int num) {
System.out.println("----bindJoinPointParams()----");
System.out.println("name:" + name);
System.out.println("number:" + num);
System.out.println("----bindJoinPointParams()----");
}
}
配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:config proxy-target-class="true">
<aop:pointcut id="bussinessBindParamProgram" expression="target(com.xgj.aop.spring.advisor.schema.bindParameter.BussinessBindParam) and args(name,num,..)" />
<aop:aspect ref="adviceMethodsBindParam">
<aop:before pointcut-ref="bussinessBindParamProgram" method="crossCutting"/>
aop:aspect>
aop:config>
<context:component-scan base-package="com.xgj.aop.spring.advisor.schema.bindParameter"/>
beans>
切点表达式通过 args(name,num,..) 绑定了连接点的两个参数,对应增强类中的入参crossCutting(String name, int num) . 这两个地方声明的参数名必须相同
测试类
package com.xgj.aop.spring.advisor.schema.bindParameter;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdviceMethodsBindParamTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/aop/spring/advisor/schema/bindParameter/conf-bindParam.xml");
BussinessBindParam bussinessBindParam = ctx.getBean(
"bussinessBindParam", BussinessBindParam.class);
// args(name,num,..) 因此会匹配到
bussinessBindParam.program("Spring", 8);
// args(name,num,..) 因此会匹配到
bussinessBindParam.fixbug("Spring4", 10, 20000);
}
}
运行结果
2017-09-15 22:47:49,244 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6ac604: startup date [Fri Sep 15 22:47:49 BOT 2017]; root of context hierarchy
2017-09-15 22:47:49,350 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/schema/bindParameter/conf-bindParam.xml]
----bindJoinPointParams()----
name:Spring
number:8
----bindJoinPointParams()----
BussinessBindParam program execute
program:Spring, number:8
----bindJoinPointParams()----
name:Spring4
number:10
----bindJoinPointParams()----
BussinessBindParam fixBug executed
program:Spring4, number:10,salary20000.0
Advisor是Spring中切面概念的对应物,是切点和增强的复合体,不过仅包含一个切点和一个增强。在AspectJ中没有对应的等价物,在aop Schema配置样式中,可以通过配置一个Advisor。通过advice-ref属性引用基于接口定义的增强,通过pointcut定义切点表达式,或者通过pointcut-ref引用一个命名的切点。
接口 及实现类
package com.xgj.aop.spring.advisor.schema.advisor;
public interface Waiter {
void greetTo(String name);
void serverTo(String name);
}
package com.xgj.aop.spring.advisor.schema.advisor;
import org.springframework.stereotype.Component;
@Component
public class NaiveWaiter implements Waiter {
@Override
public void greetTo(String name) {
System.out.println("greetTo " + name + "\n");
}
@Override
public void serverTo(String name) {
System.out.println("serverTo " + name);
}
}
前置增强
package com.xgj.aop.spring.advisor.schema.advisor;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
*
*
* @ClassName: GreetingBeforeAdvice
*
* @Description: 前置增强
*
* @author: Mr.Yang
*
* @date: 2017年9月13日 下午8:55:12
*/
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
/**
* 前置增强方法 当该方法发生异常时,将阻止目标方法的执行
*
* @param method
* 目标类方法
* @param objects
* 目标类方法入参
* @param target
* 目标类对象实例
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
String clientName = (String) args[0];
System.out.println("How are you " + clientName + " ?");
}
}
配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com..*.Waiter.greetTo(..))" id="beforeAdvice"/>
<aop:advisor advice-ref="greetingBeforeAdvice" pointcut-ref="beforeAdvice"/>
aop:config>
<context:component-scan base-package="com.xgj.aop.spring.advisor.schema.advisor"/>
<bean id="greetingBeforeAdvice" class="com.xgj.aop.spring.advisor.schema.advisor.GreetingBeforeAdvice"/>
beans>
测试类
package com.xgj.aop.spring.advisor.schema.advisor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdvisorTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/aop/spring/advisor/schema/advisor/conf-advisor.xml");
NaiveWaiter naiveWaiter = ctx.getBean("naiveWaiter", NaiveWaiter.class);
naiveWaiter.greetTo("XiaoGongJiang");
naiveWaiter.serverTo("XiaoGongJiang");
}
}
运行结果
2017-09-15 23:47:57,497 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61ee30d2: startup date [Fri Sep 15 23:47:57 BOT 2017]; root of context hierarchy
2017-09-15 23:47:57,599 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/schema/advisor/conf-advisor.xml]
How are you XiaoGongJiang ?
greetTo XiaoGongJiang
serverTo XiaoGongJiang