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

爱上源码,重学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
悬崖解析05周乙居然没有死?第二个版本的结局谁看过?本图片来源于网络真是不查不知道,一查吓一跳。万万没想到,电视剧悬崖居然会有两个截然不同的版本,而在另一个版本中,周乙并没有死,他的结局是开放式的。据说,改版结局会令人充满希望。不知面向未来!坦克世界新的一年将会有哪些新气象?速看2023前瞻(上)各位车长老爷们大家好呀!就在上周,WG发布了视频坦克世界的未来,一直对坦克世界新版本内容十分关心的小编也是第一时间粗(逐)略(帧)观看了这段时长2分多的视频。视频中要素不少,接下来郑钦文获赛事官方重点推介她和阿扎伦卡莱巴金娜一样重要!2023赛季即将开启,郑钦文也结束了自己的冬训并抵达了新赛季首站比赛举办地阿德莱德。值得一提的是,赛事主办方也非常重视郑钦文能够参赛,在官方介绍时直接将郑钦文与莱巴金娜阿扎伦卡等大请问什么是养生?养生有年龄段的划分吗?这几年随着社会的变化,大家对养生有了更深入的了解和清楚点认知,比如以前说起养生,大家就会觉得这是中老年人才会去做的事情,无非就是端着茶杯泡着不知道里面是什么的茶,喝一口品一品,有的中国将于2027年发射载人飞船登月中国首次载人登月任务最早将于2027年发射,使用的是新型航天器和长征五号运载火箭。用于将人员和货物运送到中国月球和火星的新型火箭。照片中国航天921运载火箭是长征五号运载火箭的别称11月访日外国人100万,明年初中国若废除入境隔离,访日游将会井喷据共同社报道,日本政府观光局(JNTO)21日出炉的数据显示,日本新冠限制措施解除后第一个月(11月)的入境游客人数跃升至近100万人。这是新冠疫情的影响开始显现的2020年2月(年度通胀率或达99!时隔36年捧起大力神杯,阿根廷经济能获益吗?时隔36年捧起大力神杯,这对深陷通胀压力的阿根廷意味着什么?一项最新的研究显示,世界杯夺冠国可能会因世界杯效应收获额外的经济增长,不过这还取决于该国的经济现状等多重因素。而对于世界天问詹姆斯韦伯太空望远镜发射一周年人类正在更加了解宇宙封面新闻记者闫雯雯吴德玉生日快乐,詹姆斯韦伯太空望远镜!12月25日,是詹姆斯韦伯太空望远镜升空一周年的日子。在这一年,韦伯望远镜为人类展现了宇宙的波澜壮阔和神秘,为人类探索宇宙开詹姆斯哈登拿下201121助攻数创生涯新高北京市12月24日,NBA继续进行,费城76人主场迎战洛杉矶快船,最终76人119114逆转战胜快船,取得7连胜。本场比赛,詹姆斯。哈登出战41分钟,17投6中,其中三分球6投3中他是川大机器人战队最强主帅,带领战队屡屡夺冠!近几年,川大学子在全国顶级机器人大赛中获得了瞩目的成绩,包括全国大学生机器人大赛ROBOCON全国大学生机器人大赛RoboMaster机器人世界杯赛RoboCup在今年第二十一届全生活不是不轻松,就怕走不远怡耕园ROYYA2133F羽绒服又是冬风寒,至年关。忙忙碌碌倦收尾,急急赶赶翻新篇,处处有喟然!01hr人,相互帮扶才多温暖回首这一年,团结的意义,每个人都在心中暗赞没有人人自律,
雷军宣称对标苹果,网友我看你还抄袭华为欧界报道在今年12月28日的小米发布会上面,小米发布了几款12系列的新机。而且12系列出厂将搭载MIUI13的系统。雷军高调宣布,未来对标苹果,不负米粉支持雷军高调的表示未来将继续过年想给父母换一部新手机,不知道买哪款?本文帮你精挑细选了4款最近很多人问我马上就要过年了,想给家里的父母或者老人买一部新手机,让他们刷短视频的时候能舒服一点,不要那么卡了,另外最好价格便宜一点,性价比高一点,请问有没有推荐的?很多年轻人在外游戏账号长期不用会被删除?你有没有思考过一个问题如果游戏账户长期没有使用,会不会被删除?这听上去匪夷所思,游戏账户是我的,凭什么被删除?但从现实的角度来说,这完全有可能。据海外知名科技媒体PCWorld报道2021冰雪仙境皮肤免费送守望先锋玩家直呼不给钱不好意思无论你玩的是什么游戏,只要里面有皮肤系统,相信里面都绝对会有一个系列跟冬天有关。西方的圣诞节元旦和中国的春节全都在隆冬时节,再加上冬天特有的冰雪风景,就组合出来了像什么英雄联盟的冰盘点刺客信条系列那些有趣的梗(2)上次写了一期关于刺客信条系列的游戏梗,大家表示很多梗没说到,这次再补充一期。老色鬼指的是刺客信条2刺客信条兄弟会刺客信条启示录的主角艾吉奥,育碧通过三部曲和一部微电影让玩家见证了艾现役版阿德里亚诺加强归来!实况足球手游升级后第三期精选简析上周四出现在dp8。0数据库的巴甲精选,是v5。6。0版本推出的第三期精选卡包,正常的上架时间应该是明天。届时国服是否同步推出,目前尚不知晓。虽然这期卡包中球员的整体质量略低于前两瓜保熟吗?央视2021清明上河黑神话悟空入画央视今日分享了2021年版的清明上河图,其中把一整年的重要新闻事件及各种热点融汇进了画面。图中有不少的游戏动漫电影网络热梗元素等,大家一起来找2021年的各种彩蛋吧2021版清明上吃鸡新转盘军需实战效果!披风略显多余,头盔建模惨不忍睹务实不浮夸!我是你们的情报小能手,微笑十倍镜。吃鸡手游和平精英新军需虽然没有官宣,但是已经被各路大神曝光出来!新皮肤不仅采用了古龙的主题,而且还祭出AWM的新款特效皮肤,实装的表现所有限定忍者任选其一,这还真是头一次,火影之路拿到霸体了吗?火影忍者手游的六周年庆活动已经正式开启,目前主要是集中在倒计时火影之路祈愿分享这三个活动,在后面还会有其他的一些活动陆续上线。倒计时活动没有什么特别需要说明的地方,就是一个登录领取手游行业的黄金期每个行业都有每个行业的黄金阶段,像我们的手游行业也不例外,今天就来跟大家说一下手游行业的春天,也就是我们行业的黄金阶段。手游行业的黄金期其实近在咫尺,就是我们过年这段时间,为什么这堪称国内地第一狗仔让娱乐圈闻风丧胆的卓伟怎么突然消失了?为什么卓伟突然就消失了?印象中认识卓伟,还是在各媒体推送的新闻上,以及流传于民间的各种大料,就连村口的大妈们(简称村方圆几里最优质情报局)也在谈这个明星怎么怎么的?那个明星怎么怎么