AspectJ
1、概念
AspectJ是一個基於Java語言的AOP框架。Spring2.0以後新增了對AspectJ切點表達式支持。@AspectJ 是AspectJ1.5新增功能,通過JDK5註解技術,允許直接在Bean類中定義切面。新版本Spring框架,建議使用AspectJ方式來開發AOP。主要用途:自定義開發。
2、切入點表達式
表達式execution(*com.Lily.spring_aop.*.*(..))
語法:execution(修飾符 返回值 包.類.方法名(參數) throws異常)
修飾符【一般省略】:public *(任意)
返回值【不能省略】:void String *(任意)
包:
1、固定包com.Lily. aop
2、aop包下的所有包,包括自己,包括孫包 com.Lily.aop..
3、aop包下任意子包,固定目錄Service,Service目錄任意包 com.Lily.aop.*.Service..
類【一般省略】
1、UserServiceImpl 指定類
2、 *Impl 以Impl結尾
3、 User* 以User開頭
4、 * 任意
方法名【不能省略】
1、addUser 固定方法
2、 add* 以add開頭
3、 *Do 以Do結尾
4、 * 任意
參數
1、 () 無參
2、 (int) 一個整型
3、 (int ,int) 兩個
4、 (..) 參數任意
異常【可省略,一般不寫】
3、AspectJ通知類型
AOP聯盟定義了6種通知類型,具有特性接口,必須實現對應的接口,從而確定方法名稱。
1. before:前置通知(應用:各種校驗):在方法執行前執行,如果通知拋出異常,阻止方法運行。
2. afterReturning:後置通知(應用:常規數據處理):方法正常返回後執行,如果方法中拋出異常,通知無法執行。必須在方法執行後才執行,所以可以獲得方法的返回值。
3. around:環繞通知(應用:十分強大,可以做任何事情):方法執行前後分別執行,可以阻止方法的執行。 必須手動執行目標方法
4. afterThrowing:拋出異常通知(應用:包裝異常信息):方法拋出異常後執行,如果方法沒有拋出異常,無法執行。
5. after:最終通知(應用:清理現場):方法執行完畢後執行,無論方法中是否出現異常。
4、基於xml的aop編程
1、導入Jar包
【前面AOP的包和Spring的4+1包都是默認導入】
spring-aspects-3.2.0.RELEASE.jar
2、編寫UserService接口
public interface UserService {
public void addUser();
public String updateUser();
public void deleteUser();
}
3、編寫接口的實現類
public class UserSerImpl implements UserService {
/* (non-Javadoc)
* @see com.Lily.SpringLearning.h_AspectJ.UserService#addUser()
*/
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("AspectJ add() "+this.getClass().getName());
}
/* (non-Javadoc)
* @see com.Lily.SpringLearning.h_AspectJ.UserService#updateUser()
*/
@Override
public String updateUser() {
// TODO Auto-generated method stub
System.out.println("AspectJ update() "+this.getClass().getName());
//引入Exeception
int i=1/0;
return "See my Error?";
}
/* (non-Javadoc)
* @see com.Lily.SpringLearning.h_AspectJ.UserService#deleteUser()
*/
@Override
public void deleteUser() {
// TODO Auto-generated method stub
System.out.println("ASpectJ delete() ");
}
}
4、切面類
這裏將上述的5中通知類型都寫入了,需要哪種就用哪種。【一般要求掌握的是環繞通知,因爲有前有後,但是還是要根據實際的需求來定】
public class LilyAspcet {
public void LilyBefore(JoinPoint jp){
System.out.println("前置通知: "+jp.getSignature().getName());
}
public void LilyAfterReturning(JoinPoint jp,Object obj){
System.out.println("後置通知: "+jp.getSignature().getName()+" → "+obj);
}
public Object LilyAround(ProceedingJoinPoint jp)throws Throwable{
System.out.println("around的前方法");
//手動執行目標方法
Object obj=jp.proceed();
System.out.println("around的後方法");
return obj;
}
public void LilyAfterThrowing(JoinPoint jp,Throwable e){
System.out.println("拋出異常通知 "+e.getMessage());
}
public void LilyAfter(JoinPoint jp){
System.out.println("一定會出現的 最 終 通 知 ~");
}
}
5、編寫bean.xml
【這裏要注意,不能同時使用多個通知類型,使用哪個就寫哪個,舉例是前置通知,其他的通知就將<aop:??method="?" pointcut-ref="myPointCut"/>的方法名改成對應的方法名即可】
前置通知 <aop:before method="LilyBefore" pointcut-ref="myPointCut"/>
後置通知 <aop:after-returning method="LilyAfterReturning"pointcut-ref="myPointCut" returning="obj"/>
環繞通知 <aop:around method="LilyAround"pointcut-ref="myPointCut"/>
拋出異常 <aop:after-throwing method="LilyAfterThrowing" pointcut-ref="myPointCut"throwing="e"/>
最終通知 <aop:after method="LilyAfter"pointcut-ref="myPointCut"/>
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userServiceId" class="com.Lily.SpringLearning.h_AspectJ.UserSerImpl"></bean>
<bean id="LilyAspect" class="com.Lily.SpringLearning.h_AspectJ.LilyAspcet"></bean>
<aop:config>
<aop:aspect ref="LilyAspect">
<aop:pointcut expression="execution(* com.Lily.SpringLearning.h_AspectJ.UserSerImpl.*(..))" id="myPointCut"/>
<!--aop:before method="LilyBefore" pointcut-ref="myPointCut"/>-->
<!--aop:after-returning method="LilyAfterReturning" pointcut-ref="myPointCut" returning="obj"/>-->
<!--aop:around method="LilyAround" pointcut-ref="myPointCut"/>-->
<aop:after-throwing method="LilyAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
<!--aop:after method="LilyAfter" pointcut-ref="myPointCut"/> -->
</aop:aspect>
</aop:config>
</beans>
6、編寫測試類
public class TestAspectJ {
@Test
public void test() {
String xmlPath = "com/Lily/SpringLearning/h_AspectJ/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//獲得目標類
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
}
5、基於註解的aop編程
1、需要在實現類上添加註解 @Service("userServiceId")
@Service("userServiceId")
public class UserSerImpl implements UserService {
/* (non-Javadoc)
* @see com.Lily.SpringLearning.h_AspectJ.UserService#addUser()
*/
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("AspectJ add() "+this.getClass().getName());
}
/* (non-Javadoc)
* @see com.Lily.SpringLearning.h_AspectJ.UserService#updateUser()
*/
@Override
public String updateUser() {
// TODO Auto-generated method stub
System.out.println("AspectJ update() "+this.getClass().getName());
//引入Exeception
int i=1/0;
return "See my Error?";
}
/* (non-Javadoc)
* @see com.Lily.SpringLearning.h_AspectJ.UserService#deleteUser()
*/
@Override
public void deleteUser() {
// TODO Auto-generated method stub
System.out.println("ASpectJ delete() ");
}
}
2、需要修改Aspect類,最頂端添加註解@Component和@Aspect,在對應方法上聲明切入點/前置通知/環繞通知/後置通知/拋出異常/最終通知。【注意,拋出異常的通知要寫Throwable e,後置通知要註明returning="obj"】
@Component
@Aspect
public class LilyAspcet {
//聲明公共切入點,方便後續引用。若不聲明,則每個通知需寫自己的切入點表達式。
@Pointcut("execution(* com.Lily.SpringLearning.h_AspectJ_anno.UserSerImpl.*(..))")
private void myPonitCut(){}
//@Before("execution(* com.Lily.SpringLearning.h_AspectJ_anno.UserSerImpl.*(..))")
//@Before(value="myPonitCut()")
public void LilyBefore(JoinPoint jp){
System.out.println("前置通知: "+jp.getSignature().getName());
}
// @AfterReturning(value="myPonitCut()",returning="obj")
public void LilyAfterReturning(JoinPoint jp,Object obj){
System.out.println("後置通知: "+jp.getSignature().getName()+" → "+obj);
}
// @Around (value="myPonitCut()")
public Object LilyAround(ProceedingJoinPoint jp)throws Throwable{
System.out.println("around的前方法");
//手動執行目標方法
Object obj=jp.proceed();
System.out.println("around的後方法");
return obj;
}
@AfterThrowing(value="myPonitCut()",throwing="e")
public void LilyAfterThrowing(JoinPoint jp,Throwable e){
System.out.println("拋出異常通知 "+e.getMessage());
}
//@After("myPonitCut()")
public void LilyAfter(JoinPoint jp){
System.out.println("一定會出現的 最 終 通 知 ~");
}
}
3、在xml配置文件中,掃描註解類、並確定aop註解生效。
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<context:component-scan base-package="com.Lily.SpringLearning.h_AspectJ_anno"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
註解第一次報錯
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:Line 13 in XML document from class path resource[com/Lily/SpringLearning/h_AspectJ_anno/beans.xml] is invalid; nested exceptionis org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 80;cvc-complex-type.2.4.c: 通配符的匹配很全面, 但無法找到元素 'context:component-scan' 的聲明。
atorg.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:396)
atorg.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
atorg.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
atorg.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174)
atorg.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209)
atorg.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
atorg.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:243)
atorg.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127)
atorg.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93)
atorg.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:131)
atorg.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
atorg.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451)
atorg.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
atorg.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
atcom.Lily.SpringLearning.h_AspectJ_anno.TestAspectJ.test(TestAspectJ.java:26)
atsun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
atjava.lang.reflect.Method.invoke(Method.java:606)
atorg.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
atorg.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
atorg.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
atorg.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
atorg.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
atorg.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
atorg.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
atorg.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
atorg.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
atorg.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
atorg.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
atorg.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
atorg.junit.runners.ParentRunner.run(ParentRunner.java:309)
atorg.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
atorg.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
atorg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
atorg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
atorg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
atorg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.xml.sax.SAXParseException;lineNumber: 13; columnNumber: 80; cvc-complex-type.2.4.c: 通配符的匹配很全面, 但無法找到元素 'context:component-scan' 的聲明。
atcom.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
atcom.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
atcom.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
atcom.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:368)
atcom.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:325)
atcom.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:458)
atcom.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.reportSchemaError(XMLSchemaValidator.java:3237)
atcom.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:1917)
atcom.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:746)
atcom.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:378)
atcom.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2778)
atcom.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
atcom.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117)
atcom.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
atcom.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
atcom.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
atcom.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
atcom.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243)
atcom.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:347)
atorg.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoader.java:75)
atorg.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:388)
...37 more
查了一下,xml文件忘記了添加
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
AOP註解開發總結:
@Aspect 聲明切面,修飾切面類,從而獲得通知。
@PointCut,修飾方法 private void xxx(){},之後通過 方法名 獲得切入點引用。