AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。
先以静态代理实现,静态代理关键是在代理对象和目标对象实现共同的接口,并且代理对象持有目标对象的引用。
package com.soecode.lyf.service;
public interface ISayHello {
void sayHello();
}
package com.soecode.lyf.service.impl;
import com.soecode.lyf.service.ISayHello;
public class ISayHelloImpl implements ISayHello {
@Override
public void sayHello() {
System.out.println("hello spring AOP");
}
}
静态代理类代码:
package com.soecode.lyf.web;
import com.soecode.lyf.service.ISayHello;
import com.soecode.lyf.service.impl.ISayHelloImpl;
public class ProxySayHello implements ISayHello{
private ISayHelloImpl impl;
public ProxySayHello(ISayHelloImpl im){
super();
this.impl=im;
}
@Override
public void sayHello() {
System.out.println("静态代理前");
impl.sayHello();
System.out.println("静态代理后");
}
public static void main(String[] args) {
ProxySayHello p = new ProxySayHello(new ISayHelloImpl());
p.sayHello();
}
}
日志打印:
静态代理前
hello spring AOP
静态代理后
以动态代理实现:
package com.soecode.lyf.web;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.soecode.lyf.service.ISayHello;
import com.soecode.lyf.service.impl.ISayHelloImpl;
public class DynaProxySayHello implements InvocationHandler{
private Object proxy;
public Object setProxy(Object object){
this.proxy=object;
return Proxy.newProxyInstance(this.proxy.getClass().getClassLoader(), proxy.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Class clazz = this.proxy.getClass();
//有参数用new Class[]{Method.class}
Method sayHello = clazz.getDeclaredMethod("sayHello", null);
System.out.println("动态代理就是用反射");
method.invoke(this.proxy, args);
System.out.println("代理调用结束");
return null;
}
public static void main(String[] args) {
ISayHello sayHello = (ISayHello)new DynaProxySayHello().setProxy(new ISayHelloImpl());
sayHello.sayHello();
}
}
动态代理就是用反射
hello spring AOP
代理调用结束
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
maven找引入的jar包http://mvnrepository.com/,这个比较方便。
Joinpoint(连接点):程序执行时的某个特定的点,在Spring中就是某一个方法的执行 。
Pointcut(切点):说的通俗点,spring中AOP的切点就是指一些方法的集合,而这些方法是需要被增强、被代理的。一般都是按照一定的约定规则来表示的,如正则表达式等。切点是由一类连接点组成。
Advice(通知):还是说的通俗点,就是在指定切点上要干些什么。
Advisor(通知器):其实就是切点和通知的结合 。
一、基于spring XML配置的实现
ISayHelloImpl的代码:
package com.soecode.lyf.service.impl;
import com.soecode.lyf.service.ISayHello;
public class ISayHelloImpl implements ISayHello {
@Override
public void sayHello() {
System.out.println("hello spring AOP");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//int[] a = {1};
//int r = a[2];
}
}
SayhelloInterceptor的代码:
package com.soecode.lyf.web;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class SayhelloInterceptor {
/**
* 前置通知:目标方法调用之前执行的代码
* @param jp
*/
public void doBefore(JoinPoint jp){
System.out.println("===========执行前置通知============"+System.currentTimeMillis());
}
/**
* 后置返回通知:目标方法正常结束后执行的代码
* 返回通知是可以访问到目标方法的返回值的
* @param jp
* @param result
*/
public void doAfterReturning(JoinPoint jp,String result){
System.out.println("===========执行后置通知============");
System.out.println("返回值result==================="+result);
}
/**
* 最终通知:目标方法调用之后执行的代码(无论目标方法是否出现异常均执行)
* 因为方法可能会出现异常,所以不能返回方法的返回值
* @param jp
*/
public void doAfter(JoinPoint jp){
System.out.println("===========执行最终通知============"+System.currentTimeMillis());
}
/**
*
* 异常通知:目标方法抛出异常时执行的代码
* 可以访问到异常对象
* @param jp
* @param ex
*/
public void doAfterThrowing(JoinPoint jp,Exception ex){
System.out.println("===========执行异常通知============");
}
/**
* 环绕通知:目标方法调用前后执行的代码,可以在方法调用前后完成自定义的行为。
* 包围一个连接点(join point)的通知。它会在切入点方法执行前执行同时方法结束也会执行对应的部分。
* 主要是调用proceed()方法来执行切入点方法,来作为环绕通知前后方法的分水岭。
*
* 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
* 而且环绕通知必须有返回值,返回值即为目标方法的返回值
* @param pjp
* @return
* @throws Throwable
*/
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("======执行环绕通知开始========="+System.currentTimeMillis());
// 调用方法的参数
Object[] args = pjp.getArgs();
// 调用的方法名
String method = pjp.getSignature().getName();
// 获取目标对象
Object target = pjp.getTarget();
// 执行完方法的返回值
// 调用proceed()方法,就会触发切入点方法执行
Object result=pjp.proceed();
System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
System.out.println("======执行环绕通知结束========="+System.currentTimeMillis());
return result;
}
}
spring-aop.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: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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- /myProject/src/main/java/com/soecode/lyf/service/impl/ISayHelloImpl.java -->
<bean id="iSayHelloImpl" class="com.soecode.lyf.service.impl.ISayHelloImpl" >
<!-- <property name=""></property> -->
</bean>
<bean id="sayhelloInterceptor" class="com.soecode.lyf.web.SayhelloInterceptor" ></bean>
<aop:config>
<aop:aspect ref="sayhelloInterceptor">
<!-- Pointcut(切点):spring中AOP的切点就是指一些方法的集合,而这些方法是需要被增强、被代理的。一般都是按照一定的约定规则来表示的,如正则表达式等。切点是由一类连接点组成。 -->
<aop:pointcut expression="execution(* com.soecode.lyf.service.impl.ISayHelloImpl..*(..))" id="pointcut"/>
<!-- 前置通知 -->
<aop:before method="doBefore" pointcut-ref="pointcut" />
<!-- 后置返回通知 -->
<aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result" />
<!-- 最终通知 -->
<aop:after method="doAfter" pointcut-ref="pointcut"/>
<!-- 环绕通知 -->
<aop:around method="doAround" pointcut-ref="pointcut"/>
<!-- 异常通知 -->
<aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex" />
</aop:aspect>
</aop:config>
</beans>
AOPTest的代码如下:
package com.soecode.lyf.web;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.soecode.lyf.service.ISayHello;
public class AOPTest {
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("spring/spring-aop.xml");
ISayHello iSayHello = (ISayHello)act.getBean("iSayHelloImpl");
iSayHello.sayHello();
}
}
打印的日志1:
[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d2f40eb: startup date [Wed Jan 31 11:00:52 CST 2018]; root of context hierarchy
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring/spring-aop.xml]
===========执行前置通知============1517367652653
======执行环绕通知开始=========1517367652653
hello spring AOP
输出,方法名:sayHello;目标对象:com.soecode.lyf.service.impl.ISayHelloImpl@196b16f2;返回值:null
======执行环绕通知结束=========1517367652762
===========执行最终通知============1517367652762
打印的日志2:
[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@694f23ae: startup date [Wed Jan 31 11:07:16 CST 2018]; root of context hierarchy
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring/spring-aop.xml]
===========执行前置通知============1517368036735
======执行环绕通知开始=========1517368036735
hello spring AOP
===========执行异常通知============
===========执行最终通知============1517368036844
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at com.soecode.lyf.service.impl.ISayHelloImpl.sayHello(ISayHelloImpl.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at com.soecode.lyf.web.SayhelloInterceptor.doAround(SayhelloInterceptor.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy2.sayHello(Unknown Source)
at com.soecode.lyf.web.AOPTest.main(AOPTest.java:12)
二、基于注解的实现
注解类AopAspectJ的代码:
package com.soecode.lyf.web;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
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;
@Aspect
public class AopAspectJ {
public static final String EDP="execution(* com.soecode.lyf.service.impl.ISayHelloImpl..*(..))";
/**
* 切面的前置方法 即方法执行前拦截到的方法
* 在目标方法执行之前的通知
* @param jp
*/
@Before(EDP)
public void doBefore(JoinPoint jp){
System.out.println("=========执行前置通知==========");
}
/**
* 在方法正常执行通过之后执行的通知叫做返回通知
* 可以返回到方法的返回值 在注解后加入returning
* @param jp
* @param result
*/
@AfterReturning(value=EDP,returning="result")
public void doAfterReturning(JoinPoint jp,String result){
System.out.println("===========执行后置通知============");
}
/**
* 最终通知:目标方法调用之后执行的通知(无论目标方法是否出现异常均执行)
* @param jp
*/
@After(value=EDP)
public void doAfter(JoinPoint jp){
System.out.println("===========执行最终通知============");
}
/**
* 环绕通知:目标方法调用前后执行的通知,可以在方法调用前后完成自定义的行为。
* @param pjp
* @return
* @throws Throwable
*/
@Around(EDP)
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("======执行环绕通知开始=========");
// 调用方法的参数
Object[] args = pjp.getArgs();
// 调用的方法名
String method = pjp.getSignature().getName();
// 获取目标对象
Object target = pjp.getTarget();
// 执行完方法的返回值
// 调用proceed()方法,就会触发切入点方法执行
Object result=pjp.proceed();
System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
System.out.println("======执行环绕通知结束=========");
return result;
}
/**
* 在目标方法非正常执行完成, 抛出异常的时候会走此方法
* @param jp
* @param ex
*/
@AfterThrowing(value=EDP,throwing="ex")
public void doAfterThrowing(JoinPoint jp,Exception ex) {
System.out.println("===========执行异常通知============");
}
}
spring-asp-aop.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: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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 声明spring对@AspectJ的支持 -->
<aop:aspectj-autoproxy/>
<!-- 声明一个业务类 -->
<bean id="iSayHelloImpl" class="com.soecode.lyf.service.impl.ISayHelloImpl" ></bean>
<!-- 声明通知类 -->
<bean id="aspectBean" class="com.soecode.lyf.web.AopAspectJ" />
</beans>
测试类的代码:
package com.soecode.lyf.web;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.soecode.lyf.service.ISayHello;
public class AOPTest {
public static void main(String[] args) {
//ApplicationContext act = new ClassPathXmlApplicationContext("spring/spring-aop.xml");
ApplicationContext act = new ClassPathXmlApplicationContext("spring-asp-aop.xml");
ISayHello iSayHello = (ISayHello)act.getBean("iSayHelloImpl");
iSayHello.sayHello();
}
}
打印的日志1:
[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@694f23ae: startup date [Wed Jan 31 11:24:33 CST 2018]; root of context hierarchy
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-asp-aop.xml]
======执行环绕通知开始=========
=========执行前置通知==========
hello spring AOP
===========执行最终通知============
===========执行异常通知============
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at com.soecode.lyf.service.impl.ISayHelloImpl.sayHello(ISayHelloImpl.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at com.soecode.lyf.web.AopAspectJ.doAround(AopAspectJ.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy10.sayHello(Unknown Source)
at com.soecode.lyf.web.AOPTest.main(AOPTest.java:13)
打印的日志2:
[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d2f40eb: startup date [Wed Jan 31 11:28:43 CST 2018]; root of context hierarchy
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-asp-aop.xml]
======执行环绕通知开始=========
=========执行前置通知==========
hello spring AOP
输出,方法名:sayHello;目标对象:com.soecode.lyf.service.impl.ISayHelloImpl@1bd141bb;返回值:null
======执行环绕通知结束=========
===========执行最终通知============