范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

爱上源码,重学SpringAOP深入

  AOP (Aspect Orient Programming):直译过来就是 面向切面编程。AOP 是一种编程思想
  用途:Transactions (事务调用方法前开启事务, 调用方法后提交关闭事务 )、日志、性能(监控方法运行时间)、权限控制等
  也就是对业务方法做了增强1.1 Spring AOP环境介绍
  **目标:**认识AOP基础环境,后面讲使用这个基础环境进行源码讲解tips:
  沿用ioC的工厂1)引入起步依赖    compile(project(":spring-aop"))     compile(project(":spring-context"))     compile "org.aspectj:aspectjweaver:1.9.2" 复制代码2)新建接口和实现public interface Slaver { 	void work(); }  复制代码import org.springframework.stereotype.Service;  @Service public class SlaverImpl implements Slaver { 	public void work() { 		System.out.println("进入实现类 work....."); 		try { 			Thread.sleep(1000); 		} catch (InterruptedException e) { 			e.printStackTrace(); 		}  	}  } 复制代码3)新建切面类package com.spring.test.aop.aop;  import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;  import java.util.Arrays;  //将这个类声明为一个切面,需要将其放入IOC容器中 @Aspect//声明这是一个切面 @Component//声明这是一个组件 /**  * 执行顺序  * @Around进入环绕通知...  * @Before进入前置通知:[]  * 进入实现类 work.....  * @Around方法执行耗时>>>>>: 1001  * @After进入后置通知...  * @AfterReturning进入最终通知...End!  */ public class SlaverAspect {  	//环绕通知(连接到切入点开始执行,下一步进入前置通知,在下一步才是执行操作方法) 	@Around(value = "pointCut()") 	public void around(ProceedingJoinPoint joinPoint) throws Throwable { 		System.out.println("@Around进入环绕通知..."); 		long startTime = System.currentTimeMillis(); 		joinPoint.proceed(); 		long endTime = System.currentTimeMillis(); 		System.out.println(String.format("@Around方法执行耗时>>>>>: %s", endTime - startTime)); 	}  	//前置通知(进入环绕后执行,下一步执行方法) 	@Before(value = "pointCut()") 	public void before(JoinPoint joinPoint) { 		System.out.println("@Before进入前置通知:" + Arrays.toString(joinPoint.getArgs())); 	}  	//异常通知(出错时执行) 	@AfterThrowing(value = "pointCut()", throwing = "ex") 	public void afterThrowing(JoinPoint joinPoint, Throwable ex) { 		System.out.println("@AfterThrowing进入异常通知" + Arrays.toString(joinPoint.getArgs())); 		System.out.println("@AfterThrowing异常信息:" + ex); 	}  	//后置通知(返回之前执行) 	@After(value = "pointCut()") 	public void after() {  		System.out.println("@After进入后置通知..."); 	}  	//最终通知(正常返回通知,最后执行) 	@AfterReturning(value = "pointCut()") 	public void afterReturning() { 		System.out.println("@AfterReturning进入最终通知...End!"); 	}  	//定义一个切入点 后面的通知直接引入切入点方法pointCut即可 //	@Pointcut("execution(public * com.spring.test.aop.impl.SlaverImpl.work())") 	@Pointcut(value = "execution(* com.spring.test.aop.impl.SlaverImpl.*(..))") 	public void pointCut() { 	}  }  复制代码
  AspectJ 支持 5 种类型的通知注解: @Before: 前置通知, 在方法执行之前执行 @After: 后置通知, 在方法执行之后执行 @AfterRunning: 返回通知, 在方法返回结果之后执行 @AfterThrowing: 异常通知, 在方法抛出异常之后 @Around: 环绕通知, 围绕着方法执行
  execution:用于匹配方法执行的连接点;4)新建配置文件
  resources/application-aop.xml<?xml version="1.0" encoding="UTF-8"?>  	 	 	 	  复制代码
  常见错误
  配置文件缺乏xsd的引用
  5)新建入口类public class Main {  	public static void main(String[] args) { 		ApplicationContext context = 				new ClassPathXmlApplicationContext("classpath*:application-aop.xml"); 		Slaver slaver=(Slaver)context.getBean("slaverImpl"); 		slaver.work(); 	}  } 复制代码6)运行效果
  1.2 SpringAOP和AspectJ联系tips:
  十个人有九 个人弄不懂的关系
  Spring AOP:
  Spring AOP旨在通过Spring IoC提供一个简单的AOP实现,以解决编码人员面临的最常出现的问题。这并不是完整的AOP解决方案,它只能用于Spring容器管理的beans。
  AspectJ:
  AspectJ是最原始的AOP实现技术,提供了完整的AOP解决方案。AspectJ更为健壮,相对于Spring AOP也显得更为复杂
  总结AOP是面向切面的一个思想 他有两种实现 1、Spring AOP 2、Aspectj Spring AOP的实现没有AspectJ强大 所以,Spring把Aspectj给集成(如果用,还需要单独引jar)进来了 但是;spring aop已能满足我们的需求 在进行开发时候,这两个框架是完全兼容的  说白了,就是两个框架能一起使用,就看你项目需求用到的哪种程度了 简单的;spirng aop够用了,但是spring aop借助了aspectj的注解功能, 在高级点,比如切面很多,上万个,这是就要用到aspectj的高级功能了  复制代码
  区别:AspectJ使用的是编译期和类加载时进行织入,Spring AOP利用的是运行时织入
  **依赖:**如果使用@Aspect注解,在xml里加上。但是这需要额外的jar包( aspectjweaver.jar)
  因为spring直接使用AspectJ的注解功能,注意只是使用了它 的注解功能而已。并不是核心功能 !
  运行效果如下:
  1.3 找到处理AOP的源头
  1、Spring处理AOP源头在哪里(织入)
  AspectJ编译期和类加载时进行织入、Spring AOP利用的是运行时织入猜想: 1. 在容器启动时创建?  2.在getBean时创建?  复制代码
  容器启动
  2、代理对象到底长什么样?
  将断点打在getBean的返回对象上,发现这并不是一个我们定义的对象本身,而是一个Proxy
  接下来,我们会找到$Proxy生成的始末
  3、我的接口是怎么被A0P管理上的 ?
  com.spring.test.aop.aop.SlaverAspect#pointCut//定义一个切入点 后面的通知直接引入切入点方法pointCut即可 	//参数: 	//第一个"*"符号;表示返回值的类型任意 	//.*(..)表示任何方法名,括号表示参数,两个点表示任何参数类型 	@Pointcut(value = "execution(* com.spring.test.aop.impl.SlaverImpl.*(..))") 	public void pointCut() { 		System.out.println("@进入切点..."); 	} 复制代码tips:
  当然是execution声明,改成 SlaverImpl.aaaa*(..) , 再来看getBean的对象,不再是proxy
  断点一下……1.4 代理对象是怎么生成的1、AOP其实就是用的动态代理模式,创建代理
  2、AOP织入源头在哪里
  **目标:**通过源头找代理对象是如何生成的
  找到生成代理的地方Proxy.newProxyInstance0)回顾助学
  简单回顾一下,注意,这里不仅仅是学习代理模式,还必须搞懂它的实现方式,尤其是动态代理。开始之前,我们先必须搞懂一件事情:
  那就是:代理模式
  设计模式【静态代理】 【动态代理】 回顾 (见第2小节)1)从源头找代理对象生成
  记住目标:
  spring aop使用的就是代理模式,那我们的目标就明确了:找到生成代理的地方Proxy.newProxyInstance
  先来张流程图:
  下面我们沿着图中的调用链,找到aop 代理诞生的地方
  tips:从后置处理器开始
  为什么要从后置处理器入手?
  很容易理解,没初始化好没法用,等你初始化好了功能齐备了,我再下手,代替你
  找到后置处理器
  重点关注postProcessAfterInitialization此处最好使用断点表达式,否则要循环很多次
  因为在refresh方法中的invokeBeanFactoryPostProcessors方法也会调用到这个地方
  断点表达式:
  关注点:tips:
  在BeanPostProcessor循环中,观察AnnotationAwareAspectJAutoProxyCreator
  这货就是切面的后置处理器
  AbstractAutowireCapableBeanFactory**#**applyBeanPostProcessorsAfterInitialization	//目标:循环所有的后置处理器进行调用 	//注意: 	//AOP调试,此处最好使用断点表达式,否则要循环很多次 	//因为在refresh方法中的invokeBeanFactoryPostProcessors方法也会调用到这个地方 	@Override 	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) 			throws BeansException {  		Object result = existingBean; 		//aop 		//此处getBeanPostProcessors()有8个内置后置处理器;生成代理会调用里面的 AnnotationAwareAspectJAutoProxyCreator 		for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { 			//Aop调用AbstractAutoProxyCreator#postProcessAfterInitialization, 			Object current = beanProcessor.postProcessAfterInitialization(result, beanName); 			if (current == null) { 				return result; 			} 			result = current; 		} 		return result; 	} 复制代码如上所见
  也就是说AOP模块是通过实现BeanPostProcessor集成进来的2)进入后置处理器tips:
  aop这是spring内置的一个后置处理器,生效在postProcessAfterInitialization方法
  AbstractAutoProxyCreator#postProcessAfterInitialization
  重点关注wrapIfNecessary	//如果当前的bean适合被代理,则需要包装指定的bean 	@Override 	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { 		if (bean != null) { 			// 根据给定的bean的class和name构建一个key 			Object cacheKey = getCacheKey(bean.getClass(), beanName); 			if (!this.earlyProxyReferences.contains(cacheKey)) {  				// 如果当前的bean适合被代理,则需要包装指定的bean 				return wrapIfNecessary(bean, beanName, cacheKey); 			} 		} 		return bean; 	} 复制代码
  经历wrapIfNecessary方法,重点关注点有两个:
  1是:getAdvicesAndAdvisorsForBean,找到哪些切面会作用在当前bean上,满足条件的抓出来!
  2是:createProxy,生成代理,替代slaverImpl去做事	//目标 	//1、判断当前bean是否已经生成过代理对象,或者是否是应该被略过的对象,是则直接返回,否则进行下一步 	//2、拿到切面类中的所有增强方法(拦截器:环绕、前置、后置等) 	//3、生成代理对象 	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { 		// 判断是否为空 		// 判断当前bean是否在TargetSource缓存中存在,如果存在,则直接返回当前bean 		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { 			return bean; 		} 		// 这里advisedBeans缓存了不需要代理的bean(为false的),如果缓存中存在,则可以直接返回 		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { 			return bean; 		} 		//Infrastructure基础设施 		// 用于判断当前bean是否为Spring系统自带的bean,自带的bean是 		// 不用进行代理的;shouldSkip()则用于判断当前bean是否应该被略过 		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { 			// 对当前bean进行缓存 			this.advisedBeans.put(cacheKey, Boolean.FALSE); 			return bean; 		}                          		//AOP:【关键点1】反射来过滤,看看哪些aspect的execution能匹配上当前bean 		// ===【【【【注意!这货要分两步调试讲解,修改切面表达式做对比】】】】==== 		// 将SlaverAspect的 execution改成 SlaverImpl.aaa*(..)  试试,你将得到一个空数组!!! 		// 匹配上的话列出前后和置换的方法(拦截器:环绕、前置、后置等) 		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);            		//如果拿到的增强方法不为空 		if (specificInterceptors != DO_NOT_PROXY) { 			// 对当前bean的代理状态进行缓存 			this.advisedBeans.put(cacheKey, Boolean.TRUE);                      			// 开始创建AOP代理       //  ===【关键点2】=== 			Object proxy = createProxy( 					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));                             			// 缓存生成的代理bean的类型,并且返回生成的代理bean 			this.proxyTypes.put(cacheKey, proxy.getClass()); 			//此处返回的代理和在Main函数中返回的是一样的 			//说明此处代理成功创建 			return proxy; 		} 		//如果拿到的增强方法为空,缓存起来(使用false标记不需要代理) 		this.advisedBeans.put(cacheKey, Boolean.FALSE); 		return bean; 	} 复制代码3)开始创建代理对象
  重点关注最下面的关键点:proxyFactory.getProxy(getProxyClassLoader())	//beanClass:目标对象class 	//beanaName 	//specificInterceptors:拦截器里面的拦截方法 	//targetSource:目标资源 	//目标:开始为bean创建代理 	protected Object createProxy(Class<?> beanClass, @Nullable String beanName, 								 @Nullable Object[] specificInterceptors, TargetSource targetSource) { 		//为true,DefaultListableBeanFactory 		if (this.beanFactory instanceof ConfigurableListableBeanFactory) { 			//给当前的bd设置属性setAttribute("originalTargetClass",bean的class),进入 			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); 		} 		//创建一个默认的代理工厂DefaultAopProxyFactory,父类无参构造器 		ProxyFactory proxyFactory = new ProxyFactory();   		//proxyFactory通过复制配置进行初始化 		//this为AbstractAutoProxyCreator对象,说明AbstractAutoProxyCreator继承参数实际类型 		proxyFactory.copyFrom(this); 		/** 		 * isProxyTargetClass(),默认false 		 * true 		 *目标对象没有接口(只有实现类) – 使用CGLIB代理机制 		 * false 		 * 目标对象实现了接口 – 使用JDK代理机制(代理所有实现了的接口) 		 */ 		if (!proxyFactory.isProxyTargetClass()) { 			//来判断@EnableAspectJAutoProxy注解或者XML的proxyTargetClass参数(true或者false) 			//看看用户有没有指定什么方式生成代理 			// 如果没配置就为空,此处返回false 			if (shouldProxyTargetClass(beanClass, beanName)) { 				proxyFactory.setProxyTargetClass(true); 			} else { 				//评估接口的合理性,一些内部回调接口,比如InitializingBean等,不会被实现jdk代理 				evaluateProxyInterfaces(beanClass, proxyFactory); 			} 		} 		// 把advice(增强)类型的增强包装成advisor类型(强制类型转换) 		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); 		//加入到代理工厂 		proxyFactory.addAdvisors(advisors); 		//设置要代理的类(目标类) 		proxyFactory.setTargetSource(targetSource); 		//子类实现, 定制代理 		customizeProxyFactory(proxyFactory); 		//用来控制代理工厂被设置后是否还允许修改通知,缺省值为false 		proxyFactory.setFrozen(this.freezeProxy); 		//明明是false?? 此处注意,他是在子类AbstractAdvisorAutoProxyCreator重写了advisorsPreFiltered方法 		if (advisorsPreFiltered()) { 			//设置预过滤 			proxyFactory.setPreFiltered(true); 		}  		//【关键点】通过类加载期获取代理;getProxyClassLoader为默认的类加载器 		return proxyFactory.getProxy(getProxyClassLoader()); 	} 复制代码
  进入getProxy//通过类加载期获取代理 public Object getProxy(@Nullable ClassLoader classLoader) {    //分别进入createAopProxy 和getProxy    return createAopProxy().getProxy(classLoader); } 复制代码
  先看createAopProxy
  查看返回jdk代理还是cglib代理public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 		// isOptimize:是否对代理进行优化 		// isProxyTargetClass:值为true,使用CGLIB代理,默认false 		// hasNoUserSuppliedProxyInterfaces: 		//1、如果长度为0;也就是接口为空,返回false 		//or(或的关系) 		//2、如果接口类型不是SpringProxy类型的;返回flase  		//如果条件不满足;直接走JDK动态代理(return) 		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 			Class<?> targetClass = config.getTargetClass(); 			if (targetClass == null) { 				throw new AopConfigException("TargetSource cannot determine target class: " + 						"Either an interface or a target is required for proxy creation."); 			} 			// 如果targetClass是接口类,使用JDK来生成Proxy 			//Tips 			//如果目标对象实现了接口,默认情况下会采用JDK动态代理, 			// 但也可以通过配置(proxy-target-class=true)强制使用CGLIB。 			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { 				//jdk 				return new JdkDynamicAopProxy(config); 			} 			//cglib 			return new ObjenesisCglibAopProxy(config); 		} else { 			//jdk 			return new JdkDynamicAopProxy(config); 		} 	} 复制代码
  再看getProxy	//获取最终的代理对象(由JDK生成;运行时织入) 	@Override 	public Object getProxy(@Nullable ClassLoader classLoader) { 		if (logger.isDebugEnabled()) { 			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); 		} 		//获取代理对象需要实现的接口(业务接口和内置接口) 		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); 		//判断接口中是否重写了equals和hashCode方法 		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  		/** 		 * 第一个参数是类加载器(目标类) 		 * 第二个参数是代理类需要实现的接口,即目标类实现的接口(含系统接口)(数组) 		 * 第三个是InvocationHandler,本类实现了此接口 		 */ 		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 	} 复制代码
  找到了!代理对象在bean初始化阶段装配进了spring4)代理对象验证tips:
  前面创建完成了代理对象,下面我们看下它调用的时候,是不是走了代理
  这是我们一开始的结果,下面我们来再debug到work方法时,点击debug into试试,发现调到哪里去了???
  结论:
  没错!代理对象精准的调用了JdkDynamicAopProxy里面的invoke方法
  这说明jdk动态代理生效,但是它生成的proxy字节码在jvm里,我们是看不到的,怎么破?
  arthas上!2)Arthas代理类验证
  arthas,阿里神器,主页:arthas.aliyun.com/zh-cn/
  获取之前,我们需要做点小事情,打出我们的 slaver的类路径,让arthas能找到它!
  源码:package com.aoptest;  import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;  public class Main {  	public static void main(String[] args) throws InterruptedException { 		ApplicationContext context = 				new ClassPathXmlApplicationContext("classpath*:application-aop.xml"); 		Slaver slaver=(Slaver)context.getBean("slaverImpl"); 		slaver.work(); 		System.out.printf(slaver.getClass().getName());  //打印当前对象的类名称:com.sun.proxy.$Proxy17 		Thread.sleep(Integer.MAX_VALUE); // 必须停留在这里,debug是不行的,arthas会连不上 	}  } 复制代码
  开启arthas:很简单, java -jar arthas-boot.jar启动后,在arthas里执行:jad com.sun.proxy.$Proxy17 反编译我们的代理类
  注意,jad后面的,换成你上一步控制台打印出来的
  见证奇迹的时刻……
  找到里面的work方法,结果它长这样……
  那么,this.h呢?
  杀死进程,回到debug模式,看看
  没错,就是我们的jdk动态代理类!
  现在调用关系明确了,接下来,我们就来分析下这个invoke1.5 代理对象如何调用1)先看张图
  代理对象调用链如下图 (责任链,先有个印象,很长,很长……)
  2)理解责任链
  代码中给大家准备了一个责任链小demo,它就是我们spring aop切面调用链的缩影
  spring-aop-test 项目下的 com.spring.test.aop.chain 包。
  执行里面的main方法
  生活中的例子,类似于:全公司站成一队,从队首开始签名如果先签完再交给下一个,你就是前置拦截。(签名 = 执行切面任务)如果先交给下一个,等传回来的时候再签,你就实现了后置,回来的时候再补一个个传到底后,到达队尾,老板盖章(真正的业务逻辑得到执行)然后一个个往回传(return),前面没签的那些家伙补上(后置切面任务得到执行)3)invoke方法源码分析
  通过上小节我们知道,代理模式下执行的是以下invoke
  org.springframework.aop.framework.JdkDynamicAopProxy#invoke
  代码重点关注// 2.从ProxyFactory(this.advised)中构建拦截器链,包含了目标方法的所有切面方法 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass) 		 //责任链开始调用:ReflectiveMethodInvocation.proceed();重点关注!!! retVal = invocation.proceed(); 复制代码
  责任链构建:chain里面为责任链的具体任务
  接下来,我们具体看看责任链具体的处理逻辑4)AOP核心之责任链
  思考:
  责任链调用,调用的什么?
  责任链目标:
  AOP责任链调用流程简图
  注意:上图的颜色区分
  黑色:表示正向调用,invoke的时候,在前或后执行自己的切面逻辑,然后推动责任链往下走
  红色:表示当前切面任务触发的点
  备注:ExposeInvocationInterceptor 是spring帮我们加上做上下文传递的,本图不涉及(它在最前面)
  注意,我们的链条如下:
  调试技巧:
  从 ReflectiveMethodInvocation.invoke() 开始,对照上面的责任链嵌套调用图
  每次通过debug into 进行查看,每到一环,注意对照图上的节点,看到那个invocation了!
  案例:第一环,遇proceed,再 debug into
  依次循环,步步跟进,其乐无穷~~~2 AOP基础 - 代理模式(助学)2.1 设计模式之代理背景 举个例子 假设我们想邀请一位明星,不联系明星 而是联系明星的经纪人,来达到邀请的的目的 明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决. 这就是代理思想在现实中的一个例子 复制代码
  什么是代理?
  代理(Proxy)是一种设计模式
  提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.
  这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作
  代理模式分类
  1、静态代理
  2、动态代理2.2 静态代理模式
  静态代理在使用时:
  需要定义接口、目标对象与代理对象
  重要特点:
  静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。
  接口//接口 public interface IStartBusiness { 	//邀请明星唱歌 	void sing(); }  复制代码
  目标对象,实现类//目标对象,实现类 public class StartBusinessImpl implements IStartBusiness { 	@Override 	public void sing() { 		System.out.println("sing>>>>>>>>>>>>>>>>>>>>>>>>>>>"); 	} } 复制代码
  代理对象,静态代理package com.spring.test.aop.pattern.proxy.staticproxy;  //代理对象,静态代理 public class AgentProxy implements IStartBusiness {  	//代理类持有一个目标类的对象引用 	private IStartBusiness iStartBusiness; 	//构造注入目标对象 	public AgentProxy(IStartBusiness iStartBusiness) { 		this.iStartBusiness = iStartBusiness; 	}  	@Override 	public void sing() {  		//**********方法前增强****************   		//do something 		//将请求分派给目标类执行;通过注入进入来的目标对象进行访问 		this.iStartBusiness.sing();  		//do after  		//**********方法后增强**************** 	} }  复制代码
  静态代理测试package com.spring.test.aop.pattern.proxy.staticproxy;  //静态代理测试 public class Test { 	public static void main(String[] args) { 		//目标对象 		IStartBusiness target = new StartBusinessImpl(); 		//代理对象,把目标对象传给代理对象,建立代理关系 		IStartBusiness proxy = new AgentProxy(target); 		//调用的时候通过调用代理对象的方法来调用目标对象 		proxy.sing(); 	}  }  复制代码
  输出
  优点:
  可以在被代理方法的执行前或后加入别的代码,实现诸如权限及日志的操作。
  缺点:
  1、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法
  ​总结:(记住两点)
  1、只需要知道静态代理是在运行前代理类就已经被织入进去了
  2、大规模使用静态代理难以维护(增加方法)
  有没有其他的方式可以减少代码的维护,那就是动态代理?2.3 动态代理模式
  什么是动态代理?
  动态代理类的源码是在程序运行期间由JVM根据反射等机制动态织入的,
  所以;不存在代理类的字节码文件,直接进了虚拟机。(但是有办法给抓到他)
  JDK中生成代理对象的API最重要类和接口有两个,如下Proxy
  InvocationHandler1)代理父类Proxy回顾
  所在包:java.lang.reflect.Proxy
  这是 Java 动态代理机制生成的所有动态代理类的父类,
  提供了一组静态方法来为一组接口动态地生成代理类及其对象。
  Proxy类的静态方法(了解)// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器   static InvocationHandler getInvocationHandler(Object proxy)       // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象   static Class getProxyClass(ClassLoader loader, Class[] interfaces)       // 方法 3:该方法用于判断指定类对象是否是一个动态代理类   static boolean isProxyClass(Class cl)       // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例   static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 复制代码tips:重要
  重点关注newProxyInstance
  这三个参数非常重要
  Spring Aop也是使用这个机制    public static Object newProxyInstance(ClassLoader loader,                                           Class<?>[] interfaces,                                           InvocationHandler h)         throws IllegalArgumentException 复制代码
  注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:ClassLoader loader,:指定当前目标对象使用类加载器 ;负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入2)调用处理器接口回顾
  java.lang.reflect.InvocationHandler
  这是调用处理器接口,它自定义了一个 invoke 方法(只有一个)
  用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对目标类的代理访问。
  每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
  1、目标对象(委托类)通过jdk的Proxy生成了代理对象、
  2、客户端访问代理对象,代理对象通过调用于处理器接口反射调用了目标对象方法
  InvocationHandler的核心方法
  仅仅一个方法public interface InvocationHandler {  //第一个参数既是代理类实例  //第二个参数是被调用的方法对象   // 第三个方法是调用参数    Object invoke(Object proxy, Method method, Object[] args)   } 复制代码3)动态代理代码编写
  沿用上面的 例子tips
  代理对象不需要实现接口(业务),但是目标对象一定要实现接口,否则不能用动态代理
  接口
  目标对象接口package com.spring.test.aop.pattern.proxy.dynamic; //接口 public interface IStartBusiness { 	//邀请明星唱歌 	void sing(); }  复制代码
  目标对象//目标对象 public class StartBusinessImpl implements IStartBusiness { 	@Override 	public void sing() { 		System.out.println("sing>>>>>>>>>>>>>>>>>>>>>>>>>>>"); 	} } 复制代码
  创建动态代理对象package com.spring.test.aop.pattern.proxy.dynamic;  import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;   //动态代理实现 public class DynamicProxy implements InvocationHandler { 	// 这个就是我们要代理的真实对象 	private Object obj;  	// 构造方法,给我们要代理的真实对象赋初值 	public DynamicProxy(Object object) { 		this.obj = object; 	}  	//相比静态代理,动态代理减只需要实现一个接口即可完成,而静态代理每次都要实现新加的方法以及维护被代理方法          //第一个参数既是代理类实例  //第二个参数是被调用的方法对象   // 第三个方法是调用参数    	@Override 	public Object invoke(Object object, Method method, Object[] args) 			throws Throwable {  		//********************方法前增强*************************** 		// 反射调用目标方法 		return method.invoke(obj, args); 	  		//********************方法后增强*************************** 	}  }  复制代码
  测试package com.spring.test.aop.pattern.proxy.dynamic;  import com.spring.test.aop.pattern.proxy.staticproxy.IStartBusiness;  import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;  //动态代理测试 //目标: //1、知道如何创建动态代理 //2、如何调用了invoke方法(拦截方法会用到当前知识点) public class Test {  	public static void main(String[] args) { 		// 目标对象;要代理的真实对象 		IStartBusiness target = new StartBusinessImpl();       // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 		InvocationHandler handler = new DynamicProxy(target); 		/* 		 * 第一个参数:目标对象加载器 		 * 第二个参数:目标对象接口 		 * 第三个参数:实现InvocationHandler的代理类 		 */        //生成代理对象 		IStartBusiness iStartBusiness = (IStartBusiness) Proxy.newProxyInstance(target.getClass().getClassLoader(), target 				.getClass().getInterfaces(), handler); 		iStartBusiness.sing();   	} }   复制代码
  输出
  总结
  1、相比静态代理,动态代理减只需要实现一个接口即可完成,而静态代理每次都要实现新加的方法以及维护被代理方法
  2、动态代理是靠Proxy.newProxyInstance() 生成的
  3、动态代理在调用(iStartBusiness.sing())的时候,调用到了 implements InvocationHandler 的invoke
  目标明确了:spring的代理就需要我们找到 Proxy.newProxyInstance() 在哪里……4)动态代理原理(了解)tips:
  涉及JDK字节码,不在spring的源码范围内,感兴趣的同学自己了解一下
  参考资料:www.baiyp.ren/JAVA%E5%8A%…
  代理对象内部生成流程调用链,如下图
  对象生成过程(核心)
  Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 进入newProxyInstance方法内部
  java.lang.reflect.Proxy#newProxyInstance
  重点关注核心方法getProxyClass0  //使用jdk代理工厂创建新的代理实例 @CallerSensitive     public static Object newProxyInstance(ClassLoader loader,                                           Class<?>[] interfaces,                                           InvocationHandler h)         throws IllegalArgumentException     {          //判断是否实现InvocationHandler接口,如果此处为空抛出异常         //当前h非常重要,因为在代理对象调用目标方法的时候,就是通过d的invoke方法反射调用的目标方法         //稍后会讲解         Objects.requireNonNull(h);         //克隆参数传来的 接口         final Class<?>[] intfs = interfaces.clone();         //系统内部的安全         final SecurityManager sm = System.getSecurityManager();         if (sm != null) {             //访问权限的验证             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);         }          /*          *查找或生成指定的代理类;非常重要,重点关注          */         Class<?> cl = getProxyClass0(loader, intfs);          /*          * Invoke its constructor with the designated invocation handler.          */         try {             if (sm != null) {                 //代理权限的检查(此处是新生产的代理对象c1)                 checkNewProxyPermission(Reflection.getCallerClass(), cl);             }             //从生成的代理对象(只是class对象,还不是实例化对象)中取出构造器             final Constructor<?> cons = cl.getConstructor(constructorParams);             final InvocationHandler ih = h;             if (!Modifier.isPublic(cl.getModifiers())) {                 AccessController.doPrivileged(new PrivilegedAction() {                     public Void run() {                         cons.setAccessible(true);                         return null;                     }                 });             }             //使用构造器实例化(实例化对象)             return cons.newInstance(new Object[]{h});         } catch (IllegalAccessException|InstantiationException e) {             throw new InternalError(e.toString(), e);         } catch (InvocationTargetException e) {             Throwable t = e.getCause();             if (t instanceof RuntimeException) {                 throw (RuntimeException) t;             } else {                 throw new InternalError(t.toString(), t);             }         } catch (NoSuchMethodException e) {             throw new InternalError(e.toString(), e);         }     } 复制代码
  进入getProxyClass0
  java.lang.reflect.Proxy#getProxyClass0    private static Class<?> getProxyClass0(ClassLoader loader,                                            Class<?>... interfaces) {         //判断接口数组大小,别大于65535,如果大于,提示接口超出限制         if (interfaces.length > 65535) {             throw new IllegalArgumentException("interface limit exceeded");         }          //通过接口和类加载器创建代理          //给定的接口存在,这将简单地返回缓存副本;           //否则,它将通过ProxyClassFactory创建代理类                   return proxyClassCache.get(loader, interfaces);//也就是查询+创建的过程     } 复制代码
  继续进入get方法
  java.lang.reflect.WeakCache#get
  其他不要看
  重点关注apply方法    public V get(K key, P parameter) {         Objects.requireNonNull(parameter);          expungeStaleEntries();          Object cacheKey = CacheKey.valueOf(key, refQueue);          // lazily install the 2nd level valuesMap for the particular cacheKey         ConcurrentMap> valuesMap = map.get(cacheKey);         if (valuesMap == null) {             ConcurrentMap> oldValuesMap                 = map.putIfAbsent(cacheKey,                                   valuesMap = new ConcurrentHashMap<>());             if (oldValuesMap != null) {                 valuesMap = oldValuesMap;             }         }          // create subKey and retrieve the possible Supplier stored by that         // subKey from valuesMap         Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));         Supplier supplier = valuesMap.get(subKey);         Factory factory = null;          while (true) {             if (supplier != null) {                 // supplier might be a Factory or a CacheValue instance                 V value = supplier.get();                 if (value != null) {                     return value;                 }             }             // else no supplier in cache             // or a supplier that returned null (could be a cleared CacheValue             // or a Factory that wasn"t successful in installing the CacheValue)              // lazily construct a Factory             if (factory == null) {                 factory = new Factory(key, parameter, subKey, valuesMap);             }              if (supplier == null) {                 supplier = valuesMap.putIfAbsent(subKey, factory);                 if (supplier == null) {                     // successfully installed Factory                     supplier = factory;                 }                 // else retry with winning supplier             } else {                 if (valuesMap.replace(subKey, supplier, factory)) {                     // successfully replaced                     // cleared CacheEntry / unsuccessful Factory                     // with our Factory                     supplier = factory;                 } else {                     // retry with current supplier                     supplier = valuesMap.get(subKey);                 }             }         }     } 复制代码
  进入apply方法(内部类)
  java.lang.reflect.Proxy.ProxyClassFactory#apply //目的 //1、判断;比如当前的class是否对加载器可见;访问权限。是否public的 //2、生成class字节码文件 //3、调用本地方法;通过class字节码文件生成class对象  @Override         public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {              Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);             for (Class<?> intf : interfaces) {                 /*                  * Verify that the class loader resolves the name of this                  * interface to the same Class object.                  */                 Class<?> interfaceClass = null;                 try {                     //当前的class是否对加载器可见                     interfaceClass = Class.forName(intf.getName(), false, loader);                 } catch (ClassNotFoundException e) {                 }                 if (interfaceClass != intf) {                     throw new IllegalArgumentException(                         intf + " is not visible from class loader");                 }                 /*                  * 验证类对象是否实际表示接口                  *                    */                 if (!interfaceClass.isInterface()) {                     throw new IllegalArgumentException(                         interfaceClass.getName() + " is not an interface");                 }                 /*                  * 验证此接口不是重复的。                  */                 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {                     throw new IllegalArgumentException(                         "repeated interface: " + interfaceClass.getName());                 }             }              String proxyPkg = null;     // package to define proxy class in             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;              /*              * Record the package of a non-public proxy interface so that the              * proxy class will be defined in the same package.  Verify that              * all non-public proxy interfaces are in the same package.              */             for (Class<?> intf : interfaces) {                 int flags = intf.getModifiers();                            if (!Modifier.isPublic(flags)) {                     accessFlags = Modifier.FINAL;                     String name = intf.getName();                     int n = name.lastIndexOf(".");                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));                     if (proxyPkg == null) {                         proxyPkg = pkg;                     } else if (!pkg.equals(proxyPkg)) {                               //是否public的                         throw new IllegalArgumentException(                             "non-public interfaces from different packages");                     }                 }             }              if (proxyPkg == null) {                 // 如果没有非公共代理接口,请使用com.sun.proxy                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";             }              /*              * 这里需要注意下.他对应的是代理对象proxy后的数值;比如$proxy100              *这里的100;就是此处原子生成              */             long num = nextUniqueNumber.getAndIncrement();             //$proxy100;非常熟悉了;通过断点查看代理对象看到的,就是从这里生成的             //proxyClassNamePrefix这个前缀就是 $             //这个proxyPkg就是com.sun.proxy             String proxyName = proxyPkg + proxyClassNamePrefix + num;              /*              *使用ProxyGenerator里面的工具类,帮助我们生成代理类的class内容              注意。此处存储到byte数组;目前还不是class对象              */             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                 proxyName, interfaces, accessFlags);             try {                 //此处才是真正的class对象(注意;虽然是对象;但是还没有实例化)                 return defineClass0(loader, proxyName,                                     proxyClassFile, 0, proxyClassFile.length);             } catch (ClassFormatError e) {                 /*                  * A ClassFormatError here means that (barring bugs in the                  * proxy class generation code) there was some other                  * invalid aspect of the arguments supplied to the proxy                  * class creation (such as virtual machine limitations                  * exceeded).                  */                 throw new IllegalArgumentException(e.toString());             }         }     }  复制代码
  核心代码参看以上注释
  断点查看slaver.work()		ApplicationContext context = 				new ClassPathXmlApplicationContext("classpath*:application-aop.xml"); 		Slaver slaver = (Slaver) context.getBean("slaverImpl"); 		//使用代理调用了JdkDynamicAopProxy.invoke 		slaver.work(); 		System.out.println("over>>>>>>>>>>");  复制代码
  注意里面的 $proxy17,全部都是在上面生成的
  最终这个才是实例化的对象(如上)
  作者:博学谷
  链接:https://juejin.cn/post/7158346218572087333
维密天使凯莉盖尔维密天使凯莉盖尔凯莉出生于瑞典哥德堡,她的血管里流淌着印度和澳大利亚的血液,这也是她异国情调的美丽原因。她在印度出生的母亲吉塔是一名牙医,她的澳大利亚父亲是一名足球运动员,后来转向布莱德索免于刑事,血布回归,李春江亮剑,上海吹响反击号角布莱德索免于刑事,血布回归,李春江亮剑,上海吹响反击号角上海男篮,在引进外援上,戏码是一出接一出,剧本却很复杂,看上去,也不是啥好剧本,上个赛季,上海男篮引进冯莱,一时间呼声很高,马竞雷吉隆女友一袭白裙美艳绝伦!欧冠波尔图vs马竞或21欧冠波尔图vs马竞明天202211020145思路打胜平,主队不败目前平手比分20,21或11欧冠继续敲响,出线形势基本浮出水面,马竞能否守住第三名,收获欧联淘汰赛资格?哈喽朋友们西媒体育科技吸引精英运动员据西班牙人报网站10月9日报道,在困扰全球企业界的不确定性当道之际,被称为电子竞技和体育科技的项目正在经历一段美好的时光。它们虽不能幸免于俄乌冲突导致的宏观经济困难通货膨胀螺旋和供栗战书会见越共中央总书记央广网北京11月2日消息据中央广播电视总台中国之声新闻和报纸摘要报道,11月1日,全国人大常委会委员长栗战书在钓鱼台国宾馆会见越共中央总书记阮富仲。栗战书表示,习近平总书记10月3调皮!孙颖莎抱着奖杯,丢下王楚钦就走,大头有点尴尬经过几天紧张地角逐,新乡WTT世界杯决赛落下帷幕,最终中国国乒再次笑到了最后,孙颖莎43战胜陈梦,夺得女单冠军,而王楚钦则是42战胜日本名将张本智和,夺得男单冠军!这也是继澳门冠军玖月奇迹离婚后复婚,参加活动表现尴尬,网友这才是商业式复婚众所周知,在娱乐圈里艺人的口碑非常重要,要想赚得多口碑就必须好。为了打造出一个良好的人设形象,便于自己吸金赚钱,有些艺人简直煞费苦心天天在演戏,其实也是够累的了。而对于那些明星夫妻喜提新车不容易幸得记账来助力!金秋收获十月秋风把世间的一切都调成了丰收的盛景收获,是这个季节最大的主题阿红家也迎来了今年最大的一笔收成她心心念念期盼已久的新车这辆新车的到来可不容易还得从阿红家被选做国家记账户说05后文学真正的分别没有长亭古道,没有桃花潭水,只是在同样洒满阳光的早晨,有人悄悄离去,任你怎么呼喊,都没有一点回音。比如,最疼爱我的爷爷。在我很小的时候,他带着病痛,带着遗憾和对这个世界的大S全家福曝光,不见汪小菲儿女,具俊晔与S妈首同框11月2日,大S的全家福曝光,除了汪小菲的一对儿女没有露面之外,大S家人全在,也是新女婿具俊晔和S妈的首次同框!从曝光的照片上看,具俊晔和之前露面,商演上没有什么变化,大S却和不久鹤壁云梦山有中华第一古军校云蒸霞蔚,泉涌涧飞,河南省鹤壁市淇县的云梦山险奇秀幽,九道蜿蜒而来的山梁,宛如九条从天而降的神龙,聚汇于此,人称九龙聚汇。图说云梦山古军校来源网络云梦山,又名青岩山,位于河南省鹤壁
有胃病的人可以吃馒头吗?其实真正不能吃的是这5种食物如今,胃部疾病的患者越来越多,正在呈逐年上升的趋势发展,大部分诱发的人群为2540岁的成年女性。正所谓10人9胃,根据胃病防治指南数据最新统计我国胃病的患发率高达86。3,综合人数外媒梅德韦杰夫称应该在顿巴斯举行公投据塔斯社莫斯科9月20日报道,俄罗斯联邦安全会议副主席德米特里梅德韦杰夫表示,有必要在顿涅茨克和卢甘斯克举行加入俄罗斯的全民公投。梅德韦杰夫在电报社交平台发文称全民公投不仅对于系统粮食干燥机价格多少钱一台?粮食干燥机要怎么挑选?当下秋粮采收季节,同时也伴随着秋雨绵绵的天气,如果不能及时地将这些粮食水分降到安全水分的范围,会对粮食造成非常大的损失。而解决方法也很简单,一台粮食干燥机就能全部搞定。那么粮食干燥重罚3660万!这家A股实控人被十年市场禁入中国基金报记者颜颖债券违约近四年后,曾经红极一时的知名日化企业迎来了监管调查结果。早在2021年1月,因涉嫌债券欺诈发行及信披违法,洛娃集团被证监会立案调查。日前,洛娃集团披露公告中兴通讯最新公告监事李妙娜的母亲短线交易及致歉中兴通讯公告,2022年9月20日,公司收到公司监事李妙娜出具的关于本人亲属买卖中兴通讯股票构成短线交易的情况说明及致歉,获悉李妙娜的母亲李如香于2022年9月14日至2022年9马克吐温自传妈妈艰苦的一生我妈妈是在一八九年十月的时候去世的,那年她八十八岁,真是高龄。她的一生,可真是艰苦奋斗的一生,因为四十岁的时候,她的身体便已经很虚弱了,被认为患上了不治之症,不久于人世了。二十五岁持续成长之路凯茜妈妈的涅槃重生你有没有设想过你的未来会如何度过?我在体制内工作近40年,沉溺于这种安逸的舒适区,得过且过,习惯接受符合自己世界观的东西,做一天和尚撞一天钟,在退休前的若干年更是如此。退休后更是浑花钱如流水的7位女星再有钱也养不起,她们的消费高到你想不到文闲撰奇侠编辑闲撰奇侠王传君曾经在一次采访中表示,自己在最穷的时候,卡里只剩下一百万,这让他感到非常焦虑。这就是娱乐圈的现状,当明星还在为自己只剩几百万发愁时,许多普通家庭可能一辈ampampquot红极一时ampampquot的9个组合,理念不合的背后,藏着多少不为人知的龌龊文大牌娱姐娱乐圈一直以来都不乏明星组合,从小虎队到She再到Twins,从羽泉到凤凰传奇再到筷子兄弟,他们几乎都引领了一个时代的音乐潮流。但事物发展的规律不容改变,所谓合久必分,分一生荒唐事,老来牛归群说牛群上不少朋友一直在留言,都很关心牛群,尤其是对牛群的现状十分好奇。看到这么多观众关注相声关心牛群,我是感觉特别欣慰,说明大家还是懂相声,追求真正的相声艺术,没有被现在的三俗相声演员所迷狂傲无知的张铁林,走到了今天这一步该怪谁9月份中旬,电视剧老家伙发布了首支预告,估摸着离正式播出日子也不远了。这部剧主演是张国立,王刚和张铁林。这三人自铁齿铜牙纪晓岚合作,到如今,仍然是不少人心目中的铁三角。先不说新剧如