您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

java开源项目jeecg结构与代码全解析

时间:2022-11-08 14:38:50  来源:今日头条  作者:心平氣和

一.搭建 1.前端

npm install

npm run serve

2.后端

老生常谈的配置,修改MySQLredis即可。

二.业务功能介绍

功能上jeecgboot主要提供了系列的代码生成器、模板页面、报表页面。

1.报表功能

主要提供报表的相关操作。提供了积木报表插件,可以自定义数据报表、图形报表。并将报表挂载到菜单上。

2.在线开发

也就是代码生成器,可以可视化的在页面上新建数据库表,并通过数据库表生成前后台代码。减少业务代码开发的时间。

3.系统管理

用户管理、角色管理、机构管理、消息管理等基础模块。

4.系统监控

主要负责各种日志、监控的统一处理。

5.页面组件样式

常见案例、详情页、结果页、异常页、列表页、表单页主要提供了样式页面与控件页面示例。在开发过程中如果需要模板直接复制代码即可。详情请

三.后台架构介绍 1.概括


其中报表和代码生成器没有提供源码,如果有兴趣可以自行查看jar包源码。

2.架构核心包jeecg-boot-base

jeecg-boot-base包括了下文的几个部分。

1.接口包jeecg-boot-base-api 1.对外接口jeecg-system-cloud-api

使用feign+hystrix实现了服务间调用加熔断,单机环境并没有使用。

2.服务内接口jeecg-system-local-api

该包提供了下文使用的常用方法接口。仅提供了接口并无其他配置。

2.核心配置包jeecg-boot-base-core 1.通用类common 1.api

其中为通用接口与通用返回对象。

1.Result
其中Result为所有类的返回实体,这样能够通过code编码和message获取是否成功和成功/失败的信息。此类是常用的架构设计

2.aspect

为项目的自定义注解,使用了AOP的切面方式实现,这里就不详细说了,比较简单都可以看懂。

3.constant

存放着枚举类与常量池,这里不多说了。

4.es

为操作es的通用类,主要是配置es连接和查询时动态拼接and/or的方法。

5.exception

exception为自定义的异常类。


1.JeecgBootExceptionHandler
这里详细说一下JeecgBootExceptionHandler,该类也是常见的架构设计之一,核心为@RestControllerAdvice、@ExceptionHandler。当业务代码中没有对异常拦截时,该类会自动拦截异常,并数据log日志。所以某些日志在该类配置后,就不需要在每个接口中都捕获这个异常了。

6.handler

为下文规范提供了接口类。没有其他特别说明。

7.system类

这里主要说controller、entity、service等业务代码的父类
1.JeecgController

所以controller的父类,提供了导入导出的功能。还可以在里面扩展分页、排序、常用调用方法等,这样就可以避免相同的代码多次添加。这也是架构设计中常用的技巧。
2.JeecgEntity

将通用字段如id、创建人、修改人、创建时间、修改时间等字段统一封装在一个实体中,使用其他实体继承。这也是架构设计中常用的技巧。
3.service

主要提供MyBatis-plus提供的curd方法。

8.utli

提供了一大波的工具类,如果在工作中需要,直接复制使用。

2.通用配置类config 1.mybatis


1.MybatisInterceptor
MybatisInterceptor这里主要说MybatisInterceptor,该类负责在mybatis执行语句前,拦截并获取参数,将创建人、创建时间等字符动态插入。这里上部分核心代码。

MAppedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];

String sqlId = mappedStatement.getId();

log.debug("------sqlId------" + sqlId);

//获取sql类型是插入还是修改

SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();

//获取插入参数

Object parameter = invocation.getArgs()[1];

if (parameter == null) {

return invocation.proceed();

}

if (SqlCommandType.INSERT == sqlCommandType) {

LoginUser sysUser = this.getLoginUser();

//通过反射获取入参的类

Field[] fields = oConvertUtils.getAllFields(parameter);

for (Field field : fields) {

log.debug("------field.name------" + field.getName());

try {

//将创建人信息动态加入

if ("createBy".equals(field.getName())) {

field.setAccessible(true);

Object local_createBy = field.get(parameter);

field.setAccessible(false);

if (local_createBy == null || local_createBy.equals("")) {

if (sysUser != null) {

// 登录人账号

field.setAccessible(true);

field.set(parameter, sysUser.getUsername());

field.setAccessible(false);

}

}

}

}

2.MybatisPlusSaasConfig

该类主要负责多租户,什么是多租户呢?

多租户:就是多个公司/客户公用一套系统/数据库,这就需要保证数据的权限。

该场景比较少不详细说明。

2.oss

主要从application-dev.yml获取到上传的路径与配置。

3.shiro

安全框架主要有两个目标:认证与鉴权。
认证:判断用户名密码是否正确。
鉴权:判断用户是否有权限访问该接口。

这里本文着重讲解,如果遇到shiro相关应用,可以项目直接移植使用。

1.CustomShiroFilterFactoryBean

该类主要负责解决资源中文路径问题。这里有个通用的解决方式。

新建类集成ShiroFilterFactoryBean方法,并重写核心方法createInstance(),并在注入时,注入新建的类
CustomShiroFilterFactoryBean,这样就达到的以往重新源码的功能。因为spring提供的功能都是用该思想,所以修改源码的地方就原来越少了,都可以使用该方式实现。

2.JwtFilter

同上文,复写
BasicHttpAuthenticationFilter的验证登录用户的方法,在执行登录接口后判断用户是否正确。

3.ResourceCheckFilter

负责鉴权使用,判断当前用户是否有权限访问。

//表示是否允许访问 ,如果允许访问返回true,否则false;

@Override

protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {

Subject subject = getSubject(servletRequest, servletResponse);

//获取当前url

String url = getPathWithinApplication(servletRequest);

log.info("当前用户正在访问的 url => " + url);

return subject.isPermitted(url);

}

//onAccessDenied:表示当访问拒绝时是否已经处理了; 如果返回 true 表示需要继续处理; 如果返回 false

@Override

protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

HttpServletRequest request = (HttpServletRequest) servletRequest;

HttpServletResponse response = (HttpServletResponse) servletResponse;

response.sendRedirect(request.getContextPath() + this.errorUrl);

// 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)

return false;

}

4.ShiroRealm
主要负责获取用户所有的菜单权限,并提供token的一系列方法。

//获取所有菜单权限集合

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

}

//验证用户输入的账号和密码是否正确

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {

}

//校验token的有效性

public LoginUser checkUserTokenIsEffect(String token) throws AuthenticationException {

}

//刷新token有效时间

public boolean jwtTokenRefresh(String token, String userName, String password) {

}

//清除当前用户的权限认证缓存

@Override

public void clearCache(PrincipalCollection principals) {

super.clearCache(principals);

}

5.ShiroConfig

此为shiro的核心配置类,大多数写法都是固定写法。

public class ShiroConfig {

@Value("${jeecg.shiro.excludeUrls}")

private String excludeUrls;

@Resource

LettuceConnectionFactory lettuceConnectionFactory;

@Autowired

private Environment env;

@Bean("shiroFilter")

public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {

CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();

shiroFilterFactoryBean.setSecurityManager(securityManager);

// 拦截器

Map<string, string=""> filterChAInDefinitionMap = new linkedHashMap<string, string="">();

if(oConvertUtils.isNotEmpty(excludeUrls)){

String[] permissionUrl = excludeUrls.split(",");

for(String url : permissionUrl){

filterChainDefinitionMap.put(url,"anon");

}

}

// 配置不会被拦截的链接 顺序判断 也就是不同通过token访问的地址

filterChainDefinitionMap.put("/sys/cas/client/validateLogin", "anon"); /

// 添加自己的过滤器并且取名为jwt

Map<string, filter=""> filterMap = new HashMap<string, filter="">(1);

//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】

Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY);

//前文定义的过滤器

filterMap.put("jwt", new JwtFilter(cloudServer==null));

shiroFilterFactoryBean.setFilters(filterMap);

//

ex.printStackTrace();

}

}

}

}

public interface MqListener<t> {

default void handler(T map, Channel channel) {

}

}

</t></string,></string,></string,></string,>

2.RabbitMqClient

主要在队列初始化时实现队列的初始化,而是否初始化根据使用时的@RabbitListener、@RabbitComponent判断。

public interface MqListener<t> {

default void handler(T map, Channel channel) {

}

}

@Bean

public void initQueue() {

//获取带RabbitComponent注解的类

Map<string, object=""> beansWithRqbbitComponentMap = this.applicationContext.getBeansWithAnnotation(RabbitComponent.class);

Class<!--? extends Object--> clazz = null;

//循环map

for (Map.Entry<string, object=""> entry : beansWithRqbbitComponentMap.entrySet()) {

log.info("初始化队列............");

//获取到实例对象的class信息

clazz = entry.getValue().getClass();

Method[] methods = clazz.getMethods();

//判断是否有RabbitListener注解

RabbitListener rabbitListener = clazz.getAnnotation(RabbitListener.class);

//类上有注解 就创建队列

if (ObjectUtil.isNotEmpty(rabbitListener)) {

createQueue(rabbitListener);

}

//方法上有注解 就创建队列

for (Method method : methods) {

RabbitListener methodRabbitListener = method.getAnnotation(RabbitListener.class);

if (ObjectUtil.isNotEmpty(methodRabbitListener)) {

createQueue(methodRabbitListener);

}

}

}

}

private void createQueue(RabbitListener rabbitListener) {

String[] queues = rabbitListener.queues();

//创建交换机

DirectExchange directExchange = createExchange(DelayExchangeBuilder.DELAY_EXCHANGE);

rabbitAdmin.declareExchange(directExchange);

//创建队列

if (ObjectUtil.isNotEmpty(queues)) {

for (String queueName : queues) {

Properties result = rabbitAdmin.getQueueProperties(queueName);

if (ObjectUtil.isEmpty(result)) {

Queue queue = new Queue(queueName);

addQueue(queue);

Binding binding = BindingBuilder.bind(queue).to(directExchange).with(queueName);

rabbitAdmin.declareBinding(binding);

log.info("创建队列:" + queueName);

}else{

log.info("已有队列:" + queueName);

}

}

}

}

</string,></string,></t>

3.RabbitMqConfig

为消息队列的常用配置方式。这里不多描述。

4.event
这个包主要是为使用mq发送消息使用,多类别的消息会实现JeecgBusEventHandler类,而baseApplicationEvent通过消息类型传入的不同的参数选择合适的业务类发送消息。

5.DelayExchangeBuilder

为延时队列的交换机声明与绑定。

2.jeecg-boot-starter-lock


1.如何使用分布式锁

使用时有两种方式,一种是使用注解方式,一种是使用redisson提供的API。

 

@Scheduled(cron = "0/5 * * * * ?")

@JLock(lockKey = CloudConstant.REDISSON_DEMO_LOCK_KEY1)

public void execute() throws InterruptedException {

log.info("执行execute任务开始,休眠三秒");

Thread.sleep(3000);

System.out.println("=======================业务逻辑1=============================");

Map map = new baseMap();

map.put("orderId", "BJ0001");

rabbitMqClient.sendMessage(CloudConstant.MQ_JEECG_PLACE_ORDER, map);

//延迟10秒发送

map.put("orderId", "NJ0002");

rabbitMqClient.sendMessage(CloudConstant.MQ_JEECG_PLACE_ORDER, map, 10000);

log.info("execute任务结束,休眠三秒");

}

public DemoLockTest() {

}

//@Scheduled(cron = "0/5 * * * * ?")

public void execute2() throws InterruptedException {

if (redissonLock.tryLock(CloudConstant.REDISSON_DEMO_LOCK_KEY2, -1, 6000)) {

log.info("执行任务execute2开始,休眠十秒");

Thread.sleep(10000);

System.out.println("=======================业务逻辑2=============================");

log.info("定时execute2结束,休眠十秒");

redissonLock.unlock(CloudConstant.REDISSON_DEMO_LOCK_KEY2);

} else {

log.info("execute2获取锁失败");

}

}

2.RepeatSubmitAspect

通过公平锁判断是否是多次点击按钮。

@Around("pointCut(jRepeat)")

public Object repeatSubmit(ProceedingJoinPoint joinPoint,JRepeat jRepeat) throws Throwable {

String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());

if (Objects.nonNull(jRepeat)) {

// 获取参数

Object[] args = joinPoint.getArgs();

// 进行一些参数的处理,比如获取订单号,操作人id等

StringBuffer lockKeyBuffer = new StringBuffer();

String key =getValueBySpEL(jRepeat.lockKey(), parameterNames, args,"RepeatSubmit").get(0);

// 公平加锁,lockTime后锁自动释放

boolean isLocked = false;

try {

isLocked = redissonLockClient.fairLock(key, TimeUnit.SECONDS, jRepeat.lockTime());

// 如果成功获取到锁就继续执行

if (isLocked) {

// 执行进程

return joinPoint.proceed();

} else {

// 未获取到锁

throw new Exception("请勿重复提交");

}

} finally {

// 如果锁还存在,在方法执行完成后,释放锁

if (isLocked) {

redissonLockClient.unlock(key);

}

}

}

return joinPoint.proceed();

}

3.DistributedLockHandler

该类主要是jLock的切面类,通过jLock注解参数,判断需要加锁的类型,同时加锁的方法也不相同。

//jLock切面,进行加锁

@SneakyThrows

@Around("@annotation(jLock)")

public Object around(ProceedingJoinPoint joinPoint, JLock jLock) {

Object obj = null;

log.info("进入RedisLock环绕通知...");

RLock rLock = getLock(joinPoint, jLock);

boolean res = false;

//获取超时时间

long expireSeconds = jLock.expireSeconds();

//等待多久,n秒内获取不到锁,则直接返回

long waitTime = jLock.waitTime();

//执行aop

if (rLock != null) {

try {

if (waitTime == -1) {

res = true;

//一直等待加锁

rLock.lock(expireSeconds, TimeUnit.MILLISECONDS);

} else {

res = rLock.tryLock(waitTime, expireSeconds, TimeUnit.MILLISECONDS);

}

if (res) {

obj = joinPoint.proceed();

} else {

log.error("获取锁异常");

}

} finally {

if (res) {

rLock.unlock();

}

}

}

log.info("结束RedisLock环绕通知...");

return obj;

}

//通过参数判断加锁类型

@SneakyThrows

private RLock getLock(ProceedingJoinPoint joinPoint, JLock jLock) {

//获取key

String[] keys = jLock.lockKey();

if (keys.length == 0) {

throw new RuntimeException("keys不能为空");

}

//获取参数

String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());

Object[] args = joinPoint.getArgs();

LockModel lockModel = jLock.lockModel();

if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {

throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");

}

RLock rLock = null;

String keyConstant = jLock.keyConstant();

//判断锁类型

if (lockModel.equals(LockModel.AUTO)) {

if (keys.length > 1) {

lockModel = LockModel.REDLOCK;

} else {

lockModel = LockModel.REENTRANT;

}

}

//根据不同的锁类型执行不同的加锁方式

switch (lockModel) {

case FAIR:

rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));

break;

case REDLOCK:

List<rlock> rLocks = new ArrayList<>();

for (String key : keys) {

List<string> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);

for (String s : valueBySpEL) {

rLocks.add(redissonClient.getLock(s));

}

}

RLock[] locks = new RLock[rLocks.size()];

int index = 0;

for (RLock r : rLocks) {

locks[index++] = r;

}

rLock = new RedissonRedLock(locks);

break;

case MULTIPLE:

rLocks = new ArrayList<>();

for (String key : keys) {

List<string> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);

for (String s : valueBySpEL) {

rLocks.add(redissonClient.getLock(s));

}

}

locks = new RLock[rLocks.size()];

index = 0;

for (RLock r : rLocks) {

locks[index++] = r;

}

rLock = new RedissonMultiLock(locks);

break;

case REENTRANT:

List<string> valueBySpEL = getValueBySpEL(keys[0], parameterNames, args, keyConstant);

//如果spel表达式是数组或者LIST 则使用红锁

if (valueBySpEL.size() == 1) {

rLock = redissonClient.getLock(valueBySpEL.get(0));

} else {

locks = new RLock[valueBySpEL.size()];

index = 0;

for (String s : valueBySpEL) {

locks[index++] = redissonClient.getLock(s);

}

rLock = new RedissonRedLock(locks);

}

break;

case READ:

rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).readLock();

break;

case WRITE:

rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).writeLock();

break;

}

return rLock;

}

</string></string></string></rlock>

4.RedissonLockClient

redisson客户端,提供了一大波方法,请自行查看。

public class RedissonLockClient {

@Autowired

private RedissonClient redissonClient;

@Autowired

private RedisTemplate<string, object=""> redisTemplate;

public RLock getLock(String lockKey) {

return redissonClient.getLock(lockKey);

}

public boolean tryLock(String lockName, long expireSeconds) {

return tryLock(lockName, 0, expireSeconds);

}

</string,>

5.core包

主要通过application.yml配置文件获取redis连接类型,通过根据该参数动态的选择策略类,连接redis。

public class RedissonManager {

public Redisson getRedisson() {

return redisson;

}

//Redisson连接方式配置工厂

static class RedissonConfigFactory {

private RedissonConfigFactory() {

}

private static volatile RedissonConfigFactory factory = null;

public static RedissonConfigFactory getInstance() {

if (factory == null) {

synchronized (Object.class) {

if (factory == null) {

factory = new RedissonConfigFactory();

}

}

}

return factory;

}

//根据连接类型創建连接方式的配置

Config createConfig(RedissonProperties redissonProperties) {

Preconditions.checkNotNull(redissonProperties);


Preconditions.checkNotNull(redissonProperties.getAddress(), "redis地址未配置");

RedisConnectionType connectionType = redissonProperties.getType();

// 声明连接方式

RedissonConfigStrategy redissonConfigStrategy;

if (connectionType.equals(RedisConnectionType.SENTINEL)) {

redissonConfigStrategy = new SentinelRedissonConfigStrategyImpl();

} else if (connectionType.equals(RedisConnectionType.CLUSTER)) {

redissonConfigStrategy = new ClusterRedissonConfigStrategyImpl();

} else if (connectionType.equals(RedisConnectionType.MASTERSLAVE)) {

redissonConfigStrategy = new MasterslaveRedissonConfigStrategyImpl();

} else {

redissonConfigStrategy = new StandaloneRedissonConfigStrategyImpl();

}


Preconditions.checkNotNull(redissonConfigStrategy, "连接方式创建异常");

return redissonConfigStrategy.createRedissonConfig(redissonProperties);

}

}

}

//策略实现,此类是指定redis的连接方式是哨兵。

public class SentinelRedissonConfigStrategyImpl implements RedissonConfigStrategy {

@Override

public Config createRedissonConfig(RedissonProperties redissonProperties) {

Config config = new Config();

try {

String address = redissonProperties.getAddress();

String password = redissonProperties.getPassword();

int database = redissonProperties.getDatabase();

String[] addrTokens = address.split(",");

String sentinelAliasName = addrTokens[0];

// 设置redis配置文件sentinel.conf配置的sentinel别名

config.useSentinelServers().setMasterName(sentinelAliasName);

config.useSentinelServers().setDatabase(database);

if (StringUtils.isNotBlank(password)) {

config.useSentinelServers().setPassword(password);

}

// 设置哨兵节点的服务IP和端口

for (int i = 1; i < addrTokens.length; i++) {

config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX+ addrTokens[i]);

}

log.info("初始化哨兵方式Config,redisAddress:" + address);

} catch (Exception e) {

log.error("哨兵Redisson初始化错误", e);

e.printStackTrace();

}

return config;

}

}

6.jeecg-cloud-module


这里详细的说一下jeecg-cloud-gateway,因为其他的都是开源项目没下载即用。

jeecg-cloud-system-start为封装start的使用方法,上文已经介绍了。

1.jeecg-cloud-gateway


1.GatewayRoutersConfiguration

当固定的几个路由,有特殊化的执行方法。

2.RateLimiterConfiguration

主要配置限流,与application.yml一起使用,下文配置含义是,发送过来的请求只能容纳
redis-rate-limiter.burstCapacity的配置(3次)多余的会全部丢弃(限流),每秒消费redis-rate-limiter.replenishRate(1次)。
3.FallbackController

熔断的执行方法。
4.GlobalAccessTokenFilter

全局拦截器,在调用其他服务时,将用户信息放在请求头中。

5.SentinelFilterContextConfig

使Sentinel链路流控模式生效,固定写法。

6.HystrixFallbackHandler、
SentinelBlockRequestHandler

在降级/限流时,将异常信息转换成json返回给前台。

7.LoderRouderHandler

动态刷新路由。

8.MySwaggerResourceProvider、SwaggerResourceController

将swagger地址统一管理起来

9.DynamicRouteLoader、DynamicRouteService

DynamicRouteLoader:通过application.yml判断从nacos/redis中获取路由信息,并实现动态的加载。
DynamicRouteService:为底层处理路由的API。

四.总体感想

文章到这里差不多就接近尾声了,大多数功能附带着代码都讲述了一遍。在功能上来说,jeecg提供了很多常用功能,如rabbitMq封装、积木报表、代码生成器等。这些在日常工作中有很大的概率碰上,如果有以上需求,可以来框架中直接复制粘贴即可。

但是在格式规范上,如出入参的规范,代码的写法,代码的格式化等方面,并不是特别统一,且没有严格规范。总体来说非常适合做私活与毕业设计,同时也是最早一批开源的前后端项目脚手架,爆赞。



Tags:jeecg   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
java开源项目jeecg结构与代码全解析
一.搭建 1.前端npm installnpm run serve2.后端老生常谈的配置,修改mysql与redis即可。二.业务功能介绍功能上jeecgboot主要提供了系列的代码生成器、模板页面、报表页面。1....【详细内容】
2022-11-08  Search: jeecg  点击:(898)  评论:(0)  加入收藏
java项目本地部署JeecgBoot低代码开发平台Vue3版系统源码
前言这几天整理了小程序里心愿圈里朋友们发的几个源码需求:人员管理系统、人事管理系统(模块齐全)、工资管理系统。网上是找到类似的系统源码并且测试了,但很遗憾,这类系统要么都...【详细内容】
2022-09-21  Search: jeecg  点击:(494)  评论:(0)  加入收藏
十分钟搞定JeecgBoot 单体升级微服务
JeecgBoot自开源来被问最多的就是微服务版本什么时间出呢??微服务是个趋势,特别随着中台概念的兴趣,每个公司对微服务的需求都很迫切。针对大家的需求,我们推出了Jeecg-Cloud版本...【详细内容】
2020-09-16  Search: jeecg  点击:(848)  评论:(0)  加入收藏
▌简易百科推荐
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  vivo互联网技术    Tags:Java 8   点击:(15)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20    51CTO  Tags:Java代码   点击:(24)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18    51CTO  Tags:Java   点击:(27)  评论:(0)  加入收藏
Java生产环境下性能监控与调优详解
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,...【详细内容】
2024-02-04  大雷家吃饭    Tags:Java   点击:(57)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  编程技术汇  今日头条  Tags:Java   点击:(70)  评论:(0)  加入收藏
Java中的缓存技术及其使用场景
Java中的缓存技术是一种优化手段,用于提高应用程序的性能和响应速度。缓存技术通过将计算结果或者经常访问的数据存储在快速访问的存储介质中,以便下次需要时可以更快地获取。...【详细内容】
2024-01-30  编程技术汇    Tags:Java   点击:(73)  评论:(0)  加入收藏
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  政采云技术  51CTO  Tags:JDK17   点击:(90)  评论:(0)  加入收藏
Java并发编程高阶技术
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应...【详细内容】
2024-01-19  大雷家吃饭    Tags:Java   点击:(107)  评论:(0)  加入收藏
这篇文章彻底让你了解Java与RPA
前段时间更新系统的时候,发现多了一个名为Power Automate的应用,打开了解后发现是一个自动化应用,根据其描述,可以自动执行所有日常任务,说的还是比较夸张,简单用了下,对于office、...【详细内容】
2024-01-17  Java技术指北  微信公众号  Tags:Java   点击:(99)  评论:(0)  加入收藏
Java 在 2023 年仍然流行的 25 个原因
译者 | 刘汪洋审校 | 重楼学习 Java 的过程中,我意识到在 90 年代末 OOP 正值鼎盛时期,Java 作为能够真正实现这些概念的语言显得尤为突出(尽管我此前学过 C++,但相比 Java 影响...【详细内容】
2024-01-10  刘汪洋  51CTO  Tags:Java   点击:(78)  评论:(0)  加入收藏
站内最新
站内热门
站内头条