Proxy.newProxyInstance(loader, interfaces, h)
// loader :类加载器(被代理类的)
// Class<?>[] interfaces:所实现的所有接口(被代理对象的)
//invocationHandler h:方法执行器:通过它来执行被代理对象的所有方法
2、invocationHandler 核心方法,实现方法程序的调用,可以在这里面对调用的方法进行预处理等等。
public class MathCaculatorProxy { public static Caculator getProxy(final Caculator cal){ //创建动态代理实例 /** * return 一个代理实例 * loader :类加载器(被代理类的)the class loader to define the proxy class * Class<?>[] interfaces:所实现的所有接口(被代理对象的) * InvocationHandler h:方法执行器:通过它来执行被代理对象的所有方法 */ //1、获取被代理对象的类加载器 ClassLoader loader = cal.getClass().getClassLoader(); //2、获取被代理对象的所有接口 Class<?>[] interfaces = cal.getClass().getInterfaces(); Object proxy = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler(){ //2. InvocationHandler 的核心方法 // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象 // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行 /** * invoke:每次被代理对象执行方法的时候,其实会来到这里; * 宋哲帮宝宝执行方法的地方 * Object proxy(代理), 宋哲:代理对象 * Method method, 代理对象代理的方法; * Object[] args, 方法执行时传的参数的值 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{//Throwable是Exception的父类 //真正的目标方法执行完成以后的返回值 //obj:调用哪个对象的add方法; //method.invoke以后会返回方法的返回值 //System.out.println("宋哲正在呼叫宝宝执行方法...."); //method.invoke目标方法真正被执行.... Object result = null; try { LogUtils.logStart(method, args); result = method.invoke(cal, args); LogUtils.logReturn(method, result); } catch (Exception e) { LogUtils.logException(method,e); //动态代理的时候,我们一般建议都需要将异常继续抛出去,这样外界才能知道; throw new RuntimeException(e); } return result; } }); //返回代理对象(计算器) return (Caculator) proxy; } }
1、导包
基础包
commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
AOP功能包;
AspectJ:Java社区里最完整最流行的AOP框架。
在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
⑤AspectJ支持5种类型的通知注解:
[1]@Before:前置通知,在方法执行之前执行
[2]@After:后置通知,在方法执行之后执行
[3]@AfterRunning:返回通知,在方法返回结果之后执行
[4]@AfterThrowing:异常通知,在方法抛出异常之后执行
[5]@Around:环绕通知,围绕着方法执行
切入点表达式:
通过表达式的方式定位一个或多个具体的连接点。
/**
* 1、AOP细节一:IOC容器中保存的是组件的代理对象;
*
* 为什么就不能用实际的类型获取?
* 真正的类型:com.sun.proxy.$Proxy12(代理类型);
* 为什么能用接口类型获取呢?
* 动态代理机制:被代理对象和代理对象唯一产生关联的地方就是实现了同一接口;
* 所以只能使用接口类型来获取;因为保存的是代理对象;
*
* JDK如果对象没有实现接口是不能创建动态代理的;
*
*/
所以用切面的业务组件,需要用接口来获取。
/** * 验证切面 * * @author lfy * * @Order(1):指定整数值,默认不指定,一个非常大的整数2147483647 * 数值越小优先级越高,越先执行 * */
/** * AOP细节八: * 1、通知方法; * @Before * @After * @AfterThrowing * @AfterReturing * * @Around:环绕通知;(四合一的);就是一个动态代理; * 参数: * 关注的几个点: * 1)、将目标方法执行后的返回返回出去; * return proceed = pjp.proceed(args); * 2)、将异常抛出去方便外界感知; * 3)、环绕的几个位置和其他通知在一起时:执行顺序; * 环绕通知在自己的切面里面拥有最高优先级,优先执行;环绕先进去先出来; 【环绕】前置通知 【日志】前置通知:【add】方法运行开始了;使用的参数列表【[10, 1, 1]】 方法内部打印:11 【环绕】返回通知,返回值:11 【环绕】后置通知 【日志】后置通知【add】最终结束; 【日志】返回通知【add】正常返回,返回值:【11】 * * * 多切面带环绕顺序: * 背景:BV order1 LOG(普通+环绕) order2 * BV===前置 * LOG===环绕前置 * LOG===前置 * 目标方法 * LOG===环绕返回 * LOG===环绕后置 * LOG===后置 * LOG===返回 * BV===后置 * BV===返回 * * * 环绕在单切面环境: * LOG===环绕前置 * LOG===前置 * 目标方法 * LOG===环绕返回 * LOG===环绕后置 * LOG===后置 * LOG===返回 * * 业务逻辑: * */
1)对切面类的方法进行配置
2)将切面类和业务逻辑组件都加入到容器
3)告诉SpringIOC容器哪个类是切面类@Aspect
4)开启基于注解的AOP功能
基于XML的AOP
1)对切面类的方法进行配置
2)将切面类和业务逻辑组件都加入到容器
3)告诉SpringIOC容器哪个类是切面类@Aspect
原则: <!--重要的用配置,一般的用注解 -->
指定切面的优先级
在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。
切面的优先级可以通过实现Ordered接口或利用@Order注解指定。
实现Ordered接口,getOrder()方法的返回值越小,优先级越高。
若使用@Order注解,序号出现在注解中