Spring Boot常见企业开发场景应用、自动配置原理结构分析(三)

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生网关 MSE Higress,422元/月
简介: Spring Boot常见企业开发场景应用、自动配置原理结构分析

Spring Boot常见企业开发场景应用、自动配置原理结构分析(二)https://developer.aliyun.com/article/1423060


  1. 这里导入了ActiveMQ的起步依赖
  2. 编写DAO
public interface UserRepository extends JpaRepository{
}
1. 编写Service
public interface UserService {
 /**
  * 根据ID获取用户
  * @param i
  * @return
  */
 User get(int id);
 /**
  * 查询所有用户
  * @return
  */
 List findAll();
 /**
  * 新增用户
  * @param user
  */
 void add(User user);
}
@Service
@Transactional
public class UserServiceImpl implements UserService{
 @Autowired
 private UserRepository userRepository;
 @Override
 public User get(int id) {
  return userRepository.findOne(id);
 }
 @Override
 public List findAll() {
  return userRepository.findAll();
 }
 @Override
 public void add(User user) {
  userRepository.save(user);
 }
}
1. 编写Controller
@Controller
@RequestMapping("/user")
public class UserController {
 @Autowired
 private UserService userService;
 @Autowired
 private JmsTemplate jmsTemplate;
 @RequestMapping("/findUserList")
 public String findUserList(Model model) {
  List list = userService.findAll();
  model.addAttribute("list", list);
  return "/index.jsp";
 }
 @RequestMapping("/add")
 public String add(User user) {
  userService.add(user);
  final List userList = userService.findAll();
  jmsTemplate.send("queue_page", new MessageCreator() {
   @Override
   public Message createMessage(Session session) throws JMSException {
    return session.createTextMessage(JSON.toJSONString(userList));
   }
  });
  return "redirect:/user/findUserList";
 }
}
1. 在添加一个用户,往AMQ中发送一条queue消息
2. 编写Listener
@Component
public class PageGeneratorListener {
 @Value("${freemarker.output_path}")
 private String OUTPUT_PATH;
 @Autowired
 private Configuration configuration;
 @JmsListener(destination="queue_page")
 public void genHtml(String userListStr) throws Exception {
  Template template = configuration.getTemplate("user_list.ftl");
  List userList = JSON.parseArray(userListStr, User.class);
  Map map = new HashMap();
  map.put("list", userList);
  template.process(map, new FileWriter(OUTPUT_PATH + "user_list.html"));
 }
}
1. 监听AMQ中queue_page队列的消息,如果接收到消息,使用FreeMarker重新生成一个HTML页面在服务器端
2. 编写入口
@SpringBootApplication
public class Application {
 public static void main(String[] args) {
  SpringApplication.run(Application.class);
 }
 @Bean(name="datasource")
 @Primary
 @ConfigurationProperties(prefix="c3p0")
 public ComboPooledDataSource c3p0DataSource() throws PropertyVetoException {
  ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
  return comboPooledDataSource;
 }
}
1. 编写配置文件
server.port=10086
server.context-path=/
freemarker.output_path=G:/workspace/free_test/t51/src/main/webapp/
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///springboot
c3p0.user=root
c3p0.password=000000
  1. 构建基于Spring Security访问控制应用程序
  1. 导入依赖
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.14.RELEASE</version>
 </parent>
 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.6</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
  </dependency>
  <dependency>
   <groupId>org.apache.tomcat.embed</groupId>
   <artifactId>tomcat-embed-jasper</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
   <groupId>c3p0</groupId>
   <artifactId>c3p0</artifactId>
   <version>0.9.1</version>
  </dependency>
  <dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>3.4</version>
  </dependency>
 </dependencies>
a. 导入了Spring Security的起步依赖spring-boot-starter-security
b. 编写DAO
public interface UserRepository extends JpaRepository<User, Integer>{
 // 根据用户名查询用户
 User findByUsername(String username);
}
  1. 编写Service
public interface UserService {
 /**
  * 根据ID获取用户
  * @param i
  * @return
  */
 User get(int id);
 /**
  * 查询所有用户
  * @return
  */
 List<User> findAll();
 /**
  * 新增用户
  * @param user
  */
 void add(User user);
}
@Service
@Transactional
public class UserServiceImpl implements UserService{
 @Autowired
 private UserRepository userRepository;
 @Override
 public User get(int id) {
  return userRepository.findOne(id);
 }
 @Override
 public List<User> findAll() {
  return userRepository.findAll();
 }
 @Override
 public void add(User user) {
  userRepository.save(user);
 }
}
  1. 编写Spring Security登录验证用户服务
@Service
@Transactional
public class AuthUserService implements UserDetailsService {
 @Autowired
 private UserRepository userRepository;
 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  if(StringUtils.isNotBlank(username)) {
            // 从数据库中获取用户
   User user = userRepository.findByUsername(username);
   if(user != null) {
                // 创建用户、加载角色
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
   return new org.springframework.security.core.userdetails.User(
    username, user.getPassword(), authorities);
   }
   else {
    throw new UsernameNotFoundException("用户名不存在");
   }
  }
  return null;
 }
}
  1. 编写Controller
@Controller
@RequestMapping("/user")
public class UserController {
 @Autowired
 private UserService userService;
 @RequestMapping("/findUserList")
 public String findUserList(Model model) {
  List<User> list = userService.findAll();
  model.addAttribute("list", list);
  return "/index.jsp";
 }
 @RequestMapping("/add")
 public String add(User user) {
  userService.add(user);
  return "redirect:/user/findUserList";
 }
}
  1. 编写应用入口
@SpringBootApplication
public class Application {
 public static void main(String[] args) {
  SpringApplication.run(Application.class);
 }
 @Bean(name="datasource")
 @Primary
 @ConfigurationProperties(prefix="c3p0")
 public ComboPooledDataSource c3p0DataSource() throws PropertyVetoException {
  ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
  return comboPooledDataSource;
 }
}
  1. 编写配置文件
server.port=10086
 server.context-path=/
 freemarker.output_path=G:/workspace/free_test/t51/src/main/webapp/
 c3p0.driverClass=com.mysql.jdbc.Driver
 c3p0.jdbcUrl=jdbc:mysql:///springboot
 c3p0.user=root
 c3p0.password=000000
  1. 编写页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>JSP测试</title>
 </head>
 <body>
 <table border="1">
 <form action="<%=request.getContextPath()%>/user/add" method="post">
 <tr>
 <td>用户名:<input type="text" name="username"/></td>
 <td>密码:<input type="text" name="password"/></td>
 <td><input type="submit" value="新增"/></td>
 </tr>
 </form>
 </table>
 <table border=1>
 <tr>
 <th>ID</th>
 <th>用户名</th>
 <th>密码</th>
 </tr>
 <c:forEach items="${list}" var="user">
 <tr>
 <td>${user.id}</td>
 <td>${user.username}</td>
 <td>${user.password}</td>
 </tr>
 </c:forEach>
 </table>
 </body>
 </html>
  1. 访问http://localhost:10086/user/findUserList,弹出登录对话框,输入数据库中任意的用户名和密码登录即可。

构建基于Dubbox分布式架构应用程序

启动ZooKeeper

编写服务提供者

  1. 导入依赖
<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.5.14.RELEASE</version>
 </parent>
 <dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>
 <dependency>
 <groupId>com.cjoop</groupId>
 <artifactId>spring-boot-starter-dubbox</artifactId>
 <version>0.0.1</version>
 </dependency>
 <dependency>
 <groupId>org.apache.zookeeper</groupId>
 <artifactId>zookeeper</artifactId>
 <version>3.4.7</version>
 </dependency>
 <dependency>
 <groupId>com.github.sgroschupf</groupId>
 <artifactId>zkclient</artifactId>
 <version>0.1</version>
 </dependency>
 <dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>5.1.6</version>
 </dependency>
 <dependency>
 <groupId>javax.servlet</groupId>
 <artifactId>jstl</artifactId>
 </dependency>
 <dependency>
 <groupId>org.apache.tomcat.embed</groupId>
 <artifactId>tomcat-embed-jasper</artifactId>
 </dependency>
 <dependency>
 <groupId>c3p0</groupId>
 <artifactId>c3p0</artifactId>
 <version>0.9.1</version>
 </dependency>
 <dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 <version>3.4</version>
 </dependency>
 </dependencies>
  1. 这里导入了Dubbox的起步依赖,这样配置起来很方便。
  2. 导入实体类,注意因为要在网络上传输,所以要实现Serializable接口
@Entity
@Table(name="t_user")
public class User implements Serializable{
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Integer id;
 private String username;
 private String password;
 public User() {
 }
 public User(Integer id, String username, String password) {
  this.id = id;
  this.username = username;
  this.password = password;
 }
 public User(String username, String password) {
  this(null, username, password);
 }
 public Integer getId() {
  return id;
 }
 public void setId(Integer id) {
  this.id = id;
 }
 public String getUsername() {
  return username;
 }
 public void setUsername(String username) {
  this.username = username;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 @Override
 public String toString() {
  return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
 }
}
  1. 编写DAO
public interface UserRepository extends JpaRepository<User, Integer>{
 User findByUsername(String username);
}
  1. 编写Service(注意:请使用Dubbox中的@Service,否则服务将不会被发布)
public interface UserService {
 /**
  * 根据ID获取用户
  * @param i
  * @return
  */
 User get(int id);
 /**
  * 查询所有用户
  * @return
  */
 List<User> findAll();
 /**
  * 新增用户
  * @param user
  */
 void add(User user);
}
@Service
@Transactional
public class UserServiceImpl implements UserService{
 @Autowired
 private UserRepository userRepository;
 @Override
 public User get(int id) {
  return userRepository.findOne(id);
 }
 @Override
 public List<User> findAll() {
  return userRepository.findAll();
 }
 @Override
 public void add(User user) {
  userRepository.save(user);
 }
}
  1. 编写入口
@SpringBootApplication
public class Application {
 public static void main(String[] args) {
  SpringApplication.run(Application.class);
 }
 @Bean(name="datasource")
 @Primary
 @ConfigurationProperties(prefix="c3p0")
 public ComboPooledDataSource c3p0DataSource() throws PropertyVetoException {
  ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
  return comboPooledDataSource;
 }
}
  1. 编写配置文件
server.port=10086
server.context-path=/
#配置Dubbo包扫描,自动将带有Service注解的类发布为Dubbox服务
dubbo.annotation.package=com.itheima.springboot.service
dubbo.application.name=com.itheima.user.service
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///springboot
c3p0.user=root
c3p0.password=000000
  1. 启动应用,如果服务发布成功,可以在Dubbo Admin上看到已经发布的服务

编写服务消费者

  1. 导入依赖
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.14.RELEASE</version>
 </parent>
 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
  </dependency>
  <dependency>
   <groupId>org.apache.tomcat.embed</groupId>
   <artifactId>tomcat-embed-jasper</artifactId>
  </dependency>
  <dependency>
   <groupId>com.cjoop</groupId>
   <artifactId>spring-boot-starter-dubbox</artifactId>
   <version>0.0.1</version>
  </dependency>
  <dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.7</version>
  </dependency>
  <dependency>
   <groupId>com.github.sgroschupf</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.1</version>
  </dependency>
  <dependency>
   <groupId>org.javassist</groupId>
   <artifactId>javassist</artifactId>
  </dependency>
 </dependencies>
  1. 引入实体类
public class User {
 private Integer id;
 private String username;
 private String password;
 public User() {
 }
 public User(Integer id, String username, String password) {
 this.id = id;
 this.username = username;
 this.password = password;
 }
 public User(String username, String password) {
 this(null, username, password);
 }
 public Integer getId() {
 return id;
 }
 public void setId(Integer id) {
 this.id = id;
 }
 public String getUsername() {
 return username;
 }
 public void setUsername(String username) {
 this.username = username;
 }
 public String getPassword() {
 return password;
 }
 public void setPassword(String password) {
 this.password = password;
 }
 @Override
 public String toString() {
 return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
 }
 }
1. 引入Service接口
public interface UserService {
 /**
  * 根据ID获取用户
  * @param i
  * @return
  */
 User get(int id);
 /**
  * 查询所有用户
  * @return
  */
 List<User> findAll();
 /**
  * 新增用户
  * @param user
  */
 void add(User user);
}
1. 编写Controller
 @Controller
 @RequestMapping("/user")
 public class UserController {
 @Reference
 private UserService userService;
 @RequestMapping("/findUserList")
 public String findUserList(Model model) {
 List<User> list = userService.findAll();
 model.addAttribute("list", list);
 return "/index.jsp";
 }
 @RequestMapping("/add")
 public String add(User user) {
 userService.add(user);
 return "redirect:/user/findUserList";
 }
 }
  1. 编写入口
@SpringBootApplication
 public class Application {
 public static void main(String[] args) {
 SpringApplication.run(Application.class);
 }
 }
  1. 编写配置文件
server.port=10087
 server.context-path=/
 dubbo.annotation.package=com.itheima.springboot.controller
 dubbo.application.name=com.itheima.user.web
  1. 编写页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>JSP测试</title>
 </head>
 <body>
 <table border="1">
 <form action="<%=request.getContextPath()%>/user/add" method="post">
 <tr>
 <td>用户名:<input type="text" name="username"/></td>
 <td>密码:<input type="text" name="password"/></td>
 <td><input type="submit" value="新增"/></td>
 </tr>
 </form>
 </table>
 <table border=1>
 <tr>
 <th>ID</th>
 <th>用户名</th>
 <th>密码</th>
 </tr>
 <c:forEach items="${list}" var="user">
 <tr>
 <td>${user.id}</td>
 <td>${user.username}</td>
 <td>${user.password}</td>
 </tr>
 </c:forEach>
 </table>
 </body>
 </html>
  1. 访问http://localhost:10087/user/findUserList

Spring Boot自动配置原理结构分析

通过实践,可以隐约感觉到。Spring Boot相当于基于Maven和Spring做了一个开发平台,使用这个平台可以减少配置、快速开发。那么Spring Boot到底是如何做到的呢?

回想,我们开发的第一个案例。我们只是往pom.xml中进行简单配置,就可以开始进行Spring开发了。

然后,更新项目可以看到,在Maven的依赖中,导入了很多的JAR包。

这两段配置怎么这么神奇,它到底做了什么?先来看看这个spring-boot-starter-parent的pom文件。

先是定义了很多的常量

里面还定义了一些依赖的版本锁定、插件的版本锁定。但没有导入具体的JAR包。

这个spring-boot-starter-parent从spring-boot-dependencies中继承,这个pom文件中定义了大量的版本号、以及版本锁定。

这些版本应该都是做过兼容性测试的,一般不要去修改否则出现不兼容问题是比较麻烦的。

再看看spring-boot-starter这个依赖

这个starter起步依赖中包含了导入了spring-boot依赖,spring-boot依赖导入了spring framework的核心依赖。

spring-boot-autoconfigure依赖,spring-boot-starter-logging会自动日志相关的依赖。

这个spring-boot-autoconfigure里面包含了很多玄机。

我猜想,Spring Boot是通过自动配置,来帮助我们自动创建了很多bean在IOC容器中。

所以接下来要回答两个问题:

1、Spring创建了哪些Bean?

2、因为我们之前都是通过编写很多的配置文件来创建和配置bean的,那Spring是如何读取配置来创建这些bean的?

接着猜:

以前的配置信息肯定还有,Spring不应该是把之前假设的平台全部推倒,而是把常用的配置整合起来了,就省去了我们自己来手动配置的过程。那么,我猜:每一个Starter都会有其对应的配置信息。我们来找一找spring-boot-starter的配置信息。

这个autoconfigure里面有大量的包,而且命名方式是以技术组件来命名的

要知道Spring Boot创建了哪些bean,直接去看自动配置包中,以Configuration结尾的类就可以了。要想看看具体application.properties中应该配置哪些属性,直接去看以properties文件结尾的类就可以了。

来看一段自动配置的源代码,下面这段代码是从JmsAutoConfiguration中截取出来的。

@Configuration
@ConditionalOnClass({ Message.class, JmsTemplate.class })
@ConditionalOnBean(ConnectionFactory.class)
@EnableConfigurationProperties(JmsProperties.class)
@Import(JmsAnnotationDrivenConfiguration.class)
public class JmsAutoConfiguration {
 @Configuration
 protected static class JmsTemplateConfiguration {
  private final JmsProperties properties;
  private final ObjectProvider<DestinationResolver> destinationResolver;
  private final ObjectProvider<MessageConverter> messageConverter;
  public JmsTemplateConfiguration(JmsProperties properties,
    ObjectProvider<DestinationResolver> destinationResolver,
    ObjectProvider<MessageConverter> messageConverter) {
   this.properties = properties;
   this.destinationResolver = destinationResolver;
   this.messageConverter = messageConverter;
  }
  @Bean
  @ConditionalOnMissingBean
  @ConditionalOnSingleCandidate(ConnectionFactory.class)
  public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
   JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
   jmsTemplate.setPubSubDomain(this.properties.isPubSubDomain());
   DestinationResolver destinationResolver = this.destinationResolver
     .getIfUnique();
   if (destinationResolver != null) {
    jmsTemplate.setDestinationResolver(destinationResolver);
   }
   MessageConverter messageConverter = this.messageConverter.getIfUnique();
   if (messageConverter != null) {
    jmsTemplate.setMessageConverter(messageConverter);
   }
   JmsProperties.Template template = this.properties.getTemplate();
   if (template.getDefaultDestination() != null) {
    jmsTemplate.setDefaultDestinationName(template.getDefaultDestination());
   }
   if (template.getDeliveryDelay() != null) {
    jmsTemplate.setDeliveryDelay(template.getDeliveryDelay());
   }
   jmsTemplate.setExplicitQosEnabled(template.determineQosEnabled());
   if (template.getDeliveryMode() != null) {
    jmsTemplate.setDeliveryMode(template.getDeliveryMode().getValue());
   }
   if (template.getPriority() != null) {
    jmsTemplate.setPriority(template.getPriority());
   }
   if (template.getTimeToLive() != null) {
    jmsTemplate.setTimeToLive(template.getTimeToLive());
   }
   if (template.getReceiveTimeout() != null) {
    jmsTemplate.setReceiveTimeout(template.getReceiveTimeout());
   }
   return jmsTemplate;
  }
 }

这里面有几个很重要的注解

  1. @ConditionalOnClass
    这个注解表示,如果检测到当前的JVM中加载了Message.class, JmsTemplate.class,就加载该Java Config配置。
  2. @ConditionalOnMissingBean
    这个注解表示,如果IOC容器中没有检测到这个类型的Bean,就创建一个。如果检测到了,那么就不创建了。所以,如果我们自己配置了JmsTemplate这个Bean,那这个自动配置就失效了
  3. @ConditionalOnBean
    这个注解表示,如果IOC容器中有指定类型的Bean,才加载Java Config配置。例如:这里如果检测到容器中有ConnectionFactory类型的Bean,才会创建JmsTemplate。
  4. @EnableConfigurationProperties
    这个注解表示将以Properties结尾的配置类,加载到当前的自动配置类中。一般的Starter中的Properties类都可以从application.properties中的指定前缀的属性加载。从而让我们可以轻松的自定义里面的配置。
  5. @Import
    导入其他的Java Config配置,相当于之前XML配置中的import。

结尾

大家应该有一个直接的体会,Spring Boot真的让我们的工作更加轻松了。以前要写的很多配置、导很多的依赖,现在只需要短短几行代码就可以解决问题。而且,不再需要我们去考虑版本之间的兼容问题了。相信,很快大家编写的应用都会切换到Spring Boot。它将让我们将更多的精力放在编写、设计结构、算法上。

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
6天前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
28 0
|
8天前
|
设计模式 监控 Java
解析Spring Cloud中的断路器模式原理
解析Spring Cloud中的断路器模式原理
|
5天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的北京医疗企业固定资产管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的北京医疗企业固定资产管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
|
5天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的企业职工薪资查询系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的企业职工薪资查询系统的详细设计和实现(源码+lw+部署文档+讲解等)
|
7天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的企业人才引进服务平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的企业人才引进服务平台附带文章源码部署视频讲解等
8 0
|
7天前
|
NoSQL Java MongoDB
使用Spring Boot构建响应式应用
使用Spring Boot构建响应式应用
|
7天前
|
存储 NoSQL Java
使用Spring Boot和MongoDB构建NoSQL应用
使用Spring Boot和MongoDB构建NoSQL应用
|
8天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的北京医疗企业固定资产管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的北京医疗企业固定资产管理系统附带文章源码部署视频讲解等
9 0
|
8天前
|
前端开发 Java 应用服务中间件
Spring Boot自动配置详解
Spring Boot自动配置详解
|
8天前
|
XML Java 数据格式
深入理解Spring中的依赖注入原理
深入理解Spring中的依赖注入原理