文章目录- 一、简介
- 二、名称解释
- 1、切面(Aspect)
- 2、连接点(Joinpoint)
- 3、切点(Pointcut)
- 4、通知(Advice)
- 5、通知器(Advisor)
- 6、代理(Proxy)
- 三、静态代理和动态代理的区别
- 四、静态代理实例
- 五、aop的实现原理(动态代理)
- 1、JDK动态代理
- 控制台输出:
- 2、Cglib动态代理
- 控制台输出:
- 六、两种动态代理方式区别
- 1、切面(Aspect)
- 2、连接点(Joinpoint)
- 3、切点(Pointcut)
- 4、通知(Advice)
- 5、通知器(Advisor)
- 6、代理(Proxy)
- 1、JDK动态代理
- 控制台输出:
- 2、Cglib动态代理
- 控制台输出:
一、简介
AOP是Aspect-Oriented Programming,即面向切面编程。 它是一种新的模块化机制,用来描述分散在对象/类或函数中的横切关注点。分离关注点使解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。
二、名称解释
1、切面(Aspect)
切面由切点和通知组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
2、连接点(Joinpoint)
连接点是在应用执行过程中能够插入切面(Aspect)的一个点。程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。
3、切点(Pointcut)
切点是指通知(Advice)所要织入(Weaving)的具体位置。每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点。所以切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。AOP通过“切点”定位特定的连接点。 具体举个例子:比如开车经过一条高速公路,这条高速公路上有很多个出口(连接点),但是我们不会每个出口都会出去,只会选择我们需要的那个出口(切点)开出去。 简单可以理解为,每个出口都是连接点,但是我们使用的那个出口才是切点。每个应用有多个位置适合织入通知,这些位置都是连接点。但是只有我们选择的那个具体的位置才是切点。
4、通知(Advice)
它定义在连接点做什么,为切面增强提供织入接口。例如,日志记录、权限验证、事务控制、性能检测、错误信息检测等。 Spring切面可以应用5种类型的通知: 前置通知(Before):在目标方法被调用之前调用通知功能; 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么; 返回通知(After-returning):在目标方法成功执行之后调用通知; 异常通知(After-throwing):在目标方法抛出异常后调用通知; 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
5、通知器(Advisor)
完成对目标方法的切面增强设计和关注点的设计以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor。
6、代理(Proxy)
它为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
三、静态代理和动态代理的区别
1、静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。 2、静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。 3、静态代理,在程序运行前,代理类的.class文件就已经存在了;动态代理,在程序运行时,运用反射机制动态创建而成。
四、静态代理实例
1、举一个手机缴话费的例子,TelecomOperator 类是服务类。
代码语言:javascript复制package com.service;
/**
* 定义一个电信运营商接口
*/
public interface TelecomOperator {
//查询话费余额
public void queryPhoneBal();
//缴话费
public void payPhoneBal();
}
2、TelecomOperatorImpl是实现类
代码语言:javascript复制package com.controller;
import com.service.TelecomOperator;
public class TelecomOperatorImpl implements TelecomOperator {
//查询话费余额
@Override
public void queryPhoneBal(){
System.out.println("查话费方法...");
}
//缴话费
@Override
public void payPhoneBal(){
System.out.println("缴话费方法...");
}
}
3、TelecomOperatorProxy是服务代理类
代码语言:javascript复制package com.controller;
import com.service.TelecomOperator;
/**
* 第三方代理商
*
*/
public class TelecomOperatorProxy implements TelecomOperator {
private TelecomOperatorImpl telecomOperator;
public TelecomOperatorProxy(TelecomOperatorImpl telecomOperator) {
this.telecomOperator = telecomOperator;
}
//查询话费余额
@Override
public void queryPhoneBal(){
System.out.println("切点:事务控制/日志输出");
telecomOperator.queryPhoneBal();
System.out.println("切点:事务控制/日志输出");
}
//缴话费
@Override
public void payPhoneBal(){
System.out.println("切点:事务控制/日志输出");
telecomOperator.payPhoneBal();
System.out.println("切点:事务控制/日志输出");
}
}
4、TelecomOperatorTest是测试类
代码语言:javascript复制package com.controller;
public class TelecomOperatorTest {
public static void main(String[] args) {
TelecomOperatorImpl telecomOperator = new TelecomOperatorImpl();
TelecomOperatorProxy proxy = new TelecomOperatorProxy(telecomOperator);
proxy.queryPhoneBal();
proxy.payPhoneBal();
}
}
5、控制台输出
代码语言:javascript复制切点:事务控制/日志输出
查话费方法...
切点:事务控制/日志输出
切点:事务控制/日志输出
缴话费方法...
切点:事务控制/日志输出
五、aop的实现原理(动态代理)
1、JDK动态代理
代码语言:javascript复制package com.controller;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理类
*/
public class TelecomOperatorJDKProxy implements InvocationHandler {
private Object target;
//返回代理对象
public Object newProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* @param obj 目标对象代理类的实例
* @param method 代理实例上调用父类方法的Method实例
* @param args 代入到代理实例上方法参数值的数组
*/
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable{
Object result = null;
System.out.println("切点:事务控制/日志输出");
result=method.invoke(target,args);
System.out.println("切点:事务控制/日志输出");
return result;
}
}
代码语言:javascript复制package com.controller;
import com.service.TelecomOperator;
public class TelecomOperatorJDKTest {
public static void main(String[] args) {
TelecomOperatorJDKProxy proxy = new TelecomOperatorJDKProxy();
TelecomOperator telecomOperator = (TelecomOperator)proxy.newProxy(new TelecomOperatorImpl());
telecomOperator.queryPhoneBal();
}
}
控制台输出:
代码语言:javascript复制切点:事务控制/日志输出
查话费方法...
切点:事务控制/日志输出
2、Cglib动态代理
代码语言:javascript复制package com.controller;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib动态代理类
*/
public class TelecomOperatorCglibProxy implements MethodInterceptor {
private Object target;//代理的目标对象
//创建目标对象的代理对象
public Object newProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();//该类用于生成代理对象
enhancer.setSuperclass(this.target.getClass());//设置父类
enhancer.setCallback(this);//回调方法,设置回调对象为本身
return enhancer.create();//创建代理对象
}
/**
* @param obj 目标对象代理类的实例(增强过)
* @param method 代理实例上调用父类方法的Method实例
* @param args 代入到代理实例上方法参数值的数组
* @param proxy 使用它调用父类的方法
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("切点:事务控制/日志输出");
Object object = proxy.invokeSuper(obj, args);
//Object object = proxy.invoke(target,args);
System.out.println("切点:事务控制/日志输出");
return object;
}
}
代码语言:javascript复制package com.controller;
public class TelecomOperatorCglibTest {
public static void main(String[] args) {
TelecomOperatorCglibProxy proxy = new TelecomOperatorCglibProxy();
TelecomOperatorImpl telecomOperatorimpl = (TelecomOperatorImpl)proxy.newProxy(new TelecomOperatorImpl());
telecomOperatorimpl.queryPhoneBal();
}
}
控制台输出:
代码语言:javascript复制切点:事务控制/日志输出
查话费方法...
切点:事务控制/日志输出
invoke方法调用的对象(target)没有增强过,invokeSuper方法调用的对象(obj)已经是增强了的,所以会再走一遍 MyMethodInterceptor的interceptor方法,如果是个拦截器链条,就会重新在走一次拦截器链。如果使用invoke(obj,args)就会循环调用,造成死循环,并抛异常java.lang.StackOverflowError。
六、两种动态代理方式区别
1、java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而Cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 2、JDK动态代理只能对实现了接口的类生成代理,而不能针对类;Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final 。 3、Cglib一个目标类方法会生成两个代理方法,一个重写目标方法,并实现代理逻辑,还有一个直接调用目标类方法。