spring中AOP功能的实现有以下俩种情况:
1.如果目标对象实现了接口,默认情况下会采用JDK的动态代理来实现AOP功能
2.如果目标对象没有实现接口,spring会使用CGLIB的库来实现代理类实现AOP功能
注:spring会在JDK动态代理和CGLIB之间自动选择
AOP的一些基本概念:
AOP 面向切面编程
aspect 切面/切面类 即附加功能类
joinPoint 连接点,目标类中每一个方法都是一个连接点
pointCut 切入点,一切连接点的集合
advice 通知/拦截器,用来控制切面类将来的位置(切入点的前面、后面或抛异常时)
adivsor 增强器,用来筛选目标类中那些方法是我们的连接点(需要被拦截)
target 目标对象
proxy 代理对象
wave 织入
advice(通知类型):
Before advice
前置通知,连接点执行前执行
After returning advice
返回后通知(在连接点(目标方法)正常完成后执行(没有异常))
After throwing advice
在连接点抛出异常退出时执行
After (finally) advice
在连接点退出时执行(不管连接点是否有异常都会执行)
Around Advice
环绕通知,包围一个连接点的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交,以及方法抛出异常时候回滚(相当于before、after通知一起用),这种方法相对与同时写多个通知线程安全
当AroundAdvice与before、after通知一起用时,AroundAdvice最先执行,执行完方法后执行AroundAdvice,最后执行after
执行顺序相当于
try {
try {
aroundAdvice();
beforeAdvice();
pointcut();
} finally {
afterAdvice();
}
aroundAdvice();
afterReturningAdvice();
return;
} catch (Exception e) {
afterThrowingAdvice(e);
return;
}
每一个通知都有对应的接口:
MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice、MethodInterceptor
有目标类(有切入点)(需要实现接口)、切面类
Before advice和After returning advice
新建一个类(通知类),实现通知对应接口,有切面类属性,实现对应接口内的抽象方法(方法内部不用手动调用对应切入点,Spring会自动调用)
AroundAdvice
新建一个类(通知类),实现通知对应接口,有切面类属性,实现对应接口内的抽象方法(参数为:MethodInvocation mi):
在要执行连接点时执行
Object returnValue = mi.proceed();
最后将returnValue返回即可
After throwing advice
新建一个类,实现通知对应接口 (ThrowsAdvice 是一个空接口,起标识作用),有切面类属性,增加方法afterThrowing()并实现,
这个方法可以是一参(Exception e),也可以是四参(Method method, Object[] args, Object target,Exception e)
配置xml文件:
配置目标类、切面类、通知类
然后配置代理对象:
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
<value>afterAdvice</value>
<value>aroundAdvice</value>
<value>throwAdvice</value>
</list>
</property>
</bean>
advisor 增强器
作用:筛选目标对象中要代理的方法,之前的advice是把目标对象中的所有方法全部都进行代理
spring中已经给我们提供了实现类RegexpMethodPointcutAdvisor,在xml中直接配使用就可以了
有目标类(需要实现接口)、切面类、通知器类
xml中配置目标类、切面类、通知器类
配置增强器:
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增强器要在哪一个advice起作用-->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法(连接点) -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>
配置代理对象:
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice/advisor 可以有多个 -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
注意:另外spring还提供了一个增强器:NameMatchMethodPointcutAdvisor
这个增强器的配置方式和上面的类似,不同之处在于可以利用setMappedNames方法直接注明要处理的方法名字
AutoProxy
自动代理:DefaultAdvisorAutoProxyCreator类的使用
使用原因:在配置文件中我们往往需要给很多个目标对象设置代理对象,那么就需要每个目标对象的代理对象都需要配置一套类似的标签,通过自动代理可以用很少的配置为xml文件中的目标对象自动的生成对应的代理对象
配置:
在xml中配置目标类、切面类、通知器、增强器
配置代理对象(自动代理方式):
<!-- 注意:这不是一个工厂类,所以不能用过proxy来拿代理对象 -->
<bean name="proxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>
使用自动代理的时候需要注意的方面:
1.当前的配置里面一定要配置有advisor
2.不需要向自动代理类中注入任何信息
3.不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象(CGLib的方式).
4.从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。
5.spring如何确定配置文件中哪个bean是作为目标对象:
通过advisor中筛选的方法,如果这个bean中含有advisor中所配置的方法,则这个bean将来称为我们的目标对象进行代理
AutoProxyByName
通过名字进行自动代理:BeanNameAutoProxyCreator类的使用
通过名字进行自动代理可以筛选出给哪些对象进行自动代理
配置:
在xml中配置目标类、切面类、通知器、增强器
配置代理对象(自动代理方式):
<bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 注入需要被代理的对象名字 -->
<property name="beanNames">
<list>
<value>target</value>
<value>target2</value>
<value>dao</value>
<value>service*</value>
</list>
</property>
<!-- 注入advice或者advisor -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
使用byName自动代理的时候需要注意的方面:
1.当前的配置里面"有没有"advisor的配置"都没关系"
2.需要向自动代理类中注入被代理目标对象的名字已经advice或者advisor
3.不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象.
4.从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。
aop:config标签
使用aop的专用标签来完成相关的配置
主要使用AspectJ的expression的操作:
execution(modifiers-pattern ret-type-pattern declaring-type-pattern name-pattern(param-pattern) throws-pattern)除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的。
使用前需要在xml的beans标签中加入新的schame文件,并在eclipse中进行关联配置
<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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
使用方法:
在xml中配置目标类、切面类、通知器、增强器
配置aop代理:
<aop:config>
<!-- 定义一个切入点 并给切入点起名为myPointCut -->
<!-- 切入点是一组连接点的集合 -->
<aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
<!-- 定义哪一个advice在哪一个切入点上面起作用 -->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="myPointCut" />
</aop:config>
使用规则:
最常用的返回类型模式是 *,表示匹配任意的返回类型。 一个全称限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用 * 通配符作为所有或者部分命名模式。
参数模式稍微有点复杂:
() 表示匹配一个不接受任何参数的方法
(…) 表示匹配一个接受任意数量参数的方法(零或者更多)。
() 表示匹配一个接受一个任何类型的参数的方法。
(,String) 表示匹配一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型
常用切入点表达式例子:
1)任意包下的任意类中的公共方法的执行:
execution(public * (…))
2)任何一个以“set”开始的方法的执行:
execution( set*(…))
3)AccountService 接口的任意方法的执行:
execution(* com.briup.service.AccountService.(…))
4)定义在service包里的任意方法的执行:
execution( com.briup.service..(…))
5)定义在service包或者子包里的任意方法的执行:
execution(* com.briup.service..(…))
6)execution(public * com.briup.aop.service..(…))
表达式中从左到右的号:
第一个 表示方法的返回类型不限。
第二个* 表示包中的任意一个类
第三个* 表示类中的任意一个方法
注意:<aop:config proxy-target-class=“true”> 如果这样配置则是强制使用CGLIB方式进行代理
简化配置通知器:
写一个切面类,切面类中定义多个方法(beforeAdvice,afterAdvice…),并在xml文件的aop标签中进行配置
新建一个类(切面类)并新建方法实现对应通知:
before advice、after advice、
after Returning advice 对应方法参数为JoinPoint p 例:
public void beforeTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" before...");
}
After throwing advice
对应方法参数为JoinPoint p,Exception ex 例:
public void throwingTest(JoinPoint p,Exception ex){
System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage())
}
around Advice
对应方法参数为ProceedingJoinPoint pjp
使用方法:
public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
//JoinPoint对象不能调用连接点所表示的方法
//ProceedingJoinPoint能调用连接点所表示的方法 pjp.proceed()
System.out.println(pjp.getSignature().getName()+" is start..");
//调用到连接点方法
Object obj = pjp.proceed();
System.out.println(pjp.getSignature().getName()+" is end..");
return obj;
}
xml中配置目标类、切面类(起名为handler)
配置aop代理:
<aop:config>
<!-- 定义切入点名为myPointCut -->
<aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
<!-- 定义切面类 以及需要使用的advice -->
<aop:aspect id="aspect" ref="handler">
<!-- 表示beforeAdvice会把切面类handler中的beforeTest方法织入到名字叫myPointCut的切入点上面 -->
<aop:before method="beforeTest" pointcut-ref="myPointCut"/>
<!-- after表示不管方法是否正常结束都会起作用 -->
<aop:after method="afterTest" pointcut-ref="myPointCut"/>
<!-- after-returning表示方法正常结束才会起作用(抛异常时候不起作用) -->
<aop:after-returning method="afterReturningTest" pointcut-ref="myPointCut"/>
<aop:around method="aroundTest" pointcut-ref="myPointCut"/>
<!-- throwing="ex"表示throwingTest方法中接收异常对象的名字一定要是ex -->
<aop:after-throwing method="throwingTest" pointcut-ref="myPointCut" throwing="ex"/>
</aop:aspect>
</aop:config>
注意:<aop:config proxy-target-class=“true”> 如果这样配置则是强制使用CGLIB方式进行代理
使用注解配置AOP:
其实就是在上面的类XmlHandler中加入上注解,然后去掉xml中的aop标签配置
在类名上方注 @Aspect 表示这个类是一个切面类
在这个类里新建一个方法,这个方法本身不需要有什么作用,意义就是给 @Pointcut注解一个书写的地方,因为注解只能写在方法、属性、类的上面,并且方法名作为切入点的名字
@Pointcut("execution(public * com.briup.aop.service..*.*(..))")
public void myPointCut(){}
在每个通知方法上面写对应的注解:
@Before、 @After、 @AfterReturning、 @Around、AfterThrowing
xml中配置:
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.briup.aop"/>