说到AOP,面试高频考点。说白了,就是在正常业务流程中织入一些流程化的代码。我们可能最先想到的就是两个东西,一个是拦截器,一个是代理类。
pom添加:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
拦截器代码:
package interceptor;
import java.lang.reflect.InvocationTargetException;
/**
* 拦截器接口
* @author huql
* @date 2021/4/14 23:06
* @comments:
*/
public interface Interceptor {
//事前方法
public boolean before();
//事后方法
public void after();
/**
* 取代原有事件方法
* @param invocation
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
//事后返回方法。事件没有发生异常执行
public void afterReturning();
//事后异常方法,当事件发生异常后执行
public void afterThrowing();
//是否使用around方法取代原有方法
boolean useAround();
}
package interceptor;
import java.lang.reflect.InvocationTargetException;
/**
* @author huql
* @date 2021/4/14 23:12
* @comments:
*/
public class MyInterceptor implements Interceptor {
@Override
public boolean before() {
System.out.println("before ...");
return true;
}
@Override
public void after() {
System.out.println("after ...");
}
@Override
public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
System.out.println("around before ...");
Object object = invocation.proceed();
System.out.println("around after ...");
return object;
}
@Override
public void afterReturning() {
}
@Override
public void afterThrowing() {
System.out.println("afterRunning ...");
}
@Override
public boolean useAround() {
return true;
}
}
package interceptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 拦截器 around 方法中的参数 通过proceed 反射调用原有的方法
* @author huql
* @date 2021/4/14 22:58
* @comments:
*/
public class Invocation {
private Object[] params;
private Method method;
private Object target;
public Invocation(Object target, Method method, Object[] params){
this.target = target;
this.method = method;
this.params = params;
}
//反射方法
public Object proceed() throws InvocationTargetException, IllegalAccessException{
return method.invoke(target, params);
}
public Object[] getParams() {
return params;
}
public Method getMethod() {
return method;
}
public Object getTarget() {
return target;
}
public void setParams(Object[] params) {
this.params = params;
}
public void setMethod(Method method) {
this.method = method;
}
public void setTarget(Object target) {
this.target = target;
}
}
我理解的拦截器,就是在正常业务流程中,拦截各种东西,比如 before 执行什么,after执行什么,等等。
代理类:就是之前我们可能可以通过直接调用方法来执行,现在需要通过代理类来执行。代理类会执行拦截器相关的方法。具体可见 ProxyBean 的 invoke方法。他其实是通过 this.interceptor.around 去调用 拦截器的around方法。
package interceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 将服务类和拦截方法织入对应流程
* @author huql
* @date 2021/4/14 23:20
* @comments:
*/
public class ProxyBean implements InvocationHandler {
private Object target = null;
private Interceptor interceptor = null;
/**
* 绑定代理对象
* @param target
* @param interceptor
* @return
*/
public static Object getProxyBean(Object target, Interceptor interceptor){
ProxyBean proxyBean= new ProxyBean();
//保存被代理对象
proxyBean.target = target;
//保存拦截器
proxyBean.interceptor = interceptor;
//生成代理对象
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
proxyBean);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//异常标识
boolean exceptionFlag = false;
Invocation invocation = new Invocation(target, method, args);
this.interceptor.before();
Object retObj = null;
try{
if(this.interceptor.useAround()){
retObj = this.interceptor.around(invocation);
} else {
retObj = method.invoke(target, args);
}
}catch (Exception e){
exceptionFlag = true;
}
this.interceptor.after();
if(exceptionFlag){
this.interceptor.afterThrowing();
}else {
this.interceptor.afterReturning();
return retObj;
}
return null;
}
}
正常业务代码:
package interceptor;
/**
* @author huql
* @date 2021/4/14 23:02
* @comments:
*/
public interface HelloService {
public void sayHello(String name);
}
package interceptor;
/**
* @author huql
* @date 2021/4/14 23:03
* @comments:
*/
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
if(name == null || name.trim() == ""){
throw new RuntimeException("parameter is null");
}
System.out.println("hello " + name);
}
}
测试类:
package interceptor;
import java.lang.reflect.Proxy;
/**
* @author huql
* @date 2021/4/14 23:16
* @comments:
*/
public class TestProxy {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
//按约定获取proxy
HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
proxy.sayHello("zhangsan");
System.out.println("\n =======name is null=========");
proxy.sayHello(null);
}
}
输出:
before ...
around before ...
hello zhangsan
around after ...
after ...
=======name is null=========
before ...
around before ...
after ...
afterRunning ...
Disconnected from the target VM, address: 'javadebug', transport: 'shared memory'
代码很简单,中间还涉及到两个java原生类,Proxy 和 InvocationHandler
1、InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用。
2、Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:
- loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
- interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
- InvocationHandler
h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
参考:https://blog.csdn.net/yaomingyang/article/details/80981004
和深入浅出Springboot