- 浏览: 105867 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
sheungxin:
@仑山中鸟 @zl_xzl 不好意思,代码已丢。。。自己动手试 ...
自定义类加载器与spring的集成 -
昆仑山中鸟:
同求代码,看的云里雾里。自定义监听器WebContextLis ...
自定义类加载器与spring的集成 -
xzl_xzl:
代码都是片段,能否提供下具体的代码,正好我也有类似的需求。但我 ...
自定义类加载器与spring的集成 -
sheungxin:
@add2ws 我试下,多谢!
基于oracle的增量数据采集实现总结 -
add2ws:
用得着这么麻烦么,直接用kettle插入更新,配合oracle ...
基于oracle的增量数据采集实现总结
之前也学习过自定义类加载器,通过ClassLoader直接加载需要的类。但实际业务中启动入口常常不可控,比如实际业务中我们常常使用spring对类实例进行管理。如何在spring中集成自定义ClassLoader是需要我们考虑的问题。结合之前项目单机部署的一个方案,即class加密,自定义类加载器解密。因此,我们需要解决两个个问题:
1、自定义类加载器
2、spring与类加载器的集成
引入自定义的类加载器后,类加载器列表如下:
以上为tomcat下web项目的类加载器顺序:WebappClassLoader》URLClassLoader》ContainerClassLoader》AppClassLoader》ExtClassLoader》null
其中ContainerClassLoader为自定义的类加载器,为什么放在该位置?WebappClassLoader也有parent私有属性,在构造时赋值,与基类ClassLoader一样,插入自定义类加载器,改变类加载器父子关系的时两个私有属性都需要调整(没修改过来,后续再测试下)。因为不好修改且猜测WebappClassLoader》URLClassLoader放在一起比较好,暂时放在URLClassLoader后面。这样就可以成功加载加密的类,但需要注意加密后的类不能放在tomcat下,因为会校验class有效性。
通过上面测试代码,可以正常输出“Hello World!”
但还存在一个问题,如何把加载后的类交给spring进行管理?经测验通过xml配置的方式,bean已成功加载,已交由spring容器管理。
但通常使用注解的方式,通过component-scan进行代码扫描,经测试,无法扫描到项目外的class文件,因为外部class并不在当前classpath下,具体可参见源码:
因此需要把外部类路径加入当前classpath,可以自定义classloader,继承URLClassLoader,把外部类路径加入URL数组中,代码如下:
在自定义监听器WebContextListener中调用:
把自定义的DefinedURLClassLoader放在之前ContainerClassLoader的位置,类加载顺序如下:
两者的区别在于继承的类不同,前者继承ClassLoader,后者继承URLClassLoader(URLClassLoader extends SecureClassLoader extends ClassLoader),后者好处在于可以自定义URL地址,同时具体类加载器的解析功能。下面可以打印出更改后的url,如下:
输出如下:file:/D:/MyTest/classEncrypt/extendPro/
可以看见新增加的路径,这时spring扫描时就可以找到外部类文件,可以追踪ClassPathScanningCandidateComponentProvider类,在Resource中可以看到需要加载的类文件
重新启动服务,同样失败,错误如下:
由报错可以看出HelloWorldService.class解析失败,定位到183行: switch (b[index]) ,原因在于class文件被加密,spring代码扫描是通过对编译好的字节码进行处理,ClassReader是字节码访问类,无法正确编译加密后的class文件,因此就报了以上错误。错误定位源码如下:
1、自定义类加载器加载指定外部class
2、自定义类加载器与spring集成,加载的实例交由spring进行管理
未完美解决问题:加密的外部class文件,通过xml配置的方式可以成功加载并交由spring容器进行管理;但注解、spring代码扫描(component-scan)的方式无法正确扫描加密后的类,需要解决ClassReader.readClass(),对读取到的字节数组进行处理后再交由ClassReader编译,整个类调用流程如下:
如果自定义ClassReader,下面我们就需要寻找spring可扩展点,崩溃中...有兴趣的可以试试
http://blog.csdn.net/u014723529/article/details/41958841
动态修改当前ClassLoader
http://bingobird.iteye.com/blog/606116
额外JAR包的加载问题
http://www.inter12.org/archives/702
1、自定义类加载器
2、spring与类加载器的集成
- spring与类加载器的集成
ContainerClassLoader.init();实际上是在当前类加载列表中寻找一个合适的位置插入,通过反射机制插入并更改类加载器父子关系。具体可了解jvm类加载机制及tomcat类加载器机制,两者是有差异的
- 自定义类加载器
package com.thinkgem.jeesite.test.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.net.URLClassLoader; import java.util.HashMap; import java.util.Map; public class ContainerClassLoader extends ClassLoader { private Map<String, Class<?>> loadedClasses = new HashMap<String, Class<?>>(); private static ContainerClassLoader INSTANCE; private ContainerClassLoader() { super(ContainerClassLoader.class.getClassLoader().getParent().getParent()); } /** * 初始化 */ public static void init() { INSTANCE = new ContainerClassLoader(); try { INSTANCE.addThisToParentClassLoader(ContainerClassLoader.class .getClassLoader().getParent()); } catch (Exception e) { System.err.println("设置classloader到容器中时出现错误!"); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (loadedClasses.containsKey(name)) { return loadedClasses.get(name); } return super.loadClass(name, resolve); } /** * 将this替换为指定classLoader的parent ClassLoader * * @param classLoader */ private void addThisToParentClassLoader(ClassLoader classLoader) throws Exception { URLClassLoader cl=(URLClassLoader) ClassLoader.getSystemClassLoader(); Field field = ClassLoader.class.getDeclaredField("parent"); field.setAccessible(true); field.set(classLoader, this); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String fileName="D:/MyTest/classEncrypt/extendPro/"+name.replace(".", File.separator)+".class"; File f=new File(fileName); if(f.exists()){ FileInputStream fis =null; ByteArrayOutputStream bos = null; try { //将class文件进行解密 fis = new FileInputStream(fileName); bos = new ByteArrayOutputStream(); encodeAndDecode(fis,bos); byte[] classByte = bos.toByteArray(); //将字节流变成一个class return defineClass(name,classByte,0,classByte.length); } catch (Exception e) { e.printStackTrace(); }finally{ if(bos!=null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis!=null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } return null; } /** * 加密和解密算法 * @param is * @param os * @throws Exception */ private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{ int bytes = -1; while((bytes = is.read())!= -1){ bytes = bytes ^ 0xff;//和0xff进行异或处理 os.write(bytes); } } public static void encodeClass() throws Exception{ String srcPath = "D:/sheungxin/workspaces/jeesite-master/src/main/webapp/WEB-INF/classes/com/thinkgem/jeesite/test/service/HelloWorldService.class"; String desPath = "d:/";//ClassLoaderAttachment.class输出的路径 String desFileName = srcPath.substring(srcPath.lastIndexOf("/")+1); String desPathFile = desPath + "/" + desFileName; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(desPathFile); //将class进行加密 encodeAndDecode(fis,fos); fis.close(); fos.close(); } }
引入自定义的类加载器后,类加载器列表如下:
#tomcat用WebappClassLoader加载应用,其父类加载器为URLClassLoader WebappClassLoader context: jeesite delegate: false ----------> Parent Classloader:java.net.URLClassLoader@3af49f1c #ClassLoader的子类,用于从指定JAR文件或目录中加载类和资源,ClassLoader只能读取classpath下文件 java.net.URLClassLoader@3af49f1c com.thinkgem.jeesite.test.utils.ContainerClassLoader@238281e1 sun.misc.Launcher$AppClassLoader@5c647e05 sun.misc.Launcher$ExtClassLoader@68837a77 null
以上为tomcat下web项目的类加载器顺序:WebappClassLoader》URLClassLoader》ContainerClassLoader》AppClassLoader》ExtClassLoader》null
其中ContainerClassLoader为自定义的类加载器,为什么放在该位置?WebappClassLoader也有parent私有属性,在构造时赋值,与基类ClassLoader一样,插入自定义类加载器,改变类加载器父子关系的时两个私有属性都需要调整(没修改过来,后续再测试下)。因为不好修改且猜测WebappClassLoader》URLClassLoader放在一起比较好,暂时放在URLClassLoader后面。这样就可以成功加载加密的类,但需要注意加密后的类不能放在tomcat下,因为会校验class有效性。
HelloWorldService helloWorldService=new HelloWorldService(); helloWorldService.say();
通过上面测试代码,可以正常输出“Hello World!”
但还存在一个问题,如何把加载后的类交给spring进行管理?经测验通过xml配置的方式,bean已成功加载,已交由spring容器管理。
<bean id="helloWorldService" class="com.thinkgem.jeesite.test.service.HelloWorldService"/>
但通常使用注解的方式,通过component-scan进行代码扫描,经测试,无法扫描到项目外的class文件,因为外部class并不在当前classpath下,具体可参见源码:
ClassPathBeanDefinitionScanner.doScan ClassPathScanningCandidateComponentProvider.findCandidateComponents
因此需要把外部类路径加入当前classpath,可以自定义classloader,继承URLClassLoader,把外部类路径加入URL数组中,代码如下:
package com.thinkgem.jeesite.test.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class DefinedURLClassLoader extends URLClassLoader{ public DefinedURLClassLoader(URL[] urls) { super(urls); } public DefinedURLClassLoader(URL[] urls, ClassLoader classLoader) { super(urls, classLoader); try { //添加外部类路径 this.addURL(new URL("file:/D:/MyTest/classEncrypt/extendPro/")); init(); } catch (MalformedURLException e) { e.printStackTrace(); } } public void init() { try { addThisToParentClassLoader(DefinedURLClassLoader.class .getClassLoader().getParent()); } catch (Exception e) { System.err.println("设置classloader到容器中时出现错误!"); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { return super.loadClass(name, resolve); } /** * 将this替换为指定classLoader的parent ClassLoader * * @param classLoader */ private void addThisToParentClassLoader(ClassLoader classLoader) throws Exception { Field field = ClassLoader.class.getDeclaredField("parent"); field.setAccessible(true); field.set(classLoader, this); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String fileName="D:/MyTest/classEncrypt/extendPro/"+name.replace(".", File.separator)+".class"; File f=new File(fileName); if(f.exists()){ FileInputStream fis =null; ByteArrayOutputStream bos = null; try { //将class文件进行解密 fis = new FileInputStream(fileName); bos = new ByteArrayOutputStream(); encodeAndDecode(fis,bos); byte[] classByte = bos.toByteArray(); //将字节流变成一个class return defineClass(name,classByte,0,classByte.length); } catch (Exception e) { e.printStackTrace(); }finally{ if(bos!=null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis!=null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } return null; } /** * 加密和解密算法 * @param is * @param os * @throws Exception */ private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{ int bytes = -1; while((bytes = is.read())!= -1){ bytes = bytes ^ 0xff;//和0xff进行异或处理 os.write(bytes); } } }
在自定义监听器WebContextListener中调用:
URLClassLoader ucl=(URLClassLoader) this.getClass().getClassLoader().getParent(); DefinedURLClassLoader durl=new DefinedURLClassLoader(ucl.getURLs(), ucl.getParent());
把自定义的DefinedURLClassLoader放在之前ContainerClassLoader的位置,类加载顺序如下:
WebappClassLoader context: jeesite delegate: false ----------> Parent Classloader:java.net.URLClassLoader@3af49f1c #ClassLoader的子类,用于从指定JAR文件或目录中加载类和资源,ClassLoader只能读取classpath下文件 java.net.URLClassLoader@3af49f1c com.thinkgem.jeesite.test.utils.DefinedURLClassLoader@238281e1 sun.misc.Launcher$AppClassLoader@5c647e05 sun.misc.Launcher$ExtClassLoader@68837a77 null
两者的区别在于继承的类不同,前者继承ClassLoader,后者继承URLClassLoader(URLClassLoader extends SecureClassLoader extends ClassLoader),后者好处在于可以自定义URL地址,同时具体类加载器的解析功能。下面可以打印出更改后的url,如下:
for(URL u:durl.getURLs()){ System.out.println(u.toString()); }
输出如下:file:/D:/MyTest/classEncrypt/extendPro/
可以看见新增加的路径,这时spring扫描时就可以找到外部类文件,可以追踪ClassPathScanningCandidateComponentProvider类,在Resource中可以看到需要加载的类文件
public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + this.resourcePattern; Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); ... }
重新启动服务,同样失败,错误如下:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [D:\MyTest\classEncrypt\extendPro\com\thinkgem\jeesite\test\service\HelloWorldService.class]; nested exception is java.lang.ArrayIndexOutOfBoundsException: 658 at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:303) at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:248) at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:87) ... Caused by: java.lang.ArrayIndexOutOfBoundsException: 658 at org.springframework.asm.ClassReader.<init>(ClassReader.java:183) at org.springframework.asm.ClassReader.<init>(ClassReader.java:153) at org.springframework.asm.ClassReader.<init>(ClassReader.java:426) at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:53) at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:98) at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:102) ... org.apache.tomcat.util.bcel.classfile.ClassFormatException: It is not a Java .class file ... MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); SimpleMetadataReader》ClassReader
由报错可以看出HelloWorldService.class解析失败,定位到183行: switch (b[index]) ,原因在于class文件被加密,spring代码扫描是通过对编译好的字节码进行处理,ClassReader是字节码访问类,无法正确编译加密后的class文件,因此就报了以上错误。错误定位源码如下:
public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version /* SPRING PATCH: REMOVED FOR FORWARD COMPATIBILITY WITH JDK 9 if (readShort(off + 6) > Opcodes.V1_8) { throw new IllegalArgumentException(); } */ // parses the constant pool items = new int[readUnsignedShort(off + 8)]; int n = items.length; strings = new String[n]; int max = 0; int index = off + 10; for (int i = 1; i < n; ++i) { items[i] = index + 1; int size; switch (b[index]) { case ClassWriter.FIELD: case ClassWriter.METH: case ClassWriter.IMETH: case ClassWriter.INT: case ClassWriter.FLOAT: case ClassWriter.NAME_TYPE: case ClassWriter.INDY: size = 5; break; case ClassWriter.LONG: case ClassWriter.DOUBLE: size = 9; ++i; break; case ClassWriter.UTF8: size = 3 + readUnsignedShort(index + 1); if (size > max) { max = size; } break; case ClassWriter.HANDLE: size = 4; break; // case ClassWriter.CLASS: // case ClassWriter.STR: // case ClassWriter.MTYPE default: size = 3; break; } index += size; } maxStringLength = max; // the class header information starts just after the constant pool header = index; } ... private static byte[] readClass(final InputStream is, boolean close) throws IOException { if (is == null) { throw new IOException("Class not found"); } try { byte[] b = new byte[is.available()]; int len = 0; while (true) { int n = is.read(b, len, b.length - len); if (n == -1) { if (len < b.length) { byte[] c = new byte[len]; System.arraycopy(b, 0, c, 0, len); b = c; } return b; } len += n; if (len == b.length) { int last = is.read(); if (last < 0) { return b; } byte[] c = new byte[b.length + 1000]; System.arraycopy(b, 0, c, 0, len); c[len++] = (byte) last; b = c; } } } finally { if (close) { is.close(); } } }
- 总结
1、自定义类加载器加载指定外部class
2、自定义类加载器与spring集成,加载的实例交由spring进行管理
未完美解决问题:加密的外部class文件,通过xml配置的方式可以成功加载并交由spring容器进行管理;但注解、spring代码扫描(component-scan)的方式无法正确扫描加密后的类,需要解决ClassReader.readClass(),对读取到的字节数组进行处理后再交由ClassReader编译,整个类调用流程如下:
ComponentScanBeanDefinitionParser.parse》ClassPathBeanDefinitionScanner.doScan》ClassPathScanningCandidateComponentProvider.findCandidateComponents》CachingMetadataReaderFactory.getMetadataReader》SimpleMetadataReaderFactory.getMetadataReader》SimpleMetadataReader(Resource resource, ClassLoader classLoader)》ClassReader(final InputStream is)》readClass(final InputStream is, boolean close)》ClassReader(final byte[] b)》ClassReader(final byte[] b, final int off, final int len)
如果自定义ClassReader,下面我们就需要寻找spring可扩展点,崩溃中...有兴趣的可以试试
- 尝试的其它方案
//动态加载bean到spring容器中,通过XmlBeanDefinitionReader的方式, ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"spring-context.xml"}); //web环境中获取方式:context=ContextLoader.getCurrentWebApplicationContext(); XmlBeanDefinitionReader xmlBeanDefinitionReader=new XmlBeanDefinitionReader((BeanDefinitionRegistry)context.getAutowireCapableBeanFactory()); xmlBeanDefinitionReader.setEntityResolver(new ResourceEntityResolver(context)); xmlBeanDefinitionReader.loadBeanDefinitions("spring/spring-extend.xml"); //xml文件必须在classpath中,源码中文件流代码:is = ClassLoader.getSystemResourceAsStream(this.path);
DefaultListableBeanFactory beanFactory=(DefaultListableBeanFactory) webApplicationContext.getAutowireCapableBeanFactory(); beanFactory.setBeanClassLoader(durl);
- 待尝试方案
- 参考文档
http://blog.csdn.net/u014723529/article/details/41958841
动态修改当前ClassLoader
http://bingobird.iteye.com/blog/606116
额外JAR包的加载问题
http://www.inter12.org/archives/702
评论
3 楼
sheungxin
2018-03-07
@仑山中鸟 @zl_xzl 不好意思,代码已丢。。。自己动手试试吧
2 楼
昆仑山中鸟
2018-03-01
同求代码,看的云里雾里。自定义监听器WebContextListener中调用DefinedURLClassLoader,是重写initWebApplicationContext调用吗?最后的方案2代码又是在哪里调用?
1 楼
xzl_xzl
2017-11-02
代码都是片段,能否提供下具体的代码,正好我也有类似的需求。但我的类没有加密,应该可以用上。
发表评论
-
图像缩略图的Java类库Thumbnailator
2017-03-20 15:50 1124今天经同事介绍了一款j ... -
HashMap原理简析
2016-12-26 17:22 890数据结构中的数组和链表被我们所熟知,其有优缺点刚好相反,Has ... -
java线程池回顾
2016-12-23 16:59 598线程池相关类 ExecutorServ ... -
自定义ClassLoader
2016-12-20 18:20 835进行实验 以下代码使用一个自定义类加载器,输出类加载器的层级结 ... -
java代理机制简单实现
2016-12-20 17:17 760java代理分静态代理和动态代理,动态代理有jdk代 ...
相关推荐
这样做的动机是创建隔离的类加载器,该类加载器可以轻松地与IoC框架(如Spring)和Web应用程序集成。 整个库,包括其代码库和文档,都可以在的条款和条件下。 安装 要使用JCL ,请下载并构建JCL项目,并将jcl(-...
在文件结构方面,META-INF通常包含应用元数据,如MANIFEST.MF文件,这在Java应用中用于记录类加载器和模块信息。而WEB-INF目录在Web应用中用于存放不对外直接访问的资源,如web.xml(部署描述符),以及应用的类库和...
- Spring与Struts2集成,Spring可以作为Struts2的Action的依赖注入容器,通过Spring的ApplicationContext获取服务层对象,实现业务逻辑处理。 - Struts2与Hibernate集成,通常在Action中通过SessionFactory获取...
默认情况下,Spring Boot使用Logback作为其日志系统,因为Logback在性能上优于Log4j,并且与Spring框架有良好的集成。本教程将详细介绍如何在Spring Boot中自定义日志配置,特别是通过`logback-spring.xml`文件来...
本资源“spring-security实现自定义登录认证.rar”包含了一个使用Spring Security进行登录认证的示例,以及JWT(JSON Web Tokens)与Spring Security集成的代码。 首先,让我们了解Spring Security的基本工作原理。...
将JBPM与Spring集成,可以充分利用Spring的IoC(Inversion of Control)和AOP(Aspect Oriented Programming)特性,以及其丰富的生态系统,来管理和控制工作流的生命周期。 在集成JBPM与Spring时,首先需要理解...
在进行Spring与Hibernate的集成时,首先要在`web.xml`中配置Spring的相关内容。这里有两个关键的元素:`context-param`和`listener`。`context-param`用于指定Spring应用上下文配置文件的位置,通常设置为`/WEB-INF/...
标题中提到的"Spring集成SpringSecurity依赖包"应该包含了这些必要的依赖,包括Spring Security本身和可能的Spring MVC依赖。 2. **配置Spring Security**:创建一个配置类,继承自`WebSecurityConfigurerAdapter`...
在提供的文件列表“springSecurity3”中,可能包含了实现上述集成步骤的代码示例、配置文件等,通过学习这些文件,你可以更深入地了解SpringMVC与SpringSecurity的集成过程和细节。记住,安全是Web应用的基础,理解...
以上就是关于"Spring PropertyPlaceholderConfigurer配置文件加载器集成ZooKeeper来实现远程配置读取"的详细解释,涵盖了Spring的配置处理、ZooKeeper的使用以及两者结合的实现过程。理解并掌握这一技术,有助于提升...
在进行Spring集成测试时,重要的是要确保测试环境尽可能接近生产环境,但又不会受到生产数据或复杂性的干扰。理解并熟练运用上述知识点,可以有效地提高测试覆盖率,保证代码质量,并加速软件开发的迭代流程。
#### 一、Web应用中的Spring集成 在Web应用中集成Spring,主要是通过配置`web.xml`文件来完成的。具体步骤如下: 1. **引入Spring的依赖**:首先需要在项目的pom.xml文件或build.gradle文件中添加Spring框架的相关...
要实现自定义登录,首先需要创建一个实现了`UserDetailsService`接口的类,这个接口用于加载用户信息。例如: ```java @Service public class CustomUserDetailsService implements UserDetailsService { @...
在本文中,我们将深入探讨如何将Web应用与Spring Boot框架集成帆软报表工具Finereport。Spring Boot以其简洁、高效和自动配置的特性,成为Java领域开发微服务和Web应用的首选框架。而Finereport作为一款强大的报表...
`org.springframework.context.weaving.AspectJ Weaver`是Spring集成AspectJ的关键组件,它负责在类加载时找到并应用切面。在源码中,可以看到它如何与AspectJ的`织入代理`(WeavingAdviser)协同工作,确保切面在...
在React Native应用开发中,创建一个自定义图标弹跳预加载器组件是一个常见的需求,它不仅可以提高用户体验,还可以为应用程序增添独特的视觉效果。本篇文章将深入探讨如何在React Native项目中实现这样的组件。 ...
- 如果默认的过滤器不能满足需求,可以编写自定义过滤器,插入到Spring Security的过滤器链中,实现更复杂的逻辑,比如基于URL、方法或自定义条件的权限控制。 5. **JWT令牌**: - 为了支持API的无状态认证,可以...
《Spring Boot Starter HBase:构建高效HBase操作的利器》 在Java开发中,Spring Boot以其简洁、高效的...对于那些希望在Spring Boot项目中集成HBase的开发者来说,这个自定义启动器无疑是一个值得尝试的优秀选择。
将Wink与Spring集成,可以充分利用两者的优点,实现高效、灵活的REST服务开发。 集成Spring和Apache Wink的主要目标是利用Spring的IOC容器管理Wink的组件,如资源、过滤器和消息处理器,以及利用Spring的数据访问和...
如果希望自定义的IOC更接近Spring,那么实现AOP支持也是值得考虑的,这涉及到拦截器、通知、切点表达式等概念。 6. **扩展性与灵活性**:优秀的自定义IOC应具备良好的扩展性,能够方便地添加新的功能,比如支持更多...