首页 > 代码库 > Spring面向切面 --- AspectJ --- 简单使用
Spring面向切面 --- AspectJ --- 简单使用
Spring面向切面 --- AspectJ --- 简单使用
昨天回复说说的时候突然写下了下面的一段话:分享一下:
<!--*******************************************
其实经过的记忆是可以进行道德化的篡改的,
就像夏目漱石的《我是猫》;
但是不管怎么改,真正的事实是由每一个人的碎片拼起来的;
经济学里计算成本的是在计算将来的成本而不是过去的成本,
就像动漫《未来日记》一样;
过去发生的事情总是在影响着将来,
但是过去发生的事情却不能充当将来下一个操作符的影响要素。
********************************************-->
面向切面编程用到了动态代理,感兴趣的读者可以参考我的日志:
http://www.cnblogs.com/kodoyang/p/DesignPattern_DynamicProxy.html
1.AspectJ - AOP
面向切面编程是面向对象的一个补充
在保存用户前添加一个日志,再加上时刻的记录。总之,就是方法前后加一点业务逻辑
新建资源库:Preference>Java>BuildPath>UserLibraries,AddLibrary>UserLibrary
/Spring_AOP/src/yuki/spring/aop/imitate/UserService.java
package yuki.spring.aop.imitate;public interface UserService { void save();}
/Spring_AOP/src/yuki/spring/aop/imitate/UserServiceImpl.java
package yuki.spring.aop.imitate;public class UserServiceImpl implements UserService{ public void save(){ System.out.println("user saved..."); }}
/Spring_AOP/src/yuki/spring/aop/imitate/Intercepter.java
package yuki.spring.aop.imitate;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public abstract class Intercepter implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } protected abstract void beforeMethod(); protected abstract void afterMethod(); @Override public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { beforeMethod(); m.invoke(target, args); afterMethod(); return null; }}
/Spring_AOP/src/yuki/spring/aop/imitate/LogIntercepter.java
package yuki.spring.aop.imitate;public class LogIntercepter extends Intercepter { @Override public void beforeMethod() { System.out.println("log start..."); } @Override protected void afterMethod() { System.out.println("log end..."); }}
/Spring_AOP/src/yuki/spring/aop/imitate/TimeIntercepter.java
package yuki.spring.aop.imitate;public class TimeIntercepter extends Intercepter { @Override protected void beforeMethod() { System.out.println("start:" + System.currentTimeMillis()); } @Override protected void afterMethod() { System.out.println("end:" + System.currentTimeMillis()); }}
/Spring_AOP/src/yuki/spring/aop/imitate/ProxyTest.java
package yuki.spring.aop.imitate;import java.lang.reflect.Proxy;public class ProxyTest { public static void main(String[] args) { UserService service = new UserServiceImpl(); Intercepter[] intercepters = {new LogIntercepter(), new TimeIntercepter()}; for(Intercepter intercepter : intercepters){ intercepter.setTarget(service); service = (UserService) Proxy.newProxyInstance( service.getClass().getClassLoader(), service.getClass().getInterfaces(), intercepter); } service.save(); }}
上面的程序中,两个类继承了实现InvocationHandler接口的抽象类,
也可以说是间接地实现了InvocationHandler接口;
声明为UserService接口类型的service,
在循环中每一次接收Proxy.newProxyInstance方法的计算结果后就会改变它指向的对象。
运行结果如下:
start:1409496143248log start...user saved...log end...end:1409496143250
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注解的方式实现AOP使用的是aspectj,aspectj是专门用来产生动态代理的一个框架
可以使用aspectj注解的方式实现spring的AOP,把这个逻辑织入到原来的逻辑里面去
引入jar:aspectjrt.jar、aspectj-weaver.jar:http://www.java2s.com/Code/Jar/a/aspectj.htm
在类上添加注解 @Aspect和 @Component,在方法上添加注解 @Before
切入点语法 @Before("execution(public void yuki.spring.aop.annotation"
+ ".UserServiceImpl.save(yuki.spring.aop.annotation.User))")
引入jar:aopalliance-.jar:http://www.java2s.com/Code/Jar/a/Downloadaopalliancejar.htm
@Component要加在对应的实现类上,因为getBean后要获取这个对象
/Spring_AOP/src/yuki/spring/aop/annotation/User.java
package yuki.spring.aop.annotation;public class User {}
/Spring_AOP/src/yuki/spring/aop/annotation/UserService.java
package yuki.spring.aop.annotation;public interface UserService { void save(User user);}
/Spring_AOP/src/yuki/spring/aop/annotation/UserServiceImpl.java
package yuki.spring.aop.annotation;import org.springframework.stereotype.Component;@Component("userService")public class UserServiceImpl implements UserService{ public void save(User user){ System.out.println("user saved..."); }}
/Spring_AOP/src/annotation.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <context:annotation-config></context:annotation-config> <context:component-scan base-package="yuki.spring.aop.annotation"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
/Spring_AOP/src/yuki/spring/aop/annotation/LogIntercepter.java
package yuki.spring.aop.annotation;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogIntercepter { @Before("execution(public void yuki.spring.aop.annotation" + ".UserServiceImpl.save(yuki.spring.aop.annotation.User))") public void beforeMethod() { System.out.println("method start..."); }}
/Spring_AOP/src/yuki/spring/aop/annotation/TimeIntercepter.java
package yuki.spring.aop.annotation;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Aspect@Componentpublic class TimeIntercepter { @Before("execution(" + "public * yuki.spring.aop.annotation..*.*(..)" + ")") public void beforeMethod() { System.out.println("before:" + System.currentTimeMillis()); } @AfterReturning("execution( public * yuki.spring.aop.annotation..*.*(..) )") public void afterReturningMethod() { System.out.println("afterReturning:" + System.currentTimeMillis()); }}
/Spring_AOP/src/yuki/spring/aop/annotation/PointcutIntercepter.java
package yuki.spring.aop.annotation;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class PointcutIntercepter { @Pointcut("execution( public * yuki.spring.aop.annotation..*.*(..) )") public void myMethod() {} @Before("myMethod()") public void beforeMethod() { System.out.println("PointcutIntercepter::before"); } @AfterReturning("myMethod()") public void afterReturningMethod() { System.out.println("PointcutIntercepter::afterReturning"); }}
/Spring_AOP/src/yuki/spring/aop/annotation/UserServiceTest.java
package yuki.spring.aop.annotation;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceTest { @Test public void testSave() { @SuppressWarnings("resource") ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("annotation.xml"); UserService service = (UserService) context.getBean("userService"); service.save(new User()); context.destroy(); }}
运行结果如下:
八月 31, 2014 10:59:10 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 22:59:10 CST 2014]; root of context hierarchy八月 31, 2014 10:59:10 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions信息: Loading XML bean definitions from class path resource [annotation.xml]method start...PointcutIntercepter::beforebefore:1409497152268user saved...afterReturning:1409497152268PointcutIntercepter::afterReturning八月 31, 2014 10:59:12 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 22:59:10 CST 2014]; root of context hierarchy
术语解释:
JoinPoint:连接点、切入点,在哪里把逻辑加进区
PointCut:JoinPoint的集合,可以一次性定义好多切入点
Aspect:切面,切面类加进去的逻辑可以认为是一个切面
Advice:在这个点上建议怎么办,比如 @Before
Target:被织入逻辑的被代理对象
Weave:织入
2.切面和通知
切入点:
// the execution of any public method:
execution(public * *(..))
// the execution of any method with a name beginning with "set":
execution(* set*(..))
// the execution of any method defined by the AccountService interface:
execution(* com.xyz.service.AccountService.*(..))
// the execution of any method defined in the service package:
execution(* com.xyz.service..(..))
// the execution of any method defined in the service package or a sub-package:
execution(* com.xyz.service..*.*(..))
spring也定义了自己的织入点语法,但是我们只要用AspectJ的语法就可以了
通知:
Before、AfterReturn、AfterThrowing、After(意思是finally)
Around:在ProceedingJoinPoint.proceed()前后加逻辑
如果织入点表达式相同,可以定义Pointcut
定义方法名接收织入点表达式的值,使用时 @AfterReturning("pointcut()")
/Spring_AOP/src/yuki/spring/aop/pointcut/User.java
package yuki.spring.aop.pointcut;public class User {}
/Spring_AOP/src/yuki/spring/aop/pointcut/UserService.java
package yuki.spring.aop.pointcut;public interface UserService { void save(User user);}
/Spring_AOP/src/yuki/spring/aop/pointcut/UserServiceImpl.java
package yuki.spring.aop.pointcut;import org.springframework.stereotype.Component;@Component("userService")public class UserServiceImpl implements UserService{ public void save(User user){ System.out.println("user saved..."); //throw new RuntimeException("exception..."); }}
/Spring_AOP/src/yuki/spring/aop/pointcut/TransactionAspect.java
package yuki.spring.aop.pointcut;import org.aspectj.lang.ProceedingJoinPoint;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;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class TransactionAspect { @Pointcut("execution( public * yuki.spring.aop.pointcut..*.*(..) )") public void pointcut(){} @Before("pointcut()") public void before(){ System.out.println("before"); } @AfterReturning("pointcut()") public void afterReturning(){ System.out.println("afterReturning"); } @AfterThrowing("pointcut()") public void afterThrowing(){ System.out.println("afterThrowing"); } @Around("pointcut()") public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("method around start..."); pjp.proceed(); System.out.println("method around end..."); } }
/Spring_AOP/src/pointcut.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <context:annotation-config></context:annotation-config> <context:component-scan base-package="yuki.spring.aop.pointcut"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
/Spring_AOP/src/yuki/spring/aop/pointcut/UserServiceTest.java
package yuki.spring.aop.pointcut;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceTest { @Test public void testSave() { @SuppressWarnings("resource") ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("pointcut.xml"); UserService service = (UserService) context.getBean("userService"); service.save(new User()); context.destroy(); }}
运行结果如下:
八月 31, 2014 11:09:22 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 23:09:22 CST 2014]; root of context hierarchy八月 31, 2014 11:09:22 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions信息: Loading XML bean definitions from class path resource [pointcut.xml]method around start...beforeuser saved...method around end...afterReturning八月 31, 2014 11:09:24 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 23:09:22 CST 2014]; root of context hierarchy
可以实现声明式的异常管理,
struts2已经实现了声明式的异常管理,这里略过通知执行的先后顺序
3.cglib
如果被代理的类实现了接口, 就会使用JDK自带的Proxy和InvocationHandler来实现代理
当被代理的类没有实现接口, 它会用cglib直接操作二进制码的形式来产生代理的代码
引入jar:cglib-2.2.jar:http://www.java2s.com/Code/Jar/c/Downloadcglib22jar.htm
/Spring_AOP/src/yuki/spring/aop/cglib/UserService.java
package yuki.spring.aop.cglib;import org.springframework.stereotype.Component;@Componentpublic class UserService { public void save(){ System.out.println("user saved..."); }}
/Spring_AOP/src/yuki/spring/aop/cglib/CglibAspect.java
package yuki.spring.aop.cglib;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class CglibAspect { @Pointcut("execution( public * yuki.spring.aop.cglib..*.*(..) )") public void pointcut(){} @Around("pointcut()") public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("method around start..."); pjp.proceed(); System.out.println("method around end..."); } }
/Spring_AOP/src/cglib.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <context:annotation-config></context:annotation-config> <context:component-scan base-package="yuki.spring.aop.cglib"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
/Spring_AOP/src/yuki/spring/aop/cglib/UserServiceTest.java
package yuki.spring.aop.cglib;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceTest { @Test public void testSave() { @SuppressWarnings("resource") ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("cglib.xml"); UserService service = (UserService) context.getBean("userService"); System.out.println(service.getClass()); service.save(); context.destroy(); }}
运行结果如下:
八月 31, 2014 11:14:17 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 23:14:17 CST 2014]; root of context hierarchy八月 31, 2014 11:14:17 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions信息: Loading XML bean definitions from class path resource [cglib.xml]class yuki.spring.aop.cglib.UserService$$EnhancerBySpringCGLIB$$b8ea6837method around start...user saved...method around end...八月 31, 2014 11:14:19 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 23:14:17 CST 2014]; root of context hierarchy
4.xml:AOP
pointcut可以声明在aspect的里面或外面
eclipse:从源码视图切换到设计视图
在执行UserService.save()时,发现符合配置的切入点表达式
对应的是LogAspect.before(),于是先执行before,然后save()
可以直接指定pointcut,也可以在外部指定然后再引用它
如果使用第三方类的切面类逻辑,那么就必须要使用xml配置的方式
/Spring_AOP/src/yuki/spring/aop/xml/UserService.java
package yuki.spring.aop.xml;import org.springframework.stereotype.Component;@Component("userService")public class UserService { public void save(){ System.out.println("user saved..."); }}
/Spring_AOP/src/yuki/spring/aop/xml/LogAspect.java
package yuki.spring.aop.xml;public class LogAspect { public void before() { System.out.println("method start..."); }}
/Spring_AOP/src/xml.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd "> <context:annotation-config></context:annotation-config> <context:component-scan base-package="yuki.spring.aop.xml"></context:component-scan> <bean id="logAspect" class="yuki.spring.aop.xml.LogAspect"></bean> <!-- <aop:config> <aop:pointcut id="servicePointcut" expression="execution( public * yuki.spring.aop.xml..*.*(..) )" /> <aop:aspect id="logAspect" ref="logAspect"> <aop:before method="before" pointcut-ref="servicePointcut"/> </aop:aspect> </aop:config> --> <aop:config> <aop:aspect id="logAspect" ref="logAspect"> <aop:before method="before" pointcut="execution( public * yuki.spring.aop.xml..*.*(..) )"/> </aop:aspect> </aop:config></beans>
/Spring_AOP/src/yuki/spring/aop/xml/UserServiceTest.java
package yuki.spring.aop.xml;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceTest { @Test public void testSave() { @SuppressWarnings("resource") ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("xml.xml"); UserService service = (UserService) context.getBean("userService"); service.save(); context.destroy(); }}
运行结果如下:
八月 31, 2014 11:19:01 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 23:19:01 CST 2014]; root of context hierarchy八月 31, 2014 11:19:01 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions信息: Loading XML bean definitions from class path resource [xml.xml]method start...user saved...八月 31, 2014 11:19:02 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@1637f22: startup date [Sun Aug 31 23:19:01 CST 2014]; root of context hierarchy
5.tomcat debug的热部署
在tomcat的jdk虚拟机参数中添加
-Dcom.sun.management.jmxremote=true
如果修改配置文件,使用了自定义标签的jsp页面,修改了注解,等等情况:还是要重启服务器的
在方法内部修改代码,不用重启服务器,这已经是很大的便捷了,
有兴趣的小伙伴们去研究功能更强大的热部署吧。。。。
目录结构:
本文参考了[尚学堂马士兵_Spring_AOP]的公开课程
更多好文请关注:http://www.cnblogs.com/kodoyang/
>*_*<
kongdongyang
2014/8/31
Spring面向切面 --- AspectJ --- 简单使用