SpringAOP____配置之向代理类注入Advice
来源:互联网 发布:大数据 零售业思路 编辑:程序博客网 时间:2024/06/14 04:27
何为AOP:
AOP是Aspect-Oriented Programming(面向切面编程)的简写。AOP是对OOP模式的一种补充和扩展,SpringAOP是AOP模式的一个框架。通过织入的方法将逻辑织入方法内,又不破坏代码的封装性。降低了业务逻辑之间的耦合度。
OOP术语:
方面(Aspect):相当于OOP中的类,就是封装用于横插入系统的功能。例如日志、事务、安全验证等。
通知(Advice):相当于OOP中的方法,是编写实际功能代码的地方。
连接点(Joinpoint):程序执行过程中插入方面的地方。Spring AOP只支持在方法调用和异常抛出中插入方面代码。
切入点(Pointcut):定义通知应该织人到哪些连接点上。通常切入点指的是类或方法名。例如某个通知要应用所有以abc开头的方法中,那么所有满足这个规则的方法都是切入点。
目标(Target):目标类或者目标接口。
代理(Proxy):AOP工作时是通过代理对象来访问目标对象。其实AOP的实现是通过动态代理,离不开代理模式,所以必须要有一个代理对象。
织入(Weaving):在目标对象中插入方面代码的过程就叫做织入。
xml配置AOP:
aop实现的原理其实就是利用了jdk的动态反射机制对实现了接口的类或者接口(类必须实现接口),产生一个代理类,代理类中实现了接口中的所有方法,并可以在代理类中实现自己的逻辑。
通过一个例子看看如何生成AOP。
业务类:
首先要有个接口,还要有个类实现这个接口:
接口:
package com.xy.business.logic.logicInterface;public interface TestLogicInterface {public void add(String name);public void delete(String name);public boolean exist(String name);}
接口的实现类:
package com.xy.business.logic.logicImpl;import com.xy.business.logic.logicInterface.TestLogicInterface;public class TestLogicImpl implements TestLogicInterface {@Overridepublic void add(String name) {System.out.println("用户"+name+"添加成功");}@Overridepublic void delete(String name) {System.out.println("用户"+name+"删除成功");}@Overridepublic boolean exist(String name) {if("许阳".equals(name)){return true;}throw new RuntimeException("不是许阳不给你运行!");}}
接着,我们需要写几个类用来描述我们的joinPoint:
Advice类:
前置通知(BeforeMethod):
package com.xy.aop;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;/** * @author 前置通知 * @description 方法执行之间执行 * @Todo 必须实现MethodBeforeAdvice接口 */public class BeforeMethod implements MethodBeforeAdvice { /** *@param method 要执行切点的方法 *@param arg 这些方法的参数值 *@param target 切点所在类的实例对象 */ @Override public void before(Method method, Object[] arg1, Object target) throws Throwable { String className = target.getClass().getName(); String methodName = method.getName(); System.out.println("我是执行在"+className+"的"+methodName+"方法(---->前<----)的逻辑!"); }}注意方法必须实现MethodBeforeAdvice接口,重写before方法。配置到xml中,Spring根据类实现的接口才能知道方法中的逻辑该在何时运行(joinPoint)。
后置通知(AfterMethod):
package com.xy.aop;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;/** * @author 后置通知 * @description 执行方法后执行 * @Todo 必须实现AfterReturningAdvice接口 */public class AfterMethod implements AfterReturningAdvice {/** * @param returnValue 被拦截方法的返回值 */@Overridepublic void afterReturning(Object returnValue, Method method,Object[] args, Object target) throws Throwable {String className = target.getClass().getName();String methodName = method.getName();System.out.println("我是执行在"+className+"的"+methodName+"方法(---->后<----)的逻辑");}}
环绕通知(AroundMethod):
package com.xy.aop;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;/** * @author 环绕通知 * @description 环绕通知能力最强,可以在方法调用前执行通知代码,可以决定是否还调用目标方法。也就是说它可以控制被拦截的方法的执行,还可以控制被拦截方法的返回值。 * @Todo 必须实现MthodInterceptor接口 */public class AroundMethod implements MethodInterceptor {@Override/** * @param invocation 代理类 */public Object invoke(MethodInvocation invocation) throws Throwable {String metodName = invocation.getMethod().getName();System.out.println("环绕通知准备记录时间");long startTime = System.currentTimeMillis();invocation.proceed();//调用被拦截的方法long endTime = System.currentTimeMillis();System.out.println("环绕通知: 执行"+metodName+"方法花费时间"+(endTime-startTime));//将拦截方法的返回值设置为null//return invocation.proceed();return null;}}
异常通知(throwMethod):
package com.xy.aop;import java.lang.reflect.Method;import org.springframework.aop.ThrowsAdvice;/** * * @author 方法报错通知 * @description 在方法报错之后切入的方法 * @Todo 必须实现ThrowsAdvice接口 */public class ThrowMthod implements ThrowsAdvice{/* * e表示方法抛出的异常类型,只根据异常类型切入方法public void afterThrowing(Throwable e){} */ //如果跟上面的方法同时存在,异常都能匹配上时,优先执行上面的逻辑。 public void afterThrowing(Method method, Object args, Object target, Throwable subclass) { String className = target.getClass().getName(); String methodName = method.getName(); System.out.println("异常通知:"+className+"的方法"+methodName+"出现异常,异常名称:"+subclass.getClass().getSimpleName()); } }
配置文件applicationContext.xml:
最后我们需要修改配置文件,将我们的业务层生成一个代理对象,然后将Advice注入到代理对象里面。
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><bean id="testLogic" class="com.xy.business.logic.logicImpl.TestLogicImpl"/> <!-- 定义前置通知,此处id的值可以更改,只要和注入的值对应上就行--> <bean id="beforeLogAdvice" class="com.xy.aop.BeforeMethod"></bean> <!-- 定义后置通知 --> <bean id="afterLogAdvice" class="com.xy.aop.AfterMethod"></bean> <!-- 定义异常通知 --> <bean id="throwsLogAdvice" class="com.xy.aop.ThrowMthod"></bean> <!-- 定义环绕通知 --> <bean id="logAroundAdvice" class="com.xy.aop.AroundMethod"></bean> <!-- 定义代理类,让代理类代理testLogic,我们访问代理类来执行业务中的方法 --> <bean id="proxyTest" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理的接口(jdk动态代理需要代理接口,cglib不用接口) --> <property name="proxyInterfaces"> <value>com.xy.busniess.logic.logicInterface.TestLogicInterface</value> </property> <property name="interceptorNames"> <list> <value>beforeLogAdvice</value> <value>afterLogAdvice</value> <value>throwsLogAdvice</value> <value>logAroundAdvice</value> </list> </property> <property name="target" ref="testLogic"></property> </bean> </beans>
此处可以看到,当生成一个代理对象的时候需要三个参数,一个是proxyInterfaces,值是我们需要代理的业务接口。第二个参数是interceptorNames,值是我们实现了不同Advice接口的Advice类。第三个参数target就是我们的业务的具体实现了。
测试类:
package com.xy.web;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.xy.business.logic.logicInterface.TestLogicInterface;public class mainTest {public static void main(String[] args) {//从applicationContext中拿到代理类TestLogicInterface testLogicInterface = (TestLogicInterface)new ClassPathXmlApplicationContext("applicationContext.xml").getBean("proxyTest");//代理类中实现了被代理类的所有方法,同被代理类一样被使用testLogicInterface.add("许阳");System.out.println("---------------------华丽的分割线---------------------");//执行exist方法,故意让方法报错testLogicInterface.exist("我不是许阳");}}
打印结果:
我是执行在com.xy.business.logic.logicImpl.TestLogicImpl的add方法(---->前<----)的逻辑!
环绕通知准备记录时间
用户许阳添加成功
环绕通知: 执行add方法花费时间0
我是执行在com.xy.business.logic.logicImpl.TestLogicImpl的add方法(---->后<----)的逻辑
---------------------华丽的分割线---------------------
我是执行在com.xy.business.logic.logicImpl.TestLogicImpl的exist方法(---->前<----)的逻辑!
环绕通知准备记录时间
Exception in thread "main" 异常通知:com.xy.business.logic.logicImpl.TestLogicImpl的方法exist出现异常,异常名称:RuntimeException
java.lang.RuntimeException: 不是许阳不给你运行!
at com.xy.business.logic.logicImpl.TestLogicImpl.exist(TestLogicImpl.java:22)
通过打印结果可以知道,如果在一个代理类中同时加入四种Advice,最先执行的是beforeLogAdvice,接着执行logAroundAdvice,然后执行afterLogAdvice,当出现异常后,后续所有的方法以及Advice都将被强制中断。
- SpringAOP____配置之向代理类注入Advice
- Spring--AOP--基本配置(Intruductions-- Advice 动态代理)
- 代理类的具体例子(1接口【Advice】)
- spring 通过配置向quartz 注入service
- spring 通过配置向quartz 注入service
- spring 通过配置向quartz 注入service
- AspectJ之Advice定义之Before Advice
- AspectJ之Advice定义之finally advice
- AspectJ之Advice定义之Around advice
- Spring进阶之路(10)-Advice简介以及通过cglib生成AOP代理对象
- Jboss AOP之Advice
- Spring AOP之Advice
- Spring之Advice小结
- AspectJ之 Advice扩展
- spring配置-mybatis的mapper接口动态代理生成实现类并注入
- spring tx:advice事务配置
- Spring <tx:advice>事务配置
- spring tx:advice事务配置
- Unable to handle 'index' format version '2', please update rosdistro的解决办法
- Eclipse中Build Workspace 优化
- 文件与I/O(-)
- 近一周时间,林书豪其实已经开始走出低迷,找回状态
- CI框架(十)codeigniter框架开发技巧
- SpringAOP____配置之向代理类注入Advice
- Sockets/Windows Sockets错误码
- android单元测试用例 注意点
- 大话数据结构(四) 栈和队列
- 读书笔记之21天学通Linux c编程(5天)
- Android中资源文件夹res/raw和assets的使用
- Hadoop发行版的比较与选择
- ecshop2.7.3 在php5.4下的各种错误问题处理
- jquery 常用函数