Spring AOP 分析(一)
一、前提
- 本文基于spring-aop包5.3.20版本作为前提。
- spring-aop是一种编程思想,aop是指使用切面方式进行编程,简单来说就是在一个方法执行前和执行后做处理。
二、基础知识
代理方式
-
静态代理
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
-
动态代理
静态代理的代理类需要实现与目标类一样的接口。而动态代理不需要。只需要实现InvocationHandler类; 同时分为两种代理方式: jdk动态代理(Proxy类和InvocationHandler接口来实现动态代理) cglib动态代理(继承MethodInterceptor类使用Enhancer类创建代理对象)
spring-aop中基础术语
- Aspect:切面。比如被@Aspect注解的类就是切面,理解为记录关系的类。
- Join point:连接点。可以理解为程序里面的一个方法的执行。
- Advice:通知。在指定的连接点执行的动作时机,理解为一个方法;同时Advice分为不同的类型(before、after、around),Spring框架中会用Interceptor拦截器来实现Advice,每个连接点对应一个Interceptor链。
- Pointcut:切点。用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上。
- Weaving:织入。创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP。
- Target object:目标对象,被代理的对象。
- advistor:顾问。包含一个Pointcut和一个advice,而aspect可以多个pointcut和多个advice。理解为把advice和Pointcut封装成一个advistor。
advice在spring-aop中对应的类
- @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice。
- @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice。
- @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor。
- @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor。
- @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor
关系图
左边类实现右边的类:
AspectJAfterAdvice->MethodInterceptor, AfterAdvice
AspectJAfterThrowingAdvice->MethodInterceptor, AfterAdvice
AspectJAroundAdvice->MethodInterceptor
AspectJMethodBeforeAdvice->MethodBeforeAdvice->BeforeAdvice
AspectJAfterReturningAdvice->AfterReturningAdvice->AfterAdvice
前面3个实现的原理就是依赖MethodInterceptor,后面两个分别依赖MethodBeforeAdviceInterceptor和AfterReturningAdviceInterceptor,但是这两个还是实现了MethodInterceptor。因此它们会组成一条Interceptor链路。
三、代码例子
静态代理
/**
* @description: 共同业务接口
* @author: gongjuncheng
**/
public interface ICommonService {
void add(String name);
}
/**
* @description: 被代理类,实现同一接口
* @author: gongjuncheng
**/
@Service
public class TargetServiceImpl implements ICommonService {
@Override
public void add(String name) {
System.out.println("向数据库中插入名为: "+name+" 的用户");
}
}
/**
* @description: 代理类,并且实现同一接口
* @author: gongjuncheng
**/
public class ProxyServiceImpl implements ICommonService {
/**
* 被代理对象(目标对象)
*/
private ICommonService target;
/**
* 通过构造方法注入被代理对象
* @param object
*/
public ProxyServiceImpl(ICommonService object){
this.target = object;
}
@Override
public void add(String name){
System.out.println("准备向数据库中插入数据");
target.add(name);
System.out.println("插入数据库成功");
}
}
/**
* @description: 测试静态代理
* @author: gong juncheng
*/
public class ProxyTestStatic {
/**
* 代理模式---静态代理
*/
public static void main(String[] args) {
// 实例化需要被代理的类
ICommonService target = new TargetServiceImpl();
// 通过代理类对目标类进行代理,其实就是额外对目标类的某个方法进行特殊处理,添加点东西而已
ProxyServiceImpl proxy = new ProxyServiceImpl(target);
// 调用代理类的方法,该方法就是目标类的方法。
proxy.add("123");
}
}
动态代理
jdk动态代理
/**
* @description: 实现接口动态代理-接口
* @author: gong juncheng
*/
public interface Subject {
void add(String name);
void say(String msg);
}
/**
* @description: 实现接口动态代理-实现类
* @author: gong juncheng
*/
@Service
public class SubjectImple implements Subject {
@Override
public void add(String name) {
System.out.println("向数据库中插入名为: "+name+" 的用户");
}
@Override
public void say(String msg) {
System.out.println("hello world,"+msg);
}
}
/**
* @description: 实现接口动态代理-代理工具类
* @author: gong juncheng
*/
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 被代理的目标类
*/
private Object target;
public ProxyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目标对象的方法执行之前简单的打印一下
System.out.println("------------------before------------------");
// 执行目标对象的方法
Object result = method.invoke(target, args);
// 在目标对象的方法执行之后简单的打印一下
System.out.println("-------------------after------------------");
return result;
}
/**
* 获取被代理的目标类的代理对象
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.target.getClass().getClassLoader()
,this.target.getClass().getInterfaces()
,this);
}
}
/**
* @description: 实现接口动态代理-测试启动类
* @author: gong juncheng
*/
public class ProxyTestDynamicInterface {
/**
* 代理模式---JDK动态代理
*/
public static void main(String[] args) throws IOException {
// 使用传统的代理类实现InvocationHandler接口,再通过Proxy类的静态方法创建代理类
// 1.实例化等待被代理的实现类
Subject subject = new SubjectImple();
// 2.实例化InvocationHandler 代理处理器
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(subject);
// 3.根据被代理实现类生成代理对象,这个getProxy方法可以不写在ProxyInvocationHandler里面,在这里写,ProxyInvocationHandler就可以给多个需要代理的方法共用。
Subject proxySubject = (Subject) proxyInvocationHandler.getProxy();
System.out.println("代理类路径:"+proxySubject.getClass());
proxySubject.add("熊大");
proxySubject.say("my home");
}
}
cglib动态代理
/**
* 被代理类
* @author gongjuncheng
*/
public class SourceClass {
void go() {
System.out.println("进击的巨人....");
}
void sleep() {
System.out.println("全世界阔以安息了...");
}
}
/**
* cglib动态代理的执行类
* @author gongjuncheng
*/
public class CglibProxyHandle{
/**
* 被代理类
*/
private Object target;
/**
* cglib的拦截器
*/
private MethodInterceptor methodInterceptor;
public CglibProxyHandle(Object target, MethodInterceptor methodInterceptor) {
this.target = target;
this.methodInterceptor = methodInterceptor;
}
/**
* 获取被代理的目标类的代理对象
*/
public Object getProxy(){
Enhancer enhancer = new Enhancer();
// 设置代理类的父类
enhancer.setSuperclass(target.getClass());
// 设置代理逻辑
enhancer.setCallback(methodInterceptor);
// 创建代理对象
return enhancer.create();
}
}
/**
* cglib的拦截器,真正处理被代理类的上下文操作
* @author gongjuncheng
*/
public class CglibProxyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("go")){
System.out.println("艾伦耶格尔开始出现");
methodProxy.invokeSuper(o,objects);
System.out.println("大家快跑啊!!!!");
return null;
}
return methodProxy.invokeSuper(o, objects);
}
}
/**
* @author gongjuncheng
*/
public class Test {
public static void main(String[] args) {
CglibProxyHandle proxyHandle = new CglibProxyHandle(new SourceClass(),new CglibProxyMethodInterceptor());
SourceClass sourceClass = (SourceClass) proxyHandle.getProxy();
System.out.println("生成的代理类的路径:"+sourceClass.getClass());
sourceClass.go();
sourceClass.sleep();
}
}
四、总结
本文主要介绍了AOP的一些基本知识,动态代理中jdk代理和cglib代理的不同,以及代码的实现。
下文会讲会涉及部分源码知识点,敬请期待~~~