什么是AOP?
以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建
PointCut(切入点)
切入点:即确定添加额外功能的具体位置。
我们一般通过切入点函数来实现对切入点的表示 ,切入点函数分为四种 :
* execution:最强大的切入点函数 可以表示 :⽅法切⼊点表达式、 类切⼊点表达式 、包切⼊点表达
常见用法 execution(* com.aptech.jb.epet.dao.hibimpl.*.*(..))
com.aptech.jb.epet.dao.hibimpl 包下所有的类的所有方法。。
第一个*代表所有的返回值类型
第二个*代表所有的类
第三个*代表类所有方法 最后一个..代表所有的参数。
(大致了解即可 execution啥都能做)
* args : 主要⽤于函数(⽅法) 参数的匹配
* within : 主要⽤于进⾏类、包切⼊点表达式的匹配
* @annotation: 为具有特殊注解的⽅法加⼊额外功能
Advice (通知)
常见的通知接口:
- MethodBeforeAdvice 前置通知
- AfterReturningAdvice 后置通知
- ThrowsAdvice 异常通知
- Methodinterceptor 环绕通知
如何理解切面 ?
切面=切入点+额外功能
即 切面 =pointcut+ advice
Spring如何配置切面?
配置spring的切面存在可以通过 aspect 或者 adviser 来整合配置切面
applicatioonContext.xml(adviser配置)
<!-- 注入通知类对象 -->
<bean id="before" class="domo07aop.server.Before"/>
<aop:config>
<aop:pointcut id="pc" expression="execution(* domo07aop.server.serverimpl.*(..))"/> 切入点
<aop:advisor advice-ref="before" pointcut-ref="pc"/> 通知器
</aop:config>
applicatioonContext.xml(aspect 配置)
<aop:config>
<aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
<aop:aspect ref="sleepHelperAspect">
<aop:before method="beforeSleep" pointcut-ref="sleepPointcut"/>
<aop:after method="afterSleep" pointcut-ref="sleepPointcut"/>
</aop:aspect>
</aop:config>
aspect 和adviser区别
<aop:advisor>大多用于事务管理。
<aop:aspect>大多用于日志、缓存。
<aop:aspect>定义切面时,只需要定义一般的bean就行,而定义< aop:advisor>中引用的通知时,通知必须实现Advice接口。
关于动态代理
aop的底层实现 : aop是基于动态字节码技术和反射,通过动态代理实现了spring的aop功能
动态代理分为 : JDK动态代理 cglib动态代理
AOP的动态代理 (代码实现)
动态代理实现需要: 目标对象 (JDK动态代理实现需要相应的接口 ,cglib动态代理通过继承实现代理)
*目标对象的接口
package 动态代理;
/**
* 人的接口类 (程序员 )
* @author Administrator
*
*/
public interface Person {
void say();
void eat();
void learn();
void deBug();
}
*目标对象类
public class PersonImpl implements Person{
@Override
public void say() {
System.out.println("你好,我是初级程序员,请多指教!");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("我有点不想吃饭了,减肥");
}
@Override
public void learn() {
// TODO Auto-generated method stub
System.out.println("奥里给 学无止境");
}
@Override
public void deBug() {
// TODO Auto-generated method stub
System.out.println("基操勿6");
}
}
jdk动态代理:
(jdk代理基于接口实现,动态代理类对象通过方法创建)
package 动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import org.junit.Test;
/**
* 基于jdk的动态代理
* @author Administrator
*
*/
public class JdkProxy {
@Test
public void testJdkProxy()
{
// 目标对象 (被代理对象 )
Person per=new PersonImpl();
// 对代理类对象进行增强 通过内部类的方式 简化开发 invocation 类似 spring 中的环绕通知
InvocationHandler invocation=new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(new Date());
Object ret=method.invoke(per, args);
return ret;
}
};
// 代理类和被代理类实现了相同的接口 ,通过接口来接收代理类对象
// 类加载器的作用 : 1 : 把字节码文件加载到JVM虚拟机中
// 2:在虚拟机中,为字节码创建对象
// ClassLoader:动态代理通过动态字节码文件技术把 字节码文件直接
//写入了JVM虚拟机,但是JVM中对象的创建仍然需要类加载器,所以在创
//建动态代理对象时,我们需要传入一个类加载器帮助代理对象初始化
// interfaces: 传入 被代理对象的接口 使代理对象和被代理对象实现相同的接口
// invocationHandler: 对被代理对象的方法实现增强的方法
Person perproxy=(Person)Proxy.newProxyInstance(JdkProxy.class.getClassLoader(),
per.getClass().getInterfaces(),invocation);
per.eat();
perproxy.eat();
}
}
cglib动态代理
(cglib实现动态代理基于继承,代理类继承被代理类 )(cglib如果不用spring手写实现 需要导入jar cglib-nodep-3.1.jar )
package 动态代理;
import java.lang.reflect.Method;
import org.junit.Test;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 基于 Cglib的动态代理
* @author Administrator
*
*/
public class CglibProxy {
@Test
public void test1()
{
Person per=new PersonImpl();
Enhancer enhance=new Enhancer();
// 内部类
MethodInterceptor methodinter=new MethodInterceptor()
{
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3)
throws Throwable {
System.out.println("还活着 Log.......");
Object ret=arg1.invoke(per, arg2);
return ret;
}
};
//类加载器 classLoader
enhance.setClassLoader(CglibProxy.class.getClassLoader());
// 被修饰类的字节码 (jdk传的是接口)
enhance.setSuperclass(per.getClass());
// 增强的方法
enhance.setCallback(methodinter);
PersonImpl perProxy=(PersonImpl)enhance.create();
perProxy.eat();
}
}
JDK 动态代理 和 cglib 动态代理 的异同
1: JDK通过Proxy.newInstance(classLoader,interfaces,InvocationHandler) 实现生产代理对象; 而 cglib通过 net.sf.cglib.proxy.Enhancer.create() 创建代理对象,在创建代理对象前,也需要设置
enhance.setClassLoader(), enhance.setSuperclass(),enhance.setCallback()方法设置属性; 两个方法除了一个传的是接口,一个传的是类的字节码,别的没有很多差别。
2: Proxy.newProxyInstance() 通过接⼝创建代理的实现类; Cglib动态代理 Enhancer通过继承⽗类创建的代理类