一:aop即面向切面编程,把除了业务核心的方法抽取出来,进行方法的复用。也就是在业务核心方法代码不改变的情况下对方法进行功能的增强
二:aop的底层是动态代理,也就是通过代理类来对方法进行增强(JDK动态代理)
代理模式的基本思路:被代理类A类与代理类ProxyA,在ProxyA中创建一个与A类中被代理的方法同名的成员方法,并且在这个同名方法中即加入自己要增强的逻辑实现,同时调用A中要被代理的方法,使用时调用该代理类的代理方法即可。根据创建代理类实例的方法的不同,代理就分为静态代理和动态代理,前者时直接手动生成的,这样当需要被代理类很多时,就要创建很多个代理类,后者时通过反射,运行时动态生成的,但是静态代理与动态代理的基本逻辑是一样的。
三:aop的几个概念:
AOP即面向切面编程,切面即是增强类。切入点表达式所在的面叫切面,一个切面有很多个通知,切面上的通知可以被织入到被增强的类,织入到哪里可以通过切入点表达式来指定切点,这些通知则是与业务逻辑无关却又不可缺少的代码。而切点就是被增强类的所有方法,而切入点就是被增强的具体某一个方法,功能增强即对该方法进行补充,通知则是补充的内容(根据通知在方法的位置可以分为前置通知,后置通知等)
SpringAOP的基本操作:(注解式)
1.编写一个切面:一般是一个java普通类(@Aspect)
2.把切面加入Spring容器:@Component
3.编写通知:(java方法)
4.织入通知(切面)到被增强类:使用切点表达式(@Before(execution("表达式")))
也可以在切面使用一个空方法配置切入点表达式(@Pointcut),使用切入点表达式声明织入点,然后直接在通知上调用方法,如@Before("test()")
(@annotation()注解的作用是把通知织入标注了特定注解的所有类,如@Before("@annotation(xxx.xxx.myAnnotation)"))
spring事务管理底层就是aop功能增强,通过声明某方法为事务方法,就会通过反射创建代理类,由代理类去执行事务的相关操作
四:原生的JDK动态代理(生成一个proxy代理类,由代理类调用被代理的方法)
生成一个proxy代理类的参数(Proxy类是java自带的,可以通过该静态类的静态方法生成相应代理类)
(1)控制器的类加载器:被代理类的类加载器
(2)被代理类实现的接口:可以生成一个与代理类相同的方法并实现要代理的功能
(3)控制器:实质是InvocationHandler的实现类,运行时生成的代理类运行时会创建一个与被代理方法同名的方法,该方法的实现逻辑是调用该控制器的invoke方法
注意:InvocationHandler实现类并不是代理类,代理类是通过Proxy类生成的,InvocationHandler实现类只是一个代理类与被代理类的中间层而已,该类的invoke方法就是实质就是代理类的代理方法的实现。动态生成的代理类会把该实现加进自己类中
//实现功能的增强
public Class ProxyA implements InvocationHandler{
@Override
public Object invoke(Object proxy,Method method,Object[] args){
//逻辑增强实现
//执行被代理方法
method.invoke()
}
//在InvocationHandler实现类的构造函数中创建代理类实例,该代理类实例会自动生成一个与被代理方法
//同名的方法,在该方法中实际会调用InvocationHandler实现类中的invoke()方法
public ProxyA(){
return Proxy.newProxyInstance(相应参数)
}
}
//外部调用时,直接通过new ProxyA()就可以创建代理类,但是ProxyA并不是真正的代理类,代理类是通过Proxy.newProxyInstance()生成的!!!
静态代理与动态代理的区别:静态代理会把代理类与被代理类直接绑定,代理类需要持有被代理类的成员变量,即使再增强一个一摸一样的功能时,依然要再创建一个代理类来对另外一个被代理类进行功能增强,也就是所谓的代理一百个类,就要创建一百给代理类;
而动态代理,由于使用了反射技术,代理类中是不需要持有被代理类的对象的(代理类与被代理类中间隔了一个控制器实现类,代理类并不需要持有被代理类,控制器实现类持有就可以了,并且控制器通过method对象就可以拿到被代理的方法),也就是说,无论被代理方法如何变动,改动控制器就可以了,是不需要改动代理类的。aop实质就是动态代理的一个经典实现,可以把要增强的逻辑和业务完全分开来,运行时,直接织入通知即可。
另外:原生jdk的动态代理代理的必须是基于接口的,mybatis的sqlsession.getMapper(xxxMapper.class)返回的也是一个代理类,传入一个接口参数来指定接口即可,其他参数已经被封装好了