首页 文章 精选 留言 我的

精选列表

搜索[SpringFramework],共2330篇文章
优秀的个人博客,低调大师

SpringFramework核心技术五:面向切面编程(AOP)和Spring

面向对象和面向切面两种编程方式的比较 面向方面编程(AOP)通过提供关于程序结构的另一种思考方式来补充面向对象编程(OOP)。OOP中模块化的关键单元是类,而在AOP中,模块化的单元是方面。方面可以实现关注的模块化,例如跨越多种类型和对象的事务管理。(这种担忧在AOP文献中常常被称为横切关注点。) AOP的介绍 1.基本的介绍 AOP框架是Spring的关键组件之一。虽然Spring IoC容器不依赖AOP,也就是说如果你不想使用AOP那么就不用AOP,AOP可以为Spring IoC提供功能非常强大的中间件解决方案。 Spring 2.0+ AOP Spring 2.0引入了一种使用基于模式的方法或@AspectJ注释样式编写自定义方面的更简单,更强大的方法。这两种风格都提供完全类型化的建议和AspectJ切入点语言的使用,同时仍然使用Spring AOP进行编织。 本章将讨论基于Spring 2.0+模式和@AspectJ的AOP支持。下一章将讨论较低级别的AOP支持,如Spring 1.2应用程序中常见的那样。 AOP在Spring框架中用于…… …提供声明式企业服务,特别是作为EJB声明式服务的替代品。最重要的这种服务是 声明式事务管理。 …允许用户实现自定义方面,补充他们与AOP一起使用OOP。 2.AOP概念 AOP的概念 3.Spring框架的核心 Spring框架的核心原则之一是非侵入性 ; 这是因为您不应该被迫在您的业务/域模型中引入特定于框架的类和接口。但是,在某些地方,Spring Framework确实为您提供了将Spring Framework特定的依赖项引入代码库的选项:为您提供此类选项的基本原理是因为在某些情况下,它可能更容易阅读或编写某些特定的部分功能以这种方式。Spring Framework(几乎)总是为您提供选择:您可以自由决定哪种选项最适合您的特定用例或场景。 与本章相关的一个选择是选择哪种AOP框架(以及哪种AOP样式)。您可以选择AspectJ和/或Spring AOP,并且您还可以选择@AspectJ批注样式方法或Spring XML配置样式方法。本章选择首先引入@AspectJ风格方法的事实不应视为Spring团队赞成Spring XML配置风格的@AspectJ注释风格方法。 4.AOP代理 Spring AOP默认使用AOP代理的标准JDK 动态代理。这使得任何接口(或接口集)都可以被代理。 Spring AOP也可以使用CGLIB代理。这是代理类而不是接口所必需的。如果业务对象不实现接口,则缺省使用CGLIB。因为优良的做法是编程接口而不是类; 业务类通常会实现一个或多个业务接口。在需要通知未在接口中声明的方法的情况下,或者需要将代理对象作为具体类型传递给方法的情况下(希望很少),可以 强制使用CGLIB。 掌握Spring AOP是基于代理的事实是很重要的。请参阅 了解AOP代理以彻底检查此实现细节的实际含义。 二、@AspectJ支持 5.2。@AspectJ支持 @AspectJ引用了一种将方面声明为常规Java类的注释类型。AspectJ项目引入了@AspectJ风格, 作为AspectJ 5版本的一部分。Spring使用AspectJ提供的用于切入点解析和匹配的库来解释与AspectJ 5相同的注释。AOP运行时仍然是纯粹的Spring AOP,并且不依赖于AspectJ编译器或编织器。 1.启用@AspectJ支持 要在Spring配置需要启用配置基于@AspectJ方面的Spring AOP和Spring支持使用@AspectJ切面,自动代理基于它们是否被那些方面建议豆。通过autoproxying我们的意思是,如果Spring确定一个或多个方面的bean被建议,它会自动生成该bean的代理来拦截方法调用并确保根据需要执行通知。 可以使用XML或Java风格配置启用@AspectJ支持。无论哪种情况,您还需要确保AspectJ的aspectjweaver.jar库位于应用程序的类路径中(版本1.8或更高版本)。该库可在’lib’AspectJ发行版的 目录中或通过Maven Central存储库获得。 2.启用@AspectJ支持 要使用Java启用@AspectJ支持,请@Configuration添加@EnableAspectJAutoProxy 注释: @Configuration @EnableAspectJAutoProxy public class AppConfig { } 使用XML配置启用@AspectJ支持 要使用基于XML的配置启用@AspectJ支持,请使用以下aop:aspectj-autoproxy 元素: <aop:aspectj-autoproxy/> 3.Declaring(宣布) an aspect(切面) 启用@AspectJ支持后,任何在应用程序上下文中定义的,具有@AspectJ方面(具有@Aspect注解)的类的bean 都将被Spring自动检测并用于配置Spring AOP。以下示例显示了一个不太有用的方面所需的最小定义: 应用程序上下文中的常规bean定义,指向具有@Aspect注释的bean类: <bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> <!-- configure properties of aspect here as normal --> </bean> 而NotVeryUsefulAspect类的定义,与注解 org.aspectj.lang.annotation.Aspect注释; package org.xyz; import org.aspectj.lang.annotation.Aspect; @Aspect public class NotVeryUsefulAspect { } 方面(带有注释的类@Aspect)可能具有与任何其他类一样的方法和字段。它们也可能包含切入点,建议和介绍(类型间)声明。 通过组件扫描自动检测方面 您可以在Spring XML配置中将方面类注册为常规bean,或者通过类路径扫描自动检测它们 - 就像任何其他Spring管理的bean一样。但是,请注意,@Aspect注解是不足够的classpath中自动检测:为了这个目的,你需要添加一个单独的@Component注释(或可选择地有资格,按照Spring的组件扫描仪的规则自定义构造型注解)。 与其他方面的方面建议? 在Spring AOP,它是不是可以有自己的方面从其他方面意见的目标。类上的@Aspect注释将其标记为方面,因此将其从自动代理中排除。 4.Declaring a pointcut(切入点) 回想一下,切入点确定了感兴趣的连接点,从而使我们能够控制建议何时执行。Spring AOP仅支持Spring bean的方法执行连接点,因此您可以将切入点视为匹配Spring Bean上方法的执行。切入点声明包含两部分:包含名称和任何参数的签名以及确切地确定我们感兴趣的方法执行的切入点表达式。在AOP的@AspectJ注释样式中,切入点签名由常规方法提供定义,并且使用@Pointcut注释来指示切入点表达式(用作切入点签名的方法 必须具有void返回类型)。 一个例子将帮助区分切入点签名和切入点表达式之间的区别。以下示例定义了一个名为的切入点’anyOldTransfer’,它将匹配任何名为的方法的执行’transfer’: @Pointcut("execution(* transfer(..))")// the pointcut expression private void anyOldTransfer() {}// the pointcut signature …… 三、Spring AOP还是完整的AspectJ? 使用最简单的方法。Spring AOP比使用完整的AspectJ更简单,因为不需要将AspectJ编译器/编织器引入到开发和构建过程中。如果您只需要建议Spring bean执行操作,那么Spring AOP就是正确的选择。如果您需要建议不是由Spring容器管理的对象(通常是域对象),那么您将需要使用AspectJ。如果您希望建议除简单方法执行之外的连接点(例如,字段获取或设置连接点等),则还需要使用AspectJ。 使用AspectJ时,您可以选择AspectJ语言语法(也称为“代码样式”)或@AspectJ注释样式。很显然,如果你不使用Java 5+,那么你已经选择了…使用代码风格。如果方面在您的设计中扮演重要角色,并且您可以使用针对Eclipse 的AspectJ开发工具(AJDT)插件,那么AspectJ语言语法是首选选项:它更干净更简单,因为语言是专门为写方面。如果您没有使用Eclipse,或者只有少数几个方面在应用程序中不起主要作用,那么您可能需要考虑使用@AspectJ风格,并在IDE中使用常规的Java编译,并添加一个方面编织阶段到您的构建脚本。 这个面向切面编程讲的较为抽象! 具体的下一次咱们在研究下!

优秀的个人博客,低调大师

SpringFramework核心技术四:Spring表达式使用(SpEL)

SpEL表达式不同场景下的使用 SpEL表达式是非常强大的,在不同的场景下有不同的使用方式,下面一起看看它的使用方法 一、文字表达 支持的文字表达式的类型是字符串,数值(int,real,hex),布尔值和null。字符串由单引号分隔。要将单引号本身放入字符串中,请使用两个单引号字符。 下面的清单显示了文字的简单用法。通常,它们不会像这样孤立地使用,而是作为更复杂表达式的一部分,例如在逻辑比较运算符的一侧使用文字。 ExpressionParser parser = new SpelExpressionParser(); // evals to "Hello World" String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); // evals to 2147483647 int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); Object nullValue = parser.parseExpression("null").getValue(); 数字支持使用负号,指数表示法和小数点。默认情况下,实数使用Double.parseDouble()进行分析。 二、Properties, Arrays, Lists, Maps, Indexers 使用属性引用进行浏览很容易:只需使用句点来指示嵌套的属性值。Inventor类,pupin和tesla 的实例中填入了示例中使用的Classes一节中列出的数据。为了“向下”导航并获得特斯拉的出生年份和普平的出生城市,使用了以下表达式。 // evals to 1856 int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context); 对于属性名称的第一个字母,不区分大小写。数组和列表的内容使用方括号表示法获得。 ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); // Inventions Array // evaluates to "Induction motor" String invention = parser.parseExpression("inventions[3]").getValue( context, tesla, String.class); // Members List // evaluates to "Nikola Tesla" String name = parser.parseExpression("Members[0].Name").getValue( context, ieee, String.class); // List and Array navigation // evaluates to "Wireless communication" String invention = parser.parseExpression("Members[0].Inventions[6]").getValue( context, ieee, String.class); 通过指定括号内的文字键值来获得映射的内容。在这种情况下,因为Officer的Map的键是字符串,所以我们可以指定字符串文字。 // Officer's Dictionary Inventor pupin = parser.parseExpression("Officers['president']").getValue( societyContext, Inventor.class); // evaluates to "Idvor" String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue( societyContext, String.class); // setting values parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue( societyContext, "Croatia"); 三、Inline lists 列表可以用{}符号表达式直接表达。 // evaluates to a Java list containing the four numbers List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context); List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context); {}本身意味着一个空的列表。出于性能原因,如果列表本身完全由固定文字组成,则会创建一个常量列表来表示表达式,而不是在每个评估中创建一个新列表。 四、 Inline Maps map也可以用{key:value}符号表达式直接表达。 // evaluates to a Java map containing the two entries Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context); Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context); {:}本身就意味着一张空白的地图。出于性能原因,如果地图本身由固定文字或其他嵌套常量结构(列表或地图)组成,则将创建常量地图来表示表达式,而不是在每次评估中构建新地图。引用地图键是可选的,上面的例子没有使用带引号的键。 五、Array construction 可以使用熟悉的Java语法构建数组,可以选择提供初始化程序以在构建时填充数组。 int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context); // Array with initializer int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context); // Multi dimensional array int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context) 在构建多维数组时,目前不允许提供初始化程序。 六、方法 使用典型的Java编程语法调用方法。你也可以在文字上调用方法。可变参数也被支持。 // string literal, evaluates to "bc" String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class); // evaluates to true boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( societyContext, Boolean.class); 七、操作符号 1.关系运算符 关系运算符; 相等,不等于,小于,小于或等于,大于,大于或等于支持使用标准的操作符表示法。 // evaluates to true boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); // evaluates to false boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class); null按照一条简单的规则进行更大/更小的比较:null在这里被视为无(即不为零)。因此,任何其他值总是大于null(X > null总是true),并且没有其他值X < null总是小于(总是false)。 如果您更喜欢数字比较,请避免基于数字的比较,以便null比较零(例如X > 0或X < 0)。 2.除标准关系运算符外,SpEL还支持instanceof基于正则表达式的matches运算符。 // evaluates to false boolean falseValue = parser.parseExpression( "'xyz' instanceof T(Integer)").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression( "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); //evaluates to false boolean falseValue = parser.parseExpression( "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); 请谨慎使用原始类型,因为它们会立即装箱到包装类型,因此1 instanceof T(int)计算结果false时1 instanceof T(Integer) 会true按照预期进行计算。 每个符号运算符也可以被指定为纯粹的字母等价。这避免了所使用的符号对嵌入表达式的文档类型(例如XML文档)具有特殊含义的问题。文本等价物如下所示:lt(<),gt(>),le(<=),ge(>=),eq(==), ne(!=),div(/),mod(%),not(!)。这些不区分大小写。 3.逻辑运算符 The logical operators that are supported are and, or, and not. Their use is demonstrated below. // -- AND -- // evaluates to false boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class); // evaluates to true String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- OR -- // evaluates to true boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class); // evaluates to true String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- NOT -- // evaluates to false boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class); // -- AND and NOT -- String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); 4.Mathematical operators 加法运算符可用于数字和字符串。减法,乘法和除法只能用于数字。其他支持的数学运算符是模数(%)和指数函数(^)。标准运算符优先级被强制执行。以下演示这些操作员。 // Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 String testString = parser.parseExpression( "'test' + ' ' + 'string'").getValue(String.class); // 'test string' // Subtraction int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 // Multiplication int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 // Division int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 // Modulus int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 // Operator precedence int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 八、Assignment 属性的设置是通过使用赋值运算符完成的。这通常在呼叫中完成,setValue但也可以在呼叫中完成getValue。 Inventor inventor = new Inventor(); EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); parser.parseExpression("Name").setValue(context, inventor, "Aleksandar Seovic"); // alternatively String aleks = parser.parseExpression( "Name = 'Aleksandar Seovic'").getValue(context, inventor, String.class); 九、类型 特殊T运算符可用于指定java.lang.Class(类型)的实例 。也使用此运算符调用静态方法。在 StandardEvaluationContext使用TypeLocator查找类型和 StandardTypeLocator(可替换)是建立与java.lang包的理解。这意味着T()对java.lang中类型的引用不需要完全限定,但所有其他类型引用必须是。 Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); boolean trueValue = parser.parseExpression( "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") .getValue(Boolean.class); 十、构造函数 可以使用new运算符调用构造函数。除了基本类型和String(可以使用int,float等)之外,全限定类名应该用于所有类。 //Inventor需要自己创建 Inventor einstein = p.parseExpression( "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')") .getValue(Inventor.class); //create new inventor instance within add method of List p.parseExpression( "Members.add(new org.spring.samples.spel.inventor.Inventor( 'Albert Einstein', 'German'))").getValue(societyContext); 十一、变量 可以使用语法在表达式中引用变量#variableName。变量是使用方法设置setVariable上EvaluationContext实现 Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); context.setVariable("newName", "Mike Tesla"); parser.parseExpression("Name = #newName").getValue(context, tesla); System.out.println(tesla.getName()) // "Mike Tesla" 1.this和#root变量 该变量#this总是被定义的并且涉及当前评估对象(针对其解析了非限定参考)。变量#root总是被定义并且引用根上下文对象。虽然#this可能会随着表达式的组成部分的不同而变化,但#root始终指向根。 // create an array of integers List<Integer> primes = new ArrayList<Integer>(); primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); // create parser and set variable 'primes' as the array of integers ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess(); context.setVariable("primes", primes); // all prime numbers > 10 from the list (using selection ?{...}) // evaluates to [11, 13, 17] List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression( "#primes.?[#this>10]").getValue(context); 十二、功能 您可以通过注册可在表达式字符串内调用的用户定义函数来扩展SpEL。该功能是通过注册EvaluationContext。 Method method = ...; EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); context.setVariable("myFunction", method); 例如,给定一个反转字符串的实用方法如下所示: public abstract class StringUtils { public static String reverseString(String input) { StringBuilder backwards = new StringBuilder(input.length()); for (int i = 0; i < input.length(); i++) backwards.append(input.charAt(input.length() - 1 - i)); } return backwards.toString(); } } 上述方法可以如下注册和使用: ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); context.setVariable("reverseString", StringUtils.class.getDeclaredMethod("reverseString", String.class)); String helloWorldReversed = parser.parseExpression( "#reverseString('hello')").getValue(context, String.class); 十三、Bean引用 如果已经使用bean解析器配置了评估上下文,则可以使用@符号从表达式中查找bean ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver()); // This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation Object bean = parser.parseExpression("@foo").getValue(context); 为了访问一个工厂bean本身,bean名称应该以一个&符号为前缀。 ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver()); // This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation Object bean = parser.parseExpression("&foo").getValue(context); 十四、三元运算符(If-Then-Else) 您可以使用三元运算符来执行表达式中的if-then-else条件逻辑。一个最小的例子是: String falseString = parser.parseExpression( "false ? 'trueExp' : 'falseExp'").getValue(String.class); 在这种情况下,布尔值false将返回字符串值’falseExp’。下面显示了一个更现实的例子。 parser.parseExpression("Name").setValue(societyContext, "IEEE"); societyContext.setVariable("queryName", "Nikola Tesla"); expression = "isMember(#queryName)? #queryName + ' is a member of the ' " + "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'"; String queryResultString = parser.parseExpression(expression) .getValue(societyContext, String.class); // queryResultString = "Nikola Tesla is a member of the IEEE Society" 十五、The Elvis Operator Elvis运算符缩短了三元运算符语法,并用于 Groovy语言。使用三元运算符语法,通常必须重复两次变量,例如: String name = "Elvis Presley"; String displayName = (name != null ? name : "Unknown"); Instead you can use the Elvis operator, named for the resemblance to Elvis’ hair style. ExpressionParser parser = new SpelExpressionParser(); String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown' Here is a more complex example. ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class); System.out.println(name); // Nikola Tesla tesla.setName(null); name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class); System.out.println(name); // Elvis Presley 十六、Safe Navigation operator 安全导航运算符用于避免NullPointerException和来自Groovy 语言。通常,当您访问某个对象时,可能需要在访问对象的方法或属性之前验证它是否为空。为了避免这种情况,安全导航运算符将简单地返回null而不是抛出异常。 ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); System.out.println(city); // Smiljan tesla.setPlaceOfBirth(null); city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); System.out.println(city); // null - does not throw NullPointerException!!! Elvis操作符可用于在表达式中应用默认值,例如在 @Value表达式中: @Value("#{systemProperties['pop3.port'] ?: 25}") pop3.port如果它被定义,它将注入一个系统属性,否则注入一个系统属性。 十七、 Collection Selection 选择是一种强大的表达式语言功能,允许您通过从条目中进行选择来将某个源集合转换为另一个源集合。 选择使用语法.?[selectionExpression]。这将过滤集合并返回包含原始元素子集的新集合。例如,选择可以让我们轻松获得塞尔维亚发明者名单: List<Inventor> list = (List<Inventor>) parser.parseExpression( "Members.?[Nationality == 'Serbian']").getValue(societyContext); 在列表和地图上都可以进行选择。在前一种情况下,针对每个单独列表元素评估选择标准,同时针对映射针对每个映射条目(Java类型的对象)评估选择标准 Map.Entry。地图条目将其键和值作为用于选择的属性进行访问。 这个表达式将返回一个新的地图,该地图由入口值小于27的原始地图的那些元素组成。 Map newMap = parser.parseExpression("map.?[value<27]").getValue(); 除了返回所有选定的元素之外,还可以检索第一个或最后一个值。要获得与选择相匹配的第一个条目,语法.^[selectionExpression]同时要获取语法 的最后匹配选择 .$[selectionExpression]。 十八、Collection Projection 投影允许集合驱动子表达式的评估,结果是一个新的集合。投影的语法是.![projectionExpression]。举例来说,最容易理解的是,假设我们有一个发明者名单,但想要他们出生的城市名单。实际上,我们想要为发明人列表中的每个条目评估“placeOfBirth.city”。使用投影: // returns ['Smiljan', 'Idvor' ] List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]"); 地图也可以用于驱动投影,在这种情况下,投影表达式将针对地图中的每个条目(表示为Java Map.Entry)进行评估。在地图上投影的结果是一个列表,其中包含对每个地图项的投影表达式的评估。 十九、Expression templating 表达式模板允许将文本文本与一个或多个评估块混合。每个评估块都用可以定义的前缀和后缀字符分隔,常见的选择是#{ }用作分隔符。例如, String randomPhrase = parser.parseExpression( "random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class); // evaluates to "random number is 0.7038186818312008" 该字符串通过连接文本文本’random number is ‘和评估#{ }分隔符内表达式的结果来计算,在这种情况下是调用该random()方法的结果。该方法的第二个参数parseExpression() 是类型ParserContext。该ParserContext接口用于影响如何解析表达式以支持表达式模板功能。定义TemplateParserContext如下所示。 public class TemplateParserContext implements ParserContext { public String getExpressionPrefix() { return "#{"; } public String getExpressionSuffix() { return "}"; } public boolean isTemplate() { return true; } } 二十、示例中使用的类 Inventor.java package org.spring.samples.spel.inventor; import java.util.Date; import java.util.GregorianCalendar; public class Inventor { private String name; private String nationality; private String[] inventions; private Date birthdate; private PlaceOfBirth placeOfBirth; public Inventor(String name, String nationality) { GregorianCalendar c= new GregorianCalendar(); this.name = name; this.nationality = nationality; this.birthdate = c.getTime(); } public Inventor(String name, Date birthdate, String nationality) { this.name = name; this.nationality = nationality; this.birthdate = birthdate; } public Inventor() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNationality() { return nationality; } public void setNationality(String nationality) { this.nationality = nationality; } public Date getBirthdate() { return birthdate; } public void setBirthdate(Date birthdate) { this.birthdate = birthdate; } public PlaceOfBirth getPlaceOfBirth() { return placeOfBirth; } public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) { this.placeOfBirth = placeOfBirth; } public void setInventions(String[] inventions) { this.inventions = inventions; } public String[] getInventions() { return inventions; } } PlaceOfBirth.java package org.spring.samples.spel.inventor; public class PlaceOfBirth { private String city; private String country; public PlaceOfBirth(String city) { this.city=city; } public PlaceOfBirth(String city, String country) { this(city); this.country = country; } public String getCity() { return city; } public void setCity(String s) { this.city = s; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } } Society.java package org.spring.samples.spel.inventor; import java.util.*; public class Society { private String name; public static String Advisors = "advisors"; public static String President = "president"; private List<Inventor> members = new ArrayList<Inventor>(); private Map officers = new HashMap(); public List getMembers() { return members; } public Map getOfficers() { return officers; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isMember(String name) { for (Inventor inventor : members) { if (inventor.getName().equals(name)) { return true; } } return false; } } 好啦,基本上这19种用法覆盖了SpEL表达式的所有。

优秀的个人博客,低调大师

SpringFramework核心技术二:Resources(加载资源的几种方式)

Resources的介绍 java.net.URL不幸的是,对于各种URL前缀,Java的标准类和标准处理程序不足以满足所有对低级资源的访问。例如,没有URL可用于访问需要从类路径获取的资源的标准化实现,或者相对于某个资源的获取 ServletContext。尽管可以为专用URL 前缀注册新的处理程序(类似于诸如前缀的现有处理程序http:),但这通常非常复杂,并且URL界面仍然缺少某些期望的功能,例如检查资源是否存在的方法指出。 一、资源的接口 Spring的Resource界面旨在成为一个更强大的界面,用于抽象访问低级资源。 public interface Resource extends InputStreamSource { boolean exists(); boolean isOpen(); URL getURL() throws IOException; File getFile() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); } public interface InputStreamSource { InputStream getInputStream() throws IOException; } Resource接口中一些最重要的方法是: getInputStream():定位并打开资源,返回资源InputStream读取。预计每次调用都会返回一个新的 InputStream。主叫方有责任关闭该流。 exists():返回一个boolean指示这个资源是否实际存在的物理形式。 isOpen():返回一个boolean指示此资源是否表示具有打开流的句柄。如果true,则InputStream不能被多次读取,并且必须一次只能读取,然后关闭以避免资源泄漏。将false用于所有通常的资源实现,除了InputStreamResource。 getDescription():返回此资源的描述,用于处理资源时的错误输出。这通常是完全限定的文件名或资源的实际URL。 其他方法允许您获取表示资源的实际URL或File对象(如果底层实现兼容并支持该功能)。 该Resource抽象需要资源时Spring自身广泛使用,在许多方法签名的参数类型。在某些Spring API中的其他方法(比如各种ApplicationContext实现的构造函数),可以使用一种 String以未修改的或简单的形式创建Resource适合该上下文实现的String路径,或通过路径上的特殊前缀来允许调用者指定Resource必须创建和使用特定的实现。 尽管Resource接口在Spring和Spring中被大量使用,但是在您自己的代码中使用它作为一个通用工具类来访问资源,即使您的代码不知道或关心其他任何部件的春天。虽然这会将你的代码结合到Spring上,但它实际上只是将它连接到这套小实用程序类,这些实用程序类可用作更强大的替代品URL,并且可以认为它与您用于此目的的任何其他库相当。 重要的是要注意,Resource抽象并不取代功能:它尽可能包装它。例如,UrlResource封装一个URL,并使用包装URL来完成其工作。 二、Spring内置Resource接口的实现 Resource在Spring 中有很多实现可以直接使用: 1.UrlResource对象 所述UrlResource包裹一个java.net.URL,并且可以被用于访问任何对象,该对象是通过URL正常访问,如文件,一个HTTP靶,FTP对象等的所有URL具有标准化的String表示,以使得适当的标准化的前缀被用来指示从另一个URL类型。这包括file:访问文件系统路径,http:通过HTTP协议 ftp:访问资源,通过FTP访问资源等。 A UrlResource是由Java代码使用UrlResource构造函数显式创建的,但是当您调用一个API方法时,通常会隐式创建它,该方法需要一个String 用于表示路径的参数。对于后一种情况,JavaBeans PropertyEditor将最终决定Resource要创建哪种类型。如果路径字符串包含一些众所周知的(就是它)前缀,例如classpath:,它将Resource为该前缀创建适当的专用。但是,如果它不能识别前缀,它会认为这只是一个标准的URL字符串,并且会创建一个UrlResource。 2.使用ClassPathResource 这个类代表了一个应该从classpath中获取的资源。这使用线程上下文类加载器,给定的类加载器或给定的类来加载资源。 此Resource实现支持解析,就java.io.File好像类路径资源驻留在文件系统中一样,但不支持放置在jar中并且尚未扩展(通过servlet引擎或任何环境)扩展到文件系统的classpath资源。 为了解决这个问题,各种Resource实现总是支持解析java.net.URL。 A ClassPathResource是由Java代码使用ClassPathResource 构造函数显式创建的,但是当您调用一个API方法时,通常会隐式创建它,该方法需要一个String用于表示路径的参数。 对于后一种情况,JavaBeans PropertyEditor将识别classpath:字符串路径上的特殊前缀,并ClassPathResource在这种情况下创建一个。 3.FileSystemResource 这是一个句柄的Resource实现java.io.File。它显然支持作为一个File和作为一个决议URL。 4.ServletContextResource 这是资源的Resource实现ServletContext,解释相关Web应用程序根目录中的相对路径。 这总是支持流访问和URL访问,但只允许java.io.File在Web应用程序归档文件展开并且资源物理位于文件系统上时进行访问。不管它是否被扩展,并在文件系统上这样,或者直接从JAR或其他地方像DB(可以想象)访问,实际上都依赖于Servlet容器。 5.InputStreamResource Resource一个给定的实现InputStream。只有在没有具体的Resource实施方式的情况下才能使用。特别是,如果可能的话,更喜欢 ByteArrayResource或者任何基于文件的Resource实现。 相对于其他Resource的实现,这是一个描述符已经 打开资源-因此返回true的isOpen()。如果您需要将资源描述符保存在某处,或者您需要多次读取流,请不要使用它。 6.ByteArrayResource 这是Resource给定字节数组的一个实现。它ByteArrayInputStream为给定的字节数组创建一个 。 从任何给定的字节数组中加载内容是很有用的,而不必求助于单次使用InputStreamResource。 三、ResourceLoader(资源加载器) 该ResourceLoader接口意味着可以返回(即加载)Resource实例的对象来实现。 public interface ResourceLoader { Resource getResource(String location); } 所有应用程序上下文都实现该ResourceLoader接口,因此所有应用程序上下文都可用于获取Resource实例。 当您调用getResource()特定的应用程序上下文时,并且指定的位置路径没有特定的前缀时,您将返回Resource适合该特定应用程序上下文的类型。例如,假设针对一个ClassPathXmlApplicationContext实例执行了以下代码片段: Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); 什么将返回将是一个ClassPathResource; 如果对一个FileSystemXmlApplicationContext实例执行相同的方法,你会得到一个 FileSystemResource。对于一个WebApplicationContext,你会回来 ServletContextResource,等等。 因此,您可以以适合特定应用程序上下文的方式加载资源。 另一方面,ClassPathResource通过指定特殊的classpath:前缀,你也可以强制使用,而不管应用程序的上下文类型如何: Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); 同样,可以UrlResource通过指定任何标准 java.net.URL前缀来强制使用a : Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt"); Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); 下表总结了将Strings 转换为Resources 的策略: Prefix Example Example classpath: classpath:com/myapp/config.xml Loaded from the classpath file: file:///data/config.xml Loaded as a URL, from the filesystem. [3] http: http://myserver/logo.png Loaded as a URL. (none) /data/config.xml Depends on the underlying ApplicationContext. 四、 The ResourceLoaderAware interface 该ResourceLoaderAware接口是一个特殊的标记接口,它希望被提供有对象ResourceLoader参考。 public interface ResourceLoaderAware { void setResourceLoader(ResourceLoader resourceLoader); } 当一个类实现ResourceLoaderAware并被部署到应用程序上下文中(作为一个Spring管理的bean)时,它被ResourceLoaderAware应用程序上下文识别。然后,应用程序上下文将调用 setResourceLoader(ResourceLoader),提供自己作为参数(请记住,Spring中的所有应用程序上下文实现该ResourceLoader接口)。 当然,因为a ApplicationContext是a ResourceLoader,所以bean也可以实现ApplicationContextAware接口并直接使用提供的应用程序上下文来加载资源,但是一般来说,ResourceLoader如果只需要这些,最好使用专用 接口。这些代码只会耦合到资源加载界面,它可以被认为是一个实用程序界面,而不是整个Spring ApplicationContext界面。 从Spring 2.5开始,您可以依靠自动装配ResourceLoader来实现ResourceLoaderAware接口。“传统” constructor和 byType自动装配模式(如自动装配协作者所描述的)现在能够分别为ResourceLoader构造函数参数或设置方法参数提供类型的依赖关系。为了获得更大的灵活性(包括自动装配字段和多个参数方法的能力),请考虑使用新的基于注释的自动装配功能。 下面这一句才是关键: 在这种情况下,只要字段,构造函数或方法带有 注释,ResourceLoader就会自动装入字段,构造函数参数或方法参数,该参数需要ResourceLoader类型@Autowired。有关更多信息,请参阅@Autowired。 五、资源作为依赖关系 如果bean本身要通过某种动态过程来确定并提供资源路径,那么bean可能会使用该ResourceLoader 接口来加载资源。考虑加载某种模板的例子,其中需要的特定资源取决于用户的角色。如果资源是静态的,那么ResourceLoader 完全消除接口的使用是有意义的,并且让bean暴露Resource它需要的属性,并期望它们被注入到它中。 什么使注入这些属性变得微不足道的是,所有的应用程序上下文都注册并使用一个特殊的JavaBeans PropertyEditor,它可以将String路径转换为Resource对象。因此,如果myBean具有类型的模板属性Resource,则可以使用该资源的简单字符串进行配置,如下所示: <bean id="myBean" class="..."> <property name="template" value="some/resource/path/myTemplate.txt"/> </bean> 需要注意的是资源路径没有前缀,是因为它本身是要在应用程序上下文中使用的ResourceLoader,这个资源会通过一个被加载 ClassPathResource,FileSystemResource或ServletContextResource(如适用)根据确切类型的上下文的。 如果需要强制使用特定Resource类型,则可以使用前缀。以下两个示例显示如何强制a ClassPathResource和a UrlResource(后者用于访问文件系统文件) <property name="template" value="classpath:some/resource/path/myTemplate.txt"> <property name="template" value="file:///some/resource/path/myTemplate.txt"/> 六、Application contexts and Resource paths(应用程序上下文和资源路径) 1.构建应用程序上下文 应用程序上下文构造函数(针对特定应用程序上下文类型)通常需要一个字符串或字符串数​​组作为资源的位置路径,以构成上下文定义的XML文件。 当这样的位置路径没有前缀时,Resource从该路径构建并用于加载bean定义的特定类型取决于特定的应用程序上下文,并且适用于特定的应用程序上下文。例如,如果您创建一个 ClassPathXmlApplicationContext如下: ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); ClassPathResource将使用a 将从类路径加载bean定义。但是,如果你创建一个FileSystemXmlApplicationContext如下: ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml"); bean定义将从文件系统位置加载,在这种情况下,相对于当前工作目录。 请注意,在位置路径上使用特殊类路径前缀或标准URL前缀将覆盖Resource为加载定义而创建的默认类型。所以这FileSystemXmlApplicationContext… ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); 实际上会从类路径中加载它的bean定义。但是,它仍然是一个 FileSystemXmlApplicationContext。如果随后将其用作a ResourceLoader,则任何前缀不固定的路径仍将被视为文件系统路径。 2.构造ClassPathXmlApplicationContext实例 - 快捷方式 在ClassPathXmlApplicationContext提供了多种构造方法以便于实例。其基本思想是只提供一个字符串数组,它只包含XML文件本身的文件名(没有前导路径信息),另一个也提供一个Class; 在ClassPathXmlApplicationContext 将从给定类的路径信息。 有一个例子希望能够清楚地说明这一点。考虑一下这样的目录布局: com/ foo/ services.xml daos.xml MessengerService.class 一个ClassPathXmlApplicationContext由在’services.xml’和中定义的bean组成的 ‘daos.xml’实例可以像这样被实例化… ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"}, MessengerService.class); 3.应用程序上下文构造器资源路径中的通配符 应用上下文构造器值中的资源路径可以是简单的路径(如上所示),其具有到目标资源的一对一映射,或者可以包含特殊的“classpath *:”前缀和/或内部Ant-样式正则表达式(使用Spring的PathMatcher实用程序进行匹配)。后者都是有效的通配符 此机制的一个用途是在执行组件式应用程序组装时。所有组件都可以将上下文定义片段“发布”到众所周知的位置路径,并且当使用前缀相同的路径创建最终的应用上下文时 classpath*:,所有组件片段将被自动拾取。 请注意,此通配符特定于在应用程序上下文构造函数中使用资源路径(或PathMatcher直接使用实用程序类层次结构时),并在构建时解析。它与Resource类型本身无关。无法使用classpath*:前缀来构造实际Resource的资源,因为资源一次只能指向一个资源。 3.1. Ant-style Patterns(“/”分割的样式) 当路径位置包含Ant样式时,例如: /WEB-INF/*-context.xml com/mycompany/**/applicationContext.xml file:C:/some/path/*-context.xml classpath:com/mycompany/**/applicationContext.xml 解析器遵循更复杂但定义的过程来尝试解析通配符。它为直到最后一个非通配符段的路径生成一个资源,并从中获取一个URL。如果此URL不是jar:URL或特定zip:于容器的变体(例如,在WebLogic,wsjarWebSphere等中),java.io.File则从其中获得a 并通过遍历文件系统来解析通配符。在jar URL的情况下,解析器可以从中获取java.net.JarURLConnection或手动解析jar URL,然后遍历jar文件的内容来解析通配符。 3.2 对便携性的影响 如果指定的路径已经是文件URL(无论是显式地还是隐式地,因为基础ResourceLoader是文件系统),那么通配符将保证以完全便携的方式工作。 如果指定的路径是类路径位置,则解析器必须通过Classloader.getResource()调用获取最后一个非通配符路径段URL 。由于这只是路径的一个节点(不是最后的文件),所以ClassLoader在这种情况下,它实际上是未定义的(在 javadoc中)确切返回了哪种URL。实际上,它始终java.io.File代表目录,类路径资源解析为文件系统位置,或者某种类型的jar URL,其中classpath资源解析为jar位置。但是,这个操作仍然存在可移植性问题。 如果为最后一个非通配符段获取了一个jar URL,那么解析器必须能够java.net.JarURLConnection从其中获取a 或者手动解析jar URL,以便能够遍历jar的内容并解析通配符。这可以在大多数环境中使用,但在其他环境中会失败,强烈建议您在依赖特定环境之前彻底测试来自瓶子的资源的通配符解决方案。 4.类路径*:前缀 在构建基于XML的应用程序上下文时,位置字符串可能使用特殊的classpath*:前缀: ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); 这个特殊的前缀指定必须获得与给定名称匹配的所有类路径资源(在内部,这基本上是通过ClassLoader.getResources(…​)调用发生的 ),然后进行合并以形成最终的应用程序上下文定义。 通配符类路径依赖于getResources()底层类加载器的方法。由于现在大多数应用程序服务器都提供它们自己的类加载器实现,所以在处理jar文件时行为可能会有所不同。一个简单的测试,以检查是否classpath*使用类加载器从classpath中的jar中加载文件: getClass().getClassLoader().getResources("<someFileInsideTheJar>")。使用具有相同名称但位于两个不同位置的文件进行此测试。如果返回的结果不正确,请检查应用程序服务器文档以了解可能影响类加载器行为的设置。 例如,classpath*:前缀也可以与PathMatcher位置路径其余部分的模式组合classpath*:META-INF/*-beans.xml。在这种情况下,解析策略非常简单:ClassLoader.getResources()在最后一个非通配符路径段上使用调用来获取类加载器层次结构中的所有匹配资源,然后关闭每个资源,然后使用上述相同的PathMatcher分解策略通配符子路径。 5.其他有关通配符的说明 请注意classpath*:,除非实际目标文件驻留在文件系统中,否则与Ant样式结合使用时,只能在模式启动前至少有一个根目录可靠地运行。这意味着类似的模式 classpath*:*.xml可能不会从jar文件的根目录中检索文件,而只能从扩展目录的根目录中检索文件。 Spring检索类路径条目的能力源于JDK的 ClassLoader.getResources()方法,该方法仅返回传入的空字符串的文件系统位置(指示可能的根搜索)。Spring也评估 URLClassLoader运行时配置和jar文件中的“java.class.path”清单,但这并不保证会导致可移植行为。 类路径包的扫描要求类路径中存在相应的目录条目。在使用Ant构建JAR时,请确保不要 激活JAR任务的仅文件开关。此外,在某些环境中,类路径目录可能不会基于安全策略公开,例如JDK 1.7.0_45和更高版本上的独立应用程序(这需要在您的清单中设置“受信任的库”;请参阅 http://stackoverflow.com/questions/ 19394570 / java-jre-7u45-breaks-classloader-getresources)。 在JDK 9的模块路径(Jigsaw)中,Spring的类路径扫描一般按预期工作。将资源放入专用目录也是非常值得推荐的,这样可以避免前面提到的在搜索jar文件根目录级别时的可移植性问题。 classpath:如果要在多个类路径位置中使用要搜索的根包,则不保证具有资源的Ant样式模式能够找到匹配的资源。这是因为资源如 com/mycompany/package1/service-context.xml may be in only one location, but when a path such as classpath:com/mycompany/**/service-context.xml 七、FileSystemResource警告 一个FileSystemResource未连接到FileSystemApplicationContext(即FileSystemApplicationContext不实际的ResourceLoader)将把绝对和相对路径如你所愿。相对路径是相对于当前工作目录的,而绝对路径是相对于文件系统的根。 为了向后兼容(历史)的原因然而,这改变时 FileSystemApplicationContext是ResourceLoader。在 FileSystemApplicationContext简单地让所有绑定的FileSystemResource情况下,把所有的位置路径为相对的,他们是否开始与斜线与否。实际上,这意味着以下内容是等同的:(区别在于conf前面是否需要”/”) ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml"); ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml"); 如下所示:(尽管它们有所不同,但一种情况是相对的,另一种是绝对的)。 FileSystemXmlApplicationContext ctx = ...; ctx.getResource("some/resource/path/myTemplate.txt"); FileSystemXmlApplicationContext ctx = ...; ctx.getResource("/some/resource/path/myTemplate.txt"); 在实践中,如果的确需要绝对的文件系统路径,最好是放弃使用绝对路径FileSystemResource/ FileSystemXmlApplicationContext,只是强制使用的UrlResource,通过使用file:URL前缀。 // actual context type doesn't matter, the Resource will always be UrlResource ctx.getResource("file:///some/resource/path/myTemplate.txt"); // force this FileSystemXmlApplicationContext to load its definition via a UrlResource ApplicationContext ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml"); 好啦。Spring在加载资源的几种方式,就是目前这些东西了。

优秀的个人博客,低调大师

SpringFramework核心技术一(IOC:注册一个LoadTimeWeaver)

一、什么是LoadTimeWeaver 在LoadTimeWeaver用于由Spring动态变换的类,因为它们被装载到Java虚拟机(JVM)。 要启用加载时织入,请将其添加@EnableLoadTimeWeaving到您的某个 @Configuration类中: @Configuration @EnableLoadTimeWeaving public class AppConfig { } 或者对于XML配置,请使用以下context:load-time-weaver元素: <beans> <context:load-time-weaver/> </beans> 一旦配置为ApplicationContext。它内部的任何bean都ApplicationContext 可以实现LoadTimeWeaverAware,从而接收对加载时织机实例的引用。这与Spring的JPA支持相结合特别有用, 因为JPA类转换可能需要加载时织入。有关LocalContainerEntityManagerFactoryBean更多详细信息,请参阅javadocs。有关AspectJ加载时织入的更多信息,请参阅Spring框架中使用AspectJ进行加载时织入。 好了,注册一个LoadTimeWeaver就到这里啦。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册