㈠ 动态代理jdk和cglib的区别
JDK和CGLIB生成动态代理类的区别
关于动态代理和静态代理
当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象。
按照代理对象的创建时期不同,可以分为两种:
静态代理:事先写好代理对象类,在程序发布前就已经存在了;
动态代理:应用程序发布后,通过动态创建代理对象。
静态代理其实就是一个典型的代理模式实现,在代理类中包装一个被代理对象,然后影响被代理对象的行为,比较简单,代码就不放了。
其中动态代理又可分为:JDK动态代理和CGLIB代理。
1.JDK动态代理
此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。
JDK动态代理只能针对实现了接口的类生成代理。
2.CGLIB代理
CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,
主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
JDK动态代理和CGLIB代理生成的区别
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
PS:final 所修饰的数据具有“终态”的特征,表示“最终的”意思:
final 修饰的类不能被继承。
final 修饰的方法不能被子类重写。
final 修饰的变量(成员变量或局部变量)即成为常量,只能赋值一次。
final 修饰的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有 一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用。
final 修饰的局部变量可以只声明不赋值,然后再进行一次性的赋值。
参考代码
CGLIB:
1
2
3
4
5
6
7
8
public Object createProxyObject(Object obj) {
this.targetObject = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(this);
Object proxyObj = enhancer.create();
return proxyObj;// 返回代理对象,返回的对象其实就是一个封装了“实现类”的代理类,是实现类的实例。
}
JDK:
1
2
3
4
5
public Object newProxy(Object targetObject) {// 将目标对象传入进行代理
this.targetObject = targetObject; <br> //注意这个方法的参数,后面是类实现的接口
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);// 返回代理对象
}
在代码中可以看到,在生成代理类时,传递的是实现类所实现的接口 targetObject.getClass().getInterfaces(),所以JDK只能对于接口进行做代理。如果换成类的话,则会抛java.lang.ClassCastException异常。
在Spring的源码中,可以看到很多生成代理类的代码。
动态代理的应用
AOP(Aspect-OrientedProgramming,面向切面编程),AOP包括切面(aspect)、通知(advice)、连接点(joinpoint),实现方式就是通过对目标对象的代理在连接点前后加入通知,完成统一的切面操作。
实现AOP的技术,主要分为两大类:
一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。
默认的策略是如果目标类是接口,则使用JDK动态代理技术,如果目标对象没有实现接口,则默认会采用CGLIB代理。
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
㈡ SpringBoot cglib代理出来的Bean的属性如何修改
$就是表示他是spring通过代理来产生的bean。具体修改要结合源码。
㈢ 有那位大佬能看出来上面的字是什么,或者可以用ps变得清晰一点
一 . 前言
BeanPostProcessor 是 Spring 的核心组件之一 , Bean 实现 BeanPostProcessor 可以实现很多复杂的功能
二 . PostProcessor 的结构 2.1 接口方法
该接口中主要提供了2种 , 其中提供了前置调用和后置调用 . 还可以看到 , 这里通过 default 修饰 , 所以并不是强制重写的
public interface BeanPostProcessor { default Object (Object bean, String beanName) throws BeansException { return bean; } default Object (Object bean, String beanName) throws BeansException { return bean; }}2.2 常见实现类
这里可以看到 , AOP , 定时 , 配置等都实现了相关的集成
三 . Spring 源码深入分析 3.1 使用案例
这一部分来看一下 Spring 内部是如何使用 PostProcessor 特性的 , 这一部分以 为例.
@Overridepublic Object (Object bean, String beanName) { // 可以看到 , 前置处理并没有太多处理 , 直接返回 return bean;}@Overridepublic Object (Object bean, String beanName) { if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheler || bean instanceof ScheledExecutorService) { // Ignore AOP infrastructure such as scoped proxies. return bean; } // Step 1 : 开始特殊处理 Class targetClass = AopProxyUtils.ultimateTargetClass(bean); if (!this.nonAnnotatedClasses.contains(targetClass) && AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheled.class, Scheles.class))) { // Step 2 : 获取 Method 对应 Scheled 的集合 Map> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (MethodIntrospector.MetadataLookup>) method -> { Set scheledAnnotations = AnnotatedElementUtils.( method, Scheled.class, Scheles.class); return (!scheledAnnotations.isEmpty() ? scheledAnnotations : null); }); if (annotatedMethods.isEmpty()) { this.nonAnnotatedClasses.add(targetClass); } else { // Step 3 : 方法不为空 , 执行 Process annotatedMethods.forEach((method, scheledAnnotations) -> scheledAnnotations.forEach(scheled -> processScheled(scheled, method, bean))); } } return bean;}
PS : 后面就不看了 , 主要就是通过一个 ScheledTask 运行 Runable 对象
从具体的使用上 , 不难看出 , 他是在创建Bean的时候去做补充的操作 , 那么下面来看一下具体的处理流程
这个流程在Bean 初始化流程 中进行了全面的跟踪 , 这里更关注其中的一些细节
3.2 Before/After 调用流程
入口一 : # initializeBean 调用
其中流程也比较简单 , for 循环所有的 BeanPostProcessors 进行处理
3.3 BeanPostProcessors 的管理// 在 AbstractBeanFactory 中有一个集合用于管理 BeanPostProcessorprivate final List beanPostProcessors = new ();// 不论是哪种类型 ,都会通过以下2种方式进行调用 , 先删除, 后添加public void addBeanPostProcessors(Collection beanPostProcessors) { this.beanPostProcessors.removeAll(beanPostProcessors); this.beanPostProcessors.addAll(beanPostProcessors);}public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { this.beanPostProcessors.remove(beanPostProcessor); this.beanPostProcessors.add(beanPostProcessor);}
可以通过手动添加的方式 ,实现 BeanPostProcessor 的添加 , 参考对象 AbstractApplicationContext
protected void prepareBeanFactory( beanFactory) { // ............. // Configure the bean factory with context callbacks. beanFactory.addBeanPostProcessor(new (this)); // .............}
哈哈 , 又到了这个地方 , 之前看 Refresh 代码的时候就注意到了这个地方 , 会注册相关的 BeanPostProcessors
protected void registerBeanPostProcessors( beanFactory) { // Step 1 : 调用 进行注册 .registerBeanPostProcessors(beanFactory, this);}
源码注释非常清晰 ,这里就是简单翻译了一下
public static void registerBeanPostProcessors( beanFactory, AbstractApplicationContext applicationContext) { // postProcessor 数组列表 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); // processor 计数 int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; //注册BeanPostProcessorChecker,当bean在BeanPostProcessor实例化期间创建时,即当bean不符合所有BeanPostProcessors处理的条件时,该checker会记录信息消息 beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); // 区别 PriorityOrdered, internalPostProcessor List priorityOrderedPostProcessors = new ArrayList<>(); List internalPostProcessors = new ArrayList<>(); // 有序化 List orderedPostProcessorNames = new ArrayList<>(); List nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); priorityOrderedPostProcessors.add(pp); // 运行时用于合并bean定义的后处理器回调接口 if (pp instanceof ) { internalPostProcessors.add(pp); } } // 如果实现了 Ordered 接口 , 则需要有序处理 else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } } // 首先,注册实现prioritordered的BeanPostProcessors sortPostProcessors(priorityOrderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // 接下来,注册实现Ordered的BeanPostProcessors List orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String ppName : orderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); orderedPostProcessors.add(pp); if (pp instanceof ) { internalPostProcessors.add(pp); } } sortPostProcessors(orderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, orderedPostProcessors); // 现在,注册所有常规的BeanPostProcessors List nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof ) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // 最后,重新注册所有内部BeanPostProcessors sortPostProcessors(internalPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, internalPostProcessors); // 将用于检测内部bean的后处理器重新注册为ApplicationListeners,将其移动到处理器链的末端 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));}四 . 深入定制 4.1 添加 BeanPostProcessor
PS : 当然 , 这种方式过于复杂 , 适用场景不多
@Autowiredprivate DefaultListableBeanFactory beanFactory;@Bean(initMethod = "initMethod")public CommonService getCommonService() { // 通过 BeanFactory 进行添加 beanFactory.addBeanPostProcessor(new CustomizePostProcessor()); return new CommonService();}
上面分析的时候也看到 , 实现了接口就自动添加了
五 .问题补充 5.1 与四种常见的初始化方式有什么区别 ?
回顾一下四种初始化运行的方式 :
实现 InitializingBean 接口方法 afterPropertiesSet
实现 ApplicationRunner 接口方法 run(ApplicationArguments args)
方法标注注解 @PostConstruct
@Bean(initMethod = "initMethod") 通过注解指定
: ------> this is @PostConstruct <-------: ------> this is InitializingBean <-------: ------> this is in @Bean(initMethod = "initMethod") <-------: ------> this is <-------: ------> this is <-------: ------> this is <-------: ------> this is <-------: ------> this is <-------: ------> this is <-------: Started DemoApplication in 0.822 seconds (JVM running for 2.597): ------> this is ApplicationRunner :getCommonService <-------
调用次数: 四种方法只会调用一次 ,而 和 会在每个Bean创建的时候调用
调用时机: 集中在每个 BeaninitializeBean环节调用
六. 我们能用它干什么 ?
这里不讨论是否真的有场景会使用 ,只是发散思维 , 去思考如何利用他的特点去做点什么
6.1 结合设计模式
代理是 postProcess 最合适的使用之一 , AOP 即使用了他的特性进行的代理 , 我们可以模拟 AOP , 去做一个更偏向于业务的静态代理模式 .
同时也可以实现装饰器模式 , 对 Bean 的处理进行加强 .
// 统一接口public interface Sourceable { void method();}// 对应实现类 (实际业务类)@Servicepublic class Source implements Sourceable { @Override public void method() { System.out.println("the original method!"); }}
public class AopProxyImpl implements Sourceable { private Sourceable source; public AopProxyImpl(Sourceable source) { super(); // 注意 ,代理模式是在代理类中创建一个对象 // this.source = new Source(); // 修饰模式是传入对应的对象 this.source = source; } @Override public void method() { before(); source.method(); atfer(); } private void atfer() { System.out.println("after proxy!"); } private void before() { System.out.println("before proxy!"); }}
@Servicepublic class AopPostProcessor implements BeanPostProcessor { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public Object (Object bean, String beanName) throws BeansException { logger.info("------> AopPostProcessor <-------"); return bean; } @Override public Object (Object bean, String beanName) throws BeansException { logger.info("------> AopPostProcessor <-------"); if (bean instanceof Sourceable) { logger.info("------> AopPostProcessor build Aop <-------"); AopProxyImpl proxy = new AopProxyImpl((Sourceable)bean); return proxy; } return bean; }}
这里如果没有 , 则一定会抛出 , 因为 Spring 会去坚持类是否匹配
这里我们属于静态代理 , 其实还可以更活用的使用cglib 动态代理
还有很多种整合设计模式的方式可以灵活使用
6.2 Manager 管理指定类型对象
这种方式是在 postProcessor 中对指定的Bean 进行一个管理记录
大概的使用方式是准备一个BeanManager , 然后在 postProcessor 中进行管理
@Componentpublic class BeanManager { // 对特殊的Bean 进行分组 private static Map typeOne = new ConcurrentHashMap(); private static Map typeTwo = new ConcurrentHashMap(); // PS : 这种方式适合处理更复杂的场景 , 简单场景可以考虑通过 ApplicationContext.getBean 获取对应的类}public Object (Object bean, String beanName) throws BeansException { logger.info("------> ManagerPostProcessor <-------"); if (bean instanceof TypeBean) { logger.info("------> AopPostProcessor build Aop <-------"); beanManager.getTypeOne().put(beanName, (TypeBean) bean); } return bean;}// PS : 实际上和注入的是同一个对象------> typeOne.get("typeBeanImpl") == (typeBean) :true <-------
这是最简单的使用方式 , 相对更复杂的还可以整合注解 , 整合接口或者父类 , 或者仅记录class信息等方式 ,达到自己的业务效果
6.3 注入特殊属性
从图片中我们可以看到 , BeanPostProcessor 是在 PopulateBean 环节之后进行处理的 , 那么我们可以通过这个环节 , 对 Bean 中的属性进行修饰 , 常见的使用想法包括 :
为特定属性设置动态代理
从 Remote 端获取属性 , 并且设置
这个就比较好理解了 , 实际上这个时候包括RestTemplate 都已经加载完成 , JDBC 也可以使用 , 在这个时候完全可以从远端获取配置信息
// 作用一 : 使用 JDBC (JPA 应该也已经加载可用) , 从数据库获取配置 , 判断当前类是否有特定注解 , 刷新注解的配置// 作用二 : 调用远端配置中心 , 自定义刷新配置// 作用三 : 刷新特殊的属性或者对象等等
特殊对象的刷新有多种任意的使用 , 可以根据自己的业务灵活运用
6.4 其他思路
这里只是抛砖引玉 , 欢迎大佬们提出自己的想法
重构属性
定制化过程中对类进行覆盖
总结
主要在 initializeBean 调用环节
除了initializeBean外还在多个环节被调用, 包括 等等几个 Factory 去实例化的过程中
避免循环 , ManagerPostProcessor 不会处理自己
BeanPostProcessor 在每个 bean 创建时都会调用 ,过多会影响启动效率
BeanPostProcessor 主要在populateBean 之后 , 注意前后顺序
㈣ 求Java的在线学习系统源代码
Java 程序员必须收藏的资源大全
古董级工具
这些工具伴随着Java一起出现,在各自辉煌之后还在一直使用。
Apache Ant:基于XML的构建管理工具。
cglib:字节码生成库。
GlassFish:应用服务器,由Oracle赞助支持的Java EE参考实现。
Hudson:持续集成服务器,目前仍在活跃开发。
JavaServer Faces:Mojarra是JSF标准的一个开源实现,由Oracle开发。
JavaServer Pages:支持自定义标签库的网站通用模板库。
Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。
㈤ spring cglib代理耗时太久
由于整个Spring加载和实例化bean的过程原因导致。
Spring的bean的scope设置成prototype,就是每次都要创建一个实例,可以看一下Spring的源码,创建一个Spring实例是很复杂的一个过程,CGlib代理只是其中的一步,底层也是通过反射完成的,耗时的关键不在CGlib代理,而是在整个Spring加载和实例化bean的过程。
CGlib是属于动态管理,动态代理是在不改变原有代码的情况下上进行对象功能增强,使用代理对象代替原来的对象完成功能,进而达到拓展功能的目的。
㈥ spring源码怎么读
在我们的生活之中,有形形色色的万物(Object),有飞机,有汽车,有轮船,还有我这个沧海一粟的java讲师。
试问:了解飞机底层架构的人,就一定能把飞机开好吗?精通汽车的构造和底层原理的人,就保证开车不出事故吗?或者反过来问,一个多年的开出租车的老司机,技术非常娴熟,但是你问他汽车的架构和底层原理,你觉得有多少老司机能准确的答出来?
那就对了,了解了,读懂了spring底层源码的程序员,我们并不能保证他就是一个好程序员,我心中的一个好的程序员是要能够提出解决方案的程序员,就好比清华北大确实只是个过程,最后的目的,是为国家为人民做出奉献才是我们活着的价值。所以说我并不看好去阅读spring源码这个行为。闻道有先后,术业有专攻,我们不是提供底层技术的人!绝大多数的程序员,我以为正确的做法是站在巨人的肩膀上,把前辈留下来的精髓发扬光大才是正道,简而言之,把怎么用,和为什么要这么用学好才是大多数程序员学习技术的关键。就好比去学驾照的时候,教练只需要关心你是否会开车,是否遵守交通规则,并不关心你是否了解汽车是如何在路上跑起来的。
一个人的精力和时间都是有限的,分细了来说,开发者就是技术的使用者,重点在于如何正确熟练的使用,而不是一昧地去关心底层原理,如果要完全读懂spring源码,首先,英语就得很厉害吧,其次,得花多少时间?就算读懂了,那收获和付出也不成正比,大把大把的时间为何不花在java更多,更高级,更有意义的技术上呢?笔者此话并不是说spring源码读了没用,只是让我们的时间更有价值,例如分布式微服务,区块链技术,大数据,python,作为新时代的IT佼佼者,我们是否应该随着时代的脚步,迎接崭新的未来?我奉劝大多数人,不必去看spring源码,除非你确实感兴趣作为技术研究,这样是可以的。一个正常的工作者,是不建议花宝贵的时间去看的,那学生就更不应该去看了。此话怎讲?我以为,学生应以学业为重,士不可不弘毅,任重而道远,首先应该扩展其知识的广度,和技术的娴熟使用,再培养学生独立解决问题的能力,沟通协作的能力,最后才考虑技术的深度。
笔者在多年前也被面试过很多很多问题,但是从来没有一个面试官会问spring底层源码,曾经阿里巴巴的架构师也面试过我,直到后来,我成为了面试官,当我去面试别人的时候,我也不会去问spring源码这些东西,因为99.9%企业招人进来,是要做事实的,不是让你去研究一个技术,绝大多数更在乎的是利用现有的技术,去完成一些项目功能。除了世界上极少数着名的企业他们需要技术研究员,才对这些有所要求,那要招这类人才,估计至少也是名校研究生以上吧,所以普通程序开发者,我觉得学好技术的正确与熟练使用,才是重中之重!
不宜本末倒置,不宜好高骛远。笔者从事Java行业以来,真要说全阶段全程全栈都精通的人,应属风毛菱角,故此,更应让所学知识运筹帷幄,举一反三,千里炉火,万里纯青。以上篇幅在说我对阅读spring源码的一些强烈建议,最终结论,是不看、不读就此作罢。
那有的感兴趣的人非要阅读呢?
Ok,其实阅读spring源码并不算很难。
首先,应该去官网spring.io阅读写spring框架的理念,就好比读一本书,要阅读这本书的纲要,要明白为什么要设计spring架构。
然后,应该分模块阅读,先从核心模块开始阅读:如:
IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP.
那么在阅读的时候,英文能力差的话,可以使用翻译软件翻译成中文阅读,毕竟不是每个程序员英语都很棒,毕竟中国人看中国字更容易去理解。当然,有些翻译过来的并不准确,这个时候可以利用网络,去查看那段不理解的地方。
然后,把spring的jar包下载下来,利用IDEA打开,可以使用download资源,会发现,每个方法上面都会有详细的英文注释。一边看官方的说明,一边查看代码的编写,不难发现,非常多的代码写得很精辟,所以十分考验一个程序员的java基本功,特别是反射,JDK\CGLIB代理,和java常用的设计模式等。