spring默認使用jdk的代理方式,
使用jdk的代理方式我們知道,代理的類需要實現一個接口,若果沒有就會報,java.lang.NoSuchMethodException: com.sun.proxy.$Proxy83.loginPage()異常,
這時候我們可以使用<aop:aspectj-autoproxy proxy-target-class="true"/>讓spring使用cglib的代理方式,
cglib的時候需要有默認的構造方法,class不能為final的,
如果你對項目中的action就是contorller層代理這就會報這個異常,因為contorller這層沒有實現接口,
我們使用<aop:aspectj-autoproxy proxy-target-class="false"/>來指定讓他來使用cglib的代理方式,但是: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible class; 可能會報這個異常,它的意思不能代理final的或后類,我覺的這個是使用的baseDaoImpl的問題,全部繼承了basedaoImpl在這個里面使用了
ParameterizedType pt = (ParameterizedType) this.getClass()
.getGenericSuperclass();
this.clazz = (Class<T>) pt.getActualTypeArguments()[0];
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.ClassCastException-->java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
需要這個類不實現接口,
解決方案值配置<aop:aspectj-autoproxy /> 現在發現應該是spring會自動在JDK動態代理和CGLIB之間轉換。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Spring除了支持Schema方式配置AOP,還支持注解方式:使用@AspectJ風格的切面聲明。
1 啟用對@AspectJ的支持
Spring默認不支持@AspectJ風格的切面聲明,為了支持需要使用如下配置:
java代碼:
<aop:aspectj-autoproxy/>
這樣Spring就能發現@AspectJ風格的切面並且將切面應用到目標對象。
2 聲明切面
@AspectJ風格的聲明切面非常簡單,使用@Aspect注解進行聲明:
java代碼:
@Aspect()
Public class Aspect{
……
}
然后將該切面在配置文件中聲明為Bean后,Spring就能自動識別並進行AOP方面的配置:
java代碼:
<bean id="aspect" class="……Aspect"/>
該切面就是一個POJO,可以在該切面中進行切入點及通知定義,接着往下看吧。
3 聲明切入點
@AspectJ風格的命名切入點使用org.aspectj.lang.annotation包下的@Pointcut+方法(方法必須是返回void類型)實現。
java代碼:
@Pointcut(value="切入點表達式", argNames = "參數名列表")
public void pointcutName(……) {}
value:指定切入點表達式;
argNames:指定命名切入點方法參數列表參數名字,可以有多個用“,”分隔,這些參數將傳遞給通知方法同名的參數,同時比如切入點表達式“args(param)”將匹配參數類型為命名切入點方法同名參數指定的參數類型。
pointcutName:切入點名字,可以使用該名字進行引用該切入點表達式。
java代碼:
查看復制到剪貼板打印
@Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {}
定義了一個切入點,名字為“beforePointcut”,該切入點將匹配目標方法的第一個參數類型為通知方法實現中參數名為“param”的參數類型。
4 聲明通知
@AspectJ風格的聲明通知也支持5種通知類型:
一、前置通知:使用org.aspectj.lang.annotation 包下的@Before注解聲明;
java代碼:
@Before(value = "切入點表達式或命名切入點", argNames = "參數列表參數名")
value:指定切入點表達式或命名切入點;
argNames:與Schema方式配置中的同義。
接下來示例一下吧:
1、定義接口和實現,在此我們就使用Schema風格時的定義;
2、定義切面:
java代碼:
查看復制到剪貼板打印
package cn.javass.spring.chapter6.aop;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class HelloWorldAspect2 {
}
3、定義切入點:
java代碼:
@Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {}
4、定義通知:
java代碼:
查看復制到剪貼板打印
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
5、在chapter6/advice2.xml配置文件中進行如下配置:
java代碼:
<?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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy/>
<bean id="helloWorldService"
class="cn.javass.spring.chapter6.service.impl.HelloWorldService"/>
<bean id="aspect"
class="cn.javass.spring.chapter6.aop.HelloWorldAspect2"/>
</beans>
6、測試代碼cn.javass.spring.chapter6.AopTest:
java代碼:
@Test
public void testAnnotationBeforeAdvice() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter6/advice2.xml");
IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
helloworldService.sayBefore("before");
System.out.println("======================================");
}
將輸出:
==========================================
===========before advice param:before
============say before
==========================================
切面、切入點、通知全部使用注解完成:
1)使用@Aspect將POJO聲明為切面;
2)使用@Pointcut進行命名切入點聲明,同時指定目標方法第一個參數類型必須是java.lang.String,對於其他匹配的方法但參數類型不一致的將也是不匹配的,通過argNames = "param"指定了將把該匹配的目標方法參數傳遞給通知同名的參數上;
3)使用@Before進行前置通知聲明,其中value用於定義切入點表達式或引用命名切入點;
4)配置文件需要使用<aop:aspectj-autoproxy/>來開啟注解風格的@AspectJ支持;
5)需要將切面注冊為Bean,如“aspect”Bean;
6)測試代碼完全一樣。
二、后置返回通知:使用org.aspectj.lang.annotation 包下的@AfterReturning注解聲明;
java代碼:
@AfterReturning(
value="切入點表達式或命名切入點",
pointcut="切入點表達式或命名切入點",
argNames="參數列表參數名",
returning="返回值對應參數名")
value:指定切入點表達式或命名切入點;
pointcut:同樣是指定切入點表達式或命名切入點,如果指定了將覆蓋value屬性指定的,pointcut具有高優先級;
argNames:與Schema方式配置中的同義;
returning:與Schema方式配置中的同義。
java代碼:
@AfterReturning(
value="execution(* cn.javass..*.sayBefore(..))",
pointcut="execution(* cn.javass..*.sayAfterReturning(..))",
argNames="retVal", returning="retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
其中測試代碼與Schema方式幾乎一樣,在此就不演示了,如果需要請參考AopTest.java中的testAnnotationAfterReturningAdvice測試方法。
三、后置異常通知:使用org.aspectj.lang.annotation 包下的@AfterThrowing注解聲明;
java代碼:
@AfterThrowing (
value="切入點表達式或命名切入點",
pointcut="切入點表達式或命名切入點",
argNames="參數列表參數名",
throwing="異常對應參數名")
value:指定切入點表達式或命名切入點;
pointcut:同樣是指定切入點表達式或命名切入點,如果指定了將覆蓋value屬性指定的,pointcut具有高優先級;
argNames:與Schema方式配置中的同義;
throwing:與Schema方式配置中的同義。
java代碼:
@AfterThrowing(
value="execution(* cn.javass..*.sayAfterThrowing(..))",
argNames="exception", throwing="exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
其中測試代碼與Schema方式幾乎一樣,在此就不演示了,如果需要請參考AopTest.java中的testAnnotationAfterThrowingAdvice測試方法。
四、后置最終通知:使用org.aspectj.lang.annotation 包下的@After注解聲明;
java代碼:
@After (
value="切入點表達式或命名切入點",
argNames="參數列表參數名")
value:指定切入點表達式或命名切入點;
argNames:與Schema方式配置中的同義;
java代碼:
@After(value="execution(* cn.javass..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
System.out.println("===========after finally advice");
}
其中測試代碼與Schema方式幾乎一樣,在此就不演示了,如果需要請參考AopTest.java中的testAnnotationAfterFinallyAdvice測試方法。
五、環繞通知:使用org.aspectj.lang.annotation 包下的@Around注解聲明;
java代碼:
@Around (
value="切入點表達式或命名切入點",
argNames="參數列表參數名")
value:指定切入點表達式或命名切入點;
argNames:與Schema方式配置中的同義;
java代碼:
@Around(value="execution(* cn.javass..*.sayAround(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===========around before advice");
Object retVal = pjp.proceed(new Object[] {"replace"});
System.out.println("===========around after advice");
return retVal;
}
其中測試代碼與Schema方式幾乎一樣,在此就不演示了,如果需要請參考AopTest.java中的annotationAroundAdviceTest測試方法。
6.4.5 引入
@AspectJ風格的引入聲明在切面中使用org.aspectj.lang.annotation包下的@DeclareParents聲明:
java代碼:
@DeclareParents(
value=" AspectJ語法類型表達式",
defaultImpl=引入接口的默認實現類)
private Interface interface;
value:匹配需要引入接口的目標對象的AspectJ語法類型表達式;與Schema方式中的types-matching屬性同義;
private Interface interface:指定需要引入的接口;
defaultImpl:指定引入接口的默認實現類,沒有與Schema方式中的delegate-ref屬性同義的定義方式;
java代碼:
@DeclareParents(
value="cn.javass..*.IHelloWorldService+", defaultImpl=cn.javass.spring.chapter6.service.impl.IntroductiondService.class)
private IIntroductionService introductionService;
其中測試代碼與Schema方式幾乎一樣,在此就不演示了,如果需要請參考AopTest.java中的testAnnotationIntroduction測試方法。
---------------------