目录
一、Spring框架概述
Spring是轻量级的开源的JAVAEE框架
Spring可以解决企业应用开发的复杂性
Spring有两个核心部分:IOC和Aop
IOC:控制反转,把创建对象的过程交个Spring进行管理
Aop:面向切面,不修改源代码进行功能增强
Spring特点
方便解耦,简化开发
Aop编程支持
方便程序测试
方便和其他框架进行整合
方便进行事务操作
降低API的使用难度
IOC容器
IOC底层原理
IOC接口(BeanFactory)
IOC操作Bean管理(基于XML)
IOC操作Bean管理(基于注解)
二、IOC概念和原理
2.1、什么是IOC
控制反转,把对象创建和对象之间调用过程,交给Spring进行管理
使用IOC目的:为了耦合度降低
IOC底层原理
xml解析,工厂模式,反射
2.2、IOC接口
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
- Spring提供IOC容器实现两种方式:(两个接口)
2.1 BeanFactory:
IOC容器基本实现方式,是spring内部使用接口,不提供开发人员进行使用
加载配置文件不会创建对象,在获取对象才去创建对象
2.2 ApplicationContext:
BeanFactory接口的子接口,提供更多更强大的功能,一般是由开发人员进行使用
加载配置文件时候就会把配置文件对象进行创建 - 使用ApplicationContext把加载过程交给启动服务器,不要留给运行中。
- ApplicationContext接口有实现类
盘路径 类路径
IOC操作Bean管理
- 什么是Bean管理
Spring创建对象
Spring注入属性 - Bean管理操作有两种方式
基于xml配置文件方式实现
基于注解方式实现
IOC操作Bean管理(xml)
基于xml方式创建队形
- 在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
- 在bean标签有很多属性,常用属性:
id属性:唯一标识
class属性:类全路径 - 创建对象时候,默认是执行无参构造
基于xml方式注入属性
- DI:依赖注入,注入属性
使用set方法注入
属性:类全路径 - 创建对象时候,默认是执行无参构造
三、深入理解Java基础中的集合框架
Java集合框架 (Java Collections Framework, JCF) 也称容器,这里可以类比 C++ 中的 STL,在市面上似乎还没能找到一本详细介绍的书籍。在这里主要对如下部分进行源码分析,及在面试中常见的问题。
例如,在阿里面试常问到的 HashMap 和 ConcurrentHashMap 原理等等。
Java集合框架提供了数据持有对象的方式,提供了对数据集合的操作。Java 集合框架位于java.util包下,主要有三个大类:Collection(接口)、Map(接口)、集合工具类。
3.1、Collection
- ArrayList:线程不同步。默认初始容量为 10,当数组大小不足时容量扩大为 1.5 倍。为追求效率,ArrayList 没有实现同步(synchronized),如果需要多个线程并发访问,用户可以手动同步,也可使用 Vector 替代。
- LinkedList:线程不同步。双向链接实现。LinkedList 同时实现了 List 接口和 Deque 接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)。这样看来,LinkedList 简直就是个全能冠军。当你需要使用栈或者队列时,可以考虑使用 LinkedList,一方面是因为 Java 官方已经声明不建议使用 Stack 类,更遗憾的是,Java 里根本没有一个叫做 Queue 的类(它是个接口名字)。关于栈或队列,现在的首选是 ArrayDeque,它有着比 LinkedList(当作栈或队列使用时)有着更好的性能。
- Stack and Queue:Java 里有一个叫做 Stack 的类,却没有叫做 Queue 的类(它是个接口名字)。当需要使用栈时,Java 已不推荐使用 Stack,而是推荐使用更高效的 ArrayDeque;既然 Queue 只是一个接口,当需要使用队列时也就首选 ArrayDeque 了(次选是 LinkedList )。
- Vector:线程同步。默认初始容量为 10,当数组大小不足时容量扩大为 2 倍。它的同步是通过Iterator方法加synchronized实现的。
- Stack:线程同步。继承自 Vector,添加了几个方法来完成栈的功能。现在已经不推荐使用 Stack,在栈和队列中有限使用 ArrayDeque,其次是 LinkedList。
- TreeSet:线程不同步,内部使用NavigableMap操作。默认元素 “自然顺序” 排列,可以通过Comparator改变排序。TreeSet 里面有一个 TreeMap(适配器模式)
- HashSet:线程不同步,内部使用 HashMap 进行数据存储,提供的方法基本都是调用 HashMap 的方法,所以两者本质是一样的。集合元素可以为 NULL。
- Set:Set 是一种不包含重复元素的 Collection,Set 最多只有一个 null 元素。Set 集合通常可以通过 Map 集合通过适配器模式得到。
- PriorityQueue:Java 中 PriorityQueue 实现了 Queue 接口,不允许放入 null 元素;其通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值),也就意味着可以通过数组来作为 PriorityQueue 的底层实现。
- 优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java 的优先队列每次取最小元素,C++ 的优先队列每次取最大元素)。这里牵涉到了大小关系,元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于 C++ 的仿函数)。
- NavigableSet:添加了搜索功能,可以对给定元素进行搜索:小于、小于等于、大于、大于等于,放回一个符合条件的最接近给定元素的 key。
- EnumSet:线程不同步。内部使用 Enum 数组实现,速度比HashSet快。只能存储在构造函数传入的枚举类的枚举值。
3.2、Map
- TreeMap:线程不同步,基于红黑树(Red-Black tree)的 NavigableMap 实现,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。
- TreeMap 底层通过红黑树(Red-Black tree)实现,也就意味着containsKey(),get(),put(),remove()都有着log(n)的时间复杂度。其具体算法实现参照了《算法导论》。
HashTable:线程安全,HashMap 的迭代器 (Iterator) 是fail-fast迭代器。HashTable 不能存储 NULL 的 key 和 value。 - HashMap:线程不同步。根据key的hashcode进行存储,内部使用静态内部类Node的数组进行存储,默认初始大小为 16,每次扩大一倍。当发生 Hash 冲突时,采用拉链法(链表)。JDK 1.8中:当单个桶中元素个数大于等于8时,链表实现改为红黑树实现;当元素个数小于6时,变回链表实现。由此来防止hashCode攻击。
Java HashMap 采用的是冲突链表方式。
HashMap 是 Hashtable 的轻量级实现,可以接受为 null 的键值 (key) 和值 (value),而 Hashtable 不允许。
- LinkedHashMap:保存了记录的插入顺序,在用 Iterator 遍历 LinkedHashMap 时,先得到的记录肯定是先插入的。也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比 HashMap 慢,不过有种情况例外,当 HashMap 容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap 慢,因为 LinkedHashMap 的遍历速度只和实际数据有关,和容量无关,而 HashMap 的遍历速度和他的容量有关。
- WeakHashMap:从名字可以看出它是某种 Map。它的特殊之处在于 WeakHashMap 里的 entry 可能会被 GC 自动删除,即使程序员没有调用remove()或者clear()方法。 WeakHashMap 的存储结构类似于HashMap
既然有 WeekHashMap,是否有 WeekHashSet 呢?答案是没有!不过 Java Collections 工具类给出了解决方案,Collections.newSetFromMap(Map<E,Boolean> map)方法可以将任何 Map包装成一个Set。
3.3、集合工具类
-
Collections、Arrays:集合类的一个工具类帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
-
Comparable、Comparator:一般是用于对象的比较来实现排序,两者略有区别。
类设计者没有考虑到比较问题而没有实现 Comparable 接口。这是我们就可以通过使用 Comparator,这种情况下,我们是不需要改变对象的。
一个集合中,我们可能需要有多重的排序标准,这时候如果使用 Comparable 就有些捉襟见肘了,可以自己继承 Comparator 提供多种标准的比较器进行排序。
说明:线程不同步的时候可以通过,Collections.synchronizedList() 方法来包装一个线程同步方法
四、练习写一个SpringMVC框架
1、介绍
熟悉SpringMVC框架的同学一定清楚下面这张图,
这张图就是 SpringMVC 在处理 http 请求的整个流程中所做的一些事情。
- 1、用户发送请求至前端控制器DispatcherServlet
- 2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
- 3、处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
- 4、DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 5、执行处理器(Controller,也叫后端控制器)。
- 6、Controller执行完成返回ModelAndView
- 7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- 8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- 9、ViewReslover解析后返回具体View
- 10、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
- 11、DispatcherServlet响应用户。
DispatcherServlet 主要承担接收请求、响应结果、转发等作用,剩下的就交给容器来处理!
基于上面的流程,我们可以编写出一款简化版的Spring MVC框架。
2、程序实践
首先上图!
这个就是我们简易版的Spring MVC框架的实现流程图!
1、首先创建一个DispatcherServlet类,在服务启动的时候,读取要扫描的包路径,然后通过反射将类信息存储到ioc容器,同时通过@Autowired注解,实现自动依赖注入,最后读取@RequestMapping注解中的方法,将映射路径与类的关系存储到映射容器中。
2、当用户发起请求的时候,通过请求路径到映射容器中找到对应的执行类,然后调用具体的方法,发起逻辑处理,最后将处理结果返回给前端用户!
以下是具体实践过程!
2.1、创建扫描注解
因为Spring MVC基本全部都是基于注解开发,因此我们事先也需要创建对应的注解,各个含义与Spring MVC一致!
控制层注解
/**
* 控制层注解
* @Controller
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
请求路径注解
/**
* 请求路径注解
* @RequestMapping
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
参数注解
/**
* 参数注解
* @RequestParam
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value() default "";
}
服务层注解
/**
* 服务层注解
* @Controller
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
String value() default "";
}
自动装载注解
/**
* 自动装载注解
* @Autowrited
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
String value() default "";
}
2.2、编写 DispatcherServlet 类
DispatcherServlet是一个Servlet类,主要承担的任务是:接受前端用户的请求,然后进行转发,最后响应结果给前端用户!
详细代码如下:
/**
* servlet跳转层
*/
@WebServlet(name = "DispatcherServlet",urlPatterns = "/*", loadOnStartup = 1, initParams = {@WebInitParam(name="scanPackage", value="com.example.mvc")})
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(DispatcherServlet.class);
/**请求方法映射容器*/
private static List<RequestHandler> handlerMapping = new ArrayList<>();
/**
* 服务启动的时候,进行初始化,流程如下:
* 1、扫描指定包下所有的类
* 2、通过反射将类实例,放入ioc容器
* 3、通过Autowired注解,实现自动依赖注入,也就是set类中的属性
* 4、通过RequestMapping注解,获取需要映射的所有方法,然后将类信息存放到容器中
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
try {
//1、扫描指定包下所有的类
String scanPackage = config.getInitParameter("scanPackage");
//1、扫描指定包下所有的类
List<String> classNames = doScan(scanPackage);
//2、初始化所有类实例,放入ioc容器,也就是map对象中
Map<String, Object> iocMap = doInstance(classNames);
//3、实现自动依赖注入
doAutowired(iocMap);
//5、初始化方法mapping
initHandleMapping(iocMap);
} catch (Exception e) {
logger.error("dispatcher-servlet类初始化失败!",e);
throw new ServletException(e.getMessage());
}
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
//跳转
doDispatch(request, response);
}
/**
* 扫描指定包下的类文件
* @param packageName
* @return
*/
private List<String> doScan(String packageName){
if(StringUtils.isBlank(packageName)){
throw new RuntimeException("mvc配置文件中指定扫描包名为空!");
}
return PackageHelper.getClassName(packageName);
}
private Map<String, Object> doInstance(List<String> classNames) {
Map<String, Object> iocMap = new HashMap<>();
if(!CollectionUtils.isNotEmpty(classNames)){
throw new RuntimeException("获取的类为空!");
}
for (String className : classNames) {
try {
//通过反射机制构造对象
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(Controller.class)){
//将类名第一个字母小写
String baneName = firstLowerCase(clazz.getSimpleName());
iocMap.put(baneName, clazz.newInstance());
}else if(clazz.isAnnotationPresent(Service.class)){
//服务层注解判断
Service service = clazz.getAnnotation(Service.class);
String beanName = service.value();
//如果该注解上没有自定义类名,则默认首字母小写
if(StringUtils.isBlank(beanName)){
beanName = clazz.getName();
}
Object instance = clazz.newInstance();
iocMap.put(beanName, instance);
//如果注入的是接口,可以巧妙的用接口的类型作为key
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> clazzInterface : interfaces) {
iocMap.put(clazzInterface.getName(), instance);
}
}
} catch (Exception e) {
logger.error("初始化mvc-ioc容器失败!",e);
throw new RuntimeException("初始化mvc-ioc容器失败!");
}
}
return iocMap;
}
/**
* 实现自动依赖注入
* @throws Exception
*/
private void doAutowired(Map<String, Object> iocMap) {
if(!MapUtils.isNotEmpty(iocMap)){
throw new RuntimeException("初始化实现自动依赖失败,ioc为空!");
}
for(Map.Entry<String, Object> entry : iocMap.entrySet()){
//获取对象下所有的属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
//判断字段上有没有@Autowried注解,有的话才注入
if(field.isAnnotationPresent(Autowired.class)){
try {
Autowired autowired = field.getAnnotation(Autowired.class);
//获取注解上有没有自定义值
String beanName = autowired.value().trim();
if(StringUtils.isBlank(beanName)){
beanName = field.getType().getName();
}
//如果想要访问到私有的属性,我们要强制授权
field.setAccessible(true);
field.set(entry.getValue(), iocMap.get(beanName));
} catch (Exception e) {
logger.error("初始化实现自动依赖注入失败!",e);
throw new RuntimeException("初始化实现自动依赖注入失败");
}
}
}
}
}
/**
* 初始化方法mapping
*/
private void initHandleMapping(Map<String, Object> iocMap){
if(!MapUtils.isNotEmpty(iocMap)){
throw new RuntimeException("初始化实现自动依赖失败,ioc为空");
}
for(Map.Entry<String, Object> entry:iocMap.entrySet()){
Class<?> clazz = entry.getValue().getClass();
//判断是否是controller层
if(!clazz.isAnnotationPresent(Controller.class)){
continue;
}
String baseUrl = null;
//判断类有没有requestMapping注解
if(clazz.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
baseUrl= requestMapping.value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//判断方法上有没有requestMapping
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
RequestMapping requestMethodMapping = method.getAnnotation(RequestMapping.class);
//"/+",表示将多个"/"转换成"/"
String regex = (baseUrl + requestMethodMapping.value()).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
handlerMapping.add(new RequestHandler(pattern, entry.getValue(), method));
}
}
}
/**
* servlet请求跳转
* @param request
* @param response
* @throws IOException
*/
private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
request.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
response.setContentType("text/html");
response.setHeader("content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
RequestHandler handle = getHandleMapping(request);
if(Objects.isNull(handle)){
//异常请求地址
logger.warn("异常请求地址!地址:" + request.getRequestURI());
response.getWriter().append("error request url");
return;
}
//获取参数列表
Object[] paramValues = RequestParamHelper.buildRequestParam(handle, request, response);
Object result = handle.getMethod().invoke(handle.getController(), paramValues);
if(result != null){
PrintWriter out = response.getWriter();
out.println(result);
out.flush();
out.close();
}
} catch (Exception e) {
logger.error("接口请求失败!",e);
PrintWriter out = response.getWriter();
out.println("请求异常,请稍后再试");
out.flush();
out.close();
}
}
/**
* 将类名第一个字母小写
* @param clazzName
* @return
*/
private String firstLowerCase(String clazzName){
char[] chars = clazzName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
/**
* 获取用户请求方法名
* 与handlerMapping中的路径名进行匹配
* @param request
* @return
*/
private RequestHandler getHandleMapping(HttpServletRequest request){
if(CollectionUtils.isNotEmpty(handlerMapping)){
//获取用户请求路径
String url = request.getRequestURI();
String contextPath = request.getContextPath();
String serviceUrl = url.replace(contextPath, "").replaceAll("/+", "/");
for (RequestHandler handle : handlerMapping) {
//正则匹配请求方法名
Matcher matcher = handle.getPattern().matcher(serviceUrl);
if(matcher.matches()){
return handle;
}
}
}
return null;
}
}
这里要重点介绍一下初始化阶段所做的操作!
DispatcherServlet在服务启动阶段,会调用init方法进行服务初始化,此阶段所做的事情主要有以下内容:
- 1、扫描指定包下所有的类信息,返回的结果主要是包名 + 类名
- 2、通过反射机制,将类进行实例化,将类实例化对象存储到ioc容器中,其中key是类名(小些驼峰),value是类对象
- 3、通过Autowired注解找到类对象中的属性,通过小驼峰从ioc容器中寻找对应的属性值,然后进行set操作
- 4、通过Controller和RequestMapping注解寻找需要暴露的方法,并获取对应的映射路径,最后将映射路径
- 5、最后,当前端用户发起一个请求时,DispatcherServlet获取到请求路径之后,通过与RequestMapping中的路径进行匹配,找到对应的controller类中的方法,然后通过invoke完成方法调用,将调用结果返回给前端!
2.3、编写 controller 类
当DispatcherServlet编写完成之后,紧接着我们需要编写对应的controller控制类来接受前端用户请求,下面我们以用户登录为例,程序示例如下:
编写一个LoginController控制类,接受前端用户调用
@Controller
@RequestMapping("/user")
public class LoginController {
@Autowired
private UserService userService;
/**
* 用户登录
* @param request
* @param response
* @param userName
* @param userPwd
* @return
*/
@RequestMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response,
@RequestParam("userName") String userName,
@RequestParam("userPwd") String userPwd){
boolean result = userService.login(userName, userPwd);
if(result){
return "登录成功!";
} else {
return "登录失败!";
}
}
}
编写一个UserService服务类,用于判断账户、密码是否正确
public interface UserService {
/**
* 登录
* @param userName
* @param userPwd
* @return
*/
boolean login(String userName, String userPwd);
}
最后,将项目打包成war,通过tomcat启动服务!
在浏览器中访问http://localhost:8080/user/login?userName=hello&userPwd=123,结果显示如下:
当我们将userName和userPwd换成正确的数据,访问地址如下:http://localhost:8080/user/login?userName=zhangsan&userPwd=123456
可以很清晰的看到,服务调用正常!
3、总结
本文主要以Spring MVC框架为背景,手写了一个简易版的Spring MVC框架,虽然功能简陋了一点,但是基本无张俱全,里面讲解了ioc和自动依赖注入的实现过程,还有前端发起一个路径请求,是如何映射到对应的controller类中的方法上!
当然实际的Spring MVC框架的跳转流程比这个复杂很多很多,里面包括各种拦截器、权限安全管理等等。
五、Java开发者必备10大数据工具和框架
根据外媒的一项调查报告,中软卓越专家列出了Java程序员在过去12个月内一直使用的一些工具或框架,或许会对你有意义。
先来看看大数据的概念。根据维基百科,大数据是庞大或复杂的数据集的广义术语,因此传统的数据处理程序不足以支持如此庞大的体量。
在许多情况下,使用SQL数据库存储/检索数据都是很好的选择。而现如今的很多情况下,它都不再能满足我们的目的,这一切都取决于用例的变化。
现在来讨论一些不同的非SQL存储/处理数据工具,例如,NoSQL数据库,全文搜索引擎,实时流式处理,图形数据库等。
1、MongoDB——最受欢迎的,跨平台的,面向文档的数据库。
MongoDB是一个基于分布式文件存储的数据库,使用C++语言编写。旨在为Web应用提供可扩展的高性能数据存储解决方案。应用性能高低依赖于数据库性能,MongoDB则是非关系数据库中功能最丰富,最像关系数据库的,随着MongDB 3.4版本发布,其应用场景适用能力得到了进一步拓展。
MongoDB的核心优势就是灵活的文档模型、高可用复制集、可扩展分片集群。你可以试着从几大方面了解MongoDB,如实时监控MongoDB工具、内存使用量和页面错误、连接数、数据库操作、复制集等。
2、Elasticsearch ——为云构建的分布式RESTful搜索引擎。
ElasticSearch是基于Lucene的搜索服务器。它提供了分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是比较流行的企业级搜索引擎。
ElasticSearch不仅是一个全文本搜索引擎,还是一个分布式实时文档存储,其中每个field均是被索引的数据且可被搜索;也是一个带实时分析功能的分布式搜索引擎,并且能够扩展至数以百计的服务器存储及处理PB级的数据。ElasticSearch在底层利用Lucene完成其索引功能,因此其许多基本概念源于Lucene。
3、Cassandra——开源分布式数据库管理系统。
最初是由Facebook开发的,旨在处理许多商品服务器上的大量数据,提供高可用性,没有单点故障。
Apache Cassandra是一套开源分布式NoSQL数据库系统。集Google BigTable的数据模型与Amazon Dynamo的完全分布式架构于一身。于2008开源,此后,由于Cassandra良好的可扩展性,被Digg、Twitter等Web 2.0网站所采纳,成为了一种流行的分布式结构化数据存储方案。
因Cassandra是用Java编写的,所以理论上在具有JDK6及以上版本的机器中都可以运行,官方测试的JDK还有OpenJDK 及Sun的JDK。 Cassandra的操作命令,类似于我们平时操作的关系数据库,对于熟悉MySQL的朋友来说,操作会很容易上手。
4、Redis ——开源(BSD许可)内存数据结构存储,用作数据库,缓存和消息代理。
Redis是一个开源的使用ANSI C语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis 有三个主要使其有别于其它很多竞争对手的特点:Redis是完全在内存中保存数据的数据库,使用磁盘只是为了持久性目的; Redis相比许多键值数据存储系统有相对丰富的数据类型; Redis可以将数据复制到任意数
5、Hazelcast ——基于Java的开源内存数据网格。
Hazelcast 是一种内存数据网格 in-memory data grid,提供Java程序员关键任务交易和万亿级内存应用。虽然Hazelcast没有所谓的“Master”,但是仍然有一个Leader节点(the oldest member),这个概念与ZooKeeper中的Leader类似,但是实现原理却完全不同。同时,Hazelcast中的数据是分布式的,每一个member持有部分数据和相应的backup数据,这点也与ZooKeeper不同。
Hazelcast的应用便捷性深受开发者喜欢,但如果要投入使用,还需要慎重考虑。
6、Ehcache——广泛使用的开源Java分布式缓存。
主要面向通用缓存、Java EE和轻量级容器。
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是hibernate中默认的CacheProvider。主要特性有:快速简单,具有多种缓存策略;缓存数据有两级,内存和磁盘,因此无需担心容量问题;缓存数据会在虚拟机重启的过程中写入磁盘;可以通过RMI、可插入API等方式进行分布式缓存;具有缓存和缓存管理器的侦听接口;支持多缓存管理器实例,以及一个实例的多个缓存区域;提供Hibernate的缓存实现。
7、Hadoop ——用Java编写的开源软件框架。
用于分布式存储,并对非常大的数据用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群进行高速运算和存储。Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,MapReduce则为海量的数据提供了计算。
8、Solr ——开源企业搜索平台,用Java编写,来自Apache Lucene项目。
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
与ElasticSearch一样,同样是基于Lucene,但它对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化。
9、Spark ——Apache Software Foundation中最活跃的项目,是一个开源集群计算框架。
Spark 是一种与 Hadoop 相似的开源集群计算环境,但是两者之间还存在一些不同之处,这些不同之处使 Spark 在某些工作负载方面表现得更加优越,换句话说,Spark 启用了内存分布数据集,除了能够提供交互式查询外,它还可以优化迭代工作负载。
Spark 是在 Scala 语言中实现的,它将 Scala 用作其应用程序框架。与 Hadoop 不同,Spark 和 Scala 能够紧密集成,其中的 Scala 可以像操作本地集合对象一样轻松地
10、Memcached ——通用分布式内存缓存系统。
Memcached是一套分布式快取系统,当初是Danga Interactive为了LiveJournal所发展的,但被许多软件(如MediaWiki)所使用。Memcached作为高速运行的分布式缓存服务器,具有以下的特点:协议简单,基于libevent的事件处理,内置内存存储方式。
标签:Java,String,Spring,注解,IOC,response,必修课 From: https://blog.csdn.net/qq_43556680/article/details/142631934