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;@Servicepublic 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"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 使 AspectJ 的注解起作用 --> <aop:aspectj-autoproxy/> <!-- 扫描带有注解的类,交给ioc容器治理 --> <context:component-scan base-package="com.spring.test.aop"/></beans>
常见谬误
配置文件不足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 AOP2、AspectjSpring AOP的实现没有AspectJ弱小所以,Spring把Aspectj给集成(如果用,还须要独自引jar)进来了然而;spring aop已能满足咱们的需要在进行开发时候,这两个框架是齐全兼容的说白了,就是两个框架能一起应用,就看你我的项目需要用到的哪种水平了简略的;spirng aop够用了,然而spring aop借助了aspectj的注解性能,在高级点,比方切面很多,上万个,这是就要用到aspectj的高级性能了
区别:AspectJ应用的是编译期和类加载时进行织入,Spring AOP利用的是运行时织入
依赖:如果应用@Aspect注解,在xml里加上<aop:aspectj-autoproxy />。然而这须要额定的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.newProxyInstance
0)回顾助学
简略回顾一下,留神,这里不仅仅是学习代理模式,还必须搞懂它的实现形式,尤其是动静代理。
开始之前,咱们先必须搞懂一件事件:
那就是:代理模式
设计模式【动态代理】 【动静代理】 回顾 (见第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初始化阶段拆卸进了spring
4)代理对象验证
tips:
后面创立实现了代理对象,上面咱们看下它调用的时候,是不是走了代理
这是咱们一开始的后果,上面咱们来再debug到work办法时,点击debug into试试,发现调到哪里去了???
论断:
没错!代理对象精准的调用了JdkDynamicAopProxy外面的invoke办法
这阐明jdk动静代理失效,然而它生成的proxy字节码在jvm里,咱们是看不到的,怎么破?
arthas上!
2)Arthas代理类验证
arthas,阿里神器,主页:https://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动静代理类!
当初调用关系明确了,接下来,咱们就来剖析下这个invoke
1.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<Object> 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
InvocationHandler
1)代理父类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的源码范畴内,感兴趣的同学本人理解一下
参考资料:https://www.baiyp.ren/JAVA%E5...
代理对象外部生成流程调用链,如下图
对象生成过程(外围)
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<Void>() { 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<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> 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<Class<?>, 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,全部都是在下面生成的
最终这个才是实例化的对象(如上)
如果本文对您有帮忙,欢送
关注
和点赞
`,您的反对是我保持创作的能源。转载请注明出处!