------------------------------------------------基于动态代理实现springAOP--------------------------------------------
在没有aop之前,在一个项目中,要是对某几个方法进行日志处理的话就会出现很多重复的代码,导致核心代码块变的很冗长和难以维护,在此现实问题中。像类似于aop思想就可以解决,
动态代理实例:
package com.example.springtest.factoryandproxy.inter;
/**
* ***GOOD LUCK****
*
* @Author : Wukn
* @Date : 2018/6/
*/
public interface MateInter {
public void add(Integer i);
public void delete(String str);
}
package com.example.springtest.factoryandproxy.proxy;
import com.example.springtest.factoryandproxy.inter.MateInter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* ***GOOD LUCK****
*
* @Author : Wukn
* @Date : 2018/9/
*
* 代理对象
*/
public class OperationLoggerProxy {
/**
* 需要被代理的对象
*/
public MateInter target;
public OperationLoggerProxy(MateInter operation) {
this.target = operation;
}
public MateInter getOperationFactory() {
MateInter operation = null;
//代理对象由哪一个类加载器负责
ClassLoader classLoader = target.getClass().getClassLoader();
//代理对象的类型。即其中有哪些方法
Class[] op = new Class[]{MateInter.class};
//当调用代理对象其中的方法时,改执行得1代码
InvocationHandler handler = new InvocationHandler( ) {
/**
*
* @param proxy z正在返回那个代理对象,一般情况下都不使用
* @param method 正在被调用的方法
* @param args 调用方式时,传入的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//前置 日志
System.out.println("Logger"+methodName+ Arrays.asList( args));
//执行方法
Object result = null;
try {
//返回 通知
result = method.invoke(target,args );
} catch (Exception e) {
//异常
e.printStackTrace( );
}
//后置 日志
System.out.println(methodName+result+"");
return result;
}
};
//MateInter 这里返回的方法只能是接口
operation = (MateInter) Proxy.newProxyInstance( classLoader,op,handler );
return operation;
}
}
在实际的运用用其实可以使用动态代理完成日志的打印,但是这样操作比较麻烦,于是就可以使用aop实现、
------------------------------------------------基于AspectJ实现springAOP-------------------------------------------
基于注解方式的AOP实现方式
1:首先导入相关AcpectJ的包
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2:对于普通的spring项目,需要在xml配置文件中开启全局扫描和添加支持AspectJ的扫描
springboot项目中,只需要注解就可以
package com.example.springtest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
//扫描支持AcpectJ
@EnableAspectJAutoProxy
//全局扫描
@ComponentScan(value = "com.example.springtest")
public class SpringtestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringtestApplication.class, args);
}
}
3:定义切面,切面就是一个包含所有需要设置的切点的类,该类里面有需要执行的方法
package com.example.springtest.AspectJDemo.Acpect;
/**
* ***GOOD LUCK****
*
* @Author : Wukn
* @Date : 2018/6/
*/
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**把这个类申明为一个切面
* 需要把该类放到IOC容器中,在申明一个切面
*/
@Aspect
@Component
public class LoggerAcpectJ {
/**
* 申明该方法是一个前置通知,在目标方法执行之前
*/
@Before( "execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))" )
public void beforeMethod(JoinPoint joinPoint) {
//方式名称
String methodName = joinPoint.getSignature().getName();
//参数
List<Object> args = Arrays.asList( joinPoint.getArgs() ) ;
System.out.println(methodName+args);
System.out.println("前置日志。。。。。。。。。");
}
/**
* 后置通知,在目标方法执行后(无论改方法是否出现异常)的操作
* 在后置通知中,还不能访问目标方法执行的结果
*/
@After( "execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))" )
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList( joinPoint.getArgs() );
System.out.println(methodName+args);
System.out.println("后置通知日志。。。。。。。。。。。。。。");
}
/**
* 在方法正常执行后执行的代码,无论该方法是否有异常
* returning ="result" 该方法的返回值
*/
@AfterReturning(value = "execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))",
returning ="result")
public void returnMethod(JoinPoint joinPoint,Object result) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList( joinPoint.getArgs() );
System.out.println(methodName+args+result);
System.out.println("返回通知。。。。。。");
}
/**
* 目标方法出现异常时异常通知,可以访问异常对象
* throwing ="ex" 返回的异常
* Exception ex 这个异常可以指定特定的异常
*
*/
@AfterThrowing(value = "execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))",throwing = "ex")
public void MethodThrow(JoinPoint joinPoint,Exception ex) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList( joinPoint.getArgs() );
System.out.println(methodName+args+ex);
System.out.println("返回通知。。。。。。");
}
/**
* 环绕通知需要携带ProceedingJoinPoint类型的参数,
* 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数,可以决定是否执行目标方法
* 环绕通知必须有返回值,返回值为目标方法的返回值
* @param point
*/
@Around( value = "execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))" )
public Object aroundMethod(ProceedingJoinPoint point) {
Object result = null;
String methodName = point.getSignature().getName();
//执行目标方法
try {
//前置通知
System.out.println("前置通知。。。"+methodName+Arrays.asList( point.getArgs() ));
//目标对象的返回结果
result = point.proceed();
//后置通知
System.out.println("后置通知。。。"+methodName+result);
} catch (Throwable throwable) {
//异常通知
System.out.println("异常通知。。。"+throwable);
}
//后置通知
System.out.println("环绕通知。。。。。。。。。");
return 10086;
}
}
--------------------------------------------------指定切面的优先级--------------------------------------------------------------
* 可以使用@Order(1)指定切面的优先级,值越小,优先级越高
package com.example.springtest.AspectJDemo.Acpect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* ***GOOD LUCK****
*
* @Author : Wukn
* @Date : 2018/9/3
*
* 可以使用@Order(1)指定切面的优先级,值越小,优先级越高
*
*/
@Order(1)
@Aspect
@Component
public class VlidationAspectJ {
/**
* 申明该方法是一个前置通知,在目标方法执行之前
*/
@Before( "execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))" )
public void beforeVliationMethod(JoinPoint joinPoint) {
//方式名称
String methodName = joinPoint.getSignature().getName();
//参数
List<Object> args = Arrays.asList( joinPoint.getArgs() ) ;
System.out.println(methodName+args);
System.out.println("前置参数。。。。。。。。。");
}
}
---------------------------------------------重用切点表达式---------------------------------------------------
package com.example.springtest.AspectJDemo.Acpect;
/**
* ***GOOD LUCK****
*
* @Author : Wukn
* @Date : 2018/6/
*/
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**把这个类申明为一个切面
* 需要把该类放到IOC容器中,在申明一个切面
*/
@Order(2)
@Aspect
@Component
public class LoggerAcpectJ {
/**
* 定义一个方法,用于申明切入点表达式,一般的该方法不需要再填入其他的代码
*
* 使用@Pointcut来申明切入点表达式
* 后面的其他通知直接使用方法名
*/
@Pointcut("execution(* com.example.springtest.AspectJDemo.service.MathInterImpl.add(..))")
public void declareJointPointExpression() {
}
/**
* 申明该方法是一个前置通知,在目标方法执行之前
*/
@Before( "declareJointPointExpression()" )
public void beforeMethod(JoinPoint joinPoint) {
//方式名称
String methodName = joinPoint.getSignature().getName();
//参数
List<Object> args = Arrays.asList( joinPoint.getArgs() ) ;
System.out.println(methodName+args);
System.out.println("前置日志。。。。。。。。。");
}
/**
* 后置通知,在目标方法执行后(无论改方法是否出现异常)的操作
* 在后置通知中,还不能访问目标方法执行的结果
*/
@After( "declareJointPointExpression()" )
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList( joinPoint.getArgs() );
System.out.println(methodName+args);
System.out.println("后置通知日志。。。。。。。。。。。。。。");
}
/**
* 在方法正常执行后执行的代码,无论该方法是否有异常
* returning ="result" 该方法的返回值
*/
@AfterReturning(value = "declareJointPointExpression()",
returning ="result")
public void returnMethod(JoinPoint joinPoint,Object result) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList( joinPoint.getArgs() );
System.out.println(methodName+args+result);
System.out.println("返回通知。。。。。。");
}
/**
* 目标方法出现异常时异常通知,可以访问异常对象
* throwing ="ex" 返回的异常
* Exception ex 这个异常可以指定特定的异常
*
*/
@AfterThrowing(value = "declareJointPointExpression()",throwing = "ex")
public void MethodThrow(JoinPoint joinPoint,Exception ex) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList( joinPoint.getArgs() );
System.out.println(methodName+args+ex);
System.out.println("返回通知。。。。。。");
}
/**
* 环绕通知需要携带ProceedingJoinPoint类型的参数,
* 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数,可以决定是否执行目标方法
* 环绕通知必须有返回值,返回值为目标方法的返回值
* @param point
*/
@Around( value = "declareJointPointExpression()" )
public Object aroundMethod(ProceedingJoinPoint point) {
Object result = null;
String methodName = point.getSignature().getName();
//执行目标方法
try {
//前置通知
System.out.println("前置通知。。。"+methodName+Arrays.asList( point.getArgs() ));
//目标对象的返回结果
result = point.proceed();
//后置通知
System.out.println("后置通知。。。"+methodName+result);
} catch (Throwable throwable) {
//异常通知
System.out.println("异常通知。。。"+throwable);
}
//后置通知
System.out.println("环绕通知。。。。。。。。。");
return 10086;
}
}
-----------------------------------基于配置文件的方法实现spring 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/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:component-scan base-package="com.example.springtest"></context:component-scan>-->
<!--配置目标方法的bean-->
<bean id="m1" class="com.example.springtest.AspectJDemo.service.MathInterImpl"></bean>
<!--配置切面bean1-->
<bean id = "logger1" class="com.example.springtest.AspectJDemo.Acpect.LoggerAcpectJ"></bean>
<!--配置切面bean2-->
<bean id="vali1" class="com.example.springtest.AspectJDemo.Acpect.VlidationAspectJ"></bean>
<!--配置aop-->
<aop:config>
<!--配置切点表达式-->
<aop:pointcut expression="execution(* com.example.springtest.AspectJDemo.service.MateInter.*(..))" id="pointcut"></aop:pointcut>
<!--配置切面及通知1-->
<aop:aspect ref="logger1" order="1">
<!--前置通知-->
<aop:after method="beforeMethod" pointcut-ref="pointcut"></aop:after>
<!--后置通知-->
<aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
<!--返回通知-->
<aop:after-returning method="returnMethod" pointcut-ref="pointcut" returning="result"></aop:after-returning>
</aop:aspect>
<!--配置切面及通知2-->
<aop:aspect ref="vali1" order="2">
<aop:after method="beforeVliationMethod" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>