Spring学习(八)
AOP的重要概念
1、切面 : 切点(Pointcut) + Advice【 在哪里 、加什么 】
2、Advice: 在 切点 选中的 连接点 "加入" 的 代码 就是 Advice【确定 加什么 】
3、切点( Pointcut ) : 用来 筛选 连接点 的条件就是切点, 类似于sql语句,where条件就可以理解为切点【 确定 在哪里加 ,在那些方法里面加入代码】
4、连接点( Join Point ) : 执行点 + 方位信息 所有被拦截的方法被加入了其他的方法,通过代理的方法将想要加入的想要的代码,加入的代码和原有的代码形成了连接点。一个方法有5个连接点,四个方法就有20个
- 一个正在执行的方法 (执行点) 执行前 (方位信息)
- 一个正在执行的方法 (执行点) 执行前、后 (方位信息)
- 一个正在执行的方法 (执行点) 执行并返回后 (方位信息)
- 一个正在执行的方法 (执行点) 执行抛出异常后 (方位信息)
- 一个正在执行的方法 (执行点) 执行后 (方位信息)
5、方位信息
- 方法执行前 ( before ) 、
- 执行前和执行后,执行前后都有环绕,两个结合起来才可以使用,两者之间是有联系的 ( around ) 、
- 正常执行并返回后 ( after-returning ) 、
- 方法抛出异常后 ( after-throwing ) 、
- 方法执行后 ( after )
6、执行点:一个可以执行的方法在执行时就是一个执行点
使用AOP的方式
1、使用 XML 文件中的 aop 命名空间:
- 使用 aop:advisor 标签来声明切面
- 使用 aop:aspect 标签来声明一组切面
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<aop:config proxy-target-class="true" >
...................
</aop:config>
</beans>
proxy-target-class 指示 采用 那种方式 创建 代理对象
proxy-target-class="false" 不采用 cglib 方式,而是采用 JDK 动态代理方式 ,若没有实现接口会通过 cglib 方式
proxy-target-class="true" 采用 cglib 方式(当一个类没有实现任何接口时,必须采用这种方式)动态产生字节码
2、使用 注解 声明
使用 aop:advisor 标签来声明切面
1、这种方式是基于 XML 文件中的 aop 命名空间,并通过Spring的API实现AOP。
2、主要步骤
- 写一个代理目标Mokey
package ecut.aop.xml; public class Monkey { private String name ; public void eat( String food ) {
System.out.println( this.name + " 吃 " + food );
} public void run(){
System.out.println( this.name + " 在跑步 " );
} public void sleep(){
System.out.println( this.name + " 在睡觉 " );
} public void fly(){
System.out.println( this.name + " 很牛逼,可以腾云驾雾 " );
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} } - 通过实现Spring提供了相应的Advice接口的方式编写Advice
MonkeyBeforeAdvice:
package ecut.aop.xml; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; // org.springframework.aop.BeforeAdvice 接口 继承了 org.aopalliance.aop.Advice
// org.springframework.aop.MethodBeforeAdvice 接口 继承了 BeforeAdvice
public class MonkeyBeforeAdvice implements MethodBeforeAdvice { @Override
public void before( Method method , Object[] args , Object target) throws Throwable {
System.out.println( "MonkeyBeforeAdvice 提示 : 方法[ " + method.getName() + " ]将要执行了" );
} }org.springframework.aop.BeforeAdvice 接口 继承了 org.aopalliance.aop.Advice,org.springframework.aop.MethodBeforeAdvice 接口 继承了 BeforeAdvice,实现MethodBeforeAdvice的接口并重写before方法。
MonkeyAroundAdvice:
package ecut.aop.xml; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; // org.aopalliance.intercept.Interceptor 继承了 org.aopalliance.aop.Advice
// org.aopalliance.intercept.MethodInterceptor 继承 org.aopalliance.intercept.Interceptor
public class MonkeyAroundAdvice implements MethodInterceptor{ @Override
public Object invoke( MethodInvocation invocation ) throws Throwable { System.out.println( "around advice : before" );
Object result = invocation.proceed(); // 让被 "拦截" 的方法执行
System.out.println( "around advice : after" ); /*
Method m = invocation.getMethod(); //获取被连接的 方法 对应的 Method 对象 Object[] args = invocation.getArguments(); // 获得 即将被执行的 方法的参数列表 Object target = invocation.getThis(); // 获取 代理目标 ( 被别的对象所代理的那个对象 )
// System.out.println( target ); System.out.println( "around advice : before" );
Object result = m.invoke( target , args ); // 调用 target 对象的 m 对应的方法,并传入参数 args
System.out.println( "around advice : after" );
*/ return result ;
} }org.aopalliance.intercept.Interceptor 继承了 org.aopalliance.aop.Advice,org.aopalliance.intercept.MethodInterceptor 继承 org.aopalliance.intercept.Interceptor实现MethodInterceptor的接口并重写invoke方法。这个Advice需要继续向后传递结果,因此需要将结果返回。
MonkeyAfterAdvice:
package ecut.aop.xml; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class MonkeyAfterAdvice implements AfterReturningAdvice { @Override
public void afterReturning( Object returnValue , Method method , Object[] args , Object target )
throws Throwable {
System.out.println( "方法[ " + method.getName() + " ]执行后返回了: " + returnValue );
} }需要实现AfterReturningAdvice 接口重写afterReturning方法。
- 配置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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="wk" class="ecut.aop.xml.Monkey" p:name="孙悟空" /> <bean id="beforeAdvice" class="ecut.aop.xml.MonkeyBeforeAdvice" /> <bean id="aroundAdvice" class="ecut.aop.xml.MonkeyAroundAdvice" /> <bean id="afterAdvice" class="ecut.aop.xml.MonkeyAfterAdvice" /> <!-- proxy-target-class 指示 采用 那种方式 创建 代理对象 -->
<!-- proxy-target-class="false" 不采用 cglib 方式,而是采用 JDK 动态代理方式 ,若没有实现接口会通过 cglib 方式-->
<!-- proxy-target-class="true" 采用 cglib 方式(当一个类没有实现任何接口时,必须采用这种方式)动态产生字节码-->
<aop:config proxy-target-class="true" > <!-- 声明一个切点 -->
<!-- 参数个数不确定的 ,..可以匹配任意个数的参数-->
<aop:pointcut id="my-pointcut" expression="execution(* ecut.aop.xml.Monkey.*(..))" />
<!-- 切面 ( Advisor ) : 切点(Pointcut) + Advice 【 在哪里 、加什么 】 -->
<!-- 一个 aop:advisor 就表示一个 切面 -->
<aop:advisor pointcut-ref="my-pointcut" advice-ref="beforeAdvice" /> <!-- pointcut=“切点表达式” 切点表达式 execution(修饰符 返回类型 包名.类名.方法名(参数列表) 异常类型) -->
<!-- <aop:advisor pointcut="execution(* ecut.aop.xml.Monkey.*(..))" advice-ref="beforeAdvice" /> --> <aop:advisor pointcut-ref="my-pointcut" advice-ref="aroundAdvice" /> <aop:advisor pointcut-ref="my-pointcut" advice-ref="afterAdvice" /> </aop:config> </beans>需要在配置文件中声明切点和Advice,然后aop:advisor声明切面Advice,声明切点需要通过expression属相来指定切点表达execution(修饰符 返回类型 包名.类名.方法名(参数列表) 异常类型)。
- 编写测试类
package ecut.aop.xml; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAdvisor { public static void main(String[] args) { String configLocations = "classpath:ecut/**/xml/advisor.xml" ; AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations ); Monkey m = container.getBean( "wk" , Monkey.class );
//获取的是代理类的类型,不再是Monkey
System.out.println( m.getClass() );
System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
m.eat( "苹果" );
System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
m.run();
System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
String name = m.getName() ;
//和filter,interceptor 不一样,需继续传递才会往下执行,而after,before拦截下来之后会继续往下执行,无需继续传递
System.out.println( "name: " + name ); container.close(); } }通过m.getClass可以获得代理类的类型。测试代码运行结果如下:
class ecut.aop.xml.Monkey$$EnhancerBySpringCGLIB$$44ed5ead
~~~~~~~~~~~~~~~~~~~~~~
MonkeyBeforeAdvice 提示 : 方法[ eat ]将要执行了
around advice : before
孙悟空 吃 苹果
方法[ eat ]执行后返回了: null
around advice : after
~~~~~~~~~~~~~~~~~~~~~~
MonkeyBeforeAdvice 提示 : 方法[ run ]将要执行了
around advice : before
孙悟空 在跑步
方法[ run ]执行后返回了: null
around advice : after
~~~~~~~~~~~~~~~~~~~~~~
MonkeyBeforeAdvice 提示 : 方法[ getName ]将要执行了
around advice : before
方法[ getName ]执行后返回了: 孙悟空
around advice : after
name: 孙悟空由结果可以看出,Advice按照配置文件中引用的顺序输出了。默认执行顺序为before 一定在 after之前执行,若为方位信息一致,谁在前谁先执行。
3、织入顺序:
- 对于在同一个 方位中 织入 Advice 的多个 <aop:advisor> ,可以使用 order 属性来确定织入顺序
- 测试案例
MonkeyAnotherAdvice :
package ecut.aop.xml; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MonkeyAnotherAdvice implements MethodBeforeAdvice { @Override
public void before( Method method , Object[] args , Object target) throws Throwable {
System.out.println( "MonkeyAnotherAdvice 提示 : 方法[ " + method.getName() + " ]将要执行了" );
}配置文件:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="wk" class="ecut.aop.xml.Monkey" p:name="孙悟空" /> <bean id="beforeAdvice" class="ecut.aop.xml.MonkeyBeforeAdvice" /> <bean id="anotherAdvice" class="ecut.aop.xml.MonkeyAnotherAdvice" /> <aop:config proxy-target-class="true" > <aop:pointcut id="my-pointcut" expression="execution(* ecut.aop.xml.Monkey.*(..))" /> <!-- 一个 aop:advisor 就表示一个 切面 -->
<aop:advisor pointcut-ref="my-pointcut" advice-ref="beforeAdvice" order="10000"/> <aop:advisor pointcut-ref="my-pointcut" advice-ref="anotherAdvice" order="0"/> </aop:config> </beans>数字越小越先执行,order属性所指定的数字可以不按照顺序,只要数字之前有大小之分就可以决定两个Advice的执行顺序
测试类:
package ecut.aop.xml; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAdvisorOrder { public static void main(String[] args) { String configLocations = "classpath:ecut/**/xml/advisor-order.xml" ; AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations ); Monkey m = container.getBean( "wk" , Monkey.class ); System.out.println( m.getClass() ); m.eat( "苹果" ); container.close(); } }运行结果如下:
class ecut.aop.xml.Monkey$$EnhancerBySpringCGLIB$$b4a506ad
MonkeyAnotherAdvice 提示 : 方法[ eat ]将要执行了
MonkeyBeforeAdvice 提示 : 方法[ eat ]将要执行了
孙悟空 吃 苹果织入顺序由order决定,若没有指定order属性会由配置文件中引用顺序来执行即MonkeyBeforeAdvice 先执行
使用 aop:aspect 标签来声明一组切面
1、这种方式是基于 XML 文件中的 aop 命名空间,并通过自定义类来实现AOP。
2、主要步骤
- 写一个代理目标Panda
package ecut.aop.xml; public class Panda { private String name ; public void eat( String food ) {
System.out.println( this.name + " 吃 " + food );
} public void run(){
System.out.println( this.name + " 在跑步 " );
} public void sleep(){
System.out.println( this.name + " 在睡觉 " );
} public void fly(){
System.out.println( this.name + " 很牛逼,可以腾云驾雾 " );
} public int div( int a , int b ) {
return a / b ;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} } - 不实现任何借口自定义一个Advice 类
package ecut.aop.xml; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature; public class PandaAdvices { /**
* 不要静态的,所有Advices都是实例化之后去植入这个方法
* 通过 JoinPoint 类型的对象 可以访问连接点 信息
* 如果不需要访问连接点信息,那么 可以不写 这个参数
* @param point
*/
public void before( JoinPoint point ) {
//方法签名方法签名int ecut.aop.xml.Panda.div(int,int)
Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名 String name = signature.getName() ; // 获得方法的名称 System.out.println( "PandaAdvices 提示你 : 方法[ " + name + " ]将要执行了" ); } // ProceedingJoinPoint 是 JoinPoint 接口的子接口 ,可以通过 ProceedingJoinPoint访问连接点 信息,也可以通过ProceedingJoinPoint使方法执行
public Object around( ProceedingJoinPoint point ) throws Throwable {
System.out.println( "around : before" );
Object result = point.proceed();
System.out.println( "around : after" );
return result ;//得将值返回,不然没法往后传值了
} public void afterReturn( JoinPoint point , Object result ) { Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名 String name = signature.getName() ; // 获得方法的名称 System.out.println( "方法[ " + name + " ]执行后返回: " + result ); } public void afterThrow( JoinPoint point , Throwable ex ) { Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名 String name = signature.getName() ; // 获得方法的名称 System.out.println( "方法[ " + name + " ]执行时发生异常: " + ex ); } public void after( JoinPoint point ) { Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名 String name = signature.getName() ; // 获得方法的名称 System.out.println( "方法[ " + name + " ]执行结束" ); } }连接点JoinPoint 是执行点+方位信息, 通过 JoinPoint 类型的对象 可以访问连接点信息 ,如果不需要访问连接点信息,那么 可以不写 这个参数,afterThrow和afterReturn中的参数名称要和配置文件中的名称保持一致。ProceedingJoinPoint 是 JoinPoint 接口的子接口 ,可以通过 ProceedingJoinPoint访问连接点 信息,也可以通过ProceedingJoinPoint使方法执行。
- 配置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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="panda" class="ecut.aop.xml.Panda" p:name="阿宝" /> <!-- pandaAdvices 中包含了要在 某些连接点 中需要织入的代码 -->
<bean id="pandaAdvices" class="ecut.aop.xml.PandaAdvices" /> <aop:config proxy-target-class="true" > <!-- 声明一个切点 ( 属于 整个 aop:config 标记内都可以使用,因此称作 全局切点 ) -->
<aop:pointcut id="my-pointcut" expression="execution(* ecut.aop.xml.Panda.*(..))" />
<!-- 声明的是一组切面 -->
<aop:aspect ref="pandaAdvices"> <!-- 在 aop:aspect 标记内部的 aop:pointcut 被称作 局部切点,只能在同一个 aop:aspect 标记内使用--> <aop:before pointcut-ref="my-pointcut" method="before"/> <!-- pandaAdvices.before() method所引用的方法就是确定加什么代码 ,一个method就是一个Advice--> <aop:around pointcut-ref="my-pointcut" method="around"/> <aop:after-throwing pointcut-ref="my-pointcut" method="afterThrow" throwing="ex"/>
<!-- 通过指定returning这个属哦,将返回值绑定到这个参数上,需要和method中的参数名一致 -->
<aop:after-returning pointcut-ref="my-pointcut" method="afterReturn" returning="result"/> <aop:after pointcut-ref="my-pointcut" method="after" /> <!-- pandaAdvices.after() 是无论抛并不抛出异常都要执行的Advice(类似finally)--> </aop:aspect> </aop:config> </beans>通过<aop:aspect ref="pandaAdvices">来引用自定义的Advice 类,通过<aop:aspect>标记内的aop标记来指定方位信息,pointcut-ref来引用相应的切点,并通过method属性来调用Advice 类中的Advice,method所引用的方法就是确定加什么代码 ,一个method就是一个Advice。<aop:after-throwing/>标签中的throwing属性需要和afterThrow方法中的Throwable参数名称保持一致,<aop:after-returning/>标签中
- 编写测试类
package ecut.aop.xml; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAspect { public static void main(String[] args) { String configLocations = "classpath:ecut/**/xml/aspect.xml" ; AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations ); Panda m = container.getBean( "panda" , Panda.class );
System.out.println(m.getClass());
//表面上调用的panda的eat,方法实际上m只是一个代理对象
m.eat( "面条" ); System.out.println( "~~~~~~~~~~~~~~~~~" ); String name = m.getName() ; System.out.println( "name : " + name ); System.out.println( "~~~~~~~~~~~~~~~~~" ); int r = m.div( 100 , 0 ); System.out.println( r ); container.close(); } }运行结果如下:
class ecut.aop.xml.Panda$$EnhancerBySpringCGLIB$$dac36ee3
PandaAdvices 提示你 : 方法[ eat ]将要执行了
around : before
阿宝 吃 面条
around : after
方法[ eat ]执行后返回: null
方法[ eat ]执行结束
~~~~~~~~~~~~~~~~~
PandaAdvices 提示你 : 方法[ getName ]将要执行了
around : before
around : after
方法[ getName ]执行后返回: 阿宝
方法[ getName ]执行结束
name : 阿宝
~~~~~~~~~~~~~~~~~
PandaAdvices 提示你 : 方法[ div ]将要执行了
around : before
方法[ div ]执行时发生异常: java.lang.ArithmeticException: / by zero
方法[ div ]执行结束
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ecut.aop.xml.Panda.div(Panda.java:24)
3、织入顺序
- 对于多个 <aop:aspect> 来说 可以通过 order 属性的值 来织入顺序 ( 按照 从小 到大 排列 ) 无需按照顺序排只要有大小之分就好了, 数字越大越靠后执行。
- 测试案例
AnimalAdvices:
package ecut.aop.xml; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature; public class AnimalAdvices { public void before1( JoinPoint point ) { Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名 String name = signature.getName() ; // 获得方法的名称 System.out.println( "AnimalAdvices 的 before1 提示你 : 方法[ " + name + " ]将要执行了" ); } public void before2( JoinPoint point ) { Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名 String name = signature.getName() ; // 获得方法的名称 System.out.println( "AnimalAdvices 的 before2 提示你 : 方法[ " + name + " ]将要执行了" ); } }配置文件:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="panda" class="ecut.aop.xml.Panda" p:name="阿宝" /> <bean id="pandaAdvices" class="ecut.aop.xml.PandaAdvices" /> <bean id="animalAdvices" class="ecut.aop.xml.AnimalAdvices" /> <aop:config proxy-target-class="true" > <!-- 声明一个切点 ( 属于 整个 aop:config 标记内都可以使用,因此称作 全局切点 ) -->
<aop:pointcut id="my-pointcut" expression="execution(* ecut.aop.xml.Panda.*(..))" /> <aop:aspect ref="pandaAdvices" order="150">
<aop:before pointcut-ref="my-pointcut" method="before"/> <!-- pandaAdvices.before() -->
</aop:aspect> <aop:aspect ref="animalAdvices" order="-250">
<aop:before pointcut-ref="my-pointcut" method="before2"/> <!-- pandaAdvices.before() -->
<aop:before pointcut-ref="my-pointcut" method="before1"/>
</aop:aspect> </aop:config> </beans>测试类:
package ecut.aop.xml; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAspectOrder { public static void main(String[] args) { String configLocations = "classpath:ecut/**/xml/aspect-order.xml" ; AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations ); Panda m = container.getBean( "panda" , Panda.class ); m.eat( "面条" ); container.close(); } }运行结果如下:
AnimalAdvices 的 before2 提示你 : 方法[ eat ]将要执行了
AnimalAdvices 的 before1 提示你 : 方法[ eat ]将要执行了
PandaAdvices 提示你 : 方法[ eat ]将要执行了
阿宝 吃 面条
使用注解声明切面
1、步骤
- 写一个StudentController类
package ecut.aop.annotation; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller; /**
* 控制层 ( Controller ) : 对 表示层 的用户操作 做出响应
* 实现技术可以是: Servlet 、Struts 、Spring MVC
* @Controller("sc") value ="sc" 相当于bean的名称是sc,默认名称是 studentController
*/
@Controller
public class StudentController { @Autowired
@Qualifier( "studentService" )
private StudentService studentService ; public String regist( Student s ){
System.out.println( "StudentController # regist ( Student ) " );
studentService.save( s );
return "success" ;
} public String logout() { throw new RuntimeException( "抛出异常" ); } public StudentService getStudentService() {
return studentService;
} public void setStudentService(StudentService studentService) {
this.studentService = studentService;
} } StudentAspect声明一组切面
package ecut.aop.annotation; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; @Component
@Aspect
public class StudentAspect { //@Pointcut( "execution(* ecut..StudentController.*(..))" )
@Pointcut( "execution(* ecut.aop.annotation.StudentController.*(..))" )
private void myPointcut(){
// 用声明方法的形式 来声明 一个切点,切点名称就是 方法名 , 切点表达式在 注解中指定
} //在 方位信息的注解内部 通过 方法调用的 形式 来引用已经声明的切点
@Before( "myPointcut()" )
public void before( JoinPoint point ) {
Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
String name = signature.getName() ; // 获得方法的名称
System.out.println( "StudentAspect 的 before 提示你 : 方法[ " + name + " ]将要执行了" );
} @Around( "myPointcut()" )
public Object around( ProceedingJoinPoint point ) throws Throwable {
System.out.println( "StudentAspect 的 around 提示你 : before" );
Object result = point.proceed();
System.out.println( "StudentAspect 的 around 提示你 : after" );
return result ;
} //@AfterReturning( value = "myPointcut()" , returning = "result" )
@AfterReturning( pointcut = "myPointcut()" , returning = "result" )
public void afterReturn( JoinPoint point , Object result ) {
Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
String name = signature.getName() ; // 获得方法的名称
System.out.println( "StudentAspect 的 afterReturn 提示你 : 方法[ " + name + " ]执行后返回: " + result );
} @AfterThrowing( pointcut = "myPointcut()" , throwing = "ex" )
public void afterThrow( JoinPoint point , Throwable ex ) {
Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
String name = signature.getName() ; // 获得方法的名称
System.out.println( "StudentAspect 的 afterThrow 提示你 : 方法[ " + name + " ]执行时发生异常: " + ex );
} @After( "myPointcut()" )
public void after( JoinPoint point ) {
Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
String name = signature.getName() ; // 获得方法的名称
System.out.println( "StudentAspect 的 after 提示你 : 方法[ " + name + " ]执行结束" );
} }首先使用Component注解要让spring容器知道有一个类存在,其次要使用Aspect注解声明时一组切面,在类中通过声明方法的形式 来声明 一个切点,切点名称就是方法名, 切点表达式在 注解中指定。在 方位信息的注解内部 通过 方法调用的 形式 来引用已经声明的切点。@AfterThrowing注解中的throwing属性需要和afterThrow方法中的Throwable参数名称保持一致,@AfterReturning注解中的returning属相需要和afterReturn方法中的Object 参数名称保持一致。
配置文件
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--
<bean id="studentDao" class="ecut.aop.annotation.StudentDao" />
<bean id="studentService" class="ecut.aop.annotation.StudentService" autowire="byType"/>
<bean id="studentController" class="ecut.aop.annotation.StudentController" autowire="byType"/> 扫描所有加了注解的类自动完成下面的配置
<bean id="studentDao" class="ecut.aop.annotation.StudentDao" />
<bean id="studentService" class="ecut.aop.annotation.StudentService" p:studentDao-ref="studentDao"/>
<bean id="studentController" class="ecut.aop.annotation.StudentController" p:studentService-ref="studentService"/>
-->
<!-- 启用注解的支持 -->
<!-- <context:annotation-config /> -->
<!-- 扫描这个包的所有组件 -->
<context:component-scan base-package="ecut.aop.annotation" /> <!-- 为 注解 提供 自动产生 代理对象的 支持 -->
<aop:aspectj-autoproxy proxy-target-class="true" /> </beans>配置文件中通过<context:component-scan base-package="ecut.aop.annotation" />扫描base-package所指定包及其子包的所有组件,若有注解就自动增加其配置,通过<aop:aspectj-autoproxy proxy-target-class="true" />为 注解 提供 自动产生 代理对象的 支持
- 测试类
package ecut.aop.annotation; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestStudentController { public static void main(String[] args) { Student s = new Student();
s.setId( 100 );
s.setName( "张三" ); String configLocations = "classpath:ecut/**/annotation/ioc-aop.xml" ; AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations ); StudentController sc = container.getBean( "studentController" , StudentController.class ); System.out.println( sc ); sc.regist( s ); System.out.println( "~~~~~~~~~~~~~~~~~~~~~~" ); sc.logout(); container.close(); } }
2、相关注解说明
- @org.aspectj.lang.annotation.Pointcut 用来单独声明一个切点
- @org.aspectj.lang.annotation.Before 相当于 <aop:before>
- @org.aspectj.lang.annotation.Around 相当于 <aop:around>
- @org.aspectj.lang.annotation.AfterReturning 相当于 <aop:after-returning>
- @org.aspectj.lang.annotation.AfterThrowing 相当于 <aop:after-returning>
- @org.aspectj.lang.annotation.After 相当于 <aop:after>
3、织入顺序
- 在 注解中通过 @org.springframework.core.annotation.Order 用来指定 织入顺序
- 在有多个Aspect时,在类声明前使用order注解可以控制织入顺序,在同一个Aspect,在方法中使用order属性不起作用
- 因此使用注解织入顺序的控制没有配置文件的好。
转载请于明显处标明出处:
https://www.cnblogs.com/AmyZheng/p/9277832.html
Spring学习(八)的更多相关文章
- spring学习 八 面向切面编程(AOP)概述
注:本文大部分参考 --------------------- 本文来自 -望远- 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/yanquan345/artic ...
- Spring学习(八)-----Spring注入值到集合类型的例子
下面例子向您展示Spring如何注入值到集合类型(List, Set, Map, and Properties). 支持4个主要的集合类型: List – <list/> Set – &l ...
- Spring学习八
1: Tomcat容器四个等级? Container, Engine, Servlet容器, Context 真正管理Servlet的容器是Context容器:一个context对应一个web工程. ...
- Spring学习八----------Bean的配置之Resources
© 版权声明:本文为博主原创文章,转载请注明出处 Resources 针对于资源文件的统一接口 -UrlResource:URL对应的资源,根据一个URL地址即可创建 -ClassPathResour ...
- Java框架spring 学习笔记(十八):事务管理(xml配置文件管理)
在Java框架spring 学习笔记(十八):事务操作中,有一个问题: package cn.service; import cn.dao.OrderDao; public class OrderSe ...
- Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探
由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...
- Spring学习之旅(十)--MockMvc
在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就 ...
- spring 学习之 bean 的注入方式 property和constructor-arg的使用方式
spring 学习之 bean 的注入方式 property和constructor-arg的使用方式. bean的注入方式: property 注入是: 通过setxx方法注入. construct ...
- Spring学习之AOP总结帖
AOP(面向方面编程),也可称为面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组 ...
随机推荐
- Android 使用 MPAndroidChart 实现折线图
Android 使用 MPAndroidChart 实现折线图 做Android项目的时候用到了折线图,不光折线图,还可能遇到很多的图表需要展示渲染,自己手画的话那好玩了,今天使用MPAndroidC ...
- Python求1000以内所有3或5的倍数的和。
a=0 for n in range(1,1000): if n%3==0 or n%5==0: a=a+n print(a) 运行结果: 233168
- 抽象工厂模式(JAVA反射)
实例代码(JAVA):模式动机 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方 ...
- CTF_论剑场 名侦探柯南
首先看一下是一个压缩包然后下载 解压后会发现一个图片和另一个压缩包 打开图片 发现是这个 用HxD分析一下这张图片通过搜索 zip jpg...... 然后会发现这张图片里有一个png 图片 所以判断 ...
- 2.2 selenium:org.openqa.selenium.WebDriverException: f.QueryInterface is not a function
来源: http://blog.csdn.net/qiyueqinglian/article/details/47813271 URL中地址写不全的时候,就会报如题错误. url必须是完整的,比如ht ...
- 如何在项目中新建.gitignore文件
1. 在需要创建 .gitignore 文件的文件夹, 右键选择 Git Bash 进入命令行,进入项目所在目录. 2. 输入 touch .gitignore 在文件夹就生成了一个“.gitigno ...
- 题解【洛谷P5436】【XR-2】缘分
题目背景 世间万物都置身于缘分编织的大网中.缘分未到,虽历经千劫,却不能相遇.缘分到了,在草原上都能等到一艘船.--<一禅小和尚> 题目描述 一禅希望知道他和师父之间的缘分大小.可是如何才 ...
- [LEETCODE] 初级算法/数组 1.3旋转数组
原题: 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: [1,2,3,4,5,6,7] 和 k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右 ...
- Python socket day5
下载文件 程序04,05 服务端在接收到文件名时应该用try来打开文件,不应该用with open来打开否则,如果文件名不存在,用with open 会出错误 客户端要判断服务端发送的数据是否为空,不 ...
- HttpClient与TestNG结合
1.HTTPclient插件的安装 在maven项目的pom.xml中引用HTTPclient包,如下 <dependencies> <dependency> <grou ...