Spring系列之AOP实现的两种⽅式
Spring只⽀持XML⽅式⽽没有实现注解的⽅式(也叫AspectJ⽅式)的AOP,所以要使⽤@Aspect注解,只能引⼊AspectJ相关的 jar 包:aopalliance-1.0.jar 和 aspectjweaver.jar
Spring的配置⽂件 l 中引⼊context、aop对应的命名空间;
配置⾃动扫描的包,同时使切⾯类中相关⽅法中的注解⽣效,需⾃动地为匹配到的⽅法所在的类⽣成代理对象。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xmlns:aop="/schema/aop"
xmlns:context="/schema/context"
xsi:schemaLocation="/schema/beans /schema/beans/spring-beans.xsd
/schema/aop /schema/aop/spring-aop-4.0.xsd
/schema/context /schema/context/spring-context-4.0.xsd">
<!-- 配置⾃动扫描的包 -->
<context:component-scan base-package="com.qcc.beans.aop"></context:component-scan>
<!-- ⾃动为切⾯⽅法中匹配的⽅法所在的类⽣成代理对象。 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
AOP常⽤的实现⽅式有两种,
1、采⽤声明的⽅式来实现(基于XML),
2、是采⽤注解的⽅式来实现(基于AspectJ)。
AOP中⼀些⽐较重要的概念
Joinpoint(连接点):程序执⾏时的某个特定的点,在Spring中就是某⼀个⽅法的执⾏。
Pointcut(切点):说的通俗点,spring中AOP的切点就是指⼀些⽅法的集合,⽽这些⽅法是需要被增强、被代理的。⼀般都是按照⼀定的约定规则来表⽰的,如正则表达式等。切点是由⼀类连接点组成。
Advice(通知):还是说的通俗点,就是在指定切点上要⼲些什么。
Advisor(通知器):其实就是切点和通知的结合。
注意:
1、环绕⽅法通知,环绕⽅法通知要注意必须给出调⽤之后的返回值,否则被代理的⽅法会停⽌调⽤并返回null,除⾮你真的打算这么做。
2、只有环绕通知才可以使⽤JoinPoint的⼦类ProceedingJoinPoint,各连接点类型可以调⽤代理的⽅法,并获取、改变返回值。
1、<aop:pointcut>如果位于<aop:aspect>元素中,则命名切点只能被当前<aop:aspect>内定义的元素访问到。为了能被整个<aop:config>元素中定义的所有增强访问,则必须在<aop:config>下定义切点。
2、如果在<aop:config>元素下直接定义<aop:pointcut>,必须保证<aop:pointcut>在<aop:aspect>之前定义。<aop:config>下还可以定义
<aop:advisor>,三者在<aop:config>中的配置有先后顺序的要求:⾸先必须是<aop:pointcut>,然后是<aop:advisor>,最后是
<aop:aspect>。⽽在<aop:aspect>中定义的<aop:pointcut>则没有先后顺序的要求,可以在任何位置定义。<aop:pointcut>:⽤来定义切⼊点,该切⼊点可以重⽤;<aop:advisor>:⽤来定义只有⼀个通知和⼀个切⼊点的切⾯;<aop:aspect>:⽤来定义切⾯,该切⾯可以包含多个切⼊点和通知,⽽且标签内部
的通知和切⼊点定义是⽆序的;和advisor的区别就在此,advisor只包含⼀个通知和⼀个切⼊点。
3、在使⽤spring框架配置AOP的时候,不管是通过XML配置⽂件还是注解的⽅式都需要定义pointcut"切⼊点"。
例如定义切⼊点表达式execution(* com.sample.service.impl..*.*(..))
execution()是最常⽤的切点函数,其语法如下所⽰:
整个表达式可以分为五个部分:
(1)、execution(): 表达式主体。
(2)、第⼀个*号:表⽰返回类型,*号表⽰所有的类型。
(3)、包名:表⽰需要拦截的包名,后⾯的两个句点表⽰当前包和当前包的所有⼦包,com.sample.service.impl包、⼦孙包下所有类的⽅法。
(4)、第⼆个*号:表⽰类名,*号表⽰所有的类。
(5)、*(..):最后这个星号表⽰⽅法名,*号表⽰所有的⽅法,后⾯括弧⾥⾯表⽰⽅法的参数,两个句点表
⽰任何参数。
⼀、基于XML配置的Spring AOP
采⽤声明的⽅式实现(在XML⽂件中配置),⼤致步骤为:配置⽂件中配置pointcut, 在java中⽤编写实际的aspect 类, 针对对切⼊点进⾏相关的业务处理。
业务接⼝:
package com.spring.service;
public interface IUserManagerService {
//查⽤户
public String findUser();
//添加⽤户
public void addUser();
}
业务实现:
package com.spring.service.impl;
import com.spring.service.IUserManagerService;
public class UserManagerServiceImpl implements IUserManagerService{
private String name;
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public String findUser(){
System.out.println("============执⾏业务⽅法findUser,查的⽤户是:"+name+"=============");
return name;
}
public void addUser(){
System.out.println("============执⾏业务⽅法addUser=============");
//throw new RuntimeException();
}
}
切⾯类:
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopAspect {
/**
* 前置通知:⽬标⽅法调⽤之前执⾏的代码
* @param jp
*/
public void doBefore(JoinPoint jp){
System.out.println("===========执⾏前置通知============");
}
/**
* 后置返回通知:⽬标⽅法正常结束后执⾏的代码
* 返回通知是可以访问到⽬标⽅法的返回值的
* @param jp
* @param result
*/
public void doAfterReturning(JoinPoint jp,String result){
System.out.println("===========执⾏后置通知============");
System.out.println("返回值result==================="+result);
}
/**
* 最终通知:⽬标⽅法调⽤之后执⾏的代码(⽆论⽬标⽅法是否出现异常均执⾏)
* 因为⽅法可能会出现异常,所以不能返回⽅法的返回值
* @param jp
*/
public void doAfter(JoinPoint jp){
System.out.println("===========执⾏最终通知============");
}
/**
*
* 异常通知:⽬标⽅法抛出异常时执⾏的代码
* 可以访问到异常对象
* @param jp
* @param ex
*/
public void doAfterThrowing(JoinPoint jp,Exception ex){
System.out.println("===========执⾏异常通知============");
}
/**
* 环绕通知:⽬标⽅法调⽤前后执⾏的代码,可以在⽅法调⽤前后完成⾃定义的⾏为。
* 包围⼀个连接点(join point)的通知。它会在切⼊点⽅法执⾏前执⾏同时⽅法结束也会执⾏对应的部分。
* 主要是调⽤proceed()⽅法来执⾏切⼊点⽅法,来作为环绕通知前后⽅法的分⽔岭。
*
* 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执⾏⽬标⽅法。
spring aop应用场景* ⽽且环绕通知必须有返回值,返回值即为⽬标⽅法的返回值
* @param pjp
* @return
* @throws Throwable
*/
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("======执⾏环绕通知开始=========");
// 调⽤⽅法的参数
Object[] args = Args();
// 调⽤的⽅法名
String method = Signature().getName();
/
/ 获取⽬标对象
Object target = Target();
// 执⾏完⽅法的返回值
// 调⽤proceed()⽅法,就会触发切⼊点⽅法执⾏
Object result=pjp.proceed();
System.out.println("输出,⽅法名:" + method + ";⽬标对象:" + target + ";返回值:" + result);
System.out.println("======执⾏环绕通知结束=========");
return result;
}
}
Spring配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xmlns:aop="/schema/aop"
xmlns:p="/schema/p"
xsi:schemaLocation="/schema/beans
/schema/beans/spring-beans-3.0.xsd
/schema/tx
/schema/tx/spring-tx-3.0.xsd
/schema/aop
/schema/aop/spring-aop-3.0.xsd">
<!-- 声明⼀个业务类 -->
<bean id="userManager" class="com.spring.service.impl.UserManagerServiceImpl">
<property name="name" value="lixiaoxi"></property>
</bean>
<!-- 声明通知类 -->
<bean id="aspectBean" class="com.spring.aop.AopAspect"/>
<aop:config>
<aop:aspect ref="aspectBean">
<aop:pointcut id="pointcut" expression="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))"/> <aop:before method="doBefore" pointcut-ref="pointcut"/>
<aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:after method="doAfter" pointcut-ref="pointcut"/>
<aop:around method="doAround" pointcut-ref="pointcut"/>
<aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
测试类:
package st;
import t.ApplicationContext;
import t.support.ClassPathXmlApplicationContext;
import com.spring.service.IUserManagerService;
public class TestAop {
public static void main(String[] args) throws Exception{
ApplicationContext act = new ClassPathXmlApplicationContext("l");
IUserManagerService userManager = (Bean("userManager");
userManager.findUser();
System.out.println("\n");
userManager.addUser();
}
}
⼆、使⽤注解配置AOP
采⽤注解来做aop, 主要是将写在spring 配置⽂件中的连接点写到注解⾥⾯。
业务接⼝和业务实现与上边⼀样,具体切⾯类如下:
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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;
@Aspect
public class AopAspectJ {
/**
* 必须为final String类型的,注解⾥要使⽤的变量只能是静态常量类型的
*/
public static final String EDP="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))"; /**
* 切⾯的前置⽅法即⽅法执⾏前拦截到的⽅法
* 在⽬标⽅法执⾏之前的通知
* @param jp
*/
@Before(EDP)
public void doBefore(JoinPoint jp){
System.out.println("=========执⾏前置通知==========");
}
/**
* 在⽅法正常执⾏通过之后执⾏的通知叫做返回通知
* 可以返回到⽅法的返回值在注解后加⼊returning
* @param jp
* @param result
*/
@AfterReturning(value=EDP,returning="result")
public void doAfterReturning(JoinPoint jp,String result){
System.out.println("===========执⾏后置通知============");
}
/**
* 最终通知:⽬标⽅法调⽤之后执⾏的通知(⽆论⽬标⽅法是否出现异常均执⾏)
* @param jp
*/
@After(value=EDP)
public void doAfter(JoinPoint jp){
System.out.println("===========执⾏最终通知============");
}
/**
* 环绕通知:⽬标⽅法调⽤前后执⾏的通知,可以在⽅法调⽤前后完成⾃定义的⾏为。
* @param pjp
* @return
* @throws Throwable
*/
@Around(EDP)
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("======执⾏环绕通知开始=========");
// 调⽤⽅法的参数
Object[] args = Args();
// 调⽤的⽅法名
String method = Signature().getName();
// 获取⽬标对象
Object target = Target();
// 执⾏完⽅法的返回值
// 调⽤proceed()⽅法,就会触发切⼊点⽅法执⾏
Object result=pjp.proceed();
System.out.println("输出,⽅法名:" + method + ";⽬标对象:" + target + ";返回值:" + result);
System.out.println("======执⾏环绕通知结束=========");
return result;
}
/**
* 在⽬标⽅法⾮正常执⾏完成, 抛出异常的时候会⾛此⽅法
* @param jp
* @param ex
*/
@AfterThrowing(value=EDP,throwing="ex")
public void doAfterThrowing(JoinPoint jp,Exception ex) {
System.out.println("===========执⾏异常通知============");
}
}
spring的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xmlns:aop="/schema/aop"
xmlns:p="/schema/p"
xsi:schemaLocation="/schema/beans
/schema/beans/spring-beans-3.0.xsd
/schema/tx
/schema/tx/spring-tx-3.0.xsd
/schema/aop
/schema/aop/spring-aop-3.0.xsd">
<!-- 声明spring对@AspectJ的⽀持 -->
<aop:aspectj-autoproxy/>
<!-- 声明⼀个业务类 -->
<bean id="userManager" class="com.spring.service.impl.UserManagerServiceImpl">
<property name="name" value="lixiaoxi"></property>
</bean>
<!-- 声明通知类 -->
<bean id="aspectBean" class="com.spring.aop.AopAspectJ" />
</beans>
测试类:
package st;
import t.ApplicationContext;
import t.support.ClassPathXmlApplicationContext;
import com.spring.service.IUserManagerService;
public class TestAop1 {
public static void main(String[] args) throws Exception{
ApplicationContext act = new ClassPathXmlApplicationContext("l"); IUserManagerService userManager = (Bean("userManager"); userManager.findUser();
System.out.println("\n");
userManager.addUser();
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论