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(){},之后通过 方法名 获得切入点引用。