Spring框架的AOP
Spring学习笔记(四)
本文目录
1 AOP的介绍
2 Spring的AspectJ实现AOP(annotation)
3 Spring的AspectJ实现AOP (XML)
Spring文档http://docs.spring.io/spring/docs/4.0.0.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/
一 AOP介绍
spect Oriented Programming(AOP),面向切面编程。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间降低耦合的隔离效果
简单来说就是在方法的前后加一层过滤的方法
如果类实现了interface接口,通过使用动态代理(Proxy),通过拦截一个对象的行为并添加我们需要的功能来完成
如果没有实现接口的类,可以使用Cglib代理实现AOP
AOP用途比较广,适合切面的功能都可以使用AOP,比如打日志,声明式的事务的管理,性能测试,权限检查等
通过invocationhandle和Proxy实现
测试代码
接口DO
package proxy.service; public interface Do { public void doSomething(); }
DO接口的实现
package proxy.service; public class DoImpl implements Do { @Override public void doSomething() { System.out.println("doimpl..."); } }
动态代理的实现,invocationhandle和Proxy
package proxy.service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class dynamicProxy implements InvocationHandler { private Object target; public dynamicProxy(Object target) { this.target = target; } public void before(Method method) { System.out.println(method.getName() + "调用开始"); } public void after(Method method) { System.out.println(method.getName() + "调用结束"); } public static Object newInstance(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new dynamicProxy(target)); } @Override public Object invoke(Object proxy, Method method, Object[] args) { Object result = null; try { before(method); result = method.invoke(target, args); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } finally { after(method); } return result; } }
测试类
package proxy.service.test; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import proxy.service.Do; import proxy.service.DoImpl; import proxy.service.dynamicProxy; public class dynamicProxyTest { @Before public void setUp() throws Exception { } @Test public void testDynamicProxy() { Do doSth = (Do) dynamicProxy.newInstance(new DoImpl()); doSth.doSomething(); } }
执行结果
doSomething调用开始
doimpl...
doSomething调用结束
在不改动实现类和接口的情况下,通过invocationhandle和Proxy实现动态代理在方法的前后插入了业务逻辑
二、Spring的AspectJ实现AOP(annotation)
1 添加JAR包
aspectjrt.jar aspectjweaver.jar
2 XML的配置
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation = "http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config/> <context:component-scan base-package="com"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
3 新建切面类
package com.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; 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 @Component public class logInterceptor { @Before("execution(public void com.daoImpl.UserDaoImpl.*(..))") public void beforMethod(){ System.out.println("aspectJ before method"); } @AfterReturning("execution(public void com.daoImpl.UserDaoImpl.*(..))") public void afterMethod(){ System.out.println("aspectJ after method"); } }
@Aspect在类名上方注解表示实现Aspect的类
@Before表示方法运行前
@AfterReturning表示在方法执行之后
他们的语法建议记住 execution即可
execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?) 除了返回类型模式、方法名模式和参数模式外,其它项都是可选的,可使用通配符
例如
execution( public void com.daoImpl.UserDaoImpl.* (com.entity.User) )//这里没有异常
(<修饰符模式>? <返回类型模式> <方法名模式> (<参数模式>) <异常模式>?)
具体详见传送门http://dylanxu.iteye.com/blog/1312454
测试类
package com.serviceImpl.test; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.entity.User; import com.serviceImpl.UserServiceImpl; public class UserServiceImplTest { User user; @Before public void setUp() throws Exception { user = new User(); user.setName("testName"); user.setRemark("testRemark"); } @Test public void testAdd() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml"); UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl"); UserServiceImpl.add(user);//调用方法 UserServiceImpl.update(user);//调用方法 } }
执行结果
aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method
通过@Aspect,@Before,@AfterReturning 成功的在方法的前后加入了切面逻辑
介绍另外几个常用的annotation
@Pointcut
@Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
Pointcut提供了一个切面的切入点切必须写在一个空方法上面
public class logInterceptor { @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))") public void myAop(){}; @Before("myAop()") public void beforMethod(){ System.out.println("aspectJ before method"); } @AfterReturning("myAop()") public void afterMethod(){ System.out.println("aspectJ after method"); } }
@Before("myAop()") 效果等于 @Before("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
执行效果与之前的执行效果一致
@Around
@Around("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
Around标签,可循环执行方法 注意---> pjp.proceed();重复了三次
package com.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; 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 @Component public class logInterceptor { @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))") public void myAop(){};
@Around("myAop()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("aspectJ before method"); pjp.proceed(); pjp.proceed(); pjp.proceed(); System.out.println("aspectJ after method"); } }
测试类
package com.serviceImpl.test; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.entity.User; import com.serviceImpl.UserServiceImpl; public class UserServiceImplTest { User user; @Before public void setUp() throws Exception { user = new User(); user.setName("testName"); user.setRemark("testRemark"); } @Test public void testAdd() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml"); UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl"); UserServiceImpl.add(user);//调用方法 UserServiceImpl.update(user);//调用方法 } }
执行结果
aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
testName-->testRemark save --调用UserDaoImpl!
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
testName-->testRemark update --调用UserDaoImpl!
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method
pjp.proceed();重复了三次
从结果中得到方法也确实执行了三次
通过around可以有效控制方法在这个切面里的执行次数,甚至不执行
annotation介绍到这里
通过上述资料可以掌握 @Aspect @Pointcut @Before @AfterReturning @Around 以及 execution语法
3 Spring的AspectJ实现AOP (XML)
XML配置
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config/> <context:component-scan base-package="com"></context:component-scan> <aop:config> <aop:aspect id="logInterceptor" ref="logInterceptor"> <aop:pointcut expression="execution(public void com.daoImpl.UserDaoImpl.*(..))" id="myAop" /> <aop:before pointcut-ref="myAop" method="beforMethod" /><aop:after pointcut-ref="myAop" method="afterMethod" /> </aop:aspect> </aop:config> </beans>
由于我使用的是annotation的component组件bean配置
定义<aop:config>配置
aspect的ref表示Spring管理的bean,id应该是随意的
pointcut +表达式 + id(方法名)
before/after在与method方法之间需要+表达式或者是pointcut的id来明确切入点
切面类
package com.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; 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; @Component public class logInterceptor { public void myAop(){}; public void beforMethod(){ System.out.println("aspectJ before method"); } public void afterMethod(){ System.out.println("aspectJ after method"); } // @Around("myAop()") // public void around(ProceedingJoinPoint pjp) throws Throwable{ // System.out.println("aspectJ before method"); // pjp.proceed(); // System.out.println(pjp.getStaticPart()); // System.out.println("aspectJ after method"); // } }
测试类
package com.serviceImpl.test; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.entity.User; import com.serviceImpl.UserServiceImpl; public class UserServiceImplTest { User user; @Before public void setUp() throws Exception { user = new User(); user.setName("testName"); user.setRemark("testRemark"); } @Test public void testAdd() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml"); UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl"); UserServiceImpl.add(user);//调用方法 UserServiceImpl.update(user);//调用方法 } }
执行结果与annotation的一致
aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method
around的XML配置
<aop:aspect id="logInterceptor" ref="logInterceptor"> <aop:pointcut expression="execution(public void com.daoImpl.UserDaoImpl.*(..))" id="myAop" /> <aop:before pointcut-ref="myAop" method="beforMethod" /><aop:after pointcut-ref="myAop" method="afterMethod" /> <aop:around pointcut-ref="myAop" method="around"/> </aop:aspect>
切面类
package com.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; 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; @Component public class logInterceptor { public void myAop(){}; public void beforMethod(){ System.out.println("aspectJ before method"); } public void afterMethod(){ System.out.println("aspectJ after method"); } public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("aspectJ before method"); pjp.proceed(); System.out.println(pjp.getStaticPart()); System.out.println("aspectJ after method"); } }
执行结果
aspectJ before method
aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
execution(void com.dao.UserDao.save(User))
aspectJ after method
aspectJ before method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method
execution(void com.dao.UserDao.update(User))
aspectJ after method
个人建议使用XML来完成AOP的切面配置,这样代码的可读性会比较强,而且配置较为灵活。
另外如果非实现接口的类需要做切面处理的话,需要引入JAR包,这里没继续研究。
通过上述资料可以掌握 @Aspect @Pointcut @Before @AfterReturning @Around注解和XML配置 以及 execution语法