HowToDoInJava-Spring-教程-一-
HowToDoInJava Spring 教程(一)
原文:HowToDoInJava
协议:CC BY-NC-SA 4.0
Spring 5 MVC + Hibernate 5 示例
原文: https://howtodoinjava.com/spring5/webmvc/spring5-mvc-hibernate5-example/
在本 spring 5 Hibernate 5 注解示例教程中,学习创建 Spring 5 MVC Web 应用程序,处理表单提交,集成 Hibernate 5 连接到后端数据库并为输入表单字段验证添加Hibernate 验证器。
我们将创建一个简单的屏幕,可以在其中添加用户字段(名称和电子邮件)。 这些详细信息将首先通过 Hibernate 进行验证,然后存储在 HSQL 数据库中。 该页面还将列出所有已存储的用户。
1. 开发环境
- Eclipse Neon 2
- JDK 1.8
- Spring 5.2.0.发布
- Hibernate 5.2.11.Final
- Hibernate 验证器 5.4.1.Final
- Servlet 3.1.0
- HSQLDB 1.8.0.10
- Tomcat 7 Maven 插件 2.2
2. 项目结构
该项目具有典型的 Maven Web 应用程序结构。
Spring5 WebMVC 项目结构
2.1. 类图
类图
3. Maven 依赖
在pom.xml
文件中找到用于运行此示例的项目依赖项。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.spring5.demo</groupId>
<artifactId>spring5-mvc-hibernate-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<failOnMissingWebXml>false</failOnMissingWebXml>
<spring.version>5.2.0.RELEASE</spring.version>
<hibernate.version>5.2.11.Final</hibernate.version>
<hibernate.validator>5.4.1.Final</hibernate.validator>
<c3p0.version>0.9.5.2</c3p0.version>
<jstl.version>1.2.1</jstl.version>
<tld.version>1.1.2</tld.version>
<servlets.version>3.1.0</servlets.version>
<jsp.version>2.3.1</jsp.version>
<hsqldb.version>1.8.0.10</hsqldb.version>
</properties>
<dependencies>
<!-- Spring MVC Dependency -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring ORM -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hibernate Core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Hibernate-C3P0 Integration -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate.validator}</version>
</dependency>
<!-- JSTL Dependency -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>javax.servlet.jsp.jstl-api</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>${tld.version}</version>
</dependency>
<!-- Servlet Dependency -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlets.version}</version>
<scope>provided</scope>
</dependency>
<!-- JSP Dependency -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<!-- HSQL Dependency -->
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
4. DispatcherServlet
配置
随着 Servlet 3.0 规范的发布,可以(几乎)没有 xml 配置 Servlet 容器。 为此,Servlet 规范中有ServletContainerInitializer
。 在该类中,您可以注册过滤器,监听器,servlet 等,就像您在web.xml
中传统上所做的那样。
Spring 提供了SpringServletContainerInitializer
,它知道如何处理WebApplicationInitializer
类。 AbstractAnnotationConfigDispatcherServletInitializer
类实现了WebMvcConfigurer
,而该内部实现了WebApplicationInitializer
。 它注册了ContextLoaderlistener
(可选)和DispatcherServlet
,并允许您轻松添加配置类以加载这两个类,并将过滤器应用于DispatcherServlet
并提供 Servlet 映射。
AppInitializer.java
package com.howtodoinjava.demo.spring.config;
public class AppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { HibernateConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
5. Spring WebMVC 配置
下面给出了使用注解配置的 Spring MVC 配置。
WebMvcConfig.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.howtodoinjava.demo.spring"})
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("messages");
return source;
}
@Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
WebMvcConfigurer
定义用于自定义或添加到通过使用@EnableWebMvc
启用的默认 Spring MVC 配置的选项。@EnableWebMvc
启用默认的 Spring MVC 配置,并注册DispatcherServlet
期望的 Spring MVC 基础结构组件。@Configuration
指示一个类声明一个或多个@Bean
方法,并且可以由 Spring 容器进行处理以在运行时为这些 bean 生成 bean 定义和服务请求。@ComponentScan
注解用于指定要扫描的基本软件包。 任何用@Component
和@Configuration
注解的类都将被扫描。InternalResourceViewResolver
有助于将逻辑视图名称映射为直接查看特定预配置目录下的文件。ResourceBundleMessageSource
使用指定的基本名称(这里是消息)访问资源包。LocalValidatorFactoryBean
运行javax.validation.ValidationFactory
,并通过 SpringValidator
接口以及 JSR-303Validator
接口和ValidatorFactory
接口本身公开它。
6. Hibernate 配置
该示例中使用的 Hibernate 配置是基于 Hibernate Java 的配置。
HibernateConfig.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.howtodoinjava.demo.spring.model.User;
@Configuration
@EnableTransactionManagement
public class HibernateConfig {
@Autowired
private ApplicationContext context;
@Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setConfigLocation(context.getResource("classpath:hibernate.cfg.xml"));
factoryBean.setAnnotatedClasses(User.class);
return factoryBean;
}
@Bean
public HibernateTransactionManager getTransactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(getSessionFactory().getObject());
return transactionManager;
}
}
LocalSessionFactoryBean
创建一个 HibernateSessionFactory
。 这是在 Spring 应用程序上下文中设置共享的 HibernateSessionFactory
的常用方法。EnableTransactionManagement
启用 Spring 的注解驱动的事务管理功能。HibernateTransactionManager
将来自指定工厂的 Hibernate 会话绑定到线程,可能每个工厂允许一个线程绑定的Session
。 该事务管理器适用于使用单个 HibernateSessionFactory
进行事务数据访问的应用程序,但它也支持事务内的直接DataSource
访问,即纯 JDBC。
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.archive.autodetection">class,hbm</property>
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">jdbc:hsqldb:mem:howtodoinjava</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.acquire_increment">1800</property>
<property name="hibernate.c3p0.max_statements">150</property>
</session-factory>
</hibernate-configuration>
7. Spring 控制器和 REST 映射
控制器类具有用于GET
和POST
操作的两个简单的 REST 映射。 如果输入字段未经验证,则返回相同的表单 bean 以显示错误消息。 否则返回刷新的视图。
UserController.java
package com.howtodoinjava.demo.spring.controller;
import java.util.Locale;
import javax.validation.alid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.howtodoinjava.demo.spring.model.User;
import com.howtodoinjava.demo.spring.service.UserService;
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/")
public String userForm(Locale locale, Model model) {
model.addAttribute("users", userService.list());
return "editUsers";
}
@ModelAttribute("user")
public User formBackingObject() {
return new User();
}
@PostMapping("/addUser")
public String saveUser(@ModelAttribute("user") @Valid User user,
BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("users", userService.list());
return "editUsers";
}
userService.save(user);
return "redirect:/";
}
}
8. 服务和 DAO 层
服务和 DAO 层是用@Service
和@Repository
注解注解的常规服务组件。 @Transactional
注解在服务层应用,以支持事务。
阅读更多:
@Service
和@Repository
注解
UserService Interface amd Impl
public interface UserService {
void save(User user);
List<User> list();
}
@Service
public class UserServiceImp implements UserService {
@Autowired
private UserDao userDao;
@Transactional
public void save(User user) {
userDao.save(user);
}
@Transactional(readOnly = true)
public List<User> list() {
return userDao.list();
}
}
UserDao Interface and Impl
public interface UserDao {
void save(User user);
List<User> list();
}
@Repository
public class UserDaoImp implements UserDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(User user) {
sessionFactory.getCurrentSession().save(user);
}
@Override
public List<User> list() {
@SuppressWarnings("unchecked")
TypedQuery<User> query = sessionFactory.getCurrentSession().createQuery("from User");
return query.getResultList();
}
}
User.java
package com.howtodoinjava.demo.spring.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
@Entity
@Table(name = "TBL_USERS")
public class User {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long id;
@Column(name = "USER_NAME")
@Size(max = 20, min = 3, message = "{user.name.invalid}")
@NotEmpty(message="Please Enter your name")
private String name;
@Column(name = "USER_EMAIL", unique = true)
@Email(message = "{user.email.invalid}")
@NotEmpty(message="Please Enter your email")
private String email;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
9. 视图和消息资源
最后,使用了 JSP 文件,并使用了消息资源包。
editUsers.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring5 MVC Hibernate Demo</title>
<style type="text/css">
.error {
color: red;
}
table {
width: 50%;
border-collapse: collapse;
border-spacing: 0px;
}
table td {
border: 1px solid #565454;
padding: 20px;
}
</style>
</head>
<body>
<h1>Input Form</h1>
<form:form action="addUser" method="post" modelAttribute="user">
<table>
<tr>
<td>Name</td>
<td>
<form:input path="name" /> <br />
<form:errors path="name" cssClass="error" />
</td>
</tr>
<tr>
<td>Email</td>
<td>
<form:input path="email" /> <br />
<form:errors path="email" cssClass="error" />
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">Submit</button></td>
</tr>
</table>
</form:form>
<h2>Users List</h2>
<table>
<tr>
<td><strong>Name</strong></td>
<td><strong>Email</strong></td>
</tr>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.name}</td>
<td>${user.email}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
messages.properties
user.name.invalid = Name must be between {2} and {1} characters.
user.email.invalid = Please enter valid email address.
10. 演示
让我们使用 maven tomcat7 插件 运行应用程序。 执行 Maven 目标:tomcat7:run
。
网址:http://localhost:8080
初始界面
无效输入的验证
有效表单的提交
检查服务器日志。
Console
Hibernate: call next value for hibernate_sequence
Hibernate: insert into TBL_USERS (USER_EMAIL, USER_NAME, USER_ID) values (?, ?, ?)
Hibernate: select user0_.USER_ID as USER_ID1_0_, user0_.USER_EMAIL as USER_EMA2_0_,
user0_.USER_NAME as USER_NAM3_0_ from TBL_USERS user0_
我希望您能找到这个 Spring Hibernate Web 应用程序示例,以开始开发自己的应用程序。 这主要是针对初学者的,但是它将帮助您构建带有注解的任何带有 Hibernate 集成示例的 Spring MVC。
学习愉快!
下载源码
ELK 堆栈示例教程
原文: https://howtodoinjava.com/microservices/elk-stack-tutorial-example/
通过使用微服务,我们已经能够克服许多遗留问题,它使我们能够创建稳定的分布式应用程序,并对代码,团队规模,维护,发布周期,云计算等切面进行所需的控制。 在其他领域也带来了一些挑战,例如 分布式日志管理并具有查看通常分布在许多服务之间的完整事务日志和分布式调试的功能。
实际上,挑战在于微服务之间是相互隔离的,它们不共享通用的数据库和日志文件。 随着微服务数量的增加以及我们使用自动连续集成工具启用云部署,当我们遇到任何问题时,非常有必要提供调试组件的准备。
感谢开源运动。 我们已经拥有捆绑在一起的工具,如果一起使用,它们可以起到神奇的作用。 这样的一组流行工具是 ElasticSearch, Logstash 和 Kibana – 一起称为 ELK 堆栈。 它们用于实时搜索,分析和可视化日志数据。
在本 ELK 堆栈教程中,学习将 ELK 堆栈集成到微服务生态系统中。
1. 什么是 ELK 堆栈
- Elasticsearch 是基于 JSON 的分布式搜索和分析引擎,旨在实现水平可伸缩性,最大的可靠性和易于管理的功能。
- Logstash 是动态数据收集管道,具有可扩展的插件生态系统和强大的 Elasticsearch 协同作用。
- Kibana 通过 UI 提供了数据的可视化。
1.1. ELK 堆栈架构
Logstash 根据我们设置的过滤条件处理应用程序日志文件,并将这些日志发送到 Elasticsearch。 通过 Kibana,我们可以在需要时查看和分析这些日志。
ELK 实战
2. ELK 堆栈配置
所有这三个工具均基于 JVM,在开始安装它们之前,请验证是否已正确配置 JDK。 检查是否已完成标准 JDK 1.8 安装,JAVA_HOME
和PATH
的设置。
2.1. 弹性搜索
- 从此下载页面下载 Elasticsearch 的最新版本,并将其解压缩到任何文件夹。
- 在命令提示符下运行
bin\elasticsearch.bat
。 - 默认情况下,它将从
http://localhost:9200
开始
2.2. Kibana
- 从下载页面下载最新发行版,并将其解压缩到任何文件夹中。
- 在编辑器中打开
config/kibana.yml
,并将elasticsearch.url
设置为指向您的 Elasticsearch 实例。 在我们的例子中,我们将使用本地实例,只是取消注解elasticsearch.url: "http://localhost:9200"
- 在命令提示符下运行
bin\kibana.bat
。 - 成功启动后,Kibana 将在默认端口
5601
上启动,并且 Kibana UI 将位于http://localhost:5601
2.3. Logstash
-
从下载页面下载最新发行版,并将其解压缩到任何文件夹中。
-
根据配置说明创建一个文件
logstash.conf
。 在实际演示期间,我们将再次进行精确配置。现在运行
bin/logstash -f logstash.conf
以启动 logstash
ELK 堆栈未启动并正在运行。 现在,我们需要创建一些微服务,并将 logstash 指向 API 日志路径。
3. ELK 堆栈示例 – 创建微服务
3.1. 创建 Spring Boot 项目
让我们使用 Spring Boot 创建一个应用程序,以缩短开发时间。 请按照以下步骤启动此服务。
3.2. 添加 REST 端点
添加一个RestController
类,它将暴露一些端点,例如/elk
,/elkdemo
和/exception
。 实际上,我们将仅测试少量日志语句,因此可以根据您的选择随意添加/修改日志。
package com.example.howtodoinjava.elkexamplespringboot;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ElkExampleSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(ElkExampleSpringBootApplication.class, args);
}
}
@RestController
class ELKController {
private static final Logger LOG = Logger.getLogger(ELKController.class.getName());
@Autowired
RestTemplate restTemplete;
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/elkdemo")
public String helloWorld() {
String response = "Hello user ! " + new Date();
LOG.log(Level.INFO, "/elkdemo - > " + response);
return response;
}
@RequestMapping(value = "/elk")
public String helloWorld1() {
String response = restTemplete.exchange("http://localhost:8080/elkdemo", HttpMethod.GET, null, new ParameterizedTypeReference() {
}).getBody();
LOG.log(Level.INFO, "/elk - > " + response);
try {
String exceptionrsp = restTemplete.exchange("http://localhost:8080/exception", HttpMethod.GET, null, new ParameterizedTypeReference() {
}).getBody();
LOG.log(Level.INFO, "/elk trying to print exception - > " + exceptionrsp);
response = response + " === " + exceptionrsp;
} catch (Exception e) {
// exception should not reach here. Really bad practice :)
}
return response;
}
@RequestMapping(value = "/exception")
public String exception() {
String rsp = "";
try {
int i = 1 / 0;
// should get exception
} catch (Exception e) {
e.printStackTrace();
LOG.error(e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String sStackTrace = sw.toString(); // stack trace as a string
LOG.error("Exception As String :: - > "+sStackTrace);
rsp = sStackTrace;
}
return rsp;
}
}
3.3. 配置 Spring Boot 日志记录
打开resources
文件夹下的application.properties
并添加以下配置条目。
logging.file=elk-example.log
spring.application.name = elk-example
阅读更多: Spring Boot 日志记录示例
3.4. 验证微服务生成的日志
使用mvn clean install
进行最终的 Maven 构建,并使用java -jar target\elk-example-spring-boot-0.0.1-SNAPSHOT.jar
命令启动应用程序,然后通过浏览 http://localhost:8080/elk 进行测试。
不必担心在屏幕上看到大堆栈痕迹,因为它是故意做的,以了解 ELK 如何处理异常消息。
转到应用程序根目录,并验证是否已创建日志文件(即elk-example.log
),并多次访问端点,并验证日志已添加到日志文件中。
4. Logstash 配置
我们需要创建一个 logstash 配置文件,以便它监听日志文件并将日志消息推送到弹性搜索中。 这是示例中使用的 logstash 配置,请根据您的设置更改日志路径。
input {
file {
type => "java"
path => "F:/Study/eclipse_workspace_mars/elk-example-spring-boot/elk-example.log"
codec => multiline {
pattern => "^%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}.*"
negate => "true"
what => "previous"
}
}
}
filter {
#If log line contains tab character followed by 'at' then we will tag that entry as stacktrace
if [message] =~ "\tat" {
grok {
match => ["message", "^(\tat)"]
add_tag => ["stacktrace"]
}
}
grok {
match => [ "message",
"(?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}) %{LOGLEVEL:level} %{NUMBER:pid} --- \[(?<thread>[A-Za-z0-9-]+)\] [A-Za-z0-9.]*\.(?<class>[A-Za-z0-9#_]+)\s*:\s+(?<logmessage>.*)",
"message",
"(?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME}) %{LOGLEVEL:level} %{NUMBER:pid} --- .+? :\s+(?<logmessage>.*)"
]
}
date {
match => [ "timestamp" , "yyyy-MM-dd HH:mm:ss.SSS" ]
}
}
output {
stdout {
codec => rubydebug
}
# Sending properly parsed log events to elasticsearch
elasticsearch {
hosts => ["localhost:9200"]
}
}
5. Kibana 配置
在 Kibana 中查看日志之前,我们需要配置索引模式。 我们可以将logstash-*
配置为默认配置。 我们总是可以在 logstash 端更改此索引模式,并在 Kibana 中进行配置。 为简单起见,我们将使用默认配置。
索引模式管理页面如下所示。 通过这种配置,我们将 Kibana 指向您选择的 Elasticsearch 索引。 Logstash 用logstash-YYYY.MM.DD
的名称模式创建索引。我们可以在 Kibana 控制台http://localhost:5601/app/kibana
中进行所有这些配置,然后转到左侧面板中的“管理”链接。
Kibana 中的 Logstash 配置
6. 验证 ELK 堆栈
现在,当所有组件都启动并运行后,让我们验证整个生态系统。
转到应用程序并测试端点几次,以便生成日志,然后转到 Kibana 控制台,查看日志已正确堆叠在 Kibana 中,并具有许多额外功能,例如我们可以过滤,查看不同的图形等。
这是在 Kibana 中生成的日志的视图。
Kibana 日志概述
Kibana 日志细节截图
7. ELK 堆栈教程 – 总结
在此 ELK 示例中,我们学习了配置 ELK 堆栈,并看到了如何将应用程序日志文件指向 ELK 并查看和分析 Kibana 中的日志。 我建议您使用配置并与我们分享您的经验。 例如
- 取而代之的是用 logstash 听我们的日志,我们可以使用 logback 配置来使用 TCP 附加程序通过 TCP 协议将日志发送到远程 Logstash 实例。
- 我们可以使用 Logstash 指向多个日志文件。
- 我们可以在 logstash 配置文件中使用更复杂的过滤器,根据需要执行更多操作。
- 当所有应用程序都将部署在云中时,基本上可以使用远程 ELK 群集来指向我们的日志文件或将日志推送到其中。
- 在 logstash 中创建不同的索引模式。
ELK 堆栈要求正确配置 Java 1.8。 由于系统路径中的 JDK 较旧,我在启动这些工具时遇到了困难。
下载源码
将我的问题放在评论部分。
学习愉快!
Docker 的 Hello World 示例
原文: https://howtodoinjava.com/library/docker-hello-world-example/
Docker 是用于将应用程序及其运行时环境打包在一起的开发人员工具,因此任何人都可以在任何其他计算机上部署和运行它们,而不会遇到运行时环境冲突。 它与虚拟机概念(虚拟化)非常相似,在虚拟机概念中,您可以获取 VM 映像并在任何支持的硬件上运行它。 VM 中的所有内部程序将按其原始打包的方式运行。
VM 和 docker 映像之间的区别是 docker 映像不打包整个虚拟操作系统。 就像开发人员计算机中的其他进程一样,它使用 OS 资源,仅打包应用程序,并且打包其特定于运行时的依赖项(容器化)。
虚拟化 vs 容器化
Docker 允许用户发布 docker 映像并使用其他人在 Docker Hub 等存储库中发布的映像。
在本教程中,学习在 Windows 中在 Windows 中安装 Docker 容器,创建 Docker 映像和部署 Docker 映像(作为一个简单的基于 spring boot 的微服务)。 开发人员机器。
Docker 安装
要在 Windows 7 计算机上安装 docker,请按照以下步骤操作:
为您的系统选择合适的 Docker 安装程序
在开始安装过程之前,我们需要了解适合您所使用 Windows 的确切 Docker 版本。 Docker 提供了以下两个版本的 Windows 发行版
- 对于 Windows 10,我们需要点击此链接 https://docs.docker.com/docker-for-windows/
- 对于 Windows 7、8 和更早版本,我们需要使用 Docker 工具箱,这是官方链接:https://docs.docker.com/toolbox/overview/
我们将按照本文的 Docker 工具箱安装步骤进行操作。
下载 Docker 安装程序
我们首先需要从 https://download.docker.com/win/stable/DockerToolbox.exe 下载 Docker 工具箱发行版,然后按照本地工作站中的安装步骤进行操作。
启用硬件虚拟化技术
为了使 Docker 工具箱正常工作,我们需要确保您的 Windows 系统支持硬件虚拟化技术并且已启用虚拟化。 Docker 在此处提供了详细的步骤: https://docs.docker.com/toolbox/toolbox_install_windows/#step-1-check-your-version。 如果未启用此功能,则需要转到 BIOS 选项并启用“硬件虚拟化”。 对于不同型号的计算机,BIOS 有所不同,因此请按照官方指南启用它。
运行 Docker 安装程序
一旦下载了安装程序并启用了硬件虚拟化,就可以启动安装程序。 就像安装向导指导下的另一个基于 Windows 的简单安装过程一样。
验证您的安装
要验证 docker 安装,请从“桌面”或“开始”菜单中打开 Docker Quickstart Terminal 快捷方式。 验证 Docker 提示即将到来,然后需要测试一些基本命令。 Docker 提示和示例 docker 命令将如下所示。
Docker 安装验证
记下 Docker IP
现在,我们需要记下分配给该容器的 Docker IP。 我们将访问此 IP,以访问 Docker 内部安装的应用程序。 要从命令提示符处了解 IP,请使用命令docker-machine ip
。 这是命令的示例输出。 请注意,此 IP 对于不同的 M / C 将有所不同。
docker 机器 IP 输出
创建 Docker Windows 映像
我们将首先创建基于 Spring Boot 的 REST API,添加特定于 docker 的配置,然后创建 docker 映像。
创建 Spring REST 项目
开发一个简单的 hello world 微服务进行测试。 我们使用 Spring Boot 和 Maven 和 Eclipse 作为 IDE。 添加 REST 端点,以便将该应用程序部署到 Docker 之后,我们可以通过访问其余端点来对其进行测试。
package com.example.howtodoinjava.hellodocker;
import java.util.Date;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class HelloDockerApplication {
public static void main(String[] args) {
SpringApplication.run(HelloDockerApplication.class, args);
}
}
@RestController
class HelloDockerRestController {
@RequestMapping("/hello/{name}")
public String helloDocker(@PathVariable(value = "name") String name) {
String response = "Hello " + name + " Response received on : " + new Date();
System.out.println(response);
return response;
}
}
使用服务器端口信息更新resources/application.properties
。
server.port = 9080
现在,通过将项目作为 Spring Boot 应用程序运行来测试此微服务。
添加 Docker 配置
现在在根目录中创建一个名为Dockerfile
的文件,并将以下几行添加为 Docker 配置。
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/hello-docker-0.0.1-SNAPSHOT.jar hello-docker-app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /hello-docker-app.jar" ]
Docker 在创建映像时使用它。 基本上是在声明 Java 运行时信息和目标分发。 有关更多详细信息,请遵循 docker 构建器参考。
添加 Maven Docker 插件
在pom.xml
文件中添加两个 maven 插件,以便我们在创建实例时可以使用与 Docker 相关的 maven 命令。 这些插件是dockerfile-maven-plugin
和maven-dependency-plugin
。
我们使用了构建项目所需的最少配置。
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.3.4</version>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
创建 Docker 映像
现在使用 maven 命令mvn clean install dockerfile:build
创建 docker 镜像。
从 Docker 终端构建 Docker 映像
请确保在构建映像时本地应用程序未运行,在这种情况下,您可能会遇到 maven 构建失败的情况,因为在干净的步骤中,由于 Java 进程正在使用 jar,因此它将无法删除目标文件夹。
这是 Maven 输出日志在其中构建映像的最后几行。
[INFO] Image will be built as hello-howtodoinjava/hello-docker:latest
[INFO]
[INFO] Step 1/5 : FROM openjdk:8-jdk-alpine
[INFO] Pulling from library/openjdk
[INFO] Digest: sha256:2b1f15e04904dd44a2667a07e34c628ac4b239f92f413b587538f801a0a57c88
[INFO] Status: Image is up to date for openjdk:8-jdk-alpine
[INFO] ---> 478bf389b75b
[INFO] Step 2/5 : VOLUME /tmp
[INFO] ---> Using cache
[INFO] ---> f4f6473b3c25
[INFO] Step 3/5 : ADD target/hello-docker-0.0.1-SNAPSHOT.jar hello-docker-app.jar
[INFO] ---> ce7491518508
[INFO] Removing intermediate container c74867501651
[INFO] Step 4/5 : ENV JAVA_OPTS ""
[INFO] ---> Running in f7cd27710bf3
[INFO] ---> 086226135205
[INFO] Removing intermediate container f7cd27710bf3
[INFO] Step 5/5 : ENTRYPOINT sh -c java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /hello-docker-app.jar
[INFO] ---> Running in 9ef14a442715
[INFO] ---> bf14919a32e2
[INFO] Removing intermediate container 9ef14a442715
[INFO] Successfully built bf14919a32e2
[INFO] Successfully tagged hello-howtodoinjava/hello-docker:latest
[INFO]
[INFO] Detected build of image with id bf14919a32e2
[INFO] Building jar: F:\Study\Technical Writings\docker\hello-docker\target\hello-docker-0.0.1-SNAPSHOT-docker-info.jar
[INFO] Successfully built hello-howtodoinjava/hello-docker:latest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
部署并运行 Docker 映像
因此,我们创建了 Docker 映像(即hello-docker-0.0.1-SNAPSHOT-docker-info.jar
)。 我们还在本地计算机上运行了一个已安装的 docker 容器。
现在,要在已安装的 Docker 容器中运行 docker 映像,我们将使用以下命令。
docker run -p 8080:9080 -t hello-howtodoinjava/hello-docker --name hello-docker-image
在这里,选项-p 8080:9080
很重要。 它表示将端口8080
暴露为内部端口9080
。 请记住,我们的应用程序正在 Docker 映像内的端口9080
中运行,我们将从 Docker 容器外部的端口8080
中对其进行访问。
现在,使用 URL http://192.168.99.100:8080/hello/sajal
访问该应用程序。 请注意,浏览器输出与localhost
上独立 REST API 的输出相同。
Docker Localhost 输出
停止 Docker 容器
我们可以在终端中通过命令docker ps
列出所有 docker 容器,并且可以使用命令docker stop <name>
停止 Docker 容器
总结
我们学会了在 Windows OS 中安装 Docker。 我们还学习了使用一个 REST 端点创建一个 spring boot 项目并为其构建 Docker 映像。 然后,我们学习了在 docker 容器中运行 docker 镜像,并在 docker 镜像中测试了 REST 端点。
Docker 是一个非常酷的工具,可以解决非常老的开发人员问题,“它可以在我的本地计算机上工作”。 现在,如果您的计算机可以正常运行,那么您当然也可以在其他计算机上运行它。
下载源码
学习愉快!
集成 Git 的 Spring Cloud 配置服务器
原文: https://howtodoinjava.com/spring-cloud/spring-cloud-config-server-git/
现在,微服务方法已成为任何新 API 开发的行业标准,并且几乎所有组织都在推广这种方法。 Spring Cloud 提供了出色的工具,可以在 Spring 启动框架之上构建这些微服务。
在本 Spring Cloud 配置教程中,我们将讨论称为配置服务器的特定微服务功能。 配置服务器是存储和维护所有微服务的所有可配置参数的位置。
这更像是将属性/资源文件从项目代码库外部完全外部化到外部服务,以便对任何给定属性的任何更改都不需要重新部署使用该属性的服务。 所有此类属性更改都将反映出来,而无需重新部署微服务。
1. 为什么要使用 Spring Cloud 配置服务器
配置服务器的思想来自 12 因子应用程序宣言,该宣言与开发现代云原生应用程序的最佳实践指南有关。 它建议将属性或资源文件从服务器外部化,在服务器中,这些资源或资源的值在运行时会发生变化 – 通常情况下,不同的配置在每种环境中都会有所不同。
例如,假设一个服务依赖于另一服务(针对特定业务场景调用),并且该依赖服务的 URL 是否更改为其他内容。 然后,通常我们需要使用更新的 URL 构建和部署我们的服务。 现在,如果我们采用 12 要素应用程序方法,并且从外部服务读取了这些配置属性,那么我们只需要更新配置服务器中的 URL 并刷新该客户端服务配置即可使用更新的 URL。
因此,该想法是显而易见且有效的。 现在让我们来看如何创建 Spring Cloud 配置服务器。
2. 技术栈
我们将使用基于 spring boot 的 spring cloud API,该 API 随时可用且非常受欢迎。 在 Spring 框架命名中,它称为配置服务器。 另外,我们将使用 git 配置来托管属性文件。
因此,最后,此演示的技术栈为:
- Java 1.8
- Eclipse IDE
- Spring Cloud
- Spring Boot
- Spring Rest
- GitHub 作为资源库
- Maven
- REST 客户端
首先,我们将使用 spring boot 开发两个微服务。
- 一个是配置服务器服务,在运行时提供配置
- 一种是配置客户端服务,使用作为配置服务器公开的配置。
3. 服务器配置
首先,按照给定的步骤构建配置服务器部分:
-
生成项目结构
从 spring boot 初始化器页面开始,这是创建任何基于 spring boot 的应用程序的一个很好的起点。 在这里,我们将仅选择配置服务器启动器 pom。 屏幕截图是这样的。 使用此配置,一旦生成项目,便会下载一个 zip 文件,解压缩后将其导入 eclipse 中。
使用配置服务器启动器 POM 生成服务器项目
-
在 Eclipse 中导入项目
从 spring 初始化器页面获得 zip 文件后,我们需要将其解压缩到我们选择的目录中,然后将其导入作为 Eclipse 作为 Maven 项目。
pom.xml
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
启动 Eclipse
下一步将是从命令提示符处或从 eclipse 运行
mvn clean install
,无论您喜欢什么。 在这一步中,所有必需的依赖项将从 Maven 存储库中下载。 确保从没有下载限制的任何网络上尝试该操作。 非常需要成功完成此步骤才能继续进行下一步。 -
添加
@EnableConfigServer
注解现在,打开 spring 已经提供的
SpringApplication
类,并在类之前添加@EnableConfigServer
注解,然后再次构建项目。 有了此注解,此工件将充当 spring 配置服务器。添加此注解后,类将如下所示 – 根据您在生成时提供的项目名称,类名称可以不同。 您也可以手动将类名称更改为您喜欢的名称。
SpringConfigServerApplication.java
package com.howtodoinjava.example.springconfigserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer @SpringBootApplication public class SpringConfigServerApplication { public static void main(String[] args) { SpringApplication.run(SpringConfigServerApplication.class, args); } }
-
git 存储库中的客户端属性
下一步是创建本地 git 存储库。 通过稍后在属性文件中配置其 URL,可以轻松地将其转换为远程存储库。 我们将放置外部属性文件
configuration
,配置服务器微服务将使用该文件来提供属性的外部配置。 我们需要按照以下步骤创建本地 git 存储库并签入示例属性文件。-
确保您的计算机中安装了 git shell,并且可以从命令提示符运行 git bash。 要验证它,请打开命令提示符并键入
git
,如果它可以识别该命令,则可能安装了 git 提示符;如果没有,请访问 git 网站,按照说明下载并安装。 -
现在,在桌面中创建目录
config-server-repo
。 -
然后在
config-server-repo
目录中创建文件config-server-client.properties
文件,并在其中添加消息msg = Hello world - this is from config server
。 -
然后在
config-server-repo
目录中创建另一个文件config-server-client-development.properties
文件,并在其中添加消息msg = Hello world - this is from config server – Development environment.
-
然后在
config-server-repo
目录中创建另一个文件config-server-client-production.properties
文件,并在其中添加消息msg = Hello world - this is from config server – Production environment.
-
在这里,我们为不同的环境维护相同的属性名称,因为我们通常为不同的环境(例如 url,凭据,数据库详细信息等)维护属性。这里最重要的一点是,我们需要在每个属性中在环境名称后附加连字符(
-
) 以便配置服务器可以理解。 另外,我们需要使用将在此之后创建的配置客户端服务名称来命名属性文件。 -
现在从
config-server-repo
目录中打开命令提示符,然后运行命令git init
以将该目录作为 git 存储库。 -
现在运行
git add .
将所有内容添加到此仓库中。 -
最后,我们需要通过运行命令
git commit –m "initial checkin"
来提交属性文件。 这应该检入 git 存储库中的所有文件。 这是相同的命令提示符屏幕截图。Git 中的属性检入
client-config.properties
msg = Hello world - this is from config server - default profile
client-config-development.properties
msg = Hello world - this is from config server - Development Environment
client-config-production.properties
msg = Hello world - this is from config server - Prodcution Environment
在
properties
文件夹中执行的 Git 命令$ git init $ git add . $ git commit -m "initial commit"
-
-
从配置服务器指向 Git 存储库
在
spring-config-sever
项目的src\main\resources
目录中创建一个名为bootstrap.properties
的文件,然后添加以下行。bootstrap.properties
#Server port server.port = 8888 #Git repo location spring.cloud.config.server.git.uri=E:\\devsetup\\gitworkspace\\spring-cloud\\config-git-repo #Verify any repository issue in service startup spring.cloud.config.server.git.cloneOnStart=true #Disable security of the Management endpoint management.security.enabled=false
现在让我们了解这些属性。
server.port
定义嵌入式服务器将在其上启动的端口。spring.cloud.config.server.git.uri
将绑定 git 位置以查找配置。 在这里,我们使用本地 git 仓库,但只需更改此位置即可将其切换到远程位置。management.security.enabled=false
将在/env
,/refresh
等管理点上禁用 Spring Security。这是用于开发设置的,应启用生产安全性。
因此,此步骤将指向 git 位置和服务器端口。
以上所有步骤都是我们需要在配置服务器端执行的,现在在此项目上执行最后的
mvn clean install
命令,以便正确编译所有内容并将其打包在目标文件夹以及本地 Maven 存储库中。 准备好客户端部分后,我们将启动配置服务器服务,然后我们将最终测试该功能。 -
验证配置
在嵌入式模式下运行服务的命令是
java -jar target\spring-config-server-0.0.1-SNAPSHOT.jar
,但是我们将在测试部分中重新访问它。要检查配置服务器是否可以识别属性,请首先使用项目代码库位置的命令提示符下的给定命令,从命令提示符下运行配置服务器微服务。
java -jar target\spring-config-server-0.0.1-SNAPSHOT.jar
现在打开浏览器并检查下面的 URL,它将返回 JSON 输出,在
propertySources
部分中,我们可以看到我们在属性中添加的所有属性。 这可以确保config-server
成功运行,它已经识别了 git 位置,并且正在为不同环境提供配置。http://localhost:8888/client-config/development
http://localhost:8888/client-config/production
另外,要检查服务器是否反映了属性文件中的任何更改而无需重新启动,请更改任何环境的属性和签入属性文件的值。 然后运行该特定环境的终结点,并验证更改后的属性值应立即反映出来而无需重新启动服务器。
要进行 git 签入,请在完成更改并通过任何文本编辑器保存文件后,运行命令
git add .
和git commit -m "test"
4. 客户端配置
现在,我们将进行客户端实现,在此我们将使用来自单独的微服务的那些属性,这是我们的最终目标 – 将配置外部化为不同的服务。
-
创建 Maven 项目
转到 https://start.spring.io/ Web 页面,并使用以下选定的工件生成客户端项目:
- 执行器
- 配置客户端
- 网页
- 其余存储库
生成前的屏幕如下所示; 单击生成后,我们将获得
.zip
文件下载选项。 像Spring-Config-Server
一样,将文件解压缩到某个目录中,然后将其导入 eclipse 中。pom.xml
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
生成具有列出依赖项的客户端项目
-
创建 REST 资源
添加一个
RestController
以查看响应中的服务器端属性值。 为此,请打开已生成的@SpringBootApplication
类文件,并将以下小类添加到该文件的末尾。 这非常简单明了,我们只是在/message
URL 处公开一种方法,我们将只返回由配置服务器微服务提供的msg
的属性值,该属性值已配置到本地 git 存储库(将会迁移到生产环境中的远程 git 存储库!)。SpringConfigClientApplication.java
package com.howtodoinjava.example.springconfigclient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class SpringConfigClientApplication { public static void main(String[] args) { SpringApplication.run(SpringConfigClientApplication.class, args); } @Autowired public void setEnv(Environment e) { System.out.println(e.getProperty("msg")); } } @RefreshScope @RestController class MessageRestController { @Value("${msg:Config Server is not working. Please check...}") private String msg; @GetMapping("/msg") public String getMsg() { return this.msg; } }
-
与配置服务器绑定
在
src\main\resources
目录中创建一个名为bootstrap.properties
的文件,并添加以下属性以与配置服务器以及一些必需的配置连接。bootstrap.properties
spring.application.name=client-config #Active Profile - will relate to development properties file in the server. #If this property is absent then,default profile will be activated which is #the property file without any environment name at the end. spring.profiles.active=development # N.B. this is the default: spring.cloud.config.uri=http://localhost:8888 management.security.enabled=false
现在让我们了解属性。
spring.application.name
只是将要部署的微服务的应用程序名称。spring.cloud.config.uri
是提及配置服务器 URL 的属性。 请注意,我们的配置服务器在端口8888
上运行; 通过打开 spring 配置服务器代码库的application.properties
文件进行验证,然后检查server.port=8888
。management.security.enabled=false
将在诸如/env
,/refresh
等管理端点上禁用 Spring Security。这是用于开发设置的,应启用生产安全性。
-
验证客户端配置
这是我们需要在配置客户端执行的,而不是在此项目上执行最终的
mvn clean install
命令,以便正确编译所有内容并将其打包在目标文件夹以及本地 Maven 存储库中。 我们将与服务器端一起启动配置客户端服务,最后我们将测试该功能。
5. 演示
让我们测试配置服务器应用程序。
-
生成并运行配置服务器项目
从
spring-config-server
文件夹中打开命令提示符,然后运行mvn clean install
命令。 一旦构建完成,就可以通过java -jar
命令(例如java -jar target\spring-config-server-0.0.1-SNAPSHOT.jar
)从该命令提示符本身运行应用程序。这将在本地主机的 8888 端口中启动配置服务器服务。
-
生成并运行配置客户端项目
同样,从
spring-config-client
文件夹中打开命令提示符,然后运行mvn clean install
命令。 一旦构建完成,就可以通过java -jar
命令(例如java -jar target\spring-config-client-0.0.1-SNAPSHOT.jar
)从该命令提示符本身运行应用程序。这将在
localhost
的 8080 端口中启动配置客户端服务。 -
测试 REST 端点
现在,在浏览器中,通过浏览 URL
http://localhost:8080/msg
打开/msg
REST 端点。 它应该返回config-server-client-development.properties
文件中提到的Hello world - this is from config server
。REST 端点
-
测试属性更改
现在,我们将进行属性更改,并测试该更改是否可以反映在配置客户端服务中,而无需重新启动任何微服务。
进行一些更改,在config-server-client-development.properties
中的msg
属性的值中,并在本地 git 中签入,然后在浏览器中再次单击http://localhost:8080/msg
,您将只使用旧值。为了反映新值,我们需要通过从任何 REST 客户端使用
POST
方法命中http://localhost:8080/refresh
端点来刷新配置。成功刷新配置客户端服务后,新值应反映在服务响应中。 这是因为
@RefreshScope
注解了我们已经公开的 Rest 控制器。
6. 检查是否遇到任何错误的东西
-
属性文件名称和客户端模块服务名称
spring.application.name = config-server-client
应该完全相同,否则,将不会检测到属性。 实际上,配置服务器在属性文件名的端点公开属性,如果浏览URLhttp://localhost:8888/config-server-client/development
,它将返回所有 Dev 环境值。所有 Dev 属性视图
-
确保如上所述使用
git init/add/commit
命令在 git 仓库中签入属性文件。 -
通过任何 REST 客户端调用
http://localhost:8080/refresh
的POST
方法,确保已刷新客户端服务环境。 否则,更改后的值将不会反映在客户端服务中。 -
确保在启动配置客户端服务时,配置服务器服务已在运行。 否则,注册可能会花费一些时间,这可能会在测试时造成混乱。
这就是为微服务创建配置服务器。 如果您在配置本文中提到的所有要点时遇到任何困难,请添加评论,我们很乐意调查问题。
配置服务器源码
配置客户端源码
下载 Git 仓库
学习愉快!
使用 Netflix Eureka 进行 Spring Cloud 服务发现
原文: https://howtodoinjava.com/spring-cloud/spring-cloud-service-discovery-netflix-eureka/
学习在 Netflix Eureka 注册表服务器上基于 Spring cloud 创建微服务,以及其他微服务(Eureka 客户端)如何使用它注册和发现服务来调用他们的 API。
我们将使用基于 Spring Boot 的 Spring Cloud API。 我们将使用 Netflix Eureka 服务器来构建服务注册服务器,并使用 Eureka 客户端进行注册并发现其他服务以调用 REST API。
总览
我们将为此 Netflix Eureka 示例创建三个微服务。
- Eureka 服务注册表服务器-此微服务将提供服务注册表和发现服务器。
- 学生微服务 – 这将提供一些基于学生实体的功能。 这将是一个基于 REST 的服务,最重要的是它将是一个 eureka 客户服务,它将与 eureka 服务对话以在服务注册表中注册自己。
- 学校微服务 – 与学生服务的类型相同 – 唯一增加的功能是它将使用服务查找机制调用学生服务。 我们不会使用学生服务的绝对 URL 与该服务进行交互。
这是上面列出的三个服务之间的交互图。
组件之间的交互
技术栈和运行时
- Java 1.8
- Eclipse IDE
- SpringCloud
- SpringBoot
- SpringRest
- Maven
什么是 Netflix Eureka 服务器和客户端?
众所周知,如今,微服务的发展势头非常强劲。 从单片架构过渡到基于微服务的体系结构在可维护性,可伸缩性,高可用性等切面为将来带来了许多好处。但是,与此同时,在进行这种迁移时也面临许多挑战。 其中之一是维护单个微服务地址。 这项任务可能非常复杂-取决于服务的数量及其动态性质。 如果整个基础架构都是分布式的,并且还存在一些复制,那么维护该服务地址将变得更加困难。
为了解决这个问题,在分布式计算中有一个称为“服务注册和发现”的概念,其中一个专用服务器负责维护已部署和删除的所有微服务的注册表。 这就像所有其他应用程序/微服务的电话簿一样。
可以将其视为微服务(客户端)可以注册自己并发现其他已注册微服务的查找服务。 当客户端微服务向 Eureka 注册时,它会提供元数据,例如主机,端口和运行状况指示器,从而允许其他微服务发现它。 发现服务器期望来自每个微服务实例的常规心跳消息。 如果实例开始始终无法发送心跳,发现服务器将从其注册表中删除该实例。 这样,我们将拥有一个非常稳定的微服务生态系统,彼此协作,并且最重要的是,我们不必手动维护其他微服务的地址,如果频繁进行规模扩大/缩减,则这是几乎不可能完成的任务 ,根据需要,我们使用虚拟主机专门在云环境中托管服务。
Eureka 服务注册表服务器
请按照以下步骤创建和运行 Eureka 服务器。
创建 Eureka 服务器
从 Spring Boot 初始化器页面创建一个具有两个依赖项的 Spring Boot 项目,即Eureka server
和Actuator
。 给出其他 Maven GAV 坐标并下载项目。
Eureka 服务器的服务项目生成
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。 在此步骤中,将从 maven 存储库下载所有必需的依赖项。
现在打开在下载的项目中已经生成的SpringEurekaServerApplication
类,并在该类上添加@EnableEurekaServer
注解。
package com.example.howtodoinjava.springeurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class SpringEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringEurekaServerApplication.class, args);
}
}
再次构建项目。 使用此注解,此工件将充当微服务注册表和发现服务器。
服务器配置
在src\main\resources
目录中创建一个名为application.yml
的文件。 添加这些属性:
server:
port: ${PORT:8761} # Indicate the default PORT where this service will be started
eureka:
client:
registerWithEureka: false #telling the server not to register himself in the service registry
fetchRegistry: false
server:
waitTimeInMsWhenSyncEmpty: 0 #wait time for subsequent sync
在src\main\resources
目录中创建另一个名为bootstrap.yml
的文件。 添加这些属性:
spring:
application:
name: eureka
cloud:
config:
uri: ${CONFIG_SERVER_URL:http://localhost:8888}
测试 Eureka 服务器
作为 Spring Boot 应用程序启动该应用程序。 打开浏览器并转到http://localhost:8761/
,您应该看到如下所示的 eureka 服务器主页。
没有任何客户端的 Eureka 控制台
请注意,目前尚未在此处注册任何服务,这是我们期望的,一旦我们启动客户端服务,此服务器将自动更新客户端服务的详细信息。
Eureka 客户端 – 学生服务
请按照以下步骤创建并运行运行学生服务的 Eureka 客户端。
创建 Eureka 客户端项目
从具有四个依赖项的初始化器页面创建 Spring Boot 项目,即Actuator
,Web
,Rest Repositories
和Eureka Discovery
。 给出其他 Maven GAV 坐标并下载项目。
客户端项目生成:学生微服务
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。
现在,在src
文件夹中的 Spring 运行应用程序类上添加@EnableEurekaClient
注解。 使用此注解,此工件将充当 Spring Discovery 客户端,并在连接到此服务的 eureka 服务器中进行注册。
package com.example.howtodoinjava.springeurekaclientstudentservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class SpringEurekaClientStudentServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringEurekaClientStudentServiceApplication.class, args);
}
}
客户端配置
在src\main\resources
目录中创建一个名为application.yml
的文件,然后添加以下几行。
server:
port: 8098 #default port where the service will be started
eureka: #tells about the Eureka server details and its refresh time
instance:
leaseRenewalIntervalInSeconds: 1
leaseExpirationDurationInSeconds: 2
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
healthcheck:
enabled: true
lease:
duration: 5
spring:
application:
name: student-service #current service name to be used by the eureka server
management:
security:
enabled: false #disable the spring security on the management endpoints like /env, /refresh etc.
logging:
level:
com.example.howtodoinjava: DEBUG
添加 REST API
现在添加一个RestController
并公开一个 REST 端点,以获取特定学校的所有学生详细信息。 在这里,我们公开/getStudentDetailsForSchool/{schoolname}
端点来满足业务目的。 为简单起见,我们正在对学生详细信息进行硬编码。
package com.example.howtodoinjava.springeurekaclientstudentservice.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springeurekaclientstudentservice.domain.Student;
@RestController
public class StudentServiceController {
private static Map<String, List<Student>> schooDB = new HashMap<String, List<Student>>();
static {
schooDB = new HashMap<String, List<Student>>();
List<Student> lst = new ArrayList<Student>();
Student std = new Student("Sajal", "Class IV");
lst.add(std);
std = new Student("Lokesh", "Class V");
lst.add(std);
schooDB.put("abcschool", lst);
lst = new ArrayList<Student>();
std = new Student("Kajal", "Class III");
lst.add(std);
std = new Student("Sukesh", "Class VI");
lst.add(std);
schooDB.put("xyzschool", lst);
}
@RequestMapping(value = "/getStudentDetailsForSchool/{schoolname}", method = RequestMethod.GET)
public List<Student> getStudents(@PathVariable String schoolname) {
System.out.println("Getting Student details for " + schoolname);
List<Student> studentList = schooDB.get(schoolname);
if (studentList == null) {
studentList = new ArrayList<Student>();
Student std = new Student("Not Found", "N/A");
studentList.add(std);
}
return studentList;
}
}
Student
类是一个简单的 POJO。
public class Student
{
private String name;
private String className;
public Student(String name, String className) {
super();
this.name = name;
this.className = className;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
测试 Eureka 客户端
作为 spring boot 应用程序启动该项目。 现在,验证此服务已在 Eureka 服务器中自动注册。 转到 Eureka 服务控制台并刷新页面。 现在,如果一切顺利,我们将在 eureka 服务控制台中看到学生服务的一项。 这表明 Eureka 服务器和客户端都相互了解。
已注册学生服务的 Eureka 控制台
现在,我们将验证/getStudentDetailsForSchool/{schoolname}
端点是否已启动并正在运行。 转到浏览器并转到http://localhost:8098/getStudentDetailsForSchool/abcschool
,它将为特定学校abcschool
提供学生详细信息。
学生服务响应
Eureka 客户端 – 学校服务
现在,我们将创建学校服务,该服务将在 eureka 服务器中进行注册-它将发现并调用没有硬编码 URL 路径的学生服务。
遵循创建学生服务的确切步骤,以及创建并运行运行学校服务的 Eureka 客户程序。
创建 Eureka 客户端项目
从具有四个依赖项的初始化器页面创建 Spring Boot 项目,即Actuator
,Web
,Rest Repositories
和Eureka Discovery
。 给出其他 Maven GAV 坐标并下载项目。
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。
现在,在src
文件夹中的 Spring 运行应用程序类上添加@EnableEurekaClient
注解。 使用此注解,此工件将充当 Spring 发现客户端,并在连接到此服务的 eureka 服务器中进行注册。
package com.example.howtodoinjava.springeurekaclientschoolservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class SpringEurekaClientSchoolServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringEurekaClientSchoolServiceApplication.class, args);
}
}
客户端配置
在src\main\resources
目录中创建一个名为application.yml
的文件,然后添加以下几行。 除了端口号和服务名称之外,这些配置与学生服务非常相似。
server:
port: 9098 #port number
eureka:
instance:
leaseRenewalIntervalInSeconds: 1
leaseExpirationDurationInSeconds: 2
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
healthcheck:
enabled: true
lease:
duration: 5
spring:
application:
name: school-service #service name
logging:
level:
com.example.howtodoinjava: DEBUG
添加使用学生服务的 REST API 的 REST API
现在添加一个RestController
,并公开一个 REST 点以获取学校详细信息。 该端点将使用带有应用程序名称的服务发现样式 URL,而不是带有host:port
的完整 URL。
package com.example.howtodoinjava.springeurekaclientschoolservice.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class SchoolServiceController {
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/getSchoolDetails/{schoolname}", method = RequestMethod.GET)
public String getStudents(@PathVariable String schoolname)
{
System.out.println("Getting School details for " + schoolname);
String response = restTemplate.exchange("http://student-service/getStudentDetailsForSchool/{schoolname}",
HttpMethod.GET, null, new ParameterizedTypeReference<String>() {}, schoolname).getBody();
System.out.println("Response Received as " + response);
return "School Name - " + schoolname + " \n Student Details " + response;
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
这样,我们可以摆脱特定的服务配置,并且可以赋予服务查找此处提供的 eureka 服务器和其余模板的责任。 如果多个实例正在为同一服务运行,我们也可以在此处应用负载平衡(请参阅@LoadBalanced
注解)。
我们使用的 URL 是http://student-service/getStudentDetailsForSchool/{schoolname}
。 显然,我们只使用服务名称student-service
代替host:port
。 这将由 spring 框架,eureka 服务器和 rest 模板在内部处理。
服务发现和调用演示
现在也开始学校服务。 所有这三个服务都已启动。 检查 eureka 服务器控制台。 学生和学校服务都必须在此注册。
两种服务均已注册的 Eureka 控制台
转到浏览器,然后转到http://localhost:9098/getSchoolDetails/abcschool
,它将提供特定学校abcschool
的详细信息。 内部调用了学生服务。 响应看起来像在浏览器中:
学校服务响应
检查是否遇到任何错误的事情
- 注解
@EnableEurekaServer
和@EnableEurekaClient
是应用程序生态系统的核心。 没有这两个东西将根本无法工作。 - 确保在启动配置客户端服务时,eureka 服务器服务已在运行,否则可能要花一些时间进行注册,这可能会在测试时造成混乱。
总结
我们看到了如何轻松高效地部署服务注册表和发现服务器以及客户端。 Spring 框架在内部维护着很多东西。 在这里,我们仅使用几个注解和非常少的配置即可快速完成所有操作。
这就是创建 spring eureka 服务器和微服务注册的全部内容。 如果您在执行本文时遇到任何困难,请添加评论。 我们将很乐意调查这个问题。
下载这篇文章的源码
学习愉快!
Consul 服务注册和发现示例
https://howtodoinjava.com/spring-cloud/consul-service-registration-discovery/
学习基于 Spring cloud 创建微服务,在 HashiCorp Consul 注册表服务器上注册以及其他微服务(发现客户端)如何使用它来注册和发现服务以调用其 API。
我们将使用基于 Spring Boot 的 Spring Cloud API。 我们将使用 Consul 注册表服务器来构建服务注册表服务器和通用发现客户端,后者将自行注册并发现其他服务以调用 REST API。
总览
Consul 提供服务发现,配置管理,运行状况检查和键值存储等多种功能。今天,我们将专注于服务注册和发现部分。 我们将开发以下组件来构建分布式生态系统,其中每个组件都相互依赖,但是它们之间的耦合非常松散,当然还有容错能力。
- Consul 代理 – 在充当发现/注册服务器功能的
localhost
上运行。 - 学生微服务 – 将基于学生实体提供一些功能。 这将是一个基于 REST 的服务,最重要的是它将是一个发现服务客户端,该客户端将与 Consul 服务器/代理对话以在服务注册表中注册自己。
- 学校微服务 – 与学生服务的类型相同 – 唯一增加的功能是它将通过服务查找机制调用学生服务。 我们不会使用学生服务的绝对 URL 与该服务进行交互。 我们将使用 Consul 发现功能,并在调用该功能之前先使用它查找学生服务实例。
这是相同组件的整体组件交互图。
组件图
技术栈和运行时
- Java 1.8
- Eclipse IDE
- Consul 即服务注册服务器
- SpringCloud
- SpringBoot
- SpringRest
- Maven
在本地工作站中配置 Consul
在开始练习之前,我们需要首先在localhost
中下载,配置和运行 consul 代理。
-
从 Consul 页面网站下载。 根据操作系统选择特定的程序包。 下载压缩文件后,我们需要将其解压缩到所需位置。
-
在本地工作站上启动 Consul 代理 – 我们已解压缩的 Zip 文件只有一个名为
consul.exe
的 exe 文件。 我们将在此处启动命令提示符,并使用以下命令启动代理。consul agent -server -bootstrap-expect=1 -data-dir=consul-data -ui -bind=192.168.6.1
确保输入正确的绑定地址,根据局域网设置的不同,绑定地址会有所不同。 在命令提示符下执行
ipconfig
以了解您的 IpV4 地址,并在此处使用它。ipconfig
命令代理启动命令日志
-
测试 Consul 服务器是否正在运行 – Consul 在默认端口上运行,并且代理成功启动后,浏览
http://localhost:8500/ui
,您应该会看到一个 控制台屏幕如:Consul 控制台 – 没有注册服务
因此,我们已经在本地计算机中配置了 consul,并且 consul 代理已成功运行。 现在,我们需要创建客户端并测试服务注册表和发现部分。
学生服务
请按照以下步骤创建和运行学生服务。 这将是一个发现客户端,将其注册到现在已经在我们的计算机中运行的 Consul 服务。
创建学生项目
从具有四个依赖项的初始化器页面创建一个 Spring Boot 项目。
- 执行器
- 网页
- REST 存储库
- Consul 发现
给出其他 Maven GAV 坐标并下载项目。
学生服务项目生成
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。
现在,在src
文件夹中的 Spring 运行应用程序类上添加@org.springframework.cloud.client.discovery.EnableDiscoveryClient
注解。 有了此注解,此工件将像 Spring 发现客户端一样发挥作用,并在连接到此服务的 Consul 服务器中注册自己。
package com.example.howtodoinjava.springcloudconsulstudent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudConsulStudentApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsulStudentApplication.class, args);
}
}
服务配置
打开application.properties
并添加以下属性
server.port=9098
spring.application.name: student-service
management.security.enabled=false
这是每个属性的详细信息:
server.port=9098
– 将在默认的 9098 端口中启动服务。spring.application.name: student-service
– 将使用student-service
标签在 Consul 服务器中注册自己,其他服务也将使用此名称本身查找该服务。management.security.enabled=false
– 此练习实际上不是必需的,但是它将在执行器模块提供的管理端点中禁用 SpringSecurity。
添加 REST API
现在添加一个RestController
并公开一个剩余端点,以获取特定学校的所有学生详细信息。 在这里,我们公开/getStudentDetailsForSchool/{schoolname}
端点来满足业务目的。 为简单起见,我们正在对学生详细信息进行硬编码。
package com.example.howtodoinjava.springcloudconsulstudent.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springcloudconsulstudent.domain.Student;
@RestController
public class StudentServiceController {
private static Map<String, List<Student>> schooDB = new HashMap<String, List<Student>>();
static {
schooDB = new HashMap<String, List<Student>>();
List<Student> lst = new ArrayList<Student>();
Student std = new Student("Sajal", "Class IV");
lst.add(std);
std = new Student("Lokesh", "Class V");
lst.add(std);
schooDB.put("abcschool", lst);
lst = new ArrayList<Student>();
std = new Student("Kajal", "Class III");
lst.add(std);
std = new Student("Sukesh", "Class VI");
lst.add(std);
schooDB.put("xyzschool", lst);
}
@RequestMapping(value = "/getStudentDetailsForSchool/{schoolname}", method = RequestMethod.GET)
public List<Student> getStudents(@PathVariable String schoolname) {
System.out.println("Getting Student details for " + schoolname);
List<Student> studentList = schooDB.get(schoolname);
if (studentList == null) {
studentList = new ArrayList<Student>();
Student std = new Student("Not Found", "N/A");
studentList.add(std);
}
return studentList;
}
}
Student.java
模型
package com.example.howtodoinjava.springcloudconsulstudent.domain;
public class Student {
private String name;
private String className;
public Student(String name, String className) {
super();
this.name = name;
this.className = className;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
验证学生服务
作为 spring boot 应用程序启动该项目。 现在,确认此服务已在 Consul 服务器中自动注册。 转到 Consul 代理控制台并刷新页面。 现在,如果一切顺利,我们将在 Consul 代理控制台中看到student-service
的一项。
Consul 控制台的学生服务注册
这表明 Consul 服务器和客户端都相互了解,这是 Consul 服务器和学生服务之间发生的一种自动注册和发现。
现在,我们将验证/getStudentDetailsForSchool/{schoolname}
端点是否已启动并正在运行。 转到浏览器并转到http://localhost:9098/getStudentDetailsForSchool/abcschool
,它将为特定学校abcschool
提供学生详细信息。
学生服务响应
学校服务 – 发现客户端
现在,我们将创建学校服务,该服务将在 Consul 服务器中进行注册-它将发现并调用没有硬编码 URL 路径的学生服务。
请按照相同的步骤创建和运行学校服务。 这将是一个发现客户端,将其注册到现在已经在我们的计算机中运行的 Consul 服务。
它将内部调用已开发的学生服务,并将使用 Consul 服务发现功能来发现学生实例。
创建学校项目
从具有四个依赖项的初始化器页面创建一个 Spring Boot 项目。
- 执行器
- 网页
- REST 存储库
- Consul 发现
给出其他 Maven GAV 坐标并下载项目。
学校生成
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。
现在,在src
文件夹中的 Spring 运行应用程序类上添加@org.springframework.cloud.client.discovery.EnableDiscoveryClient
注解。 有了此注解,此工件将像 Spring 发现客户端一样发挥作用,并在连接到此服务的 Consul 服务器中注册自己。
package com.example.howtodoinjava.springcloudconsulschool;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudConsulSchoolApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsulSchoolApplication.class, args);
}
}
服务配置
打开application.properties
并添加以下属性
server.port=8098
spring.application.name: school-service
management.security.enabled=false
这是每个属性的详细信息:
server.port=8098
– 将在默认的 8098 端口中启动服务spring.application.name: school-service
– 将使用school-service
标签在 Consul 服务器中注册自己。management.security.enabled=false
– 将在执行器模块提供的管理端点中禁用 SpringSecurity。
添加使用学生服务的 REST API 的 REST API
现在添加一个RestController
,并公开一个 REST 点以获取学校详细信息。 该端点将使用带有应用程序名称的服务发现样式 URL,而不是带有host:port
的完整 URL 约定。
SchoolServiceController.java
package com.example.howtodoinjava.springcloudconsulschool.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springcloudconsulschool.delegate.StudentServiceDelegate;
@RestController
public class SchoolServiceController {
@Autowired
StudentServiceDelegate studentServiceDelegate;
@RequestMapping(value = "/getSchoolDetails/{schoolname}", method = RequestMethod.GET)
public String getStudents(@PathVariable String schoolname)
{
System.out.println("Going to call student service to get data!");
return studentServiceDelegate.callStudentServiceAndGetData(schoolname);
}
}
StudentServiceDelegate.java
package com.example.howtodoinjava.springcloudconsulschool.delegate;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class StudentServiceDelegate
{
@Autowired
RestTemplate restTemplate;
public String callStudentServiceAndGetData(String schoolname)
{
System.out.println("Consul Demo - Getting School details for " + schoolname);
String response = restTemplate.exchange("http://student-service/getStudentDetailsForSchool/{schoolname}", HttpMethod.GET, null, new ParameterizedTypeReference<String>() {}, schoolname).getBody();
System.out.println("Response Received as " + response + " - " + new Date());
return "School Name - " + schoolname + " ::: Student Details " + response + " - " + new Date();
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
看上面的代码。 在StudentServiceDelegate
中,我们使用RestTemplate
调用学生服务,并将学生服务的 URL 用作http://<font color="RED">student-service/getStudentDetailsForSchool/{schoolname}
。
这样,我们就可以摆脱特定的服务配置,并且可以将服务查找职责赋予consul
服务器和此处提供的其余模板。 如果多个实例正在为同一服务运行,我们也可以在此处应用负载平衡(请参阅@LoadBalanced
注解)。
示例
逐步执行以下步骤以了解整个过程:
-
检查 Consul Agent 是否仍在运行 – 打开浏览器,然后浏览
http://localhost:8500/ui
。 它应该显示如上所述的 Consul 控制台。 -
检查学生服务是否已运行 – 从 Consul 管理页面和浏览器中检查学生服务是否已启动并正在运行。 如果没有启动该服务,并验证它已在 Consul 服务器中注册。
-
启动并检查学校服务 – 从命令提示符启动学校服务,并检查它是否已在 Consul 服务器中注册。
-
打开浏览器并使用 URL
http://localhost:8098//getSchoolDetails/abcschool
测试学校 REST 服务。 它将给出以下响应,并将使用 Consul 服务在内部调用学生服务。学校服务响应
-
还可以尝试通过
java -jar "-Dserver.port=9099 target\spring-cloud-consul-student-0.0.1-SNAPSHOT.jar
更改端口来启动多实例学生服务。这些服务也将在consul 中注册,并且因为我们在RestTemplate
中使用@LoadBalanced
注解,负载平衡也将在内部完成,请检查相应的学生服务控制台以确认在多实例场景中调用了哪个实例。一旦我们注册了多个服务和多个实例,Consul 服务器的外观将如此。
Consul 所有服务正在运行
检查是否遇到任何错误的内容
- 注解
@EnableDiscoveryClient
和 Consul 代理运行是应用程序生态系统的核心。 没有这两个东西将根本无法工作。 - 确保在启动学校服务,学生服务,Consul 服务器时,代理已在运行,否则可能要花一些时间进行注册,并且在测试时可能会造成混淆。
总结
我们看到了如何高效地部署 Consul 服务注册表和发现服务器以及客户端。 Spring 框架在内部维护着很多东西。 在这里,我们仅使用几个注解和非常少的配置即可快速完成所有操作。
这就是为 spring 服务创建微服务的服务。
如果您在执行本文时遇到任何困难,请添加注解。 我们将很乐意调查这个问题。
下载源码
祝您学习愉快!
Hystrix 断路器模式 – SpringCloud
原文:https://howtodoinjava.com/spring-cloud/spring-hystrix-circuit-breaker-tutorial/
了解如何利用称为 Hystrix 的 Spring cloud Netflix 堆栈组件之一来实现断路器,同时调用基础微服务。 通常需要在某些基础服务永久关闭/抛出错误的应用程序中启用容错功能,我们需要自动退回到程序执行的不同路径。 这与使用大量底层微服务的生态系统的分布式计算风格有关。 这是断路器模式的帮助之处,Hystrix 是构建此断路器的工具。
Hystrix 示例
Hystrix 配置通过四个主要步骤完成。
-
添加 Hystrix 启动器和仪表板依赖项。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
-
添加
@EnableCircuitBreaker
注解 -
添加
@EnableHystrixDashboard
注解 -
添加注解
@HystrixCommand(fallbackMethod = "myFallbackMethod")
什么是断路器模式?
如果我们在基于微服务的体系结构上设计系统,则通常会开发许多微服务,并且这些微服务会在实现某些业务目标时相互影响很大。 现在,我们所有人都可以假设,如果所有服务都已启动并且正在运行,并且每个服务的响应时间令人满意,那么这将给出预期的结果。
现在,如果当前生态系统的任何服务出现问题并停止为请求提供服务,将会发生什么。 这将导致超时/异常,并且由于此单点故障,整个生态系统将变得不稳定。
在这里,断路器模式非常方便,一旦发现任何此类情况,它将流量重定向到回退路径。 它还会密切监视有缺陷的服务,并在服务恢复正常后恢复流量。
因此,断路器是进行服务调用的方法的一种包装,它监视服务的运行状况,一旦出现问题,断路器将跳闸,所有进一步的调用都将返回到断路器,最后一旦服务回来了就自动恢复! 太酷了吧?
断路器调用顺序
Hystrix 断路器示例
为了演示断路器,我们将创建以下两个微服务,其中第一个微服务依赖于另一个。
- 学生微服务 – 将提供
Student
实体的一些基本功能。 这将是一个基于 REST 的服务。 我们将从School
服务中调用此服务,以了解断路器。 它将在本地主机的端口8098
上运行。 - 学校微服务 – 同样是一个简单的基于 REST 的微服务,我们将使用 Hystrix 实现断路器。
Student
服务将从此处调用,一旦学生服务不可用,我们将测试后备路径。 它将在本地主机的端口 9098 上运行。
技术栈和演示运行时
- Java 1.8
- Eclipse 作为 IDE
- Maven 作为构建工具
- Spring Cloud Hystrix 作为断路器框架
- SpringBoot
- SpringRest
创建学生服务
请按照以下步骤创建和运行学生服务-一个简单的 REST 服务,提供学生实体的一些基本功能。
创建 spring boot 项目
从 Spring Boot 初始化器页面创建具有三个依赖项的 Spring Boot 项目,即Web
,Rest Repositories
和Actuator
。 给出其他 Maven GAV 坐标并下载项目。
学生服务生成
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。 在此步骤中,将从 maven 存储库下载所有必需的依赖项。
服务器端口设置
打开application.properties
并添加端口信息。
server.port = 8098
这将使该应用程序在默认端口 8098 上运行。通过在启动服务器时提供 -Dserver.port = XXXX
参数,可以轻松地覆盖此应用程序。
创建 REST API
现在添加一个称为StudentServiceController
的 REST 控制器类,并公开一个其余端点,以获取特定学校的所有学生详细信息。 在这里,我们公开/getStudentDetailsForSchool/{schoolname}
端点来满足业务目的。 为简单起见,我们正在对学生详细信息进行硬编码。
StudentServiceController.java
package com.example.howtodoinjava.springhystrixstudentservice.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springhystrixstudentservice.domain.Student;
@RestController
public class StudentServiceController {
private static Map<String, List<Student>> schooDB = new HashMap<String, List<Student>>();
static {
schooDB = new HashMap<String, List<Student>>();
List<Student> lst = new ArrayList<Student>();
Student std = new Student("Sajal", "Class IV");
lst.add(std);
std = new Student("Lokesh", "Class V");
lst.add(std);
schooDB.put("abcschool", lst);
lst = new ArrayList<Student>();
std = new Student("Kajal", "Class III");
lst.add(std);
std = new Student("Sukesh", "Class VI");
lst.add(std);
schooDB.put("xyzschool", lst);
}
@RequestMapping(value = "/getStudentDetailsForSchool/{schoolname}", method = RequestMethod.GET)
public List<Student> getStudents(@PathVariable String schoolname) {
System.out.println("Getting Student details for " + schoolname);
List<Student> studentList = schooDB.get(schoolname);
if (studentList == null) {
studentList = new ArrayList<Student>();
Student std = new Student("Not Found", "N/A");
studentList.add(std);
}
return studentList;
}
}
Student.java
package com.example.howtodoinjava.springhystrixstudentservice.domain;
public class Student {
private String name;
private String className;
public Student(String name, String className) {
super();
this.name = name;
this.className = className;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
建立并测试学生服务
现在,使用mvn clean install
进行最终构建,并使用命令java -jar target\spring-hystrix-student-service-0.0.1-SNAPSHOT.jar
运行服务器。 这将在默认端口8098
中启动学生服务。
打开浏览器,然后输入http://localhost:8098/getStudentDetailsForSchool/abcschool
。
它应在浏览器中显示以下输出:
学生服务响应
创建学校服务 - 启用 Hystrix
与学生服务类似,为学校创建另一个微服务。 它将在内部调用已开发的学生服务。
生成 Spring Boot 项目
从 Spring Boot 初始化器页面主要使用那些依赖项创建一个 Spring Boot 项目。
- Web – REST 端点
- 执行器 – 提供基本管理 URL
- Hystrix – 启用断路器
- Hystrix 仪表板 – 启用一个与断路器监控相关的仪表板屏幕
给出其他 Maven GAV 坐标并下载项目。
学校服务项目
将项目解压缩并作为现有 maven 项目导入到 Eclipse 中。 在此步骤中,将从 maven 存储库下载所有必需的依赖项。
服务器端口设置
打开application.properties
并添加端口信息。
server.port = 9098
这将使该应用程序在默认端口 9098 上运行。通过在启动服务器时提供 -Dserver.port = XXXX
参数,可以轻松地覆盖此应用程序。
启用 Hystrix 设置
打开SpringHystrixSchoolServiceApplication
,即使用@SpringBootApplication
生成的类,并添加@EnableHystrixDashboard
和@EnableCircuitBreaker
注解。
这将在应用程序中启用 Hystrix 断路器,还将添加一个在 Hystrix 提供的localhost
上运行的有用仪表板。
package com.example.howtodoinjava.springhystrixschoolservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
@EnableCircuitBreaker
public class SpringHystrixSchoolServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringHystrixSchoolServiceApplication.class, args);
}
}
添加 REST 控制器
在我们将公开/getSchoolDetails/{schoolname}
端点的位置添加SchoolServiceController
Rest 控制器,该端点将仅返回学校详细信息及其学生详细信息。 对于学生详细信息,它将调用已经开发的学生服务端点。 我们将创建一个代表层StudentServiceDelegate.java
来调用学生服务。 这个简单的代码看起来像
SchoolServiceController.java
package com.example.howtodoinjava.springhystrixschoolservice.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.howtodoinjava.springhystrixschoolservice.delegate.StudentServiceDelegate;
@RestController
public class SchoolServiceController {
@Autowired
StudentServiceDelegate studentServiceDelegate;
@RequestMapping(value = "/getSchoolDetails/{schoolname}", method = RequestMethod.GET)
public String getStudents(@PathVariable String schoolname) {
System.out.println("Going to call student service to get data!");
return studentServiceDelegate.callStudentServiceAndGetData(schoolname);
}
}
StudentServiceDelegate
我们将在此处执行以下操作以启用 Hystrix 断路器。
- 通过提供的 Spring 框架调用学生服务
RestTemplate
- 添加 Hystrix 命令以启用回退方法 –
@HystrixCommand(fallbackMethod = "callStudentServiceAndGetData_Fallback")
– 这意味着我们将不得不添加另一个具有相同签名的方法callStudentServiceAndGetData_Fallback
,当实际的学生服务关闭时将调用该方法。 - 添加后备方法 –
callStudentServiceAndGetData_Fallback
,它将仅返回一些默认值。
package com.example.howtodoinjava.springhystrixschoolservice.delegate;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
public class StudentServiceDelegate {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "callStudentServiceAndGetData_Fallback")
public String callStudentServiceAndGetData(String schoolname) {
System.out.println("Getting School details for " + schoolname);
String response = restTemplate
.exchange("http://localhost:8098/getStudentDetailsForSchool/{schoolname}"
, HttpMethod.GET
, null
, new ParameterizedTypeReference<String>() {
}, schoolname).getBody();
System.out.println("Response Received as " + response + " - " + new Date());
return "NORMAL FLOW !!! - School Name - " + schoolname + " ::: " +
" Student Details " + response + " - " + new Date();
}
@SuppressWarnings("unused")
private String callStudentServiceAndGetData_Fallback(String schoolname) {
System.out.println("Student Service is down!!! fallback route enabled...");
return "CIRCUIT BREAKER ENABLED!!! No Response From Student Service at this moment. " +
" Service will be back shortly - " + new Date();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
建立和测试学校服务
现在,使用mvn clean install
进行最终构建,并使用命令java -jar target\spring-hystrix-school-service-0.0.1-SNAPSHOT.jar
运行服务器。 这将在默认端口 9098 中启动学校服务。
如上所述启动学生服务,然后通过打开浏览器并键入http://localhost:9098/getSchoolDetails/abcschool
来测试学校服务。 它应该在浏览器中显示以下输出:
学校服务响应
测试 Hystrix 断路器 - 演示
打开浏览器,然后输入http://localhost:9098/getSchoolDetails/abcschool
。
它应在浏览器中显示以下输出:
学校服务响应
现在我们已经知道,学校服务在内部调用学生服务,并且正在从该服务获取学生详细信息。 因此,如果两个服务都在运行,则学校服务将显示学生服务返回的数据,正如我们在上面的学校服务浏览器输出中所看到的那样。 这是电路关闭状态。
现在,让我们停止学生服务,只需在学生服务服务器控制台中按CTRL + C
(停止服务器),然后从浏览器再次测试学校服务。 这次它将返回回退方法响应。 Hystrix 出现在这里,它会频繁地监视学生服务,并且在停机时,Hystrix 组件已打开电路并启用了后备路径。
这是浏览器中的回退输出。
学校服务响应回退路径
再次启动学生服务,等待片刻,然后返回学校服务,它将再次开始以正常流程进行响应。
Hystrix 仪表板
当我们添加了 hystrix 仪表板依赖时,hystrix 在以下 URL 中提供了一个不错的仪表盘和 Hystrix 流:
-
http://localhost:9098/hystrix.stream
– Hystrix 产生的连续流。 这只是健康检查结果以及 Hystrix 正在监视的所有服务呼叫。 示例输出在浏览器中将如下所示:Hystrix 流式输出
-
http://localhost:9098/hystrix
– 这是可视仪表板的初始状态。Hystrix 初始仪表盘
-
现在,在仪表板中添加
http://localhost:9098/hystrix.stream
,以获得由 Hystrix 组件监视的电路的有意义的动态视觉表示。 在主页中提供流式输入后的虚拟仪表盘:Hystrix 虚拟仪表盘
总结
这就是可以制造 Spring 开关 Hystrix 断路器的全部内容,我们已经测试了电路开放路径和电路闭合路径。 自行进行设置,并使用不同的组合服务状态以更清楚地了解整个概念。
如果您在执行本文时遇到任何困难,请添加评论。 我们将很乐意调查这个问题。
下载源码
学习愉快!
如何将 Spring Boot 应用程序部署到 Cloud Foundry 平台
原文: https://howtodoinjava.com/spring-cloud/pivotal-cloud-foundry-spring-boot-example/
如今, 云计算和微服务已经成为非常流行的概念,几乎所有组织都在对其进行快速投资和调整。 当前市场上只有少数流行的云供应器,Cloud Foundry 是其中之一。这是 PaaS 服务,我们可以在其中轻松部署和管理应用程序,Cloud Foundry 将负责其余基于云的产品,例如可伸缩性,高可用性等。
今天,我们将学习从在本地工作站中设置 Cloud Foundry 开始在 Cloud Foundry 中部署 Spring Boot 应用程序。 当前有许多可用的 Cloud Foundry 发行版,在本文中,我们将主要集中在称为 Pivotal Web 服务的 Pivotal Cloud Foundry 平台上。
什么是 Cloud Foundry
Cloud Foundry 是一个开源平台即服务(PaaS),可为您提供选择的云,开发人员框架和应用程序服务。 它是开源的,由 Cloud Foundry 组织管理。 最初的 Cloud Foundry 由 VMware 开发,目前由 GE,EMC 和 VMware 的合资公司 Pivotal 管理。
现在,由于 Cloud Foundry 是开源产品,因此许多受欢迎的组织当前单独提供了该平台,以下是当前经过认证的供应器的列表。
- Pivotal Cloud Foundry
- IBM Bluemix
- HPE Helion Stackato 4.0
- Atos Canopy
- CenturyLink App Fog
- GE Predix
- 华为 FusionStage
- SAP Cloud 平台
- Swisscom 应用程序云
Windows 上的 Cloud Foundry 安装
这是 Windows 的安装步骤,对于其他操作系统,Cloud Foundry 拥有非常好的文档,我们可以轻松地遵循。
Cloud Foundry 在命令提示符下运行良好,Cloud Foundry 提供了一个名为cf
的命令行工具,几乎可以为我们完成所有活动。 因此,要使该工具(cf
命令)在本地工作站中可用,首先我们需要安装并配置 Cloud Foundry 命令行(CLI)接口。
-
下载 CF Windows 安装程序。 它将提示您进行下载。 保存 zip 文件分发。
-
将 zip 文件解压缩到工作站中合适的位置。
解压安装器
-
成功进行解压缩之后,双击
cf
CLI 可执行文件。CF 安装器的位置
-
出现提示时,单击“安装”,然后单击“关闭”。 这是相同的示例步骤。 这是非常直接的,您可以选择默认值。
第一步
第二步
第三步
第四步
-
通过打开终端窗口并输入
cf
来验证安装。 如果安装成功,则显示cf
CLI 帮助列表。 这表明您已准备好使用本地工作站上的任何 Cloud Foundry 平台。Cloud Foundry 安装验证
现在,我们将继续进行 Pivotal Web 服务帐户注册,并开发一个示例应用程序,并推送到 Cloud Foundry。
设置 PWS 控制台
现在,我们需要在 Pivotal 中创建一个帐户,以便在 Pivotal Cloud Foundry 平台中部署我们的应用程序。 我们需要在下一页中注册才能开始注册过程。 它是免费的,它只会询问一些非常常见的问题,例如电子邮件地址,姓名等。
Pivotal web 服务控制台注册
注册完成后,我们可以通过枢纽 Web 服务控制台的登录屏幕登录控制台。
成功提供登录凭据后,我们将进入 CloudFoundry 控制台,在其中我们可以查看所有已部署的应用程序,可以监视这些应用程序并进行更多活动。 在这里,我们需要添加 org 和 space 等,这是非常简单和自我描述的。 下面是登录后的一个示例控制台屏幕。
目前尚未部署任何应用程序,因为我们尚未推送任何应用程序。
登录后的 PWS 控制台
使用 CLI 从 PWS 控制台登录和注销
- 登录到 PWS – 我们将使用
cf login -a api.run.pivotal.io
命令从本地工作站中安装的 CLI 工具登录到关键 Web 服务控制台。 它将 CLI 工具登录到 PWS 平台,以便我们可以从工作站部署和管理应用程序。 发出命令后,它将询问注册的电子邮件和密码,一旦成功提供,它将登录到平台。 - 从 PWS 控制台注销 – 一旦完成了该会话的所有工作,我们将使用命令
cf logout
从平台注销。
//To login
>> cf login -a api.run.pivotal.io
//To logout
>> cf logout
这是从命令提示符登录和注销的样子。
CF 的登录和注销
创建 Spring Boot 应用程序
现在,我们将创建一个 Spring Boot 应用程序,并将其部署到 PWS 控制台并从 Cloud Foundry 本身进行访问。 我们将创建一个应用程序,该应用程序将公开一个简单的 REST 端点,将其部署在 Pivotal Web 服务平台中后,将从我们的工作站进行测试。
技术栈
我们将使用下面的技术栈进行 Spring Boot 应用程序的开发和测试。
- SpringBoot
- Spring REST
- Maven
- Eclipse
- Cloud Foundry CLI
- 网页浏览器
生成 Spring Boot 应用程序
从 spring boot 初始化器页面开始,这是创建任何基于 spring boot 的应用程序的一个很好的起点。 在这里,我们将仅选择配置服务器启动器 pom。 屏幕截图是这样的。 使用此配置,一旦我们生成了项目,便会下载一个 zip 文件,我们将其解压缩后直接导入 eclipse 中。
Spring Boot 项目生成
将项目导入 Eclipse 以将其作为现有 Maven 项目。 让 maven 为您下载依赖项和设置类路径条目。
添加 REST 控制器和端点
我们需要添加简单的 REST 端点来测试从 Cloud Foundry 进行的部署。 自动化项目生成SpringHelloworldCfApplication.java
已提供的开放启动应用程序类,并添加以下几行以添加一个简单的端点,该端点将根据输入内容进行回显。
最后一堂课如下。
package com.example.howtodoinjava.springhelloworldcf;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class SpringHelloworldCfApplication {
public static void main(String[] args) {
SpringApplication.run(SpringHelloworldCfApplication.class, args);
}
}
@RestController
class MessageRestController {
@RequestMapping("/hello")
String getMessage(@RequestParam(value = "name") String name) {
String rsp = "Hi " + name + " : responded on - " + new Date();
System.out.println(rsp);
return rsp;
}
}
项目配置
在src\main\resources
目录的bootstrap.properties
文件中添加上下文路径和必需的属性,并在其中添加两个属性。
server.contextPath = /hello
management.security.enabled = false
这将为应用程序设置一个上下文路径/hello
,而management.security.enabled=false
将禁用 Spring Boot 管理端点(如/env, /refresh
等)的安全性。
本地测试
最后,在嵌入式 tomcat 容器中的 Local 中构建和测试应用程序。 为此,将应用程序作为 spring boot 应用程序启动。
转到浏览器并输入http://localhost:8080/hello?name=howtodoinjava
。 它应该回显该名称以及一些问候消息和响应过程时间。
REST API 输出
现在,我们将在已注册的 Pivotal Cloud Foundry 中部署应用程序。
在 Cloud Foundry 平台中部署 Spring Boot 应用程序
由于我们已经配置了 Cloud Foundry CLI,因此我们将使用 CLI cf push
命令在 Cloud Foundry 控制台中部署应用程序。
登录到 PWS 控制台
为此,请打开命令提示符并转到 Maven 应用程序的主目录,然后使用cf login -a api.run.pivotal.io
命令登录到重要的 Web 服务控制台。
它将要求提供注册的凭据,最后登录到控制台。
将应用程序推送到控制台
现在我们需要使用命令cf push
推送应用程序。
cf push spring-helloworld-cf -p target\spring-helloworld-cf-0.0.1-SNAPSHOT.jar
这会将应用程序部署到上一步中已登录的 PWS 控制台中。
通过cf push
来部署
在随附的日志文件中阅读有关push
命令的完整控制台日志。
验证应用程序部署
验证进入 PWS 控制台以检查是否显示了新部署的应用程序。 如果在前面的步骤中一切正常,则屏幕将如下所示。
在 PWS 中部署的应用程序 – 数量增加。
现在,单击上一步中突出显示的“应用程序”部分,以转到应用程序详细信息屏幕。 下面的视图将显示,并将以高亮显示将应用程序部署到的 URL。 请注意该 URL,以从浏览器进行测试。 在这种情况下,它将类似于https://spring-helloworld-cf.cfapps.io/
。 此 URL 将根据我们选择的应用程序名称而更改。
在 PWS 控制台中发布的应用程序 URL。
测试 REST 端点
现在访问浏览器,并使用 cf 控制台中发布的 url 主机访问应用程序。 对于此应用程序,URL 为https://spring-helloworld-cf.cfapps.io/hello?name=howtodoinjava
。
直接从 cf 访问应用程序
恭喜!! 您已成功将第一个 Spring Boot 应用程序部署到 Pivotal Cloud Foundry 平台中。
总结
因此,我们已经能够在 Pivotal Cloud Foundry 控制台中成功开发和部署一个 Spring Boot 应用程序,并能够从我们的本地工作站访问该应用程序。 此外,我们进行了 cf CLI 配置并在 PWS 试用帐户中注册。
就是这个话题了。 我建议您现在将自己的应用程序部署在 Cloud Foundry 上并进行测试。
将我的问题放在评论部分。
下载源码
学习愉快!
Netflix Zuul 示例 – Zuul API 网关模式 – Spring Cloud 教程
原文: https://howtodoinjava.com/spring-cloud/spring-cloud-api-gateway-zuul/
学习使用 Netflix Zuul 以及与 Spring Cloud 的牢固绑定来创建负载均衡器。 在这里,我们将主要关注 API 网关模式及其用法。 我们将构建一个 netflix zuul 示例,在此示例中,我们将创建一个微服务生态系统并测试其 Zuul API 网关在整个生态系统中的有效性和适用性。
这是一种非常常见的微服务模式,Zuul 的创建者 Netflix 大量智能地利用了这种模式,Netflix 声称所有 Netflix 流量首先进入 Zuul 集群,该集群主要负责基于不同的动态路由,监控,弹性和安全性。 基于 groovy 的自定义过滤器。
1. Zuul 在微服务生态系统中是什么位置?
构建微服务时,一个常见的问题是为系统的客户端应用程序提供唯一的网关。 您的服务被划分为小型微服务应用程序,否则用户不应该看到它们,否则可能会导致大量的开发/维护工作。 此外,在某些情况下,整个生态系统网络流量可能会通过单个点,这可能会影响群集的性能。
为解决此问题,Netflix(微服务的主要采用者)创建并开放了其 Zuul 代理服务器,并在之后将其开源,Pivotal 下面的 Spring 将其收纳在 Spring Cloud 技术栈中, 只需几个简单的步骤即可轻松有效地使用 zuul。
Zuul 是一种边缘服务,代理对多个支持服务的请求。 它为您的生态系统提供了统一的“前门”,允许任何浏览器,移动应用程序或其他用户界面使用来自多个主机的服务。 您可以将 Zuul 与其他 Netflix 堆栈组件(例如 Hystrix 进行容错)和 Eureka 进行服务发现进行集成,或将其用于管理系统中的路由规则,过滤器和负载平衡。 最重要的是,所有这些组件都可以通过 spring boot / cloud 方法由 spring 框架很好地适应。
前端具有 Zuul 网关的微服务生态系统
2. Zuul 组件
Zuul 主要具有四种类型的过滤器,使我们能够拦截任何特定事务在请求处理的不同时间轴上的流量。 我们可以为特定的网址格式添加任意数量的过滤器。
- 前置过滤器 – 在路由请求之前被调用。
- 后置过滤器 – 在路由请求后调用。
- 路由过滤器 – 用于路由请求。
- 错误过滤器 – 在处理请求时发生错误时调用。
Zuul 内部具有不同过滤器的请求处理流程
3. netflix zuul 示例概述
现在,让我们使用 Zuul 代理创建一个简单而有意义的生态系统,以免脏污。 我们将创建以下工件来演示整个过程:
-
学生微服务 – 基于 Spring Boot 的微服务,它将仅公开一个 URL 以启用某些搜索功能。 为简单起见,我们只返回硬编码的值,但在现实世界中,我们可以连接到此服务的任何位置以获取数据。
-
Zuul 网关服务代理 – 将再次基于 Spring Boot,它将基本上拦截所有学生服务的流量并应用一系列请求过滤器,然后路由到基础服务,并在响应服务时再次路由, 它将应用一些响应过滤。 由于它是网关,因此我们可以有效地使用过滤器来采取许多有趣且有用的操作。
网关服务的一些常见责任是:
- 在网关层应用微服务身份验证和安全性以保护实际服务
- 我们可以通过启用一些日志记录以获取边缘的有意义的数据和统计信息,从而对进入生态系统的所有流量进行微服务洞察并监视,以便为我们提供准确的生产视图。
- 动态路由可以根据需要将请求路由到不同的后端群集。
- 我们可以通过逐渐增加到新集群的流量来进行运行时压力测试,以便在许多情况下(例如, 群集具有新的硬件和网络设置,或者已部署了新版本的生产代码。
- 我们可以进行动态减载,即为每种类型的请求分配容量,并丢弃超出限制的请求。
- 我们可以应用静态响应处理,即直接在边缘构建一些响应,而不是将其转发到内部集群进行处理。
技术栈和运行时
- Java 1.8 和 Eclipse IDE 作为开发环境
- Spring Cloud Zuul 作为网关代理供应器
- Spring Boot 作为应用程序框架
- Spring Rest 用于将微服务公开为 REST
- Maven 作为构建工具
4. 创建学生微服务
请按照以下步骤开发学生微服务,该服务将公开几个 REST 终结点,这些终结点以后可通过 zuul 代理进行访问。 稍后我们将研究 zuul 部分,让我们首先创建学生服务。
4.1. 创建 Spring Boot 项目
从 spring 初始化器页面创建一个具有相关性(即Web
和Rest Repositories
)的 Spring Boot 项目。 给出其他 Maven GAV 坐标并下载项目。
学生服务 Maven 项目生成
将项目解压缩并将其作为existing maven project
导入 Eclipse。 在此步骤中,使用命令mvn clean install
重新构建 Maven,以便正确下载所有 Maven 依赖项。
4.2. 添加一些 REST 端点
现在,我们将仅向该服务添加一些 REST 端点,以便稍后测试代理。 为此,我们需要通过添加注解@RestController
来添加一个 REST 控制器。 为简单起见,我们将添加一个模型类Student
。
完成所有更改后,该类将如下所示。
package com.example.springboostudentservice;
import java.util.Date;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SpringBootStudentServiceApplication
{
@RequestMapping(value = "/echoStudentName/{name}")
public String echoStudentName(@PathVariable(name = "name") String name)
{
return "hello <strong style=\"color: red;\">" + name + " </strong> Responsed on : " + new Date();
}
@RequestMapping(value = "/getStudentDetails/{name}")
public Student getStudentDetails(@PathVariable(name = "name") String name)
{
return new Student(name, "Pune", "MCA");
}
public static void main(String[] args)
{
SpringApplication.run(SpringBootStudentServiceApplication.class, args);
}
}
class Student
{
String name;
String address;
String cls;
public Student(String name, String address, String cls) {
super();
this.name = name;
this.address = address;
this.cls = cls;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public String getCls() {
return cls;
}
}
4.3. 应用配置
现在打开application.properties
文件并添加这些条目。
spring.application.name=student
server.port=8090
在这里,我们通过属性spring.application.name=student
为该服务命名,并且通过server.port=8090
定义默认端口。 我们需要覆盖默认端口,因为我们将在本地主机中运行不同微服务的多个实例。
4.4. 验证学生服务
最后,使用命令mvn clean install
进行 maven 构建,并通过运行命令java -jar target\spring-boot-zuulgatwayproxy-student-service-0.0.1-SNAPSHOT.jar
作为 Spring Boot 应用程序启动该项目。 现在,一旦服务器启动,请转到浏览器并测试端点是否正常运行。
http://localhost:8090/echoStudentName/Sajal
浏览器输出
http://localhost:8090/getStudentDetails/Sajal
浏览器输出
现在,我们将使用 Zuul 创建实际的代理服务。
5. 创建 Zuul 网关服务代理
这将再次是基于 Spring Boot 的微服务,但它具有特殊功能。 它将使用 zuul 创建一个 API 网关代理,该代理将代理学生服务。 稍后,我们可以添加任何数量的微服务,例如学生服务,并能够创建强大的微服务生态系统。
5.1. 创建 Spring Boot 项目
从具有Zuul
依赖关系的 spring 初始化器页面创建一个 Spring Boot 项目。 给出其他 Maven GAV 坐标并下载项目。
Zuul 代理服务 Maven 项目生成
将项目解压缩并将其作为现有的 maven 项目导入 Eclipse。 在此步骤中,使用命令mvn clean install
重新构建 Maven,以便正确下载所有 Maven 依赖项。
5.2. 启用 Zuul 服务代理
现在,在src
文件夹中的 Spring 运行应用程序类上添加@EnableZuulProxy
注解。 使用此注解,此工件将像 Zuul 服务代理一样工作,并将启用 API 网关层的所有功能,如前所述。 然后,我们将添加一些过滤器和路由配置。
package com.example.springbootzuulgatwayproxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import com.example.springbootzuulgatwayproxy.filters.ErrorFilter;
import com.example.springbootzuulgatwayproxy.filters.PostFilter;
import com.example.springbootzuulgatwayproxy.filters.PreFilter;
import com.example.springbootzuulgatwayproxy.filters.RouteFilter;
@SpringBootApplication
@EnableZuulProxy
public class SpringBootZuulgatwayproxyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootZuulgatwayproxyApplication.class, args);
}
@Bean
public PreFilter preFilter() {
return new PreFilter();
}
@Bean
public PostFilter postFilter() {
return new PostFilter();
}
@Bean
public ErrorFilter errorFilter() {
return new ErrorFilter();
}
@Bean
public RouteFilter routeFilter() {
return new RouteFilter();
}
}
5.3. Zuul 路由配置
打开application.properties
并添加以下条目:
#Zuul routes. Here for /student path, we are routing to localhost:8090 with extra path after that.
zuul.routes.student.url=http://localhost:8090
#Ribbon is auto integrated with Zuul and for this exercise we are not using that.
ribbon.eureka.enabled=false
#Will start the gateway server @8080
server.port=8080
在这里,zuul.routes.student.url
将路由所有请求/student
的流量到实际的学生服务服务器。 这样,我们也可以将路由添加到其他服务。
ribbon.eureka.enabled
与 Zuul 自动集成。
server.port
– 需要覆盖默认端口,因为我们将在本地主机中运行不同微服务的多个实例。
5.4. 添加 Zuul 过滤器
如我们已经描述的,我们现在将添加一些过滤器,Zuul 支持 4 种类型的过滤器,即pre
,post
,route
和error
。 在这里,我们将创建每种类型的过滤器。
要编写过滤器,我们基本上需要执行以下步骤:
- 需要扩展
com.netflix.zuul.ZuulFilter
- 需要覆盖
filterType
,filterOrder
,shouldFilter
和run
方法。 这里filterType
方法只能返回四个字符串 –pre/post/route/error
中的任何一个。 依赖此值,过滤器将像特定过滤器一样工作。 run
方法是根据我们的要求放置过滤器逻辑的地方。- 同样,我们可以根据需要添加任意数量的任何特定过滤器,在这种类型的过滤器执行阶段,这种情况
filterOrder
将用于确定该过滤器的顺序。
前置过滤器代码 – 我们将添加以下前置过滤器。 目前,出于测试目的,过滤器除了println
以外无所作为。 但是实际上,这些功能足以执行前面提到的许多重要切面。
package com.example.springbootzuulgatwayproxy.filters;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
public class PreFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("Request Method : " + request.getMethod() + " Request URL : " + request.getRequestURL().toString());
return null;
}
}
后置过滤器
package com.example.springbootzuulgatwayproxy.filters;
import com.netflix.zuul.ZuulFilter;
public class PostFilter extends ZuulFilter {
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
System.out.println("Inside Response Filter");
return null;
}
}
路由过滤器
package com.example.springbootzuulgatwayproxy.filters;
import com.netflix.zuul.ZuulFilter;
public class RouteFilter extends ZuulFilter {
@Override
public String filterType() {
return "route";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
System.out.println("Inside Route Filter");
return null;
}
}
错误过滤器
package com.example.springbootzuulgatwayproxy.filters;
import com.netflix.zuul.ZuulFilter;
public class ErrorFilter extends ZuulFilter {
@Override
public String filterType() {
return "error";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
System.out.println("Inside Route Filter");
return null;
}
}
5.5. 注册 zuul 过滤器
创建这些过滤器的 Bean 定义以进行自动注册和启用。
@Bean
public PreFilter preFilter() {
return new PreFilter();
}
@Bean
public PostFilter postFilter() {
return new PostFilter();
}
@Bean
public ErrorFilter errorFilter() {
return new ErrorFilter();
}
@Bean
public RouteFilter routeFilter() {
return new RouteFilter();
}
6. Netflix Zuul 示例演示
因此,我们启用了 Zuul,添加了所需的配置并开发了过滤器。 因此,我们可以进行基本测试以了解整个过程。
使用命令mvn clean install
进行 Maven 构建,并通过运行命令java -jar target\spring-boot-zuulgatwayproxy-0.0.1-SNAPSHOT.jar
作为 Spring Boot 应用程序启动该项目。 现在,服务器启动后,进入浏览器并通过访问学生服务(名称为/student
)来测试端点是否正常运行。
http://localhost:8080/student/getStudentDetails/Sajal
代理服务输出
http://localhost:8080/student/echoStudentName/Sajal
代理服务输出
7. 总结
关于 netflix zuul 过滤器示例的全部内容。 我建议您自己做,添加更多基础服务,并通过代理路由请求,应用不同类型的过滤器,并在过滤器中添加真实的逻辑。
下载源码
将我的问题放在评论部分。
学习愉快!
Spring Cloud Zipkin 和 Sleuth 示例
原文: https://howtodoinjava.com/spring-cloud/spring-cloud-zipkin-sleuth-tutorial/
Zipkin 是用于微服务生态系统中分布式跟踪的非常有效的工具。 通常,分布式跟踪是对分布式事务中每个组件的延迟度量,其中调用多个微服务来为单个业务用例提供服务。 假设从我们的应用程序中,我们必须为事务调用 4 个不同的服务/组件。 在启用了分布式跟踪的情况下,我们可以测量哪个组件花费了多少时间。
这在调试过程中非常有用,当涉及大量基础系统并且应用程序在任何特定情况下变慢时。 在这种情况下,我们首先需要确定哪个底层服务实际上是缓慢的。 一旦发现服务缓慢,我们便可以解决该问题。 分布式跟踪有助于识别生态系统中的缓慢组件。
Zipkin
Zipkin 最初是在 Twitter 上开发的,基于 Google 论文的概念,该论文描述了 Google 内部构建的分布式应用程序调试器 – 精简程序。 它管理此数据的收集和查找。 要使用 Zipkin,将对应用程序进行检测以向其报告计时数据。
如果要对生态系统中的延迟问题或错误进行故障排除,则可以根据应用程序,跟踪的长度,注解或时间戳来对所有跟踪进行过滤或排序。 通过分析这些跟踪,可以确定哪些组件未按预期执行,并可以对其进行修复。
内部有 4 个模块:
- 收集器 – 一旦任何组件将跟踪数据发送到 Zipkin 收集器守护程序,Zipkin 收集器就会对其进行验证,存储和索引以进行查找。
- 存储 – 此模块在后端存储和索引查找数据。 支持 Cassandra,ElasticSearch 和 MySQL。
- 搜索 – 此模块提供了一个简单的 JSON API,用于查找和检索存储在后端的跟踪。 该 API 的主要使用者是 Web UI。
- Web UI – 一个非常好的 UI 界面,用于查看轨迹。
如何安装 Zipkin
可以在快速入门页上找到适用于不同操作系统的详细安装步骤,包括 Docker 映像。 对于 Windows 安装,只需从 maven 存储库下载最新的 Zipkin 服务器,然后使用以下命令运行可执行 jar 文件。
java -jar zipkin-server-1.30.3-exec.jar
Zipkin 启动后,我们可以在http://localhost:9411/zipkin/
上看到 Web UI。
上面的命令将使用默认配置启动 Zipkin 服务器。 对于高级配置,我们可以配置许多其他内容,例如存储,收集器监听器等。
要在 Spring Boot 应用程序中安装 Zipkin,我们需要在 Spring Boot 项目中添加 Zipkin 启动器依赖项。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
Sleuth
Sleuth 是 Spring Cloud 系列的工具。 它用于生成跟踪 ID,span id,并将这些信息添加到标头和 MDC 中的服务调用中,以便诸如 Zipkin 和 ELK 等用于存储,索引和处理日志文件之类的工具可以使用它。由于它来自 Spring Cloud 系列,因此一旦添加到CLASSPATH
中,它就会自动集成到常见的通信渠道,例如:
- 使用
RestTemplate
等发出的请求。 - 通过 Netflix Zuul 微代理的请求
- 在 Spring MVC 控制器处收到的 HTTP 标头
- 通过诸如 Apache Kafka 或 RabbitMQ 等消息传递技术的请求。
使用侦探非常容易。 我们只需要在 spring boot 项目中添加它的启动 pom。 它将 Sleuth 添加到项目中,因此在其运行时也是如此。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
到目前为止,我们已经将 Zipkin 和 Sleuth 集成到微服务中并运行 Zipkin 服务器。 让我们看看如何利用此设置。
Zipkin 和 Sleuth 集成示例
对于此演示,让我们创建 4 个基于 spring boot 的微服务。 它们都将同时具有 Zipkin 和 Sleuth 启动器依赖项。 在每个微服务中,我们将公开一个端点,从第一个服务中我们将调用第二个服务,从第二个服务中我们将调用第三个服务,以此类推,以此类推。
正如我们已经提到的,Sleuth 会自动与 rest 模板一起使用,因此它将将此检测到的服务调用信息发送到连接的 Zipkin 服务器。 然后 Zipkin 将开始进行延迟计算以及其他一些统计数据(如服务调用详细信息)的记账。
微服务交互
创建微服务
所有这四个服务将具有相同的配置,唯一的不同是端点更改的服务调用详细信息。 让我们创建具有 Web,Rest Repository,Zipkin 和 Sleuth 依赖项的 Spring Boot 应用程序。
我将这些服务打包在一个父项目中,以便可以将这四个服务一起构建以节省时间。 如果愿意,可以继续进行个人设置。 另外,我还添加了有用的 Windows 脚本,因此只需一个命令即可启动/停止所有服务。
这是一个示例 rest 控制器,它公开一个端点并使用 rest 模板调用一个下游服务。
package com.example.zipkinservice1;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ZipkinService1Application {
public static void main(String[] args) {
SpringApplication.run(ZipkinService1Application.class, args);
}
}
@RestController
class ZipkinController{
@Autowired
RestTemplate restTemplate;
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean
public AlwaysSampler alwaysSampler() {
return new AlwaysSampler();
}
private static final Logger LOG = Logger.getLogger(ZipkinController.class.getName());
@GetMapping(value="/zipkin")
public String zipkinService1()
{
LOG.info("Inside zipkinService 1..");
String response = (String) restTemplate.exchange("http://localhost:8082/zipkin2",
HttpMethod.GET, null, new ParameterizedTypeReference<String>() {}).getBody();
return "Hi...";
}
}
应用程序设置
由于所有服务都将在一台计算机上运行,因此我们需要在不同的端口上运行它们。 另外,要在 Zipkin 中进行标识,我们需要提供适当的名称。 因此,请在资源文件夹下的application.properties
文件中配置应用程序名称和端口信息。
server.port = 8081
spring.application.name = zipkin-server1
同样,对于其他 3 个服务,我们将使用端口 8082,8083,8084,名称也将类似于zipkin-server2
,zipkin-server3
和zipkin-server4
。
另外,我们有意在上次服务中引入了延迟,以便我们可以在 Zipkin 中进行查看。
示例
使用微服务中的命令mvn clean install
进行最终的 Maven 构建,启动所有 4 个应用程序以及 zipkin 服务器。
对于快速启动和停止,使用批处理文件Start-all.bat
和Stop-all.bat
。
现在,从浏览器测试第一个服务端点几次 – http://localhost:8081/zipkin
。 请注意,上述 4 种服务之一有意延迟。 因此,延迟是预期的最终响应,只是不要放弃。
API 调用成功后,我们可以在 zipkin UI http://localhost:9411/zipkin/
上看到延迟统计信息。 在第一个下拉菜单中选择第一个服务,然后单击查找跟踪按钮。
Zipkin 主界面
您应该看到这种类型的 UI,可以在其中通过查看跟踪数据来进行性能分析。
查找跟踪 UI
一个特定的事务概述
特定服务调用统计的详细信息
总结
在本教程中,我们学习了使用 Zipkin 分析服务调用中的延迟。 我们还了解了 Sleuth 如何帮助我们创建元数据并将其传递给 Zipkin。
希望这些信息对您使用 Zipkin 和 Sleuth 进行分布式跟踪很有帮助。
下载源码
将我的问题放在评论部分。
学习愉快!
Spring cloud ribbon 和 Eureka – 客户端负载均衡器示例
原文: https://howtodoinjava.com/spring-cloud/spring-boot-ribbon-eureka/
在此 Spring 云教程中,学习在 Spring Boot / Cloud 项目中使用 Netflix Ribbon 使用客户端负载平衡。 学习构建基于微服务的应用程序,这些应用程序使用 Ribbon 作为客户端负载平衡器,并使用 eureka 作为注册表服务。 了解如何在负载均衡器下动态添加微服务的新实例。
1. 传统的服务器端负载均衡
服务器端负载平衡涉及单片应用程序,在这些应用程序中,负载平衡器后面的应用程序实例数量有限。 我们将 war / ear 文件部署到多个服务器实例中,这些实例基本上是已部署了相同应用程序的服务器池,并在其前面放置了负载均衡器。
负载平衡器具有公共 IP 和 DNS。 客户端使用该公共 IP / DNS 发出请求。 负载平衡器决定将内部应用程序服务器请求转发到哪个请求。 它主要使用轮询或粘性会话算法。 我们称其为服务器端负载平衡。
1.1. 微服务架构中的问题
通常,服务器端负载平衡是一项手动工作,我们需要手动将实例添加/删除到负载平衡器才能工作。 因此,理想情况下,我们要失去当今随需应变的可伸缩性,以自动发现和配置何时分拆任何新实例。
另一个问题是制定故障转移策略以为客户提供无缝的体验。 最后,我们需要一个单独的服务器来承载负载均衡器实例,这会对成本和维护产生影响。
2. 客户端负载平衡
为了克服传统负载平衡的问题,客户端负载平衡成为了现实。 它们作为内置组件驻留在应用程序中,并与应用程序捆绑在一起,因此我们不必将它们部署在单独的服务器中。
现在,让我们可视化全局。 在微服务架构中,我们将不得不开发许多微服务,并且每个微服务在生态系统中可能具有多个实例。 为了克服这种复杂性,我们已经有了使用服务发现模式的流行解决方案。 在 SpringBoot 应用程序中,我们在服务发现空间中提供了两个选项,例如 Eureka,Consul,动物园管理员等。
现在,如果一个微服务想要与另一个微服务进行通信,则通常会使用发现客户端来查找服务注册表,并且 Eureka 服务器会将该目标微服务的所有实例返回给调用者服务。 然后,调用者服务负责选择要发送请求的实例。
在这里,客户端负载平衡成为现实,它会自动处理这种情况下的复杂性,并以负载平衡的方式委派给适当的实例。 注意,我们可以指定要使用的负载平衡算法。
3. Netflix Ribbon – 客户端负载平衡器
Spring Cloud 系列的 Netflix Ribbon 提供了这种功能来设置客户端负载平衡以及服务注册表组件。 Spring Boot 具有非常好的配置 Ribbon 客户端负载平衡器的简便方法。 它提供以下功能
- 负载均衡
- 容错能力
- 异步和响应模型中的多种协议(HTTP,TCP,UDP)支持
- 缓存和批处理
要获取 Ribbon 二进制文件,请转到 maven 中心。 这是在 Maven 中添加依赖项的示例:
pom.xml
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version>2.2.2</version>
</dependency>
4. Netflix Ribbon 示例
4.1. 技术栈
- Java,Eclipse,Maven 作为开发环境
- Spring-boot 和 Cloud 作为应用程序框架
- Eureka 即服务注册表服务器
- Ribbon 作为客户端负载均衡器
我们将创建以下组件,并查看整个生态系统如何在分布式环境中进行协调。
- 使用 Spring 运行的两个微服务。 一个需要根据业务需求调用另一个
- Eureka 服务注册服务器
- 调用微服务中的 Ribbon 通过服务发现以负载平衡的方式调用其他服务
- 以负载平衡的方式调用服务,而无需发现服务
4.2. 创建后端微服务
我们将使用 Spring boot 创建一个简单的微服务,并将公开简单的 REST 端点。 使用spring-boot-web
和服务发现客户端依赖关系创建一个名为ribbon-server
的简单 Spring Boot 项目,以将其托管在 Web 服务器中,并公开一个 Rest 控制器进行测试。
为此,我们需要转到 https://start.spring.io/ 并提供 Maven 坐标并选择依赖项。 下载包含框架项目的 zip 文件。 然后,一旦解压缩到合适的文件夹中,我们就需要将其导入 eclipse 中。
从 Spring Initializer 生成的项目
4.2.1. 创建 REST 端点
编写一个 Rest 控制器,并如下所示公开一个 Rest 端点。
MyRestController.java
package com.example.ribbonserver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyRestController {
@Autowired
Environment environment;
@GetMapping("/")
public String health() {
return "I am Ok";
}
@GetMapping("/backend")
public String backend() {
System.out.println("Inside MyRestController::backend...");
String serverPort = environment.getProperty("local.server.port");
System.out.println("Port : " + serverPort);
return "Hello form Backend!!! " + " Host : localhost " + " :: Port : " + serverPort;
}
}
4.2.2 启用发现客户端
注册此服务到 eureka,我们需要在应用程序类中添加@EnableDiscoveryClient
。 另外,我们需要在应用程序属性文件中添加以下条目。
RibbonServerApplication.java
package com.example.ribbonserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonServerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonServerApplication.class, args);
}
}
application.properties
spring.application.name=server
server.port = 9090
eureka.client.serviceUrl.defaultZone= http://${registry.host:localhost}:${registry.port:8761}/eureka/
eureka.client.healthcheck.enabled= true
eureka.instance.leaseRenewalIntervalInSeconds= 1
eureka.instance.leaseExpirationDurationInSeconds= 2
4.3. Eureka 服务注册表服务器
创建服务发现服务器。 这也很容易。 只是我们需要使用 Eureka 服务器作为依赖项来创建上述的 spring boot 项目,并执行以下配置。
4.3.1. Eureka 服务器配置
准备好 Spring Boot 服务并将其导入 Eclipse 后,在 Spring Boot 应用程序类中添加@EnableEurekaServer
注解,并在应用程序属性文件中添加以下配置。
RibbonEurekaServerApplication.java
package com.example.ribboneurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class RibbonEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonEurekaServerApplication.class, args);
}
}
application.properties
spring.application.name= ${springboot.app.name:eureka-serviceregistry}
server.port = ${server-port:8761}
eureka.instance.hostname= ${springboot.app.name:eureka-serviceregistry}
eureka.client.registerWithEureka= false
eureka.client.fetchRegistry= false
eureka.client.serviceUrl.defaultZone: http://${registry.host:localhost}:${server.port}/eureka/
4.4. 创建另一个微服务
遵循上一节的内容,创建另一个名为ribbon-client
的服务,并增加了权限spring-cloud-starter-netflix-ribbon
。 下载后,将项目导入 eclipse 中并执行以下配置。
4.4.1. 碳带配置
在应用程序类中,添加两个注解@RibbonClient
和@EnableDiscoveryClient
,以启用 Ribbon 和 Eureka 客户端进行服务注册。
RibbonClientApplication.java
package com.example.ribbonclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@EnableDiscoveryClient
@SpringBootApplication
@RibbonClient(name = "server", configuration = RibbonConfiguration.class)
public class RibbonClientApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonClientApplication.class, args);
}
}
在application.properties
中,我们需要进行以下配置。 在这里server.ribbon.listOfServers
被禁用,我们可以启用它以手动将服务器添加到此负载均衡器。 我们将在“测试”部分中对此进行检查。 其他属性不言自明。
application.properties
spring.application.name=client
server.port=8888
eureka.client.serviceUrl.defaultZone= http://${registry.host:localhost}:${registry.port:8761}/eureka/
eureka.client.healthcheck.enabled= true
eureka.instance.leaseRenewalIntervalInSeconds= 1
eureka.instance.leaseExpirationDurationInSeconds= 2
server.ribbon.eureka.enabled=true
#server.ribbon.listOfServers=localhost:9090,localhost:9091,localhost:9092
server.ribbon.ServerListRefreshInterval=1000
#logging.level.root=TRACE
现在,我们需要为 Ribbon 创建另一个配置类,以提及负载平衡算法和运行状况检查。 现在,我们将使用 Ribbon 所提供的默认值,但是在此类中,我们可以很好地覆盖它们并添加我们的自定义逻辑。
RibbonConfiguration.java
package com.example.ribbonclient;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AvailabilityFilteringRule;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.PingUrl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class RibbonConfiguration {
@Autowired
IClientConfig config;
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
5. 测试应用程序
5.1. 启动组件
执行最终的构建使用命令mvn clean install
,并检查构建是否成功。 如果有任何错误,则需要进行修复。 一旦成功完成所有 Maven 项目的构建,我们将一个接一个地启动服务。
首先是 Eureka,然后是后端微服务,最后是前端微服务。
要启动每个微服务,我们将使用'java -jar -Dserver.port=XXXX target/YYYYY.jar'
命令。
5.2. 部署后端微服务的多个实例
为此,我们需要为此使用不同的端口,以在特定端口中启动服务,我们需要以这种方式传递该端口。
java -jar -Dserver.port=XXXX target/YYYYY.jar
。 我们将在端口 9090、9091 和 9092 端口中创建此服务的 3 个实例。
5.3. 验证 Eureka 服务器
现在,在浏览器中转到http://localhost:8761/
,并检查 eureka 服务器正在运行,并且所有微服务都已注册了所需数量的实例。
5.4. 检查客户端负载平衡是否正常
在前端微服务中,我们使用RestTemplate
调用后端微服务。 使用@LoadBalanced
注解,将剩余温度作为客户端负载平衡器启用。
现在转到浏览器,打开客户端微服务 REST 端点http://localhost:8888/client/frontend
,看看响应来自任何后端实例。
要了解此后端服务器正在返回其运行端口,我们也在客户端微服务响应中也显示了该端口。 尝试刷新此 URL 几次,然后注意到后端服务器的端口不断变化,这意味着客户端负载平衡正在工作。 现在尝试添加更多后端服务器实例,并检查是否也在 eureka 服务器中注册并最终在 Ribbon 中考虑,因为一旦将在 eureka 中注册,Ribbon 也会自动将请求发送到新实例。
5.5. 使用硬编码后端进行测试,而无需发现服务
转到前端微服务application.properties
文件并启用它。
application.properties
server.ribbon.listOfServers=localhost:9090,localhost:9091,localhost:9092
server.ribbon.eureka.enabled=false
现在测试客户端网址。 您将仅从注册的实例获得响应。 现在,如果您在其他端口中启动后端微服务的新实例,Ribbon 将不会向新实例发送请求,除非我们在 Ribbon 中手动注册。
如果您在测试时遇到困难,我也建议您从所有应用程序中删除所有与 eureka 相关的配置,并停止 eureka 服务器。 希望您也不会在测试中遇到任何困难。
6. 总结
因此,我们已经看到了在 SpringBoot 微服务开发中可以轻松地将 Ribbon 和 Eureka 一起使用。 因此,下次如果您需要这种要求,则可以使用这种方法。 如果您对此有任何疑问,请在下面评论。
下载源码
学习愉快!
参考文献:
Spring Cloud Ribbon 文档
Spring 和 CORS
原文: https://howtodoinjava.com/spring5/webmvc/spring-mvc-cors-configuration/
CORS(跨域资源共享)允许网页从其他域(例如, CDN 中的字体,CSS 或静态图像。 CORS 有助于将来自多个域的 Web 内容提供给通常具有同源安全策略的浏览器。
在此示例中,我们将学习在方法级别和全局级别在 Spring MVC 应用程序中启用启用 Spring CORS 支持。
阅读更多: Java CORS 过滤器示例
1. Spring CORS – @CrossOrigin
的方法级别
Spring MVC 提供@CrossOrigin
注解。 该注解将注解的方法或类型标记为允许跨源请求。
1.1. Spring CORS 允许所有
默认情况下,@CrossOrigin
允许所有来源,所有标头,在@RequestMapping
注解中指定的 HTTP 方法和maxAge
为 30 分钟。
您可以通过为注解属性赋值来覆盖默认的 CORS 设置:
属性 | 描述 |
---|---|
origins |
允许来源的列表。 它的值位于预先响应和实际响应的Access-Control-Allow-Origin 标头中。 |
"*" – 表示允许所有来源。 |
|
如果未定义,则允许所有来源。 | |
allowedHeaders |
可以在实际请求中使用的请求标头列表。 在预先响应标头Access-Control-Allow-Headers 中使用值。 |
"*" – 表示允许客户端请求的所有标头。 |
|
如果未定义,则允许所有请求的标头。 | |
methods |
支持的 HTTP 请求方法列表。 如果未定义,则使用由RequestMapping 注解定义的方法。 |
exposedHeaders |
浏览器将允许客户端访问的响应标头列表。 在实际响应标头Access-Control-Expose-Headers 中设置值。 |
如果未定义,则使用一个空的公开头列表。 | |
allowCredentials |
它确定浏览器是否应包括与请求关联的任何 cookie。 |
false – 不应该包含 cookie。 |
|
" " (空字符串) – 表示未定义。 |
|
true – 预先响应将包含标头Access-Control-Allow-Credentials ,其值设置为 true。 |
|
如果未定义,则允许凭据。 | |
maxAge |
预先响应的缓存持续时间的最长期限(以秒为单位)。 在标头Access-Control-Max-Age 中设置值。 |
如果未定义,则将最大年龄设置为 1800 秒(30 分钟)。 |
1.2. 类/控制器级别的@CrossOrigin
HomeController.java
@CrossOrigin(origins = "*", allowedHeaders = "*")
@Controller
public class HomeController
{
@GetMapping(path="/")
public String homeInit(Model model) {
return "home";
}
}
阅读更多 – Spring 5 MVC 示例
1.3. 方法级别的@CrossOrigin
HomeController.java
@Controller
public class HomeController
{
@CrossOrigin(origins = "*", allowedHeaders = "*")
@GetMapping(path="/")
public String homeInit(Model model) {
return "home";
}
}
1.4. @CrossOrigin
的方法级别覆盖
homeInit()
方法只能从http://example.com
域访问。 HomeController
中的其他方法可以从所有域访问。
HomeController.java
@Controller
@CrossOrigin(origins = "*", allowedHeaders = "*")
public class HomeController
{
@CrossOrigin(origins = "http://example.com")
@GetMapping(path="/")
public String homeInit(Model model) {
return "home";
}
}
2. Spring CORS – 全局 CORS 配置
2.1. 实现WebMvcConfigurer
要为整个应用程序启用 CORS,请使用WebMvcConfigurer
添加CorsRegistry
。
CorsConfiguration.java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class CorsConfiguration implements WebMvcConfigurer
{
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST");
}
}
2.2. WebMvcConfigurer
Bean
在 Spring Boot 应用程序中,建议仅声明一个WebMvcConfigurer
bean。
CorsConfiguration.java
@Configuration
public class CorsConfiguration
{
@Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8080");
}
};
}
}
2.3. Spring Security 的 CORS
要通过 Spring Security启用 CORS 支持,请配置CorsConfigurationSource
bean 并使用HttpSecurity.cors()
配置。
WebSecurityConfig.java
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
//other config
}
@Bean
CorsConfigurationSource corsConfigurationSource()
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
将我的问题放在评论部分。
学习愉快!
下载源码
Spring AOP
Spring AOP 教程示例
原文: https://howtodoinjava.com/spring-aop-tutorial/
在本 Spring AOP 教程中,通过示例学习什么是面向切面的编程。 还通过示例了解什么是建议,连接点和切入点表达式以及如何在 Spring 应用程序中使用它们。
1. Spring AOP – 简介
Spring AOP 支持在 Spring 应用程序中进行面向切面的编程。 在 AOP 中,各个切面可以实现关注点的模块化,例如事务管理,日志记录或跨多个类型和对象的安全性(通常称为跨切点关注点)。
AOP 提供了一种使用简单的可插拔配置在实际逻辑之前,之后或周围动态添加横切关注点的方法。 现在和现在,维护代码都很容易。 您只需更改配置文件即可添加/删除关注点,而无需重新编译完整的源代码(如果您要使用要求 XML 配置的切面)。
2. 什么是建议,连接点和切入点
- AOP 中的一个重要术语是建议。 它是切面在特定连接点处采取的操作。
- 连接点是程序的执行点,例如方法的执行或异常的处理。 在 Spring AOP 中,连接点始终代表方法的执行。
- 切入点是与连接点匹配的谓词或表达式。
- 建议与切入点表达式关联,并在与该切入点匹配的任何连接点处运行。
- Spring 默认使用 AspectJ 切入点表达语言。
Spring AOP
3. AOP 建议的类型
Spring AOP 中有五种建议。
- 事前建议:在连接点之前执行的建议,但是它不能阻止执行流程前进到连接点(除非它抛出异常)。
- 返回后建议:在连接点正常完成后要执行的建议:例如,如果某个方法返回而没有抛出异常。
- 抛出后建议:如果方法因抛出异常而退出,则要执行的建议。
- 事后建议:无论连接点退出的方式如何(正常或特殊返回),都将执行建议。
- 周围建议:围绕连接点的建议,例如方法调用。 这是最有力的建议。 周围建议可以在方法调用之前和之后执行自定义行为。 它还负责选择是返回连接点还是通过返回其自身的返回值或抛出异常来捷径建议的方法执行。
4. Spring AOP 示例
4.1. Maven 依赖
在编写任何代码之前,您将需要将 Spring AOP 依赖项导入到您的项目中。
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
4.2. 切面和切入点表达式
编写使用@Aspect
注解注解的切面类,并编写切入点表达式以匹配连接点方法。
EmployeeCRUDAspect.java
@Aspect
public class EmployeeCRUDAspect {
@Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
public void logBeforeV1(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBeforeV1() : " + joinPoint.getSignature().getName());
}
}
4.3. 方法(连接点)
编写要在其上执行建议并与切入点表达式匹配的方法。
EmployeeManager.java
@Component
public class EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId) {
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
}
在上面的示例中,logBeforeV1()
将在 getEmployeeById()
方法之前执行,因为它与连接点表达式匹配。
4.4. 运行应用程序
运行该应用程序并查看控制台。
TestAOP.java
ublic class TestAOP
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext
("com/howtodoinjava/demo/aop/applicationContext.xml");
EmployeeManager manager = context.getBean(EmployeeManager.class);
manager.getEmployeeById(1);
}
}
程序输出:
Console
EmployeeCRUDAspect.logBeforeV1() : getEmployeeById
Method getEmployeeById() called
Spring aop 初学者教程,并带有示例。
5. Spring AOP XML 配置示例
-
Spring AOP AspectJ XML 配置示例
学习使用 XML 配置来配置 AOP 切面。
-
Spring AOP 事前建议示例
学习使用
<aop:before/>
配置事前建议 AOP 切面。 -
Spring AOP 返回后建议示例
学习使用
<aop:after-returning/>
配置返回后建议 AOP 切面。 -
Spring AOP 抛出后建议示例
学习使用
<aop:after-throwing/>
配置抛出后建议 AOP 切面。 -
Spring AOP 事后建议示例
学习使用
<aop:after/>
配置事后建议 AOP 切面。 -
Spring AOP 周围建议示例
学习使用
<aop:around/>
配置围绕建议 AOP 切面。
6. Spring AOP AspectJ 注解示例
-
Spring AOP AspectJ 注解配置示例
学习使用 aspectj 注解配置来配置 AOP 切面。
-
Spring AOP AspectJ
@Before
示例学习使用
@Before
注解配置事前建议 AOP 切面。 -
Spring AOP AspectJ
@After
示例学习使用
@After
注解配置事后建议 AOP 切面。 -
Spring AOP AspectJ
@Around
示例学习使用
@Around
注解围绕建议 AOP 切面。 -
Spring AOP AspectJ
@AfterReturning
示例学习使用
@AfterReturning
注解返回后建议 AOP 切面。 -
Spring AOP AspectJ
@AfterThrowing
示例学习使用
@AfterThrowing
注解抛出后建议 AOP 切面。
7. 更多 Spring AOP 教程
-
Spring AOP Aspects 排序
在需要按特定顺序执行的多个切面中,学习对切面执行进行排序。
-
Spring AOP AspectJ 切入点表达式示例
学习编写切点表达式以匹配各种连接点。
8. 面试题
热门 Spring AOP 面试问题与答案
Java 面试中一些最常问到的 Spring AOP 面试问题。
9. Spring AOP 资源:
Spring AOP 文档
AspectJ
学习愉快!
Spring AOP – AspectJ 注解配置示例
原文: https://howtodoinjava.com/spring-aop/spring-aop-aspectj-example-tutorial-using-annotation-config/
Spring 的关键组件之一是面向切面编程(AOP)框架。 尽管 Spring IoC 容器不依赖于 AOP,这意味着您不需要时就不需要使用 AOP,但 AOP 是对 Spring IoC 的补充,可以提供功能强大的中间件解决方案。 就像 OOP 中模块化的关键单元是类,AOP 中模块化的单元是切面。 切面使关注点(也可以理解为横切关注点)实现模块化,例如跨多种类型和对象的事务管理。
AspectJ 已成长为一个完整且流行的 AOP 框架,Spring 支持在其 AOP 框架中使用以 AspectJ 注解编写的 POJO 切面。 由于越来越多的 AOP 框架支持 AspectJ 注解,因此您的 AspectJ 样式的切面更有可能在支持 AspectJ 的其他 AOP 框架中重用。
不幸的是,AOP 术语不是很直观,因此我将首先创建一个示例应用程序,然后将这些术语与示例中的用法相关联。
Spring AOP + AspectJ 示例
在编写任何代码之前,您将需要将 Spring AOP 依赖项导入到您的项目中。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
在此示例中,我采用EmployeeManager
类的非常基本的实现,该实现具有一些方法集,这些方法应基本上将EmployeeDTO
对象选择/修改为数据库。
EmployeeDTO.java
public class EmployeeDTO {
private Integer id;
private String firstName;
private String lastName;
//Setters and Getters
}
EmployeeManager.java
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
@Component
public class EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId) {
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee() {
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee) {
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId) {
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee) {
System.out.println("Method updateEmployee() called");
}
}
然后,我创建了一个日志记录切面,它将记录执行了哪个方法。
EmployeeCRUDAspect.java
@Aspect
public class EmployeeCRUDAspect {
@Before("execution(* EmployeeManager.getEmployeeById(..))")
public void logBeforeV1(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBeforeV1() : " + joinPoint.getSignature().getName());
}
@Before("execution(* EmployeeManager.*(..))")
public void logBeforeV2(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBeforeV2() : " + joinPoint.getSignature().getName());
}
@After("execution(* EmployeeManager.getEmployeeById(..))")
public void logAfterV1(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logAfterV1() : " + joinPoint.getSignature().getName());
}
@After("execution(* EmployeeManager.*(..))")
public void logAfterV2(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logAfterV2() : " + joinPoint.getSignature().getName());
}
}
applicationContext.xml
文件具有以下配置,以使用注解配置来启用 AOP 和 IoC 容器。
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.howtodoinjava.demo.aop" />
<bean id="loggingAspect" class="com.howtodoinjava.demo.aop.EmployeeCRUDAspect" />
现在测试 AOP 配置和其他内容。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAOP
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/howtodoinjava/demo/aop/applicationContext.xml");
EmployeeManager manager = context.getBean(EmployeeManager.class);
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Output:
EmployeeCRUDAspect.logBeforeV1() : getEmployeeById
EmployeeCRUDAspect.logBeforeV2() : getEmployeeById
Method getEmployeeById() called
EmployeeCRUDAspect.logAfterV1() : getEmployeeById
EmployeeCRUDAspect.logAfterV2() : getEmployeeById
EmployeeCRUDAspect.logBeforeV2() : createEmployee
Method createEmployee() called
EmployeeCRUDAspect.logAfterV2() : createEmployee
很好,AOP 配置成功。 现在继续学习 AOP 术语。
AOP 关键概念
现在让我们定义一些中心的 AOP 概念和术语,并与上面的示例相关联。
1)切面:涉及多个类别的关注点的模块化。 事务管理是企业 Java 应用程序中横切关注的一个很好的例子。
在我们的示例中,我们创建了一个日志记录切面。 要创建切面,您需要在切面类上应用@Aspect
注解并将其注册到applicationContext.xml
文件中。
@Aspect
public class EmployeeCRUDAspect {
...
}
这是您将切面注册到上下文中的方式。
<bean id="loggingAspect" class="com.howtodoinjava.demo.aop.EmployeeCRUDAspect" />
请记住首先使用<aop:aspectj-autoproxy/>
将 AOP 支持添加到您的上下文中。
2)连接点:在程序执行期间的点,例如方法的执行或异常的处理。 在 Spring AOP 中,连接点始终代表方法的执行。
在我们的示例中,EmployeeManager
内部定义的所有方法都是连接点。
3)建议:某个切面在特定连接点采取的操作。 不同类型的建议包括“周围”,“事前”和“事后”建议。 许多 AOP 框架(包括 Spring)将建议建模为拦截器,并在连接点周围维护了一系列拦截器。
在我们的示例中,所有logBefore()
和logAfter()
方法都是建议。
4)切入点:与连接点匹配的谓词。 建议与切入点表达式关联,并在与该切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。 切入点表达式匹配的连接点的概念是 AOP 的核心,默认情况下,Spring 使用 AspectJ 切入点表达语言。
在我们的示例中,在@Before
和@After
注解(即execution(* EmployeeManager.getEmployeeById(..))
)中传递的表达式是切入点。
5)简介:代表类型声明其他方法或字段。 Spring AOP 允许您向任何建议对象引入新接口(和相应的实现)。 例如,您可以使用简介使 Bean 实现IsModified
接口,以简化缓存。
我将为此创建一个单独的示例。
6)目标对象:对象由一个或多个切面建议。 也称为建议对象。 由于 Spring AOP 是使用运行时代理实现的,因此该对象将始终是代理对象。
在我们的示例中,建议使用EmployeeManager
对象,因此它是目标对象。
7)AOP 代理:由 AOP 框架创建的对象,用于实现切面协定(建议方法执行等)。 在 Spring 框架中,AOP 代理将是 JDK 动态代理或 CGLIB 代理。
在我们的示例中,当我们向EmployeeManager
类的 bean 引用询问时,将创建一个代理对象。 您可以在下图中看到代理的类,该类是运行时调试器的屏幕截图。
因此,我们现在很好,可以将 spring AOP 的关键术语与实际代码关联起来。 现在,让我们进一步列出类型的建议,这些建议可在 Spring AOP 中使用。
- 事前建议:建议在连接点之前执行,但不具有阻止执行流程前进到连接点的能力(除非它引发异常)。 要使用此建议,请使用上面示例中使用的
@Before
注解。 - 返回后建议:在连接点正常完成后要执行的建议:例如,如果某个方法返回而没有引发异常。 要使用此建议,请使用
@AfterReturning
注解。 - 抛出后建议:如果方法因引发异常而退出,则要执行的建议。 要使用此建议,请使用
@AfterThrowing
注解。 - 事后建议:无论连接点退出的方式如何(正常或特殊返回),均应执行的建议。 要使用此建议,请在上面的示例中使用
@After
注解。 - 周围建议:围绕连接点的建议,例如方法调用。 这是最有力的建议。 要使用此建议,请使用
@Around
注解。
周围建议可以在方法调用之前和之后执行自定义行为。 它还负责选择是返回连接点还是通过返回其自身的返回值或引发异常来捷径建议的方法执行。 周围的建议可以编写如下。
@Around("execution(* EmployeeManager.getEmployeeById(..))")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("Write code for before advise");
joinPoint.proceed(); //continue to called method i.e. EmployeeManager.getEmployeeById()
System.out.println("Write code for after advise");
}
这就是本入门教程的全部内容。 我将发布更多的教程,其中包含 spring Aspectj aop 概念的更多详细信息。
祝您学习愉快!
参考:Spring 文档
Spring AOP + AspectJ XML 配置示例
原文: https://howtodoinjava.com/spring-aop/spring-aop-aspectj-xml-configuration-example/
如果您是仍然对 JDK 1.4 感兴趣的极少数开发人员之一,或者您正在维护一个旧的 Spring 应用程序,其中 AOP 代码已用 XML 配置文件编写,那么此文章适合您。 在此处学习如何使用基于 xml 的配置来定义和使用 AspectJ 的 spring aop。
阅读更多内容:使用注解配置的 Spring AOP AspectJ 示例
1)如何声明切面
使用<aop:aspect>
元素声明一个切面,并使用ref
属性引用该支持 bean,如下所示:
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAspectBean">
...
</aop:aspect>
</aop:config>
<bean id="loggingAspectBean" class="com.howtodoinjava.demo.aop.EmployeeCRUDLoggingAspect" />
2)如何定义切入点
切入点有助于确定要使用不同建议执行的连接点。 切入点将定义如下:
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAspectBean">
<aop:pointcut id="loggingOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.*(..))" />
<aop:pointcut id="transactionOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.getEmployeeById(..))" />
</aop:aspect>
</aop:config>
<bean id="loggingAspectBean" class="com.howtodoinjava.demo.aop.EmployeeCRUDLoggingAspect" />
在上面的示例中,两个切入点(loggingOperation
和transactionOperation
)将与EmployeeManager
类中定义的方法匹配。 其中loggingOperation
切入点将匹配EmployeeManager
中定义的所有方法,而transactionOperation
仅匹配EmployeeManager.getEmployeeById()
方法执行。
阅读更多内容:Spring AOP AspectJ 切入点表达式以及示例
3)定义建议
您可以使用<aop:advise_name>
元素在<aop:aspect>
中声明五个建议中的任何一个,如下所示:
<aop:config>
<!-- Spring AOP Pointcut definitions -->
<aop:pointcut id="loggingOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.*(..))" />
<aop:pointcut id="transactionOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.getEmployeeById(..))" />
<!-- Spring AOP aspect -->
<aop:aspect id="loggingAspect" ref="loggingAspectBean">
<!-- Spring AOP advises -->
<aop:before pointcut-ref="loggingOperation" method="logBefore" />
<aop:after pointcut-ref="loggingOperation" method="logAfter" />
</aop:aspect>
</aop:config>
基于 XML 的 Spring AOP 配置的示例应用程序
在此示例中,我在EmployeeManager
接口内定义的所有方法的日志建议之前和之后应用EmployeeManager.getEmployeeById()
方法的事务建议。 完整的配置文件如下:
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop/
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- We don't need to this; This is required only in annotation based AOP support -->
<!-- <aop:aspectj-autoproxy /> -->
<aop:config>
<!-- Spring AOP Pointcut definitions -->
<aop:pointcut id="loggingOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.*(..))" />
<aop:pointcut id="transactionOperation"
expression="execution(* com.howtodoinjava.demo.aop.EmployeeManager.getEmployeeById(..))" />
<!-- Spring AOP aspect 1 -->
<aop:aspect id="loggingAspect" ref="loggingAspectBean">
<!-- Spring AOP advises -->
<aop:before pointcut-ref="loggingOperation" method="logBefore" />
<aop:after pointcut-ref="loggingOperation" method="logAfter" />
</aop:aspect>
<!-- Spring AOP aspect 2 -->
<aop:aspect id="transactionAspect" ref="transactionAspectBean">
<aop:before pointcut-ref="transactionOperation" method="getEmployeeById" />
</aop:aspect>
</aop:config>
<!-- Spring AOP aspect instances -->
<bean id="loggingAspectBean" class="com.howtodoinjava.demo.aop.EmployeeCRUDLoggingAspect" />
<bean id="transactionAspectBean" class="com.howtodoinjava.demo.aop.EmployeeCRUDTransactionAspect" />
<!-- Target Object -->
<bean id="employeeManager" class="com.howtodoinjava.demo.aop.EmployeeManagerImpl" />
</beans>
本示例中使用的其他文件是:
EmployeeDTO.java
public class EmployeeDTO {
private Integer id;
private String firstName;
private String lastName;
//Setters and Getters
}
EmployeeManager.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
EmployeeManagerImpl.java
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId) {
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee() {
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee) {
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId) {
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee) {
System.out.println("Method updateEmployee() called");
}
}
EmployeeCRUDLoggingAspect.java
public class EmployeeCRUDLoggingAspect
{
public void logBefore(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBefore() : " + joinPoint.getSignature().getName());
}
public void logAfter(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logAfter() : " + joinPoint.getSignature().getName());
}
}
EmployeeCRUDTransactionAspect.java
public class EmployeeCRUDTransactionAspect
{
public void getEmployeeById(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDTransactionAspect.getEmployeeById() : "
+ joinPoint.getSignature().getName());
}
}
TestAOP.java
public class TestAOP
{
@SuppressWarnings("resource")
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("com/howtodoinjava/demo/aop/applicationContext.xml");
EmployeeManager manager = ( EmployeeManager ) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
manager.deleteEmployee(100);
}
}
Output:
EmployeeCRUDAspect.logBefore() : getEmployeeById
EmployeeCRUDTransactionAspect.getEmployeeById() : getEmployeeById
Method getEmployeeById() called
EmployeeCRUDAspect.logAfter() : getEmployeeById
EmployeeCRUDAspect.logBefore() : createEmployee
Method createEmployee() called
EmployeeCRUDAspect.logAfter() : createEmployee
EmployeeCRUDAspect.logBefore() : deleteEmployee
Method deleteEmployee() called
EmployeeCRUDAspect.logAfter() : deleteEmployee
在下面的评论部分中给我任何评论/查询。
祝您学习愉快!
Spring AOP AspectJ @Before
注解示例
原文: https://howtodoinjava.com/spring-aop/aspectj-before-annotation-example/
在这个 Spring aop 示例中,我们将学习使用 Aspectj @Before
注解。 @Before
带注解的方法恰好在所有与切入点表达式匹配的方法之前运行。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在@Before
注解中传递的切入点信息来调用切面方法。
AspectJ @Before
注解的用法
您可以按以下方式使用@Before
注解。
@Aspect
public class LoggingAspect {
@Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logBeforeAllMethods(JoinPoint joinPoint) { ... }
@Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))")
public void logBeforeGetEmployee(JoinPoint joinPoint) { ... }
}
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring core,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
启用 AspectJ 支持
在 XML 配置文件中,您可以添加aop:aspectj-autoproxy
元素以启用@AspectJ
注解支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- Enable @AspectJ annotation support -->
<aop:aspectj-autoproxy />
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写 AspectJ 注解的类和方法
用切入点信息编写 aspectj 注解的类和方法。
@Aspect
public class LoggingAspect {
@Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logBeforeAllMethods(JoinPoint joinPoint)
{
System.out.println("****LoggingAspect.logBeforeAllMethods() : " + joinPoint.getSignature().getName());
}
@Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))")
public void logBeforeGetEmployee(JoinPoint joinPoint)
{
System.out.println("****LoggingAspect.logBeforeGetEmployee() : " + joinPoint.getSignature().getName());
}
@Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.createEmployee(..))")
public void logBeforeCreateEmployee(JoinPoint joinPoint)
{
System.out.println("****LoggingAspect.logBeforeCreateEmployee() : " + joinPoint.getSignature().getName());
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
****LoggingAspect.logBeforeAllMethods() : getEmployeeById
****LoggingAspect.logBeforeGetEmployee() : getEmployeeById
Method getEmployeeById() called
****LoggingAspect.logBeforeAllMethods() : createEmployee
****LoggingAspect.logBeforeCreateEmployee() : createEmployee
Method createEmployee() called
显然,在相关联的关节上执行切面建议。
学习愉快!
参考文献:
Spring AOP 参考
@Before
注解
@Aspect
注解
AspectJ 注解配置示例
不同切入点表达式以及示例
Spring AOP AspectJ @After
注解示例
原文: https://howtodoinjava.com/spring-aop/aspectj-after-annotation-example/
在这个 Spring aop 示例中,我们将学习使用 Aspectj @After
注解。 @After
带注解的方法在与切入点表达式匹配的所有方法之后准确运行。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在@After
注解中传递的切入点信息来调用切面方法。
AspectJ @After
注解用法
您可以按以下方式使用@After
注解。
@Aspect
public class LoggingAspect {
@After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAfterAllMethods(JoinPoint joinPoint) { ... }
@After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))")
public void logAfterGetEmployee(JoinPoint joinPoint) { ... }
}
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
启用 AspectJ 支持
在 XML 配置文件中,您可以添加aop:aspectj-autoproxy
元素以启用@AspectJ
注解支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- Enable @AspectJ annotation support -->
<aop:aspectj-autoproxy />
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写 AspectJ 注解的类和方法
用切入点信息编写 aspectj 注解的类和方法。
@Aspect
public class LoggingAspect {
@After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAfterAllMethods(JoinPoint joinPoint)
{
System.out.println("****LoggingAspect.logAfterAllMethods() : " + joinPoint.getSignature().getName());
}
@After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))")
public void logAfterGetEmployee(JoinPoint joinPoint)
{
System.out.println("****LoggingAspect.logAfterGetEmployee() : " + joinPoint.getSignature().getName());
}
@After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.createEmployee(..))")
public void logAfterCreateEmployee(JoinPoint joinPoint)
{
System.out.println("****LoggingAspect.logAfterCreateEmployee() : " + joinPoint.getSignature().getName());
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Method getEmployeeById() called
****LoggingAspect.logAfterAllMethods() : getEmployeeById
****LoggingAspect.logAfterGetEmployee() : getEmployeeById
Method createEmployee() called
****LoggingAspect.logAfterAllMethods() : createEmployee
****LoggingAspect.logAfterCreateEmployee() : createEmployee
显然,在相关联的关节上执行切面建议。
学习愉快!
参考文献:
Spring AOP 参考
@After
注解
@Aspect
注解
AspectJ 注解配置示例
不同切入点表达式示例
Spring AOP AspectJ @Around
注解示例
原文: https://howtodoinjava.com/spring-aop/aspectj-around-annotation-example/
在此 Spring 操作示例中,我们将学习使用 Aspectj @Around
注解。 @Around
带注解的方法在与切入点表达式匹配的所有方法之前和之后运行。
在此示例中,我们将创建一个简单的 spring 应用程序,添加记录切面周围的内容,然后基于@Around
注解中传递的切入点信息调用切面方法。
1. AspectJ @Around
注解用法
@Around
建议包围了连接点,例如方法调用。 这是最有力的建议。 围绕建议可以在方法调用之前和之后执行自定义行为。 它还负责选择是返回连接点还是通过返回其自身的返回值或引发异常来捷径建议的方法执行。
@Aspect
public class LoggingAspect {
@Around("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAroundAllMethods(ProceedingJoinPoint joinPoint) { ... }
@Around("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))")
public void logAroundGetEmployee(ProceedingJoinPoint joinPoint) { ... }
}
不要忘记使用ProceedingJoinPoint
作为参数。 您必须调用ProceedingJoinPoint.proceed()
方法,否则将执行原始方法。
2. 项目结构
Spring AOP 项目结构
3. Spring AOP AspectJ maven 依赖项
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
4. 启用 Spring AOP AspectJ 支持
在 XML 配置文件中,您可以添加aop:aspectj-autoproxy
元素以启用@AspectJ
注解支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- Enable @AspectJ annotation support -->
<aop:aspectj-autoproxy />
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
5. 需要执行@Around
切面的方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
6. 编写 AspectJ 注解的类和方法
用切入点信息编写 aspectj 注解的类和方法。
@Aspect
public class LoggingAspect {
@Around("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAroundAllMethods(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("****LoggingAspect.logAroundAllMethods() : " + joinPoint.getSignature().getName() + ": Before Method Execution");
try {
joinPoint.proceed();
} finally {
//Do Something useful, If you have
}
System.out.println("****LoggingAspect.logAroundAllMethods() : " + joinPoint.getSignature().getName() + ": After Method Execution");
}
@Around("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))")
public void logAroundGetEmployee(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("****LoggingAspect.logAroundGetEmployee() : " + joinPoint.getSignature().getName() + ": Before Method Execution");
try {
joinPoint.proceed();
} finally {
//Do Something useful, If you have
}
System.out.println("****LoggingAspect.logAroundGetEmployee() : " + joinPoint.getSignature().getName() + ": After Method Execution");
}
@Around("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.createEmployee(..))")
public void logAroundCreateEmployee(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("****LoggingAspect.logAroundCreateEmployee() : " + joinPoint.getSignature().getName() + ": Before Method Execution");
try {
joinPoint.proceed();
} finally {
//Do Something useful, If you have
}
System.out.println("****LoggingAspect.logAroundCreateEmployee() : " + joinPoint.getSignature().getName() + ": After Method Execution");
}
}
7. Spring AspectJ @Around
示例
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
****LoggingAspect.logAroundAllMethods() : getEmployeeById: Before Method Execution
****LoggingAspect.logAroundGetEmployee() : getEmployeeById: Before Method Execution
Method getEmployeeById() called
****LoggingAspect.logAroundGetEmployee() : getEmployeeById: After Method Execution
****LoggingAspect.logAroundAllMethods() : getEmployeeById: After Method Execution
****LoggingAspect.logAroundAllMethods() : createEmployee: Before Method Execution
****LoggingAspect.logAroundCreateEmployee() : createEmployee: Before Method Execution
Method createEmployee() called
****LoggingAspect.logAroundCreateEmployee() : createEmployee: After Method Execution
****LoggingAspect.logAroundAllMethods() : createEmployee: After Method Execution
显然,在相关连接点周围执行切面建议。
学习愉快!
参考文献:
Spring AOP 参考
@Around
注解
@Aspect
注解
AspectJ 注解配置示例
不同切入点表达式以及示例
Spring AOP AspectJ @AfterReturning
注解示例
原文: https://howtodoinjava.com/spring-aop/aspectj-after-returning-annotation-example/
在这个 Spring aop 示例中,我们将学习使用 Aspectj @AfterReturning
注解。 @AfterReturning
带注解的方法在方法(与切入点表达式匹配)正常执行后运行,并且不会引发任何错误/异常。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在@AfterReturning
注解中传递的切入点信息来调用切面方法。
AspectJ @AfterReturning
注解用法
例如,如果方法返回时没有抛出异常,则在连接点正常完成之后执行 AspectJ @AfterReturning
通知。
@Aspect
public class LoggingAspect {
@AfterReturning("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAroundAllMethods() { ... }
@AfterReturning(pointcut="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))", returning="retVal")
public void logAroundGetEmployee(Object retVal) { ... }
}
有时您需要访问从方法返回的实际返回值,您可以使用@AfterReturning
注解中的returning
属性获得该返回值。
returning
属性中使用的名称必须与建议方法中的参数名称相对应。 当方法执行返回时,该返回值将作为相应的参数值传递到通知方法。
请注意,任何returning
子句也将匹配限制为仅返回指定类型(在此情况下为Object
或子类型,将匹配任何返回值)的值的那些方法执行。
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
启用 AspectJ 支持
在 XML 配置文件中,您可以添加aop:aspectj-autoproxy
元素以启用@AspectJ
注解支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- Enable @AspectJ annotation support -->
<aop:aspectj-autoproxy />
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写 AspectJ 注解的类和方法
用切入点信息编写 aspectj 注解的类和方法。
@Aspect
public class LoggingAspect {
@AfterReturning("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAfterReturningAllMethods() throws Throwable
{
System.out.println("****LoggingAspect.logAfterReturningAllMethods() ");
}
@AfterReturning(pointcut="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))", returning="retVal")
public void logAfterReturningGetEmployee(Object retVal) throws Throwable
{
System.out.println("****LoggingAspect.logAfterReturningGetEmployee() ");
System.out.println(((EmployeeDTO)retVal).getId());
}
@AfterReturning("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.createEmployee(..))")
public void logAfterReturningCreateEmployee() throws Throwable
{
System.out.println("****LoggingAspect.logAfterReturningCreateEmployee() ");
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Method getEmployeeById() called
****LoggingAspect.logAfterReturningAllMethods()
****LoggingAspect.logAfterReturningGetEmployee()
null //As there is no employee id as of now
Method createEmployee() called
****LoggingAspect.logAfterReturningAllMethods()
****LoggingAspect.logAfterReturningCreateEmployee()
明确切面建议在相关连接点上执行。
学习愉快!
参考文献:
Spring AOP 参考
@AfterReturning
注解
@Aspect
注解
AspectJ 注解配置示例
不同切入点表达式以及示例
Spring AOP AspectJ @AfterThrowing
示例
原文: https://howtodoinjava.com/spring-aop/aspectj-afterthrowing-annotation-example/
在这个 Spring aop 示例中,我们将学习使用 Aspectj @AfterThrowing
注解。 @AfterThrowing
带注解的方法在通过抛出异常退出方法(与切入点表达式匹配)后运行。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在@AfterThrowing
注解中传递的切入点信息来调用切面方法。
AspectJ @AfterThrowing
注解用法
在连接点无法正常完成并最终引发异常之后,执行 AspectJ @AfterThrowing
通知。
@Aspect
public class LoggingAspect {
@AfterThrowing ("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAfterThrowingAllMethods() throws Throwable { ... }
}
通常,您希望建议仅在引发给定类型的异常时才运行,并且通常还需要访问通知正文中的引发异常。 使用throwing
属性既可以限制匹配(如果需要,也可以使用Throwable
作为异常类型),并将抛出的异常绑定到advice
参数。
@Aspect
public class LoggingAspect {
@AfterThrowing (pointcut = "execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))", throwing = "ex")
public void logAfterThrowingAllMethods(SomeCustomException ex) throws Throwable { ... }
}
throwing
属性中使用的名称必须与建议方法中的参数名称相对应。 当通过抛出异常退出方法执行时,该异常将作为相应的参数值传递给通知方法。
throwing
子句还将匹配仅限制为引发指定类型异常(在这种情况下为SomeCustomException
)的那些方法执行。
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
启用 AspectJ 支持
在 XML 配置文件中,您可以添加aop:aspectj-autoproxy
元素以启用@AspectJ
注解支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- Enable @AspectJ annotation support -->
<aop:aspectj-autoproxy />
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
//Throwing an exception
throw new NullPointerException("ID not found");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写 AspectJ 注解的类和方法
用切入点信息编写 aspectj 注解的类和方法。
@Aspect
public class LoggingAspect {
@AfterThrowing (pointcut = "execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))", throwing = "ex")
public void logAfterThrowingAllMethods(Exception ex) throws Throwable
{
System.out.println("****LoggingAspect.logAfterThrowingAllMethods() " + ex);
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Method getEmployeeById() called
Method createEmployee() called
****LoggingAspect.logAfterThrowingAllMethods() java.lang.NullPointerException: ID not found
Exception in thread "main" java.lang.NullPointerException: ID not found
at com.howtodoinjava.app.service.impl.EmployeeManagerImpl.createEmployee(EmployeeManagerImpl.java:26)
明确切面建议在相关连接点上执行。
学习愉快!
参考文献:
Spring AOP 参考
@AfterThrowing
注解
@Aspect
注解
AspectJ 注解配置示例
不同切入点表达式以及示例
Spring AOP 事前建议示例
原文: https://howtodoinjava.com/spring-aop/aspectj-before-advice-example/
在这个 Spring aop 示例中,我们将学习使用<aop:before/>
配置来使用 AOP 事前建议。 具有事前建议配置的方法,在与作为参数传递的切入点表达式匹配的方法之前立即运行。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在<aop:before/>
xml 配置中传递的切入点信息来调用切面方法。
创建 Spring AOP 事前建议
要使用 xml 配置创建事前建议,请按以下方式使用<aop:before/>
。
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- before advice -->
<aop:before method="logBeforeAllMethods" pointcut-ref="loggingPointcuts" />
</aop:aspect>
</aop:config>
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
添加 Spring AOP 配置
在 XML 配置文件中,您可以添加aop:config
元素以添加 AOP 支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- before advice -->
<aop:before method="logBeforeAllMethods" pointcut-ref="loggingPointcuts" />
</aop:aspect>
</aop:config>
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写切面类和方法
编写切面类和要作为建议执行的方法。
package com.howtodoinjava.app.aspect;
import org.aspectj.lang.JoinPoint;
public class LoggingAspect {
public void logBeforeAllMethods(JoinPoint jp) throws Throwable
{
System.out.println("****LoggingAspect.logBeforeAllMethods() " + jp.getSignature().getName());
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
****LoggingAspect.logBeforeAllMethods() getEmployeeById
Method getEmployeeById() called
****LoggingAspect.logBeforeAllMethods() createEmployee
Method createEmployee() called
明确切面建议在相关连接点上执行。
学习愉快!
参考文献:
Spring AOP 参考
AspectJ 项目
不同的切入点表达式以及示例
Spring Security 5 – Java 配置
原文: https://howtodoinjava.com/spring5/security5/security-java-config-enablewebsecurity-example/
本 Java 示例借助@EnableWebSecurity
注解和WebSecurityConfigurerAdapter
类启用了 Spring Security Java 配置。
此示例基于 spring webmvc Hibernate 集成示例构建。
1. 包含 Spring Security 5 依赖项
包括 SpringSecurity 包。 我正在使用 maven,因此为 spring security 5 添加了相应的依赖项。
pom.xml
<properties>
<failOnMissingWebXml>false</failOnMissingWebXml>
<spring.version>5.0.7.RELEASE</spring.version>
</properties>
<!-- Spring MVC Dependency -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security Core -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security Config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security Web -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
2. 创建 Spring Security 5 配置 – @EnableWebSecurity
我创建了这个简单的安全配置,并添加了两个演示用户user
和admin
。
SecurityConfig.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("user").password(passwordEncoder.encode("123456")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder.encode("123456")).roles("USER", "ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").hasAnyRole("ADMIN", "USER")
.and().formLogin()
.and().logout().logoutSuccessUrl("/login").permitAll()
.and().csrf().disable();
}
}
3. 初始化 Spring Security
在 Spring 中,使用DelegatingFilterProxy
实现安全性。 要使用 Java 配置的 spring 容器注册它,您应该使用AbstractSecurityWebApplicationInitializer
。
Spring 将在应用程序启动期间检测到此类的实例,并在其他已注册过滤器之前注册DelegatingFilterProxy
以使用springSecurityFilterChain
。 它还注册了ContextLoaderListener
。
SpringSecurityInitializer.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
//no code needed
}
另外,包括SecurityConfig
至AppInitializer
。
AppInitializer.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { HibernateConfig.class, SecurityConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
4. 验证 Spring Security
启动应用程序并启动主页。 您将获得一个登录页面。 这意味着 SpringSecurity 装置已配置并正常工作。
登录表单
使用用户名/密码登录 - user
和123456
登录成功
学习愉快!
下载源码
Spring AOP 事后建议示例
原文: https://howtodoinjava.com/spring-aop/aspectj-after-advice-example/
在这个 Spring aop 示例中,我们将学习使用<aop:after/>
配置来使用 AOP 事后建议。 配置为事后建议的方法,在与作为参数传递的切入点表达式匹配的方法之后立即运行。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在<aop:after/>
xml 配置中传递的切入点信息来调用切面方法。
创建 Spring AOP 事后建议
要使用 xml 配置创建后续建议,请按以下方式使用<aop:after/>
。
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- after advice -->
<aop:after method="logAfterAllMethods" pointcut-ref="loggingPointcuts" />
</aop:aspect>
</aop:config>
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
添加 Spring AOP 配置
在 XML 配置文件中,您可以添加aop:config
元素以添加 AOP 支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- after advice -->
<aop:after method="logAfterAllMethods" pointcut-ref="loggingPointcuts" />
</aop:aspect>
</aop:config>
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写切面类和方法
编写切面类和要作为建议执行的方法。
package com.howtodoinjava.app.aspect;
import org.aspectj.lang.JoinPoint;
public class LoggingAspect {
public void logAfterAllMethods(JoinPoint jp) throws Throwable
{
System.out.println("****LoggingAspect.logBeforeAllMethods() " + jp.getSignature().getName());
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Method getEmployeeById() called
****LoggingAspect.logBeforeAllMethods() getEmployeeById
Method createEmployee() called
****LoggingAspect.logBeforeAllMethods() createEmployee
明确切面建议在相关连接点上执行。
学习愉快!
参考文献:
Spring AOP 参考
AspectJ 项目
不同的切入点表达式以及示例
Spring AOP 围绕建议示例
原文: https://howtodoinjava.com/spring-aop/aspectj-around-advice-example/
在这个 Spring aop 示例中,我们将学习使用<aop:around/>
配置来使用 AOP 围绕建议。 配置为围绕建议的方法,在与作为参数传递的切入点表达式匹配的方法之前运行。 在此阶段,您必须确定是否应调用原始方法,因为从此处您可以终止应用程序流并直接返回值,而无需调用原始建议方法。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在<aop:around/>
xml 配置中传递的切入点信息来调用切面方法。
创建 Spring AOP 围绕建议
要使用 xml 配置创建围绕建议,请按以下方式使用<aop:around/>
。
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- around advice -->
<aop:around method="logAroundAllMethods" pointcut-ref="loggingPointcuts" />
</aop:aspect>
</aop:config>
建议方法将具有类型为ProceedingJoinPoint
的方法参数,可用于调用建议方法。
public void logAroundAllMethods(ProceedingJoinPoint pjp) throws Throwable
{
System.out.println("****LoggingAspect.logAroundAllMethods() - Before method call");
pjp.proceed();
System.out.println("****LoggingAspect.logAroundAllMethods() - After method call");
}
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
添加 Spring AOP 配置
在 XML 配置文件中,您可以添加aop:config
元素以添加 AOP 支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<aop:around method="logAroundAllMethods" pointcut-ref="loggingPointcuts" />
</aop:aspect>
</aop:config>
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写切面类和方法
编写切面类和要作为建议执行的方法。
package com.howtodoinjava.app.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggingAspect {
public void logAroundAllMethods(ProceedingJoinPoint pjp) throws Throwable
{
System.out.println("****LoggingAspect.logAroundAllMethods() - Before method call");
pjp.proceed();
System.out.println("****LoggingAspect.logAroundAllMethods() - After method call");
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
****LoggingAspect.logAroundAllMethods() - Before method call
Method getEmployeeById() called
****LoggingAspect.logAroundAllMethods() - After method call
****LoggingAspect.logAroundAllMethods() - Before method call
Method createEmployee() called
****LoggingAspect.logAroundAllMethods() - After method call
显然,在相关联点上执行的切面为围绕建议。
学习愉快!
参考文献:
Spring AOP 参考
AspectJ 项目
不同的切入点表达式以及示例
Spring AOP 返回后建议示例
原文: https://howtodoinjava.com/spring-aop/aspectj-after-returning-advice-example/
在这个 Spring aop 示例中,我们将学习使用<aop:after-returning/>
配置来使用 AOP 返回后建议。 在返回后建议后配置为的方法,将在这些方法之后立即运行,这些方法与作为参数传递的切入点表达式匹配,并且通常会终止,即它们不会引发任何异常。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在<aop:after-returning/>
xml 配置中传递的切入点信息来调用切面方法。
创建 Spring AOP 返回后建议
要使用 xml 配置创建返回后建议,请按以下方式使用<aop:after-returning/>
。
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- after returning advice -->
<aop:after-returning method="logAfterReturingAllMethods" pointcut-ref="loggingPointcuts" returning="retVal" />
</aop:aspect>
</aop:config>
如果要检查它们,可以使用returning
属性从建议的方法接收返回值。 在这里,我传递了retVal
命名参数,该参数需要传递给通知方法。
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
添加 Spring AOP 配置
在 XML 配置文件中,您可以添加aop:config
元素以添加 AOP 支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- after returning advice -->
<aop:after-returning method="logAfterReturingAllMethods" pointcut-ref="loggingPointcuts" returning="retVal" />
</aop:aspect>
</aop:config>
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写切面类和方法
编写切面类和要作为建议执行的方法。
package com.howtodoinjava.app.aspect;
import org.aspectj.lang.JoinPoint;
public class LoggingAspect {
public void logAfterReturingAllMethods(Object retVal) throws Throwable
{
System.out.println("****LoggingAspect.logAfterReturingAllMethods() " + retVal);
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Method getEmployeeById() called
****LoggingAspect.logAfterReturingAllMethods() com.howtodoinjava.app.model.EmployeeDTO@13bc476
Method createEmployee() called
****LoggingAspect.logAfterReturingAllMethods() null
在连接点上执行返回后建议。
学习愉快!
参考文献:
Spring AOP 参考
AspectJ 项目
不同的切入点表达式以及示例
Spring AOP 抛出后建议示例
原文: https://howtodoinjava.com/spring-aop/aspectj-after-throwing-advice-example/
在这个 Spring aop 示例中,我们将学习使用<aop:after-throwing/>
配置来使用 AOP 抛出后建议。 配置为引发建议后的方法,将在这些方法之后立即运行,这些方法与作为参数传递的切入点表达式匹配,并终止于异常,即它们引发任何异常。
在此示例中,我们将创建简单的 spring 应用程序,添加日志记录切面,然后基于在<aop:after-throwing/>
xml 配置中传递的切入点信息来调用切面方法。
创建 Spring AOP 抛出后建议
要使用 XML 配置创建抛出后建议,请按以下方式使用<aop:after-throwing/>
。
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- after throwing advice -->
<aop:after-throwing method="logAfterThrowingAllMethods" pointcut-ref="loggingPointcuts" throwing="ex" />
</aop:aspect>
</aop:config>
如果要检查异常,可以使用throwing
属性从建议的方法捕获抛出的异常实例。 在这里,我传递了ex
命名参数,该参数需要传递给通知方法。
项目结构
Spring AOP 项目结构
Spring AOP AspectJ Maven 依赖关系
我添加了 spring 核心,spring aop 和 Aspectj 依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringAOPExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AOP Examples</name>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
添加 Spring AOP 配置
在 XML 配置文件中,您可以添加aop:config
元素以添加 AOP 支持。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut expression="execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))" id="loggingPointcuts"/>
<!-- after throwing advice -->
<aop:after-throwing method="logAfterThrowingAllMethods" pointcut-ref="loggingPointcuts" throwing="ex" />
</aop:aspect>
</aop:config>
<!-- Employee manager -->
<bean id="employeeManager" class="com.howtodoinjava.app.service.impl.EmployeeManagerImpl" />
<!-- Logging Aspect -->
<bean id="loggingAspect" class="com.howtodoinjava.app.aspect.LoggingAspect" />
</beans>
需要执行切面的服务方法
EmployeeManager.java
和EmployeeManagerImpl.java
public interface EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId);
public List<EmployeeDTO> getAllEmployee();
public void createEmployee(EmployeeDTO employee);
public void deleteEmployee(Integer employeeId);
public void updateEmployee(EmployeeDTO employee);
}
public class EmployeeManagerImpl implements EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId)
{
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
public List<EmployeeDTO> getAllEmployee()
{
System.out.println("Method getAllEmployee() called");
return new ArrayList<EmployeeDTO>();
}
public void createEmployee(EmployeeDTO employee)
{
System.out.println("Method createEmployee() called");
// Throw some exception from here
throw new NullPointerException("Error while creating exception");
}
public void deleteEmployee(Integer employeeId)
{
System.out.println("Method deleteEmployee() called");
}
public void updateEmployee(EmployeeDTO employee)
{
System.out.println("Method updateEmployee() called");
}
}
编写切面类和方法
编写切面类和要作为建议执行的方法。
package com.howtodoinjava.app.aspect;
public class LoggingAspect {
public void logAfterThrowingAllMethods(Exception ex) throws Throwable
{
System.out.println("****LoggingAspect.logAfterThrowingAllMethods() " + ex);
}
}
测试 Spring AspectJ 的配置和执行
现在,我们来测试以上配置的切面是否在给定的切入点信息上执行。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.app.model.EmployeeDTO;
import com.howtodoinjava.app.service.EmployeeManager;
public class TestMain
{
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeManager manager = (EmployeeManager) context.getBean("employeeManager");
manager.getEmployeeById(1);
manager.createEmployee(new EmployeeDTO());
}
}
Method getEmployeeById() called
Method createEmployee() called
****LoggingAspect.logAfterThrowingAllMethods() java.lang.NullPointerException: Error while creating exception
Exception in thread "main" java.lang.NullPointerException: Error while creating exception
在连接点上执行抛出后建议。
学习愉快!
参考文献:
Spring AOP 参考
AspectJ 项目
不同的切入点表达式以及示例
Spring AOP AspectJ 切入点表达式示例
原文: https://howtodoinjava.com/spring-aop/aspectj-pointcut-expressions/
在本教程中,我列出了一些示例,这些示例将帮助您编写切入点表达式,以将任何类型的方法连接点匹配到 Spring 应用程序中。 有关完整的 AspectJ 切入点语言,请参阅 AspectHT 网站上的 AspectJ 编程指南。
1. 如何匹配方法签名模式
最典型的切入点表达式用于通过其签名来匹配许多方法。
1.1. 匹配另一个包中一个类中的所有方法
例如,以下切入点表达式与EmployeeManager
接口中声明的所有方法匹配。 前面的通配符将方法与任何修饰符(公共,保护和私有)和任何返回类型匹配。 参数列表中的两个点匹配任意数量的参数。
execution(* com.howtodoinjava.EmployeeManager.*(..))
1.2. 匹配同一包中一个类中的所有方法
如果目标类或接口与此切面位于同一包中,则可以省略包名称。
execution(* EmployeeManager.*(..))
1.3. 匹配EmployeeManager
中的所有公共方法
在开始处使用public
关键字,并使用*匹配任何返回类型。
execution(public * EmployeeManager.*(..))
1.4. 匹配返回类型为EmployeeDTO
的EmployeeManager
中的所有公共方法
在开始时使用public
关键字并返回类型。
execution(public EmployeeDTO EmployeeManager.*(..))
1.5. 匹配返回类型为EmployeeDTO
和第一个参数为EmployeeDTO
的EmployeeManager
中的所有公共方法
在开始时使用public
关键字并返回类型。 另外,还要指定您的第一个参数。 其余参数可以通过两个点进行匹配。
execution(public EmployeeDTO EmployeeManager.*(EmployeeDTO, ..))
1.6. 匹配返回类型为EmployeeDTO
和带有确定参数的EmployeeManager
中的所有公共方法
在开始时使用public
关键字并返回类型。 另外,还要指定所有参数类型。
execution(public EmployeeDTO EmployeeManager.*(EmployeeDTO, Integer))
2. 如何匹配类签名模式
当应用于 Spring AOP 时,这些切入点的范围将缩小以仅匹配特定类型内的所有方法执行。
2.1. 匹配包com.howtodoinjava
中的类中定义的所有方法
这很像前面的例子。
within(com.howtodoinjava.*)
2.2. 匹配在com.howtodoinjava
包以及所有子包中的类中定义的所有方法
对于包含,子包使用两个点。
within(com.howtodoinjava..*)
2.3. 匹配另一个包中的类的所有方法
与前面使用执行关键字的示例非常相似。
within(com.howtodoinjava.EmployeeManagerImpl)
2.4. 匹配同一包中的类的所有方法
如果使用相同的软件包,请删除软件包名称。
within(EmployeeManagerImpl)
2.5. 匹配EmployeeManager
接口的所有实现类中的所有方法
使用+
(加号)匹配接口的所有实现。
within(EmployeeManagerImpl+)
3. 如何匹配类名模式
您可以匹配所有具有相同命名模式的 bean,例如
3.1. 匹配名称以Manager
结尾的 bean 中定义的所有方法。
这很简单。 使用*
来匹配 Bean 名称中的任何前缀,然后匹配单词。
bean(*Manager)
4. 如何组合切入点表达式
在 AspectJ 中,切入点表达式可以与运算符&& (and)
,|| (or)
和! (not)
组合。 例如
4.1. 匹配以Manager
或DAO
结尾的所有方法
使用'||'
符号将两个表达式组合在一起。
bean(*Manager) || bean(*DAO)
我希望当您在应用程序中确定正确的切入点表达式时遇到任何困难时,上述信息对您有所帮助。
学习愉快!
Spring AOP – 切面顺序
原文: https://howtodoinjava.com/spring-aop/spring-aop-specifying-aspects-ordering/
在上一个教程中,我们了解了 spring aop 关键术语和示例 。 这些我们创建了一个日志记录切面,然后应用于UserManager
类。 假设您的应用程序中有多个切面,并且可以将它们应用于某种方法。 如果将多个切面应用到同一个连接点,除非您使用@Order
注解或org.springframework.core.Ordered
接口明确指定了切面,否则将不会确定这些切面的优先级/顺序。 在这个例子中,我们将看到一个有序切面的例子。
指定切面顺序
如前所述,要指定切面的顺序,您有两种方法:
1)使用@Order
注解指定切面的排序
这很简单。 使用如下注解。
@Aspect
@Order(0)
public class EmployeeCRUDTransactionAspect
{
@Before("execution(* EmployeeManager.getEmployeeById(..))")
public void getEmployeeById(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDTransactionAspect.getEmployeeById() : " + joinPoint.getSignature().getName());
}
}
@Aspect
@Order(1)
public class EmployeeCRUDLoggingAspect
{
@Before("execution(* EmployeeManager.getEmployeeById(..))")
public void logBefore(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBefore() : " + joinPoint.getSignature().getName());
}
}
2)通过实现org.springframework.core.Ordered
接口指定切面排序
这太容易了。
@Aspect
public class EmployeeCRUDLoggingAspect implements Ordered
{
//Override this method
public int getOrder() {
return 0;
}
@Before("execution(* EmployeeManager.getEmployeeById(..))")
public void logBefore(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDAspect.logBefore() : " + joinPoint.getSignature().getName());
}
}
@Aspect
public class EmployeeCRUDTransactionAspect implements Ordered
{
//Override this method
public int getOrder() {
return 1;
}
@Before("execution(* EmployeeManager.getEmployeeById(..))")
public void getEmployeeById(JoinPoint joinPoint)
{
System.out.println("EmployeeCRUDTransactionAspect.getEmployeeById() : " + joinPoint.getSignature().getName());
}
}
现在该测试顺序是否有效。 在applicationContext.xml
文件中配置两个切面。
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.howtodoinjava.demo.aop" />
<bean id="transactionAspect" class="com.howtodoinjava.demo.aop.EmployeeCRUDTransactionAspect" />
<bean id="loggingAspect" class="com.howtodoinjava.demo.aop.EmployeeCRUDLoggingAspect" />
让我们运行以下示例:
public class TestAOP
{
@SuppressWarnings("resource")
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("com/howtodoinjava/demo/aop/applicationContext.xml");
EmployeeManager manager = context.getBean(EmployeeManager.class);
manager.getEmployeeById(1);
}
}
Output:
EmployeeCRUDAspect.logBefore() : getEmployeeById
EmployeeCRUDTransactionAspect.getEmployeeById() : getEmployeeById
Method getEmployeeById() called
很棒。 Spring AOP 切面的顺序正在按预期工作。
祝您学习愉快!
带有加载时织入的非公开方法上的 Spring 事务
原文: https://howtodoinjava.com/spring-transaction/spring-transactions-on-non-public-methods-with-load-time-weaving/
在本教程中,我们将学习如何在非公开方法上的任何 Spring 应用程序中应用事务(通过默认 spring AOP 只能对 IoC 容器中的 bean 的公共方法配置建议)。 使用此技术,您可以管理非公共方法的事务,也可以管理在 Spring IoC 容器外部创建的对象(即不受 IoC 容器管理)中的任何方法上的事务。
使用AnnotationTransactionAspect
管理事务
Spring 具有一个名为AnnotationTransactionAspect
的 AspectJ 切面,它可以管理任何对象的任何方法的事务,即使这些方法是非公开的或对象是在 Spring IoC 容器外部创建的也是如此。
该切面将管理带有@Transactional
注解的任何方法的事务。 要启用此有用的切面,您可以通过以下两种方式之一来更改配置:
@EnableLoadTimeWeaving
注解<context:load-time-weaver />
; 组态
使用@EnableLoadTimeWeaving
注解
必须在配置类上使用@EnableLoadTimeWeaving
注解以在加载时获取。 您需要做的就是定义@EnableTransactionManagement
注解并将其mode
属性设置为Aspectj
。
切面说,容器应使用加载时或编译时织入来启用事务通知。 另一个值代理表示容器应使用默认的 Spring AOP 机制。
请务必注意,aspect 模式不支持在接口上配置@Transactional
注解(应仅在类上使用)。
配置示例如下所示:
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
@EnableLoadTimeWeaving
public class MyApplicationConfiguration
{
//Configuration code
}
使用<context:load-time-weaver/>
配置
如果您在应用程序中使用了基于 XML 的配置,则可以使用以下配置更改来启用此功能。
<beans>
<context:load-time-weaver />
<tx:annotation-driven mode="aspectj"/>
<!-- Other beans declarations-->
</beans>
要启用加载时织入,还必须包括spring-instrument
模块。 如果您使用的是 Maven,请将以下依赖项添加到您的项目中。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${spring.version}</version>
</dependency>
在下面将您的问题和评论丢给我。
祝您学习愉快!
参考: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-aj-ltw
Spring 热门 AOP 面试问题及答案
原文: https://howtodoinjava.com/interview-questions/top-spring-aop-interview-questions-with-answers/
在经历了 Spring 核心面试问题 之后,让我们介绍一下 Spring AOP 面试问题 您的下一次技术面试。 同样,请随时提出一些新问题,这些新问题不在本文中,因此我可以将其包括在内,以使更多的读者受益。
Table of Contents
Describe Spring AOP?
What is the difference between concern and cross-cutting concern in Spring AOP?
What are the available AOP implementations?
What are the different advice types in spring?
What is Spring AOP Proxy?
What is Introduction?
What is joint point and Point cut?
What is Weaving in AOP?
描述 Spring AOP?
Spring AOP(面向切面的编程) 补充了 OOP ,因为它也提供了模块化。 在 OOP 中,关键单元是对象,但是在 AOP 中,关键单元是切面或关注点(仅假设应用程序中具有独立模块)。 一些切面具有集中的代码,但是其他切面可能是分散的或缠结的,例如。 记录或事务。 这些分散的切面称为跨领域关注点。 横切关注点是可能影响整个应用程序的关注点,应尽可能集中在代码中的一个位置,例如事务管理,身份验证,日志记录,安全性等。
AOP 提供了一种使用简单的可插拔配置在实际逻辑之前,之后或周围动态添加横切关注点的方法。 现在和将来,维护代码也变得很容易。 您只需更改配置文件即可添加/删除关注点,而无需重新编译完整的源代码(如果您要使用要求 XML 配置的切面)。
可以通过以下两种主要方式使用 Spring AOP。 但是广泛使用的方法是 Spring AspectJ 注解样式。
1)通过 AspectJ 注解样式
2)通过 Spring XML 配置样式
Spring AOP 中的关注和跨领域关注之间有什么区别?
关注的是我们希望在应用程序的模块中具有的行为。关注可以定义为我们要解决特定业务问题的功能。 例如。 在任何电子商务应用程序中,不同的关注点(或模块)可能是库存管理,运输管理,用户管理等。
跨领域关注是适用于整个应用程序(或多个模块)的关注。 例如日志记录,安全性和数据传输是应用程序几乎每个模块都需要考虑的问题,因此它们被称为跨领域关注点。
有哪些可用的 AOP 实现?
下面列出了基于 Java 的主要 AOP 实现:
- AspectJ
- Spring AOP
- JBoss AOP
您可以在 Wiki 页面 中找到 AOP 实现的重要列表。
Spring 有哪些不同的建议类型?
建议是您有兴趣在应用程序的其他模块上应用的跨领域关注点的实现。 建议主要有 5 种:
- 事前建议:在连接点之前执行的建议,但是它不能阻止执行流程前进到连接点(除非它引发异常)。 要使用此建议,请使用
@Before
注解。 - 返回后建议:在连接点正常完成后要执行的建议。 例如,如果某个方法返回而没有引发异常。 要使用此建议,请使用
@AfterReturning
注解。 - 抛出后建议:如果方法因引发异常而退出,则要执行的建议。 要使用此建议,请使用
@AfterThrowing
注解。 - 事后建议:无论连接点退出的方式如何(正常或特殊返回),均应执行的建议。 要使用此建议,请使用
@After
注解。 - 周围建议:围绕连接点的建议,例如方法调用。 这是最有力的建议。 要使用此建议,请使用
@Around
注解。
什么是 Spring AOP 代理?
代理是一种常用的设计模式。 简而言之,代理是一个看起来像另一个对象的对象,但是在幕后添加了特殊功能。
Spring AOP 是基于代理的。 AOP 代理是由 AOP 框架创建的对象,用于在运行时实现切面协定。
Spring AOP 默认将标准 JDK 动态代理用于 AOP 代理。 这使得可以代理任何接口(或一组接口)。 Spring AOP 也可以使用 CGLIB 代理。 这是代理类而不是接口所必需的。
如果业务对象未实现接口,则默认使用 CGLIB。
什么是简介?
简介使切面可以声明建议对象实现其在实际中没有的任何其他接口,并代表那些对象提供该接口的实现。
使用@DeclareParents
注解进行介绍。
阅读有关简介的更多信息。
什么是连接点和切点?
连接点是程序的执行点,例如方法的执行或异常的处理。 在 Spring AOP 中,连接点始终表示方法执行。 例如,如果您在EmployeeManager
接口驾驶室内定义的所有方法都涉及到交叉问题,则这些方法都被视为连接点。
切入点是与连接点匹配的谓词或表达式。 通知与切入点表达式关联,并且在与切入点匹配的任何连接点运行(例如,表达式execution(* EmployeeManager.getEmployeeById(..))
以匹配EmployeeManager
接口中的方法getEmployeeById()
)。 切入点表达式匹配的连接点的概念是 AOP 的核心,默认情况下,Spring 使用 AspectJ 切入点表达语言。
什么是织入?
Spring AOP 框架仅支持有限类型的 AspectJ 切入点,并允许将切面应用于 IoC 容器中声明的 bean。 如果您想使用其他切入点类型或将外观[应用于在 Spring IoC 容器外部创建的对象),则必须在 Spring 应用程序中使用 AspectJ 框架并使用其织入功能。
织入是将切面与其他外部应用程序类型或对象链接以创建建议对象的过程。 这可以在编译时(例如,使用 AspectJ 编译器),加载时或在运行时完成。 像其他纯 Java AOP 框架一样,Spring AOP 仅在运行时执行织入。 相反,AspectJ 框架同时支持编译时和加载时织入。
AspectJ 的编译时织入是通过称为ajc
的特殊 AspectJ 编译器完成的。 它可以将切面织入到 Java 源文件中,并输出织入的二进制类文件。 它还可以将切面织入到已编译的类文件或 JAR 文件中。 此过程称为编译后织入。 您可以在 Spring IoC 容器中声明它们之前,对类进行编译时和编译后织入。 Spring 根本不参与织入过程。 有关编译时和编译后织入的更多信息,请参考 AspectJ 文档。
当类加载器将目标类加载到 JVM 时,就会发生 AspectJ 加载时织入(也称为 LTW)。 对于要织入的类,需要特殊的类加载器来增强目标类的字节码。 AspectJ 和 Spring 都提供了加载时织入器,以向类加载器添加加载时织入功能。 您只需要简单的配置即可启用这些加载时织布器。
现在该轮到您分享更多您在以前的面试中遇到的Spring AOP 面试问题,以便我可以将其包含在本帖子中,并使其对他人也更有用。
祝您学习愉快!
Spring MVC
Spring MVC 教程
原文: https://howtodoinjava.com/spring-mvc-tutorial/
给定的 Spring MVC 教程和示例应用程序将帮助您构建健壮的 Web 应用程序并解决开发中遇到的问题。
Hello World 的例子
-
Spring MVC Hello World 示例
编写 Spring 4 MVC 应用程序,了解后端发生了什么。
-
@RequestMapping
注解示例使用
@RequestMapping
注解编写 MVC 应用程序。 -
带有 Maven 和 JSTL 的 **Spring 3 mvc hello world 应用
学习使用 maven 创建 Spring 3.x MVC 应用程序进行依赖管理。
表单处理示例
-
显示,验证和提交表单的示例
编写基于 HTML 表单的 spring mvc 应用程序及其端到端流程的完整示例。
-
ResourceBundleViewResolver
配置示例在 spring mvc 应用程序中编写
ResourceBundleViewResolver
的示例。 -
InternalResourceViewResolver
配置示例在 spring mvc 应用程序中编写
InternalResourceViewResolver
的示例。 -
国际化 (i18n) 和本地化 (i10n) 示例
在 Spring MVC 应用程序中处理国际化和本地化功能。
-
MessageSourceAware
Java Bean 示例编写消息源感知 bean 的 Spring MVC 示例。
-
HandlerInterceptor
示例使用
HandlerInterceptor
编写 spring MVC 拦截器。 -
SimpleMappingExceptionResolver
示例使用
SimpleMappingExceptionResolver
编写异常处理逻辑。 -
<context:annotation-config>
vs<context:component-scan>
了解
<context:annotation-config>
与<context:component-scan>
之间的区别。
验证示例
-
弹出和验证下拉菜单的示例
编写示例以填充和验证 Spring MVC 应用程序中的选择框字段。
-
Bean 验证 – JSR-303 注解
使用
JSR-303
注解编写验证。 -
自定义验证示例
在 Spring MVC 应用程序中编写自定义验证逻辑。
文件上传/下载示例
-
Spring MVC 多文件上传示例
使用 spring MVC 编写多个文件上传控制器的示例。
-
带有进度条的 Spring MVC 多文件上传
使用 spring MVC 在 UI 上编写带有进度条的多个文件上传控制器的示例。
-
Spring MVC 文件下载控制器示例
使用 spring MVC 编写文件下载控制器的示例。
Spring MVC 面试问题
-
Spring MVC 面试问题和答案
Spring MVC 面试最常见的 20 个问题。
常见错误
java.lang.NoClassDefFoundError
:无法初始化类org.hibernate.validator.engine.ConfigurationImpl
资源:
Spring 4 MVC 文档
Spring 3 MVC 文档
Bean 验证
Spring Security 5 登录表单示例
原文: https://howtodoinjava.com/spring5/security5/login-form-example/
在此 Spring Security 5 教程中,学习将基于的自定义登录表单的安全性添加到我们的 Spring WebMVC 应用程序中。 我正在使用 Spring Security 5 构建此示例。 本教程还讨论了从会话注销。
1. 包含 Spring Security 5 依赖项
包括 SpringSecurity 包。 我正在使用 maven,因此为 spring security version 5.0.7.RELEASE 添加了相应的依赖项。
pom.xml
<properties>
<failOnMissingWebXml>false</failOnMissingWebXml>
<spring.version>5.2.0.RELEASE</spring.version>
</properties>
<!-- Spring MVC Dependency -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security Core -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security Config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Security Web -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
2. 创建 Spring Security 配置
2.1. 配置身份验证和 URL 安全
- 我创建了这个简单的安全配置,并添加了两个演示用户
user
和admin
。 @EnableWebSecurity
启用 Spring Security 的网络安全支持,并提供 Spring MVC 集成。WebSecurityConfigurerAdapter
提供了一组用于启用特定 Web 安全配置的方法。configure(HttpSecurity http)
用于保护需要安全性的不同 URL。- 它在 URL
/login
上配置自定义登录页面。 如果用户名/密码匹配,则将请求重定向到/home
,否则登录页面会刷新并显示相应的错误消息。 - 它还配置从会话注销。 注销后,用户将再次重定向到登录页面。
我已使用
auth.inMemoryAuthentication()
使用内存中身份验证。 您可以使用auth.jdbcAuthentication()
配置 JDBC 身份验证,或使用auth.ldapAuthentication()
配置 LDAP 身份验证。
SecurityConfig.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("user").password(passwordEncoder.encode("123456")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder.encode("123456")).roles("USER", "ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.antMatchers("/**")
.hasAnyRole("ADMIN", "USER")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home")
.failureUrl("/login?error=true")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login?logout=true")
.invalidateHttpSession(true)
.permitAll()
.and()
.csrf()
.disable();
}
}
了解更多:登录表单的 Spring Security XML Config
2.2. 将 Spring Security 绑定到 Web 应用程序
在 Spring Web 应用程序中,使用DelegatingFilterProxy
实现安全性。 要使用 Java 配置的 spring 容器注册它,您应该使用AbstractSecurityWebApplicationInitializer
。
Spring 将在应用程序启动期间检测到此类的实例,并在其他已注册过滤器之前注册DelegatingFilterProxy
以使用springSecurityFilterChain
。 它还注册了ContextLoaderListener
。
SpringSecurityInitializer.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
//no code needed
}
另外,包括SecurityConfig
至AppInitializer
。
AppInitializer.java
package com.howtodoinjava.demo.spring.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { HibernateConfig.class, SecurityConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
3. 添加自定义登录表单
3.1. LoginController
添加登录控制器方法来处理登录和注销请求。
LoginController.java
package com.howtodoinjava.demo.spring.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class LoginController
{
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout,
Model model) {
String errorMessge = null;
if(error != null) {
errorMessge = "Username or Password is incorrect !!";
}
if(logout != null) {
errorMessge = "You have been successfully logged out !!";
}
model.addAttribute("errorMessge", errorMessge);
return "login";
}
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/login?logout=true";
}
}
3.2. login.jsp
创建将接受username
和password
的login.jsp
文件; 并将它们发布到 URL /login
。
login.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body onload='document.loginForm.username.focus();'>
<h1>Spring Security 5 - Login Form</h1>
<c:if test="${not empty errorMessge}"><div style="color:red; font-weight: bold; margin: 30px 0px;">${errorMessge}</div></c:if>
<form name='login' action="/login" method='POST'>
<table>
<tr>
<td>UserName:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
4. Spring Security 5 登录表单演示
-
用 maven 运行命令
tomcat7:run
启动应用程序。启动主页http://localhost:8080/home
。它将重定向到登录页面http://localhost:8080/login
。登录表单
-
输入不正确的用户名或密码。 将显示登录错误消息。
不正确的用户名或密码
-
输入正确的用户名和密码组合。
user
和123456
。将显示主页。主页
-
单击注销按钮。 用户将被注销,然后重定向回到登录页面。
注销
学习愉快!
下载源码
Spring MVC Hello World 示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-hello-world-example/
在此示例中,我们将使用 Spring MVC 框架构建一个 Hello World Web 应用程序。 Spring MVC 是 Spring 框架的最重要模块之一。 它基于功能强大的 Spring IoC 容器 构建,并广泛使用了容器功能以简化其配置。
Table Of Contents
What is MVC Framework?
Dispatcher Servlet (Spring Controller)
Spring MVC Hello World Example
Runtime Dependencies
Configuration Files web.xml and spring-servlet.xml
Request Handler EmployeeController.java
View Model EmployeeVO.java
Dao Classes
Service layer Classes
View employeesListDisplay.jsp
下载源码
什么是 MVC 框架?
模型视图控制器(MVC) 是众所周知的 设计模式 ,用于设计基于 UI 的应用程序。 它主要通过在应用程序中分离模型,视图和控制器的角色来将业务逻辑与 UI 分离。 通常,模型负责封装应用程序数据以呈现视图。 视图仅应显示此数据,而不包括任何业务逻辑。 控制器负责接收来自用户的请求并调用后端服务(管理器或 dao)进行业务逻辑处理。 处理后,后端服务可能会返回一些数据以供视图显示。 控制器收集这些数据并准备模型以供视图显示。 MVC 模式的核心思想是将业务逻辑与 UI 分开,以允许它们独立更改而不会互相影响。
在 Spring MVC 应用程序中,模型通常由 POJO 对象组成,这些对象由服务层处理并由持久层持久化。 视图通常是使用 Java 标准标记库(JSTL)编写的 JSP 模板。 控制器部分由调度程序 servlet 扮演,我们将在本教程中详细了解。
一些开发人员将服务层和 DAO 层类视为 MVC 中模型组件的一部分。 我对此有不同的看法。 我不认为服务层和 DAO 层类是 MVC 框架的一部分。 通常,Web 应用程序是 3 层体系结构,即数据服务表示。MVC 实际上是表示层的一部分。
分派器 Servlet(Spring 控制器)
在最简单的 Spring MVC 应用程序中,控制器是您唯一需要在 Java Web 部署描述符(即web.xml
文件)中配置的 servlet。 一个 Spring MVC 控制器(通常称为分派器 Servlet)实现前端控制器设计模式,每个 Web 请求都必须经过它,以便它可以管理整个请求生命周期。
将 Web 请求发送到 Spring MVC 应用程序时,调度程序 Servlet 首先接收该请求。 然后,它会组织在 Spring 的 Web 应用程序上下文中配置的不同组件(例如,实际的请求处理器控制器和视图解析器)或控制器本身中存在的注解,这些都是处理请求所需的。
Spring 分派器 servlet
要在 Spring 3.0 中定义控制器类,必须使用@Controller
注解标记一个类。 当带@Controller
的带注解的控制器接收到请求时,它将寻找适当的处理器方法来处理该请求。 这要求控制器类通过一个或多个处理器映射将每个请求映射到一个处理器方法。 为此,控制器类的方法用@RequestMapping
注解修饰,使其成为处理器方法。
处理器方法处理完请求后,它将控制权委派给一个视图,该视图表示为处理器方法的返回值。 为了提供一种灵活的方法,处理器方法的返回值并不代表视图的实现,而是逻辑视图,即没有任何文件扩展名。 您可以将这些逻辑视图正确地映射到applicationContext
文件中,这样就可以轻松更改视图层代码,甚至无需触摸请求处理器类代码。
视图解析器负责为逻辑名称解析正确的文件。 根据视图实现的设计,控制器类将视图名称解析为视图实现后,便会呈现对象。
Spring MVC Hello World 示例
在此应用程序中,我将创建仅具有一项功能的最简单的员工管理应用程序演示,即列出系统中所有可用的员工。 让我们记下该应用程序的目录结构。
Spring mvc hello world 目录结构
现在,将所有涉及的文件写入此 hello world 应用程序中。
pom.xml
在pom.xml
下面的文件包含 spring mvc 的依赖项和用于编写 jsp 文件的 taglibs 支持。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>springmvcexample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springmvcexample Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<!-- Tag libs support for view layer -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>springmvcexample</finalName>
</build>
</project>
web.xml
这个最小的web.xml
文件声明了一个 servlet(即调度程序 servlet)来接收所有类型的请求。 调度程序 servlet 在这里充当前端控制器。
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring Web MVC Hello World Application</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-servlet.xml
(也可以有 applicationContext.xml
)
我们在请求处理器,服务和 dao 层上使用带注解的类,因此我已对基本包“ com.howtodoinjava.demo
”中的所有类文件启用了注解处理。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
EmployeeController.java
类级别和方法级别的注解@RequestMapping
确定将在其中调用方法的 URL。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.howtodoinjava.demo.service.EmployeeManager;
@Controller
@RequestMapping("/employee-module")
public class EmployeeController
{
@Autowired
EmployeeManager manager;
@RequestMapping(value = "/getAllEmployees", method = RequestMethod.GET)
public String getAllEmployees(Model model)
{
model.addAttribute("employees", manager.getAllEmployees());
return "employeesListDisplay";
}
}
阅读更多:如何使用
@Component
,@Repository
,@Service
和@Controller
注解?
EmployeeVO.java
此类充当 MVC 模式的模型。
package com.howtodoinjava.demo.model;
import java.io.Serializable;
public class EmployeeVO implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id;
private String firstName;
private String lastName;
//Setters and Getters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + "]";
}
}
EmployeeDAO.java
三层体系结构中第三层的类。 负责与基础数据库存储进行交互。
import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;
public interface EmployeeDAO
{
public List<EmployeeVO> getAllEmployees();
}
EmployeeDAOImpl.java
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.demo.model.EmployeeVO;
@Repository
public class EmployeeDAOImpl implements EmployeeDAO {
public List<EmployeeVO> getAllEmployees()
{
List<EmployeeVO> employees = new ArrayList<EmployeeVO>();
EmployeeVO vo1 = new EmployeeVO();
vo1.setId(1);
vo1.setFirstName("Lokesh");
vo1.setLastName("Gupta");
employees.add(vo1);
EmployeeVO vo2 = new EmployeeVO();
vo2.setId(2);
vo2.setFirstName("Raj");
vo2.setLastName("Kishore");
employees.add(vo2);
return employees;
}
}
EmployeeManager.java
3 层体系结构中第二层的类。 负责与 DAO 层进行交互。
import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;
public interface EmployeeManager
{
public List<EmployeeVO> getAllEmployees();
}
EmployeeManagerImpl.java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.howtodoinjava.demo.dao.EmployeeDAO;
import com.howtodoinjava.demo.model.EmployeeVO;
@Service
public class EmployeeManagerImpl implements EmployeeManager {
@Autowired
EmployeeDAO dao;
public List<EmployeeVO> getAllEmployees()
{
return dao.getAllEmployees();
}
}
employeeListDisplay.jsp
该 jsp 用于显示系统中的所有员工。 它循环遍历员工集合,并将其详细信息打印在表中。 这适合 MVC 模式的视图层。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<title>Spring MVC Hello World</title>
</head>
<body>
<h2>All Employees in System</h2>
<table border="1">
<tr>
<th>Employee Id</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
<c:forEach items="${employees}" var="employee">
<tr>
<td>${employee.id}</td>
<td>${employee.firstName}</td>
<td>${employee.lastName}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
现在将应用程序部署到您的应用程序服务器中(我正在使用 tomcat 7)。 并点击 URL http://localhost:8080/springmvcexample/employee-module/getAllEmployees
。 如果正确配置了所有内容,则会在下面的屏幕上看到。
应用前端 UI
下载源码
如果某些内容不适合您或在本教程中不清楚,请给我留言。
祝您学习愉快!
使用 Maven 和 JSTL 的 Spring MVC Hello World 示例
原文: https://howtodoinjava.com/spring-mvc/spring-3-mvc-hello-world-application-with-maven-and-jstl/
在本 Spring MVC 教程中,我们将使用 Spring MVC 框架构建 Hello World 应用程序。 逐步遵循给定的说明并学习基础知识。 我们已经使用 JSTL 来编写视图文件。
1. 开发环境
- Maven
- Eclipse juno
- Tomcat 7
2. 创建 Spring MVC 应用程序
2.1. 创建 Maven Web 应用程序
要创建 Maven Web 应用程序,请打开命令提示符,并将当前工作目录放入 Eclipse 工作区。
现在执行以下命令。
$ mvn archetype:generate -DgroupId=com.howtodoinjava.mvc
-DartifactId=firstSpringApplication
-DarchetypeartifactId=maven-archetype-webapp
-DinteractiveMode=false
请从上述命令中删除换行符。 我添加它是为了使其可读。
现在在给定命令下运行。 这会将创建的项目转换为 Eclipse Web 项目。
$ cd firstSpringApplication
$ mvn eclipse:eclipse -Dwtpversion=2.0
2.2. 更新 Maven 依赖项
使用这些依赖关系更新pom.xml
文件。
pom.xml
<properties>
<spring.version>3.0.5.RELEASE</spring.version>
</properties>
<!-- Spring 3 dependencies -->
<dependency>
<groupid>org.springframework</groupid>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupid>javax.servlet</groupid>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupid>taglibs</groupid>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
3. 创建分派器 Servlet
3.1. 更新web.xml
更新web.xml
以获取 Servlet 映射和 spring 配置位置。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>www.howtodoinjava.com</display-name>
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
3.2. Bean 配置文件
我们将需要在WEB-INF
文件夹中写入配置文件(spring-mvc-servlet.xml
)。 我们可以随意命名,但是请记住,它必须与我们在web.xml
中声明的 servlet 名称匹配。
spring-mvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.web" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
3. 创建 Spring MVC 控制器
该请求处理器将接收对特定 URL 的所有请求,并返回模型数据以查看配置的视图处理器。
DemoController.java
package com.howtodoinjava.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/access")
public class DemoController
{
@RequestMapping(method = RequestMethod.GET)
public String printWelcome(ModelMap model)
{
model.addAttribute("message", "Spring 3 MVC Hello World !! Thanks to www.howtodoinjava.com");
return "helloWorld";
}
}
4. 创建视图
我们的视图是一个 jsp 文件,当从服务器返回相关视图时,它将在浏览器中呈现。
helloWorld.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jstl/xml" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql" %>
<html>
<head>
<title>My first example using Spring 3 MVC</title>
</head>
<body>
<h1>Welcome message : <c:out value="${message}"></c:out></h1>
</body>
</html>
5. 演示
要运行该应用程序,请在 eclipse 中配置 tomcat 服务器。 现在,右键单击项目,然后选择运行方式。 在子菜单上,选择在服务器上运行。
现在在浏览器中输入:http://localhost:8080/firstSpringApplication/access
我们应该能够在浏览器中看到消息Spring 3 MVC Hello World !! Thanks to www.howtodoinjava.com
。
如果您在运行或部署给定应用程序时发现任何问题,请给我留言。
下载源码
学习愉快!
Spring @RequestMapping
注解示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-requestmapping-annotation-examples/
在 spring mvc hello world 应用程序中,我们看到了具有端到端功能(不包括任何数据库访问)的非常基本的员工管理应用程序。 在学习 spring mvc 模块的下一步中,我将提供@RequestMapping
注解的一些示例,以说明如何使用@RequestMapping
以不同的方式将 URL 映射到控制器方法。 我再次使用与 Spring MVC Hello World 应用程序相同的代码库,并使用@RequestMapping
注解仅更改控制器类。
1)仅方法级别的@RequestMapping
注解
在@RequestMapping
注解的这种用法中,必须提供完整的路径作为value
属性。 例如,请看下面具有基本 CRUD 操作的控制器代码。
@Controller
public class EmployeeController
{
@RequestMapping("/employee-management/employees")
public String getAllEmployees(Model model)
{
//application code
return "employeesList";
}
@RequestMapping("/employee-management/employees/add")
public String addEmployee(EmployeeVO employee)
{
//application code
return "employeesDetail";
}
@RequestMapping("/employee-management/employees/update")
public String updateEmployee(EmployeeVO employee)
{
//application code
return "employeesDetail";
}
@RequestMapping(value={"/employee-management/employees/remove","/employee-management/employees/delete"})
public String removeEmployee(@RequestParam("id") String employeeId)
{
//application code
return "employeesList";
}
}
如果可以将多个网址映射到单个方法,则可以将具有所有不同网址的字符串参数数组传递给value
属性,例如,我们在上面的示例中针对removeEmployee()
方法执行了此操作。 如果您调用 URL <BASE_URL>/employee-management/employees/remove
或<BASE_URL>/employee-management/employees/delete
。
2)类级别以及方法级别的@RequestMapping
注解
在上面的示例中要注意的一件事是,/ employee-management / employees
是映射到所有方法的每个 URL 的一部分。 如果我们以某种共同的步伐放它,并且每种方法应该仅具有所需的标识符,那将是很好的。
这可以通过将@RequestMapping
注解放在类级别和方法级别上来完成。 看下面的例子。
@Controller
@RequestMapping("/employee-management/employees/*")
public class EmployeeController
{
@RequestMapping
public String getAllEmployees(Model model)
{
//application code
return "employeesList";
}
@RequestMapping("/add")
public String addEmployee(EmployeeVO employee)
{
//application code
return "employeesDetail";
}
@RequestMapping("/update")
public String updateEmployee(EmployeeVO employee)
{
//application code
return "employeesDetail";
}
@RequestMapping(value={"/remove","/delete"})
public String removeEmployee(@RequestParam("id") String employeeId)
{
//application code
return "employeesList";
}
}
现在,我们也在类级别应用了注解。 请注意,此更改不会更改映射的行为。 它们与以前完全相同。
要注意的另一件事是,第一种方法getAllEmployees()
缺少 URL 值。 由于类级别使用/employee-management/employees/
URL 通配符,因此,如果没有其他与任何请求匹配的处理器,则将该处理器方法作为catch
块执行。 因此,任何 URL 请求(例如/employee-management/employees/list
或/employee-management/employees/abcd
或/employee-management/employees/
)触发此方法。
3)仅使用 HTTP 请求类型的@RequestMapping
注解
也有可能在类级别上只有一个@RequestMapping
注解,而在方法级别上您没有指定 URL 值。 只需指定 HTTP 请求类型,即可将每种不同的 http 类型映射到不同的方法。 这种设计在 RESTFul Web 服务 中非常流行。
@Controller
@RequestMapping("/employee-management/employees")
public class EmployeeController
{
@RequestMapping (method = RequestMethod.GET)
public String getAllEmployees(Model model)
{
//application code
return "employeesList";
}
@RequestMapping (method = RequestMethod.POST)
public String addEmployee(EmployeeVO employee)
{
//application code
return "employeesDetail";
}
@RequestMapping (method = RequestMethod.PUT)
public String updateEmployee(EmployeeVO employee)
{
//application code
return "employeesDetail";
}
@RequestMapping (method = RequestMethod.DELETE)
public String removeEmployee(@RequestParam("id") String employeeId)
{
//application code
return "employeesList";
}
}
请注意,在此示例代码中,将使用相同的 URL 访问所有方法,但使用不同的 http 请求类型。
如果您想使用上面的@RequestMapping
注解示例,则可以在此处下载源代码。
下载源码
在评论部分中将您的评论/问题交给我。
祝您学习愉快!
Spring MVC 自定义验证器示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-custom-validator-example/
在 spring mvc 表单提交教程中,我们学习了如何显示显示表单和提交表单数据,包括使用BindingResult.rejectValue()
验证输入。 在此示例中,我们将学习为EmployeeVO
模型对象构建更强大的验证器。 该验证器是 Validator
接口的自定义实现。 在此示例中,我将修改上一个教程中构建的表单提交示例所使用的代码。
阅读更多: Spring MVC 显示,验证和提交表单示例
下载源码
自定义验证器实现
Spring MVC 通过实现Validator
接口的验证器对象来支持验证。 您可以编写以下验证器来检查是否填写了必需的表单字段。
package com.howtodoinjava.demo.validator;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.howtodoinjava.demo.model.EmployeeVO;
@Component
public class EmployeeValidator implements Validator
{
public boolean supports(Class clazz) {
return EmployeeVO.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors)
{
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "error.firstName", "First name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "error.lastName", "Last name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "error.email", "Email is required.");
}
}
在此验证器中,您可以使用ValidationUtils
类中的rejectIfEmptyOrWhitespace()
和rejectIfEmpty()
之类的实用方法来验证所需的表单字段。 如果这些表单字段中的任何一个为空,这些方法将创建一个字段错误并将其绑定到该字段。 这些方法的第二个参数是属性名称,而第三个和第四个是错误代码和默认错误消息。
很多时候,验证错误并非特定于字段。 例如,结束日期应大于开始日期。 在这种情况下,可以使用reject()
方法创建要绑定到EmployeeVO
对象而不是字段的对象错误。
例如errors.reject("invalid.dateDiff", "结束日期应大于开始日期。");
要将此自定义验证器激活为 spring 托管 bean,您需要执行以下操作之一:
1)在EmployeeValidator
类中添加@Component
注解,并在包含此类声明的包上激活注解扫描。
<context:component-scan base-package="com.howtodoinjava.demo" />
2)或者,您可以直接在上下文文件中注册验证器类 bean。
<bean id="employeeValidator" class="com.howtodoinjava.demo.validator.EmployeeValidator" />
控制器变更
要应用此验证器,您需要对控制器执行以下修改。
1)包括控制器类的验证器引用,并对其进行自动标记,以确保在需要时可用。
@Autowired
EmployeeValidator validator;
2)下一个更改是在控制器的 post 方法中,该方法在用户提交表单时调用。
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status)
{
//Validation code
validator.validate(employeeVO, result);
//Check validation errors
if (result.hasErrors()) {
return "addEmployee";
}
//Store the employee information in database
//manager.createNewRecord(employeeVO);
//Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
验证方法返回后,结果参数(即BindingResult
)将包含验证过程的结果。 您可以使用result.hasErrors()
方法调用检查输入是否有错误。 如果检测到任何错误,您可以再次渲染表单视图,以使用户更正其输入。
如果未发现错误,则result.hasErrors()
将返回false
,然后您可以简单地处理输入并将用户重定向到下一个视图。
本示例中使用的完整源代码敌人控制器如下:
package com.howtodoinjava.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import com.howtodoinjava.demo.model.EmployeeVO;
import com.howtodoinjava.demo.service.EmployeeManager;
import com.howtodoinjava.demo.validator.EmployeeValidator;
@Controller
@RequestMapping("/employee-module/addNew")
@SessionAttributes("employee")
public class EmployeeController
{
@Autowired
EmployeeManager manager;
@Autowired
EmployeeValidator validator;
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model)
{
EmployeeVO employeeVO = new EmployeeVO();
model.addAttribute("employee", employeeVO);
return "addEmployee";
}
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status)
{
validator.validate(employeeVO, result);
if (result.hasErrors()) {
return "addEmployee";
}
//Store the employee information in database
//manager.createNewRecord(employeeVO);
//Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
@RequestMapping(value = "/success", method = RequestMethod.GET)
public String success(Model model)
{
return "addSuccess";
}
}
下载源码
将您的问题放到我的评论部分。
祝您学习愉快!
Spring Bean 验证 – JSR-303 注解
https://howtodoinjava.com/spring-mvc/spring-bean-validation-example-with-jsr-303-annotations/
JSR-303 bean 验证是一个规范,其目的是通过注解标准化 Java bean 的验证。 JSR-303 标准的目的是在 Java bean 类中直接使用注解。 这允许直接在要验证的代码中指定验证规则,而不是在单独的类中创建验证规则。 到目前为止,我们了解了使用BindingResult.rejectValue()
和自定义验证器实现在 spring mvc 中进行的验证。 在本示例中,我们将学习使用基于 JSR-303 标准的注解来验证 Spring 管理的 bean。
阅读更多:提交表单示例 & 自定义验证程序示例
应用验证后,前端 UI 上的错误消息将如下所示:
Spring MVC JSR 303 验证注解示例
添加 JSR-303 和 Hibernate 验证器依赖关系
要使与 Spring 一起使用 JSR-303 注解,您需要在pom.xml
中添加以下依赖项。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
为了使验证真正起作用,您还需要一个实现,例如 Hibernate 验证器。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
应用 JSR-303 注解
添加 JSR-303 依赖关系后,您需要做的第一件事是用必要的 JSR-303 注解装饰 Java bean。 参见下面的EmployeeVO
类,其字段使用诸如@Size
和@Pattern
之类的注解进行注解。
package com.howtodoinjava.demo.model;
import java.io.Serializable;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class EmployeeVO implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id;
@Size(min = 3, max = 20)
private String firstName;
@Size(min = 3, max = 20)
private String lastName;
@Pattern(regexp=".+@.+\\.[a-z]+")
private String email;
//Setters and Getters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + ", email=" + email + "]";
}
}
阅读更多:所有受支持的 JSR-303 注解的列表
控制器变更
要应用此验证器,您需要对控制器执行以下修改。
1)包括对控制器类的验证器引用,以便您可以跨控制器中的所有方法访问它。
private Validator validator;
2)下一个更改是在控制器的post
方法中,该方法在用户提交表单时调用。
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status) {
Set<ConstraintViolation<EmployeeVO>> violations = validator.validate(employeeVO);
for (ConstraintViolation<EmployeeVO> violation : violations)
{
String propertyPath = violation.getPropertyPath().toString();
String message = violation.getMessage();
// Add JSR-303 errors to BindingResult
// This allows Spring to display them in view via a FieldError
result.addError(new FieldError("employee",propertyPath,
"Invalid "+ propertyPath + "(" + message + ")"));
}
if (result.hasErrors()) {
return "addEmployee";
}
// Store the employee information in database
// manager.createNewRecord(employeeVO);
// Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
请注意,与早期的 Spring 特定验证方法不同,validator
字段未分配给任何 bean,而是分配了javax.validation.ValidatorFactory
类型的工厂类。 这就是 JSR-303 验证的工作方式。 分配过程在控制器的构造函数中完成。
在submitForm()
方法中,第一步包括创建javax.validation.ConstraintViolation
类型的Set
,以保存从验证EmployeeVO
对象的实例而检测到的所有错误。 分配给此Set
的值来自执行validator.validate(employeeVO)
的结果,该validator.validate(employeeVO)
用于在作为EmployeeVO
对象实例的employeeVO
字段上运行验证过程。
验证过程完成后,将在违反集上声明一个循环,以提取EmployeeVO
对象中遇到的任何可能的验证错误。 由于违规集包含特定于 JSR-303 的错误,因此有必要提取原始错误消息并将其以 Spring MVC 特定格式放置。 这样就可以将验证错误显示在 Spring 管理的视图中,就像它们是由 Spring 验证程序生成的一样。
测试应用
就这样。 JSR-303 验证配置已完成。 现在测试应用程序。
1)输入网址:http://localhost:8080/springmvcexample/employee-module/addNew
它将显示空白表格。
Spring MVC 表单示例 – 空白表单
2)在不填写任何字段的情况下,提交表格。您将得到特定于每个字段的错误。
Spring MVC JSR 303 验证注解示例
3)填写所有字段,然后按Submit
按钮。 将显示成功页面。
Spring MVC 表单示例 – 成功消息
作为参考,完整的pom.xml
文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>springmvcexample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springmvcexample Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<!-- Tag libs support for view layer -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
<!-- JSR 303 Dependencies -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
</dependencies>
<build>
<finalName>springmvcexample</finalName>
</build>
</project>
而EmployeeController
类如下:
package com.howtodoinjava.demo.controller;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import com.howtodoinjava.demo.model.EmployeeVO;
import com.howtodoinjava.demo.service.EmployeeManager;
@Controller
@RequestMapping("/employee-module/addNew")
@SessionAttributes("employee")
public class EmployeeController {
@Autowired
EmployeeManager manager;
private Validator validator;
public EmployeeController()
{
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
EmployeeVO employeeVO = new EmployeeVO();
model.addAttribute("employee", employeeVO);
return "addEmployee";
}
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status) {
Set<ConstraintViolation<EmployeeVO>> violations = validator.validate(employeeVO);
for (ConstraintViolation<EmployeeVO> violation : violations)
{
String propertyPath = violation.getPropertyPath().toString();
String message = violation.getMessage();
// Add JSR-303 errors to BindingResult
// This allows Spring to display them in view via a FieldError
result.addError(new FieldError("employee",propertyPath,
"Invalid "+ propertyPath + "(" + message + ")"));
}
if (result.hasErrors()) {
return "addEmployee";
}
// Store the employee information in database
// manager.createNewRecord(employeeVO);
// Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
@RequestMapping(value = "/success", method = RequestMethod.GET)
public String success(Model model) {
return "addSuccess";
}
}
将我的询问放在评论部分。
祝您学习愉快!
Spring MVC 填充和验证下拉列表示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-populate-and-validate-dropdown-example/
如果您正在使用 Spring MVC 开发的任何面向客户的 Web 应用程序,那么您可能还需要在应用程序 UI 中的某个位置使用下拉框。 本教程将帮助您显示预填充的下拉列表,然后验证用户在提交表单时是否选择了任何值。
这篇文章是我以前关于 spring mvc 验证(使用 JSR-303 注解 )的继续。 我将修改相同的源代码。
在此示例中,我将显示一个用于向系统中添加新员工的表单。 该表格将有一个下拉列表,列出所有部门。 应用程序用户必须在提交表单之前从下拉列表中选择一个值。
Spring MVC 下拉菜单示例 – 空白表单
下载源码
Table of Contents
Model Classes
Adding PropertyEditorSupport
View layer changes for displaying Dropdown box
Dropdown validation changes
Test The Application
模型类
DepartmentVO.java
package com.howtodoinjava.demo.model;
public class DepartmentVO
{
public DepartmentVO(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
private Integer id;
private String name;
//Setters and Getters
@Override
public String toString() {
return "DepartmentVO [id=" + id + ", name=" + name + "]";
}
}
EmployeeVO.java
此类具有DepartmentVO
的关联属性。
package com.howtodoinjava.demo.model;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotEmpty;
public class EmployeeVO implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id;
@NotEmpty
private String firstName;
private String lastName;
private String email;
@NotNull
private DepartmentVO department;
//Setters and Getters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + ", email=" + email
+ ", department=" + department + "]";
}
}
添加PropertyEditorSupport
我们不会在 UI 中将DepartmentVO
显示为 java 对象,而是当用户提交绑定到Department
字段的属性时,HTTP POST 中只会出现一个字符串值。 我们需要某种机制将字符串值转换回DepartmentVO
实例并注入EmployeeVO
实例。
Spring 为此提供了PropertyEditorSupport
类。
DepartmentEditor.java
package com.howtodoinjava.demo.convertor;
import java.beans.PropertyEditorSupport;
import com.howtodoinjava.demo.model.DepartmentVO;
public class DepartmentEditor extends PropertyEditorSupport
{
//This will be called when user HTTP Post to server a field bound to DepartmentVO
@Override
public void setAsText(String id)
{
DepartmentVO d;
switch(Integer.parseInt(id))
{
case 1: d = new DepartmentVO(1, "Human Resource"); break;
case 2: d = new DepartmentVO(2, "Finance"); break;
case 3: d = new DepartmentVO(3, "Information Technology"); break;
default: d = null;
}
this.setValue(d);
}
}
可以,但是 spring 怎么会知道我们有此类用于转换目的。 为此,我们必须告诉 Spring。 我们可以通过以下方式在控制器类中进行操作。
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(DepartmentVO.class, new DepartmentEditor());
}
现在,每次将表单提交到EmployeeController
且字段绑定到department
的字段时,DepartmentEditor
都会用于将字符串值转换为DepartmentVO
实例。
EmployeeController
的完整代码如下。
EmployeeController.java
package com.howtodoinjava.demo.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import com.howtodoinjava.demo.convertor.DepartmentEditor;
import com.howtodoinjava.demo.model.DepartmentVO;
import com.howtodoinjava.demo.model.EmployeeVO;
import com.howtodoinjava.demo.service.EmployeeManager;
@Controller
@RequestMapping("/employee-module/addNew")
@SessionAttributes("employee")
public class EmployeeController
{
@Autowired
EmployeeManager manager;
private Validator validator;
public EmployeeController()
{
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(DepartmentVO.class, new DepartmentEditor());
}
@ModelAttribute("allDepartments")
public List<DepartmentVO> populateDepartments()
{
ArrayList<DepartmentVO> departments = new ArrayList<DepartmentVO>();
departments.add(new DepartmentVO(-1, "Select Department"));
departments.add(new DepartmentVO(1, "Human Resource"));
departments.add(new DepartmentVO(2, "Finance"));
departments.add(new DepartmentVO(3, "Information Technology"));
return departments;
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
EmployeeVO employeeVO = new EmployeeVO();
model.addAttribute("employee", employeeVO);
return "addEmployee";
}
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status) {
Set<ConstraintViolation<EmployeeVO>> violations = validator.validate(employeeVO);
for (ConstraintViolation<EmployeeVO> violation : violations)
{
String propertyPath = violation.getPropertyPath().toString();
String message = violation.getMessage();
// Add JSR-303 errors to BindingResult
// This allows Spring to display them in view via a FieldError
result.addError(new FieldError("employee", propertyPath, "Invalid "+ propertyPath + "(" + message + ")"));
}
if (result.hasErrors()) {
return "addEmployee";
}
// Store the employee information in database
// manager.createNewRecord(employeeVO);
System.out.println(employeeVO);
// Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
@RequestMapping(value = "/success", method = RequestMethod.GET)
public String success(Model model) {
return "addSuccess";
}
}
要了解有关此控制器中更多代码段的更多信息,请参考本 spring mvc 显示形式教程 。
查看更改以显示下拉框
要显示下拉列表,您必须将部门的集合提供给 jsp 文件。 这是从控制器完成的。 请注意,在此示例中,我对集合进行了硬编码。 在生产类应用程序中,您将需要动态构建此集合。
@ModelAttribute("allDepartments")
public List<DepartmentVO> populateDepartments()
{
ArrayList<DepartmentVO> departments = new ArrayList<DepartmentVO>();
departments.add(new DepartmentVO(-1, "Select Department"));
departments.add(new DepartmentVO(1, "Human Resource"));
departments.add(new DepartmentVO(2, "Finance"));
departments.add(new DepartmentVO(3, "Information Technology"));
return departments;
}
可以在form:select
标记的 JSP 文件内部访问此allDepartments
属性。
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Add Employee Form</title>
<style>
.error
{
color: #ff0000;
font-weight: bold;
}
</style>
</head>
<body>
<h2><spring:message code="lbl.page" text="Add New Employee" /></h2>
<br/>
<form:form method="post" modelAttribute="employee">
<%-- <form:errors path="*" cssClass="error" /> --%>
<table>
<tr>
<td><spring:message code="lbl.firstName" text="First Name" /></td>
<td><form:input path="firstName" /></td>
<td><form:errors path="firstName" cssClass="error" /></td>
</tr>
<tr>
<td><spring:message code="lbl.lastName" text="Last Name" /></td>
<td><form:input path="lastName" /></td>
<td><form:errors path="lastName" cssClass="error" /></td>
</tr>
<tr>
<td><spring:message code="lbl.email" text="Email Id" /></td>
<td><form:input path="email" /></td>
<td><form:errors path="email" cssClass="error" /></td>
</tr>
<!-- DROPDOWN code -->
<tr>
<td><spring:message code="lbl.department" text="Department" /></td>
<td><form:select path="department" items="${allDepartments}" itemValue="id" itemLabel="name" /></td>
<td><form:errors path="department" cssClass="error" /></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Add Employee"/></td>
</tr>
</table>
</form:form>
</body>
</html>
下拉验证
为了验证下拉框,我们使用了以下内容。
1)我们在EmployeeVO
内的部门字段中使用了@NotNull
注解。 如果字段为null
,使用 JSR-303 的 spring 验证将自动引发错误。
@NotNull
private DepartmentVO department;
2)由于具有@NotNull
注解,因此我们要做的就是在department
字段中为所有意外值设置null
。 这是在DepartmentEditor
内部完成的,因为在进入控制器代码之前,会调用DepartmentEditor.setAsText()
来设置部门的正确值。
public void setAsText(String id)
{
DepartmentVO d;
switch(Integer.parseInt(id))
{
case 1: d = new DepartmentVO(1, "Human Resource"); break;
case 2: d = new DepartmentVO(2, "Finance"); break;
case 3: d = new DepartmentVO(3, "Information Technology"); break;
default: d = null;
}
this.setValue(d);
}
在上面的代码中,仅当下拉选择值是 1,2 或 3 时; 那么只会设置部门的有效实例。 否则,部门将设置为null
。 此null
将引发错误。
测试下拉菜单
1)部署应用程序并输入 URL:http://localhost:8080/springmvcexample/employee-module/addNew
Spring MVC 下拉菜单示例 – 空白表达
2)填写名字并提交表格。 您将验证消息。
Spring MVC 下拉菜单示例 – 下拉菜单验证
3)从列表中选择一个部门并提交表格。 您将成功页面。
Spring MVC 表单示例 – 成功消息
下载源码
将我的问题放在评论框中。
祝您学习愉快!
Spring MVC 示例 – 显示,验证和提交表单
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-display-validate-and-submit-form-example/
在任何 spring web mvc 应用程序中,我们经常必须处理表单。 应用程序首先显示一个表单,然后用户填写该表单并将其提交给服务器。 在服务器上,应用程序需要捕获表单输入并处理输入(例如,存储在数据库中)并返回成功视图。 在此 spring mvc 示例中,我们将学习显示表单,然后学习处理提交的表单字段。
在此示例中,我们将创建具有添加员工功能的员工管理模块。 它具有以下功能:
- 在初始页面加载时显示空白表格
- 如果提交的表单具有空字段,则显示错误消息
- 成功提交表单后,重定向到另一个屏幕,显示成功消息
Spring MVC Form Example – 空白表单
下载源码
目录
1. 创建模型数据
2. 创建表单视图
3. 创建表单控制器
4. 表单验证
5. 演示
让我们开始一个接一个地添加应用程序组件,然后记下重要的事情。
1. Spring MVC 模型数据
对于此示例应用程序,EmployeeVO
类用作模型。 它将保存数据,视图将使用这些数据来呈现并发布回控制器。
EmployeeVO.java
package com.howtodoinjava.demo.model;
import java.io.Serializable;
public class EmployeeVO implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id;
private String firstName;
private String lastName;
private String email;
//Getters and Setters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + ", email=" + email + "]";
}
}
2. Spring MVC 表单视图
该应用程序使用两个视图,即一个用于显示表单,另一个用于显示成功消息。
2.1. 输入表单视图
addEmployee.jsp
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Add Employee Form</title>
</head>
<body>
<h2><spring:message code="lbl.page" text="Add New Employee" /></h2>
<br/>
<form:form method="post" modelAttribute="employee">
<table>
<tr>
<td><spring:message code="lbl.firstName" text="First Name" /></td>
<td><form:input path="firstName" /></td>
</tr>
<tr>
<td><spring:message code="lbl.lastName" text="Last Name" /></td>
<td><form:input path="lastName" /></td>
</tr>
<tr>
<td><spring:message code="lbl.email" text="Email Id" /></td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Add Employee"/></td>
</tr>
</table>
</form:form>
</body>
</html>
要点:
- Spring
<form:form>
标签声明了两个属性。 用于指示表单的method="post"
属性在提交时执行 HTTP POST 请求。 并且用于表示表单数据的modelAttribute="employee
属性被绑定到名为员工的模型。 - 表单的各种
<form:input>
标签使用属性路径来指示它们绑定到的表单字段。 它们向用户显示该字段的原始值,该值要么是绑定的属性值,要么是由于绑定错误而被拒绝的值。 它们必须在<form:form>
标签内使用,该标签定义了一种通过其名称绑定到modelAttribute
的格式。 - 最后,您可以找到标准的 HTML 标记
<input type="submit" />
,该标记会生成一个“提交”按钮,并触发向服务器发送数据,然后是关闭表单的标记。
请注意,<spring:message>
标签用于显示类路径中存在的消息资源文件的字段标签。 在我们的例子中,消息资源的内容如下:
messages.properties
lbl.page=Add New Employee
lbl.firstName=First Name
lbl.lastName=Last Name
lbl.email=Email Id
2.2. 成功页面视图
addSuccess.jsp
<html>
<head>
<title>Add Employee Success</title>
</head>
<body>
Employee has been added successfully.
</body>
</html>
该文件非常简单,仅显示成功消息。
3. Spring MVC 表单控制器
一个非常简单的 spring mvc 控制器,用于处理表单提交。
EmployeeController.java
@Controller
@RequestMapping("/employee-module/addNew")
@SessionAttributes("employee")
public class EmployeeController
{
@Autowired
EmployeeManager manager;
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model)
{
EmployeeVO employeeVO = new EmployeeVO();
model.addAttribute("employee", employeeVO);
return "addEmployee";
}
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status)
{
//Store the employee information in database
//manager.createNewRecord(employeeVO);
//Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
@RequestMapping(value = "/success", method = RequestMethod.GET)
public String success(Model model)
{
return "addSuccess";
}
}
要点:
- 控制器首先使用标准
@Controller
注解以及@RequestMapping
注解,该注解允许通过 URLhttp://localhost:8080/springmvcexample/employee-module/addNew
访问控制器 - 在浏览器中输入该 URL 时,它将向您的 Web 应用程序发送 HTTP GET 请求。 这进而触发
setupForm
方法的执行,该方法根据其@RequestMapping
注解被指定为参加这种类型的请求。 - 由于表单可能包含错误,因此丢失用户在以后每次提交时提供的任何有效数据可能会带来不便。 为了解决此问题,
@SessionAttributes
用于将员工字段保存到用户的会话中,以便将来对员工字段的任何引用实际上都是在相同的引用上进行的,无论表单是提交两次还是多次。 setupForm
方法将Model
对象定义为输入参数,用于将模型数据发送到视图(即表单)。 在处理器方法内部,创建了一个空的EmployeeVO
对象,并将其作为属性添加到控制器的模型对象中。 然后,控制器将执行流程返回到addEmployee
视图,在这种情况下,该视图解析为我们在上面看到的addEmployee.jsp
。- 填写表单字段后,提交表单会触发 HTTP POST 请求,该请求又会调用
submitForm
方法。@ModelAttribute("employee") EmployeeVO employeeVO
用于引用员工对象。 包含用户新提交的数据的BindingResult
对象。 如果需要访问用户的会话,则使用SessionStatus
对象。 - 在将用户重定向到成功页面之前,我们应该清除会话数据,因为现在它已无用。 这是通过在
SessionStatu
的对象上调用setComplete()
方法来完成的。 - 在数据库中创建员工后,
submitForm
方法返回名为redirect:addNew/success
的视图。 视图名称中的forward:
前缀用于避免称为重复表单提交的问题。
当您在表单成功视图中刷新网页时,刚刚提交的表单将再次重新提交。 为避免此问题,您可以应用 post/redirect/get 设计模式,该模式建议在成功处理表单提交后重定向到另一个 URL,而不是直接返回 HTML 页面。
4. Spring MVC 表单验证
到现在为止,我们的示例应用程序能够显示表单,并接收带有填充值的提交表单。 在现实生活中的应用中,用户在填写表格时会犯很多错误。 验证应该始终在客户端进行,但是为了保护数据完整性,您还应该在服务器端进行数据验证。
验证可以添加到应用程序中,分为两个步骤,即首先在视图层,然后在控制器代码。
4.1. 修改后的addEmployee.jsp
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Add Employee Form</title>
<style>
.error
{
color: #ff0000;
font-weight: bold;
}
</style>
</head>
<body>
<h2><spring:message code="lbl.page" text="Add New Employee" /></h2>
<br/>
<form:form method="post" modelAttribute="employee">
<%-- <form:errors path="*" cssClass="error" /> --%>
<table>
<tr>
<td><spring:message code="lbl.firstName" text="First Name" /></td>
<td><form:input path="firstName" /></td>
<td><form:errors path="firstName" cssClass="error" /></td>
</tr>
<tr>
<td><spring:message code="lbl.lastName" text="Last Name" /></td>
<td><form:input path="lastName" /></td>
<td><form:errors path="lastName" cssClass="error" /></td>
</tr>
<tr>
<td><spring:message code="lbl.email" text="Email Id" /></td>
<td><form:input path="email" /></td>
<td><form:errors path="email" cssClass="error" /></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Add Employee"/></td>
</tr>
</table>
</form:form>
</body>
</html>
您还需要更新消息资源文件。
messages.properties
lbl.page=Add New Employee
lbl.firstName=First Name
lbl.lastName=Last Name
lbl.email=Email Id
//Error messages
error.firstName=First Name can not be blank
error.lastName=Last Name can not be blank
error.email=Email Id can not be blank
4.2. 修改的SubmitForm()
方法
EmployeeController.java
@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
BindingResult result, SessionStatus status)
{
//Validation code start
boolean error = false;
System.out.println(employeeVO); //Verifying if information is same as input by user
if(employeeVO.getFirstName().isEmpty()){
result.rejectValue("firstName", "error.firstName");
error = true;
}
if(employeeVO.getLastName().isEmpty()){
result.rejectValue("lastName", "error.lastName");
error = true;
}
if(employeeVO.getEmail().isEmpty()){
result.rejectValue("email", "error.email");
error = true;
}
if(error) {
return "addEmployee";
}
//validation code ends
//Store the employee information in database
//manager.createNewRecord(employeeVO);
//Mark Session Complete
status.setComplete();
return "redirect:addNew/success";
}
5. Spring MVC 示例 – 演示
在测试之前,请添加其他基本文件。
5.1. Spring MVC 配置文件
spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
</beans>
5.2. web.xml
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring Web MVC Hello World Application</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5.3. pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>springmvcexample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springmvcexample Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<!-- Tag libs support for view layer -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>springmvcexample</finalName>
</build>
</project>
现在测试应用程序。
1)输入 URL:http://localhost:8080/springmvcexample/employee-module/addNew
:它将显示空白表格。
Spring MVC 表单示例 – 空白表单
2)填写名字字段,然后单击“添加员工”按钮。 这将列出不能将姓氏和电子邮件字段提交为空白的验证消息。
Spring MVC 表单示例 – 验证消息
3)现在正确填写所有三个值,并提交表格。 现在您将能够看到成功消息。
Spring MVC 表单示例 – 成功消息
以上就是这个基本但重要的 spring mvc crud 示例,它涉及在 Spring MVC 中提交表单。 让我继续发布有关您的疑问和建议的信息。
下载源码
学习愉快!
Spring MessageSourceAware
Java Bean 示例
原文: https://howtodoinjava.com/spring-mvc/spring-messagesourceaware-java-bean-example/
如果要将不同语言环境的 i18n 资源包访问到 Java 源代码中,则该 Java 类必须实现MessageSourceAware
接口。 在实现MessageSourceAware
接口之后,spring 上下文将通过类需要实现的setMessageSource(MessageSource messageSource)
setter 方法自动将MessageSource
引用注入到类中。
如何在 Spring Bean 中访问MessageSource
如前所述,使您的 bean 类MessageSourceAware
成为给定的方式。
package com.howtodoinjava.demo.controller;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
@Controller
public class EmployeeController implements MessageSourceAware
{
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
public void readLocaleSpecificMessage()
{
String englishMessage = messageSource.getMessage("first.name", null, Locale.US);
System.out.println("First name label in English : " + englishMessage);
String chineseMessage = messageSource.getMessage("first.name", null, Locale.SIMPLIFIED_CHINESE);
System.out.println("First name label in Chinese : " + chineseMessage);
}
}
现在,Web 应用程序的“资源”文件夹中有两个属性文件。 (文件应在运行时位于类路径中)。
messages_zh_CN.properties
和messages_zh_CN.properties
#messages_en_US.properties
first.name=FirstName in English
#messages_zh_CN.properties
first.name=FirstName in Chinese
现在测试我们是否能够加载特定于语言环境的属性。
package springmvcexample;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.howtodoinjava.demo.controller.EmployeeController;
public class TestSpringContext
{
@SuppressWarnings("resource")
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext( new String[] { "/spring-servlet.xml" });
EmployeeController controller = (EmployeeController) context.getBean(EmployeeController.class);
controller.readLocaleSpecificMessage();
}
}
Output:
First name label in English : FirstName in English
First name label in Chinese : FirstName in Chinese
显然,我们能够在 Java Bean 中以特定于语言环境的方式访问资源。
祝您学习愉快!
Spring MVC XmlViewResolver
配置示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-xmlviewresolver-configuration-example/
在基于 Spring MVC 的应用程序中,请求处理的最后一步是返回逻辑视图名称。 这里DispatcherServlet
必须将控制委派给视图模板,以便呈现信息。 该视图模板根据返回的逻辑视图名称决定应呈现哪个视图。 这些视图模板是在 Web 应用程序上下文中声明的一个或多个视图解析器 bean。 这些 bean 必须实现DispatcherServlet
的ViewResolver
接口才能自动检测到它们。 Spring MVC 附带了几个ViewResolver
实现。 在此示例中,我们将查看这样的视图解析器模板,即XmlViewResolver
。
与InternalResourceViewResolver
相反,其中每个逻辑视图名称都直接映射到视图的物理位置,在XmlViewResolver
的情况下,视图被声明为 Spring bean。 您可以在与 Web 应用程序上下文相同的配置文件中声明视图 Bean,但最好将它们隔离在单独的配置文件中。
默认情况下, XmlViewResolver
从/WEB-INF/views.xml
加载视图 bean,但是可以通过位置属性覆盖此位置。
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/admin-views.xml</value>
</property>
</bean>
在admin-views.xml
配置文件中,您可以通过设置类名称和属性来将每个视图声明为普通的 Spring bean。 例如
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="home" class="org.springframework.web.servlet.view.JstlView">
<property name="url" value="/WEB-INF/jsp/home.jsp" />
</bean>
<bean id="admin/home" class="org.springframework.web.servlet.view.JstlView">
<property name="url" value="/WEB-INF/jsp/admin/home.jsp" />
</bean>
<bean id="logOffRedirect" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="home" />
</bean>
</beans>
上面配置中的前两个 bean 非常明显。 逻辑视图名称home
映射到/WEB-INF/jsp/home.jsp
,而admin / home
映射到/WEB-INF/jsp/admin/home.jsp
。
第三个 bean 不映射任何物理视图文件,而是将请求重定向到 URL home
,该 URL 实际上由 URL/home
的控制器处理。 无论控制器将返回什么逻辑名称,都将在 bean 映射中查找该视图,然后获取实际的视图文件。
如果有什么需要更多的解释的话,请问我。
祝您学习愉快!
Spring MVC 国际化(i18n)和本地化(i10n)示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-internationalization-i18n-and-localization-i10n-example/
国际化是设计软件应用程序的过程,这样它可以潜在地适应各种语言和地区而无需进行工程更改。 本地化是通过添加特定于语言环境的组件并翻原文本来使国际化软件适应特定区域或语言的过程(Wiki)。 Spring 框架随LocaleResolver
一起提供,以支持国际化和本地化。 本教程将帮助您学习如何在基于 Spring MVC 的 Web 应用程序中添加国际化支持。
目录
1)添加特定于语言环境的消息资源
2)在 Spring 上下文中添加 LocaleResolver 配置
3)更改 JSP 以显示特定于语言环境的消息
4)项目结构
5)测试应用程序
6)其他项目文件
让我们开始分析为在 Spring Web 应用程序中添加 i18n 支持而需要进行的更改。
1)添加特定于语言环境的消息资源
如果要支持多个语言环境,那么第一步显然就是让每个特定于语言环境的属性文件都具有该语言环境特定语言的文本。 在我们的示例中,我支持两种语言环境。 第一个是使用英语的美国语言,第二个是中文。
messages.properties
lbl.Id=Employee Id
lbl.firstName=First Name
lbl.lastName=First Name
lbl.page=All Employees in System
messages_zh_CN.properties
lbl.Id=\u5458\u5DE5ID
lbl.firstName=\u540D\u5B57
lbl.lastName=\u59D3
lbl.page=\u7CFB\u7EDF\u4E2D\u7684\u6240\u6709\u5458\u5DE5
请注意这些属性文件的命名约定。 特定于语言环境的文件的名称后附加了语言环境短代码。
2)在 Spring 上下文中添加LocaleResolver
配置
为了使 Spring MVC 应用程序支持国际化,您将需要注册两个 bean。
1. SessionLocaleResolver
SessionLocaleResolver
通过检查用户会话中的预定义属性来解析语言环境。 如果会话属性不存在,则此语言环境解析器将从accept-language
HTTP 标头中确定默认语言环境。
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
2. LocaleChangeInterceptor
LocaleChangeInterceptor
监听器检测当前 HTTP 请求中是否存在特殊参数。 可以使用此拦截器的paramName
属性自定义参数名称。 如果当前请求中存在此类参数,则此拦截器会根据参数值更改用户的语言环境。
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
该应用程序的完整应用程序上下文文件如下所示:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
</beans>
3)更改 JSP 以显示特定于语言环境的消息
下一步是进行视图更改,以支持显示特定于区域设置的文本消息。 可以通过以下方式使用 Spring TLD 来完成此操作。
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Spring MVC Hello World</title>
</head>
<body>
<h2><spring:message code="lbl.page" text="All Employees in System" /></h2>
<table border="1">
<tr>
<th><spring:message code="lbl.Id" text="Employee Id" /></th>
<th><spring:message code="lbl.firstName" text="First Name" /></th>
<th><spring:message code="lbl.lastName" text="Last Name" /></th>
</tr>
<c:forEach items="${employees}" var="employee">
<tr>
<td>${employee.id}</td>
<td>${employee.firstName}</td>
<td>${employee.lastName}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
4)项目结构
此应用程序的完整结构是这样的:
Spring i18n 项目结构和文件
5)测试应用程序
点击网址:http://localhost:8080/springmvcexample/employee-module/getAllEmployees
如您所见,所有标签均以英语显示。
Spring i18n – En 语言环境
点击网址:http://localhost:8080/springmvcexample/employee-module/getAllEmployees?lang=zh_CN
现在,语言环境已更改为中文,并且所有标签都以中文显示。
Spring i18n – CN 语言环境
6)其他项目文件
让我们列出该应用程序涉及的其他文件。
web.xml
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring Web MVC Hello World Application</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
EmployeeController.java
package com.howtodoinjava.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.howtodoinjava.demo.service.EmployeeManager;
@Controller
@RequestMapping("/employee-module")
public class EmployeeController
{
@Autowired
EmployeeManager manager;
@RequestMapping(value="/getAllEmployees", method = RequestMethod.GET)
public String welcome(Model model)
{
model.addAttribute("employees",manager.getAllEmployees());
return "employeesListDisplay";
}
}
EmployeeDAO.java
package com.howtodoinjava.demo.dao;
import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;
public interface EmployeeDAO
{
public List<EmployeeVO> getAllEmployees();
}
EmployeeDAOImpl.java
package com.howtodoinjava.demo.dao;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.demo.model.EmployeeVO;
@Repository
public class EmployeeDAOImpl implements EmployeeDAO {
public List<EmployeeVO> getAllEmployees()
{
List<EmployeeVO> employees = new ArrayList<EmployeeVO>();
EmployeeVO vo1 = new EmployeeVO();
vo1.setId(1);
vo1.setFirstName("Lokesh");
vo1.setLastName("Gupta");
employees.add(vo1);
EmployeeVO vo2 = new EmployeeVO();
vo2.setId(2);
vo2.setFirstName("Raj");
vo2.setLastName("Kishore");
employees.add(vo2);
return employees;
}
}
EmployeeManager.java
import java.util.List;
import com.howtodoinjava.demo.model.EmployeeVO;
public interface EmployeeManager
{
public List<EmployeeVO> getAllEmployees();
}
EmployeeManagerImpl.java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.howtodoinjava.demo.dao.EmployeeDAO;
import com.howtodoinjava.demo.model.EmployeeVO;
@Service
public class EmployeeManagerImpl implements EmployeeManager {
@Autowired
EmployeeDAO dao;
public List<EmployeeVO> getAllEmployees()
{
return dao.getAllEmployees();
}
}
EmployeeVO.java
package com.howtodoinjava.demo.model;
import java.io.Serializable;
public class EmployeeVO implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id;
private String firstName;
private String lastName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + "]";
}
}
请让我知道是否有任何疑问或想法。
祝您学习愉快!
Spring
Spring MVC 拦截器示例 – XML 和 Java 注解配置
原文: https://howtodoinjava.com/spring-core/spring-mvc-interceptor-example/
在本 Spring 教程中,我们将学习在 spring mvc 应用程序中使用 spring mvc 拦截器。 本教程非常简短,仅关注 Spring 拦截器配置和用法。
众所周知,拦截器是特殊的 Web 编程结构,每次发出某些预先配置的 Web 请求时都会被调用。 由于它们的重要性,它们始终是产品生命周期中早期设计的最重要和最基本的功能部分。
拦截器通常在将其移交给控制器处理器方法之前进行一些处理。
1. 创建 Spring MVC 拦截器
1.1. 通过实现HandlerInterceptor
的 Spring MVC 拦截器
HandlerInterceptor
接口定义了 3 种方法。
preHandle(request, response, handler)
– 用于在移交给处理器方法之前拦截请求。 这里的handler
是选择的用于处理请求的处理器对象。postHandler(request, response, handler, modelAndView)
– 用于在处理器完成请求处理后拦截请求,但DispatcherServlet
尚未呈现视图。afterCompletion(request, response, handler, exception)
– 处理器执行完成并且视图也呈现后,将调用它。
HandlerInterceptor 示例
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class DemoInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Inside pre handle");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Inside post handle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception)
throws Exception {
System.out.println("Inside after completion");
}
}
1.2. 通过扩展HandlerInterceptorAdapter
的 Spring 拦截器
HandlerInterceptorAdapter
是HandlerInterceptor
接口的抽象适配器类。
它可以帮助我们仅实现所需的前置或后置处理器方法。 我们没有被迫实现所有方法。 此抽象类中方法的所有默认实现均为“空”。
HandlerInterceptorAdapter 示例
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.ModelAndView;
public class DemoInterceptor extends HandlerInterceptorAdapter
{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Inside pre handle");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Inside post handle");
}
}
2. Spring 拦截器示例
2.1. Spring Web Maven 依赖项
添加 Spring 5 Web 依赖关系以创建 Spring MVC 应用程序。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
2.2. 编写 Spring 拦截器
package com.howtodoinjava.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class DemoInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Inside pre handle");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Inside post handle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception)
throws Exception {
System.out.println("Inside after completion");
}
}
2.3. 在 bean 配置中配置 spring 拦截器
2.3.1. Spring 拦截器 XML 配置
XML 配置有助于添加将在其上调用拦截器的路径模式。 另外,我们可以将拦截器配置为对所有 Web 请求都调用。
<!-- Configures Interceptors -->
<mvc:interceptors>
<!-- This XML will intercept all URIs -->
<bean class="com.howtodoinjava.interceptor.DemoInterceptor"></bean>
<!-- This XML will apply interceptor to only configured URIs -->
<!--
<mvc:interceptor>
<mvc:mapping path="/users"></mvc:mapping>
<bean class="com.howtodoinjava.interceptor.DemoInterceptor"></bean>
<mvc:interceptor>
-->
</mvc:interceptors>
2.3.2. Spring 拦截器 Java 配置
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
DemoInterceptor demoInterceptor() {
return new DemoInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demoInterceptor());
}
}
2.4. 示例
现在,如果我们启动服务器并访问 URL:http://localhost:8080/firstSpringApplication/users
,我们将在控制台输出中看到在拦截器方法中编写的语句。
下载给定的源代码以使用它,并更详细地了解 Spring MVC 拦截器。
下载源码
学习愉快!
Spring HandlerInterceptor
示例
https://howtodoinjava.com/spring-mvc/spring-intercepting-requests-using-handlerinterceptor-with-example/
如您所知,servlet 过滤器可以在处理该 servlet 之前和之后对它们处理的每个 Web 请求进行预处理和后处理。 以类似的方式,您可以在 Spring Web 应用程序中使用HandlerInterceptor
接口来对由 Spring MVC 处理器处理的 Web 请求进行预处理和后处理。 这些处理器通常用于处理返回/提交的模型属性,并将它们传递给视图/处理器。
HandlerInterceptor
接口
处理器拦截器是在 Spring 的 Web 应用程序上下文中配置的,因此它们可以利用任何容器功能,并引用容器中声明的任何 bean。 可以为特定的 URL 映射注册处理器拦截器,因此它仅拦截映射到某些 URL 的请求。
每个处理器拦截器都必须实现HandlerInterceptor
接口,该接口包含要实现的三种回调方法:preHandle()
,postHandle()
和afterCompletion()
。
preHandle()
:在请求处理器处理请求之前。postHandle()
:请求处理器处理完请求后。 它提供对返回的ModelAndView
对象的访问,因此您可以在其中操作模型属性。afterCompletion()
:在完成所有请求处理之后,即在渲染视图之后。
HandlerInterceptorAdapter
类
HandlerInterceptor
接口的问题在于,无论是否需要,新类都必须实现所有三种方法。 为避免覆盖,可以使用HandlerInterceptorAdapter
类。 此类实现HandlerInterceptor
并提供默认的空白实现。
因此,无论您想在处理器中覆盖什么方法,都可以从HandlerInterceptorAdapter
对其进行扩展。
HandlerInterceptor
接口示例
我在这里重用为 Spring MVC hello world 应用程序 创建的代码。 我正在创建一个CustomRequestHandler
类。 此类方法(即preHandle()
,postHandle()
和afterCompletion()
)将按照上述讨论进行调用。
package com.howtodoinjava.demo.handlers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class CustomRequestHandler extends HandlerInterceptorAdapter
{
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception
{
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
public void postHandle( HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)
throws Exception
{
long startTime = (Long) request.getAttribute("startTime");
request.removeAttribute("startTime");
long endTime = System.currentTimeMillis();
modelAndView.addObject("totalTime", endTime - startTime);
System.out.println("Request Prcessing Time :: " + (endTime - startTime));
}
public void afterCompletion( HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception exceptionIfAny)
throws Exception
{
System.out.println("View Rendered !!");
}
}
现在,创建请求处理器后,必须将其声明为 spring 配置文件,以便 spring 可以在适当的时候将请求传递给它。
处理器拦截器已注册到DefaultAnnotationHandlerMapping
bean,负责将拦截器应用于标有@Controller
注解的任何类。 您可以在拦截器属性中指定多个拦截器,其类型为数组。
<bean id="customRequestHandler" class="com.howtodoinjava.demo.handlers.CustomRequestHandler" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="customRequestHandler" />
</list>
</property>
</bean>
现在,当您再次运行 hello world 应用程序时,它将在输出下方显示。
Request Prcessing Time :: 15
View Rendered !!
仅将HandlerInterceptor
接口应用于某些 URL
好吧,使用上述方法,您的处理器将被应用到应用程序中的所有控制器上,而不管它们映射到什么 URL。 如果您只想将处理器映射到某些 URL,则必须使用<mvc:interceptors>
标签。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/somepath_one/*"/>
<ref bean="customRequestHandler_one" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/somepath_two/*"/>
<ref bean="customRequestHandler_two" />
</mvc:interceptor>
</mvc:interceptors>
这就是这个快速提示。
祝您学习愉快!
Spring MVC 在 ajax 和 jquery 中使用进度条上传多个文件
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-multi-file-upload-with-progress-bar/
此 Spring MVC 示例演示了如何在进度条中使用多文件上传功能,而服务器端代码是用 Spring MVC 编写的。 我已经修改了 spring MVC 多文件上传示例上一篇文章的代码,该示例没有进度栏功能,而是在新的显示页面上显示了上传的图像和数据。
本示例使用纯 JavaScript 代码和 HTML5 控件来构建客户端代码。 如果您想使用任何 JavaScript 库,例如 jQuery 然后请相应地修改代码。
1. 文件上传和进度栏的客户端视图
在下面的productForm.jsp
中给出了 spring MVC 项目中的 JSP 文件-但它仅包含 HTML 代码,因此如果您愿意,可以轻松地放置相同的代码。
该文件具有onUploadProgress()
函数,该函数显示上传文件的进度。 还有其他功能可以显示上载过程和支持活动的完成情况。
productForm.jsp
<!DOCTYPE html>
<html>
<head>
<script>
var totalFileLength, totalUploaded, fileCount, filesUploaded;
//To log everything on console
function debug(s) {
var debug = document.getElementById('debug');
if (debug) {
debug.innerHTML = debug.innerHTML + '<br/>' + s;
}
}
//Will be called when upload is completed
function onUploadComplete(e) {
totalUploaded += document.getElementById('files').files[filesUploaded].size;
filesUploaded++;
debug('complete ' + filesUploaded + " of " + fileCount);
debug('totalUploaded: ' + totalUploaded);
if (filesUploaded < fileCount) {
uploadNext();
} else {
var bar = document.getElementById('bar');
bar.style.width = '100%';
bar.innerHTML = '100% complete';
alert('Finished uploading file(s)');
}
}
//Will be called when user select the files in file control
function onFileSelect(e) {
var files = e.target.files; // FileList object
var output = [];
fileCount = files.length;
totalFileLength = 0;
for (var i = 0; i < fileCount; i++) {
var file = files[i];
output.push(file.name, ' (', file.size, ' bytes, ', file.lastModifiedDate.toLocaleDateString(), ')');
output.push('<br/>');
debug('add ' + file.size);
totalFileLength += file.size;
}
document.getElementById('selectedFiles').innerHTML = output.join('');
debug('totalFileLength:' + totalFileLength);
}
//This will continueously update the progress bar
function onUploadProgress(e) {
if (e.lengthComputable) {
var percentComplete = parseInt((e.loaded + totalUploaded) * 100 / totalFileLength);
var bar = document.getElementById('bar');
bar.style.width = percentComplete + '%';
bar.innerHTML = percentComplete + ' % complete';
} else {
debug('unable to compute');
}
}
//the Ouchhh !! moments will be captured here
function onUploadFailed(e) {
alert("Error uploading file");
}
//Pick the next file in queue and upload it to remote server
function uploadNext() {
var xhr = new XMLHttpRequest();
var fd = new FormData();
var file = document.getElementById('files').files[filesUploaded];
fd.append("multipartFile", file);
xhr.upload.addEventListener("progress", onUploadProgress, false);
xhr.addEventListener("load", onUploadComplete, false);
xhr.addEventListener("error", onUploadFailed, false);
xhr.open("POST", "save-product");
debug('uploading ' + file.name);
xhr.send(fd);
}
//Let's begin the upload process
function startUpload() {
totalUploaded = filesUploaded = 0;
uploadNext();
}
//Event listeners for button clicks
window.onload = function() {
document.getElementById('files').addEventListener('change', onFileSelect, false);
document.getElementById('uploadButton').addEventListener('click', startUpload, false);
}
</script>
</head>
<body>
<div style="width:55%">
<h1>HTML5 Ajax Multi-file Upload With Progress Bar</h1>
<div id='progressBar' style='height: 20px; border: 2px solid green; margin-bottom: 20px'>
<div id='bar' style='height: 100%; background: #33dd33; width: 0%'>
</div>
</div>
<form style="margin-bottom: 20px">
<input type="file" id="files" multiple style="margin-bottom: 20px"/><br/>
<output id="selectedFiles"></output>
<input id="uploadButton" type="button" value="Upload" style="margin-top: 20px"/>
</form>
<div id='debug' style='height: 100px; border: 2px solid #ccc; overflow: auto'></div>
</div>
</body>
</html>
2. 讨论上传进度功能
上面的代码足以说明问题,您在理解上应该不会遇到任何问题。 但是,让我们总结一下要点:
- “上传” 按钮不是提交按钮。 因此,单击它不会提交包含表单。 实际上,该脚本使用
XMLHttpRequest
对象进行上传。 totalFileLength
变量保存要上传的文件的总长度。totalUploaded
是到目前为止已上传的字节数。fileCount
包含要上传的文件数,filesUploaded
指示已上传的文件数。window.onload()
使用onFileSelect
功能映射文件输入元素的更改事件,并使用startUpload
映射按钮的单击事件。- 当用户单击“上传”按钮时,将调用
startUpload
函数,然后依次调用uploadNext
函数。uploadNext
上传所选文件集中的下一个文件。 首先创建一个XMLHttpRequest
对象和一个FormData
对象,接下来要上载的文件将附加到该对象。 - 然后,
uploadNext
函数将XMLHttpRequest
对象的进度事件附加到onUploadProgress
和load
事件,并将error
事件附加到onUploadComplete
和onUploadFailed
。 - 在上载进度期间,会反复调用
onUploadProgress
功能,从而有机会更新进度条。 - 上载完成后,将调用
onUploadComplete
函数。
3. 多个文件上传控制器和模型类
Spring MVC 文件上传控制器和模型类如下所示:
3.1. Spring MVC 多文件上传控制器
DemoProductController.java
package com.howtodoinjava.demo.controller;
import java.io.File;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import com.howtodoinjava.demo.model.UploadedFile;
@Controller
public class DemoProductController
{
@RequestMapping("/save-product")
public void saveFile(HttpServletRequest servletRequest,
@ModelAttribute UploadedFile uploadedFile,
BindingResult bindingResult, Model model) {
MultipartFile multipartFile = uploadedFile.getMultipartFile();
String fileName = multipartFile.getOriginalFilename();
try {
File file = new File(servletRequest.getServletContext().getRealPath("/image"), fileName);
multipartFile.transferTo(file);
} catch (IOException e) {
e.printStackTrace();
}
}
@RequestMapping(value = "/product-input-form")
public String inputProduct(Model model) {
return "productForm";
}
}
3.2. Spring MVC 多文件模型类
UploadedFile.java
package com.howtodoinjava.demo.model;
import org.springframework.web.multipart.MultipartFile;
public class UploadedFile {
private static final long serialVersionUID = 1L;
private MultipartFile multipartFile;
public MultipartFile getMultipartFile() {
return multipartFile;
}
public void setMultipartFile(MultipartFile multipartFile) {
this.multipartFile = multipartFile;
}
}
4. 用于构建示例的其他文件
4.1. web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>Spring Web MVC Hello World Application</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<multipart-config>
<max-file-size>20848820</max-file-size>
<max-request-size>418018841</max-request-size>
<file-size-threshold>1048576</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4.2. spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:resources mapping="/image/**" location="/image/" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>
</beans>
5. Spring MVC 多个文件上传,带有进度条演示
-
点击 URL:
http://localhost:8080/springmvcexample/product-input-form
下面的屏幕将在浏览器上加载。
带有进度条的多文件上传 - 输入表单
-
选择文件,然后单击上载按钮
-
如果一切正常,那么您将获得如下所示的上传进度栏和上传的文件信息。
带有进度条的多文件上传 - 上传成功
请给我发送有关 Spring mvc 多文件上传的问题和问题,并提供进度条示例。
学习愉快!
Spring MVC 多文件上传示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-multi-file-upload-example/
Spring MVC 为任何应用程序中的多文件上传功能提供了开箱即用的支持。 本教程使用CommonsMultipartResolver
,并且需要 apache commons 文件上载和 apache commons io 依赖项。
pom.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
1. Spring MVC MultipartFile
接口
上传到 Spring MVC 应用程序的文件将包装在MultipartFile
对象中。 我们需要做的就是编写一个属性类型为MultipartFile
的域类。 该界面具有获取上传文件的名称和内容的方法,例如 getBytes()
,getInputStream()
,getOriginalFilename()
, getSize()
,isEmpty()
和tranferTo()
。
例如,要将上传的文件保存到文件系统,我们可以使用transferTo
方法:
文件上传到文件系统
File file = new File(...);
multipartFile.transferTo(file);
2. 用于文件上传的域类
您需要创建一个具有必要属性的简单域类,并创建一个用于存储List<MultipartFile>
类型的文件的域类。
为了构建此示例,我编写了此域类。
Product.java
public class Product implements Serializable
{
private static final long serialVersionUID = 74458L;
@NotNull
@Size(min=1, max=10)
private String name;
private String description;
private List<MultipartFile> images;
//getters and setters
}
3. Spring MVC 多文件上传控制器
在控制器类中,我们将在域类中获取上载文件的预填充详细信息。 只需获取详细信息,然后根据应用程序设计将文件存储在文件系统或数据库中即可。
DemoProductController.java
@Controller
public class DemoProductController
{
@RequestMapping("/save-product")
public String uploadResources( HttpServletRequest servletRequest,
@ModelAttribute Product product,
Model model)
{
//Get the uploaded files and store them
List<MultipartFile> files = product.getImages();
List<String> fileNames = new ArrayList<String>();
if (null != files && files.size() > 0)
{
for (MultipartFile multipartFile : files) {
String fileName = multipartFile.getOriginalFilename();
fileNames.add(fileName);
File imageFile = new File(servletRequest.getServletContext().getRealPath("/image"), fileName);
try
{
multipartFile.transferTo(imageFile);
} catch (IOException e)
{
e.printStackTrace();
}
}
}
// Here, you can save the product details in database
model.addAttribute("product", product);
return "viewProductDetail";
}
@RequestMapping(value = "/product-input-form")
public String inputProduct(Model model) {
model.addAttribute("product", new Product());
return "productForm";
}
}
4. Spring MVC 配置更改
为了支持多部分请求,我们需要在配置文件中声明multipartResolver
bean。
beans.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="20848820" />
</bean>
等效的 Java 注解配置为:
@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver()
{
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(20848820);
return multipartResolver;
}
此外,我们可能希望将服务器上的文件存储路径映射为资源。 这将是 spring mvc 文件上传目录。
<mvc:resources mapping="/image/**" location="/image/" />
本示例使用的完整配置文件为:
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:resources mapping="/image/**" location="/image/" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="20848820" />
</bean>
</beans>
5. Spring MVC 视图上传文件
我已经写了两个 JSP 文件。 一个用于显示文件上传表单,用户将在其中填写其他详细信息并选择要上传的文件。 其次,我们将显示带有其他详细信息的上传文件。
productForm.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<title>Add Product Form</title>
</head>
<body>
<div id="global">
<form:form commandName="product" action="save-product" method="post" enctype="multipart/form-data">
<fieldset>
<legend>Add a product</legend>
<p>
<label for="name">Product Name: </label>
<form:input id="name" path="name" cssErrorClass="error" />
<form:errors path="name" cssClass="error" />
</p>
<p>
<label for="description">Description: </label>
<form:input id="description" path="description" />
</p>
<p>
<label for="image">Product Images: </label>
<input type="file" name="images" multiple="multiple"/>
</p>
<p id="buttons">
<input id="reset" type="reset" tabindex="4">
<input id="submit" type="submit" tabindex="5" value="Add Product">
</p>
</fieldset>
</form:form>
</div>
</body>
</html>
viewProductDetail.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Save Product</title>
</head>
<body>
<div id="global">
<h4>The product has been saved.</h4>
<h5>Details:</h5>
Product Name: ${product.name}<br/>
Description: ${product.description}<br/>
<p>Following files are uploaded successfully.</p>
<ol>
<c:forEach items="${product.images}" var="image">
<li>${image.originalFilename}
<img width="100" src="<c:url value="/image/"/>${image.originalFilename}"/>
</li>
</c:forEach>
</ol>
</div>
</body>
</html>
Spring MVC 多文件上传示例
当我们用http://localhost:8080/springmvcexample/product-input-form
进入浏览器时,我们得到以下屏幕:
Spring MVC 文件上传表单
我们填写详细信息并提交表格,我们将在其他页面中获取提交的详细信息和所有上传的文件:
文件成功上传
在与 spring mvc 多文件上传示例相关的评论部分中,向我提出您的问题和建议。
学习愉快!
Spring MVC 下载文件控制器示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-download-file-controller-example/
在 Spring MVC 应用程序中,要将文件之类的资源下载到浏览器,您需要在控制器中执行以下操作。
- 将
void
返回类型用于您的请求处理方法,然后将HttpServletResponse
添加为该方法的参数。 - 将响应的内容类型设置为文件的内容类型。 如果您不知道内容类型是什么,或者希望浏览器始终显示
Save As
对话框,请将其设置为APPLICATION/OCTET-STREAM
(不区分大小写)。 - 添加一个名为
Content-Disposition
的 HTTP 响应标头,并为其赋予值attachment; filename=fileName
,其中fileName
是应出现在“文件下载”对话框中的默认文件名。
1. Sping MVC 文件下载控制器
让我们看一下文件下载控制器的示例实现。
@Controller
@RequestMapping("/download")
public class FileDownloadController
{
@RequestMapping("/pdf/{fileName:.+}")
public void downloadPDFResource( HttpServletRequest request,
HttpServletResponse response,
@PathVariable("fileName") String fileName)
{
//If user is not authorized - he should be thrown out from here itself
//Authorized user will download the file
String dataDirectory = request.getServletContext().getRealPath("/WEB-INF/downloads/pdf/");
Path file = Paths.get(dataDirectory, fileName);
if (Files.exists(file))
{
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment; filename="+fileName);
try
{
Files.copy(file, response.getOutputStream());
response.getOutputStream().flush();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
如果您只想为授权用户启用下载功能,请先在该方法中检查用户的登录状态,然后再允许下载,否则将其重定向到登录屏幕。
现在,如果您点击应用程序 URL:http://localhost:8080/springmvcexample/download/pdf/sample.pdf
,您将能够在浏览器中获得另存为对话框,如下所示:
文件下载窗口
该文件位于文件夹/WEB-INF/downloads/pdf
中。 您可以自由更改路径 – 确保同时更改控制器代码。
文件下载项目结构
2. 防止文件下载的交叉引用
很多时候,其他网站可能会作为直接链接交叉引用您网站中的文件。 您可能不想允许它。 要禁止来自其他域的所有下载请求,您可以检查referer
标头中是否包含您的域名。
仅当referer
标头不为null
时,我们修改后的FileDownloadController
才会将文件发送到浏览器。 这将防止通过在浏览器中键入图像的 URL 或来自其他域的请求来直接下载图像。
@Controller
@RequestMapping("/download")
public class FileDownloadController
{
@RequestMapping("/pdf/{fileName:.+}")
public void downloadPDFResource( HttpServletRequest request,
HttpServletResponse response,
@PathVariable("fileName") String fileName,
@RequestHeader String referer)
{
//Check the renderer
if(referer != null && !referer.isEmpty()) {
//do nothing
//or send error
}
//If user is not authorized - he should be thrown out from here itself
//Authorized user will download the file
String dataDirectory = request.getServletContext().getRealPath("/WEB-INF/downloads/pdf/");
Path file = Paths.get(dataDirectory, fileName);
if (Files.exists(file))
{
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment; filename="+fileName);
try
{
Files.copy(file, response.getOutputStream());
response.getOutputStream().flush();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
现在,如果您尝试直接从浏览器中访问 URL,则会收到此错误:
java.lang.IllegalStateException: Missing header 'referer' of type [java.lang.String]
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.raiseMissingHeaderException(HandlerMethodInvoker.java:797)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveRequestHeader(HandlerMethodInvoker.java:566)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:355)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:172)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:446)
将我的问题放在评论部分。
学习愉快!
Spring MVC 面试问题与答案
原文: https://howtodoinjava.com/interview-questions/spring-mvc-interview-questions-with-answers/
这些 Spring MVC 面试问题和答案均已编写,可帮助您为 面试 做好准备,并快速地对总体概念进行修订。 如果有多余的时间,我强烈建议您更深入地研究每个概念。 您知道的越多,您就会越有信心。
什么是 Spring MVC 框架?
Spring Web MVC 框架提供 MVC 架构 (模型视图控制器)和现成的组件,这些组件可用于开发灵活且松散耦合的 Web 应用程序。 MVC 模式导致分离应用程序的不同切面(输入逻辑,业务逻辑和 UI 逻辑),同时在应用程序的模型,视图和控制器部分之间提供松散的耦合。
与其他 MVC 框架相比,Spring 框架具有许多优势。
- 角色明确分开 – 控制器,验证器,命令对象,表单对象,模型对象,
DispatcherServlet
,处理器映射,视图解析器等。每个角色都可以由专门的对象来实现。 - 框架和应用程序类的功能强大且直接的配置选项作为 JavaBeans。
- 可重用的业务代码 – 无需重复。 您可以将现有业务对象用作命令或表单对象,而不是对其进行镜像,以扩展特定的框架基类。
- 可定制的绑定和验证
- 可自定义的处理器映射和视图分辨率
- 可自定义的区域设置和主题解析
- 在 Spring 2.0 中引入的 JSP 表单标记库(FTL)使在 JSP 页面中编写表单变得更加容易。 等等
什么是DispatcherServlet
和ContextLoaderListener
?
与许多其他 Web MVC 框架一样,Spring 的 Web MVC 框架请求驱动围绕一个处理所有 HTTP 请求和响应的中央Servlet
设计。 Spring 的DispatcherServlet
所做的不只是这些。 它与 Spring IoC 容器完全集成在一起,因此您可以使用 Spring 拥有的所有功能。
收到 HTTP 请求后,DispatcherServlet
会参考HandlerMapping
(配置文件)来调用相应的控制器。控制器接收请求并调用适当的服务方法并设置模型数据,然后将视图名称返回给DispatcherServlet
。
DispatcherServlet
将从ViewResolver
获得帮助,以获取请求的已定义视图。 一旦视图完成,DispatcherServlet
将模型数据传递到视图,该视图最终在浏览器上呈现。
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
默认情况下,DispatcherServlet
使用<servlet_name>-servlet.xml
加载其配置文件。 例如。 对于上面的web.xml
文件,DispatcherServlet
将尝试在类路径中找到spring-servlet.xml
文件。
ContextLoaderListener
读取 spring 配置文件(在web.xml
中具有contextConfigLocation
的值),对其进行解析并加载在该配置文件中定义的 bean。 例如
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Spring MVC 的前端控制器类是什么?
前端控制器定义为“处理 Web 应用程序所有请求的控制器”。DispatcherServlet
(实际上是 Servlet)是 Spring MVC 中的前端控制器,它拦截每个请求,然后将请求分派/转发给适当的控制器。
将 Web 请求发送到 Spring MVC 应用程序时,调度程序 Servlet 首先接收该请求。 然后,它会组织在 Spring 的 Web 应用程序上下文中配置的不同组件(例如,实际的请求处理器控制器和视图解析器)或控制器本身中存在的注解,这些都是处理请求所需的。
如何使用基于 Java 的配置?
要配置基于 Java 的 MVC 应用程序,请首先添加所需的依赖项。
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<!-- Tag libs support for view layer -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
现在,在web.xml
文件中添加DispatcherServlet
条目,以便所有传入请求仅通过DispatcherServlet
发出。
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
现在,在 spring 配置文件中添加以下条目。
<beans>
<!-- Scan all classes in this path for spring specific annotations -->
<context:component-scan base-package="com.howtodoinjava.demo" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- Vierw resolver configuration -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
添加控制器代码。
@Controller
@RequestMapping("/employee-module")
public class EmployeeController
{
@Autowired
EmployeeManager manager;
@RequestMapping(value = "/getAllEmployees", method = RequestMethod.GET)
public String getAllEmployees(Model model)
{
model.addAttribute("employees", manager.getAllEmployees());
return "employeesListDisplay";
}
}
另外,您还应该添加管理器和 Dao 图层类。 最后,添加 JSP 文件以显示视图。
阅读更多: Spring MVC Hello World 示例
我们如何使用 Spring 创建返回 JSON 响应的 Rest Web 服务?
为了在您的 spring 应用程序中添加 JSON 支持,您需要在第一步中添加 Jackson 依赖项。
<!-- Jackson JSON Processor -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
现在,您准备好从 MVC 控制器返回 JSON 响应。 您要做的就是从方法中返回带有 JAXB 注解的对象,并在此返回类型上使用@ResponseBody
注解。
@Controller
public class EmployeeRESTController
{
@RequestMapping(value = "/employees")
public @ResponseBody EmployeeListVO getAllEmployees()
{
EmployeeListVO employees = new EmployeeListVO();
//Add employees
return employees;
}
}
或者,您可以使用@RestController
注解代替@Controller
注解。 这将消除使用@ResponseBody
的需要。
@RestController = @Controller + @ResponseBody
因此,您可以如下编写上述控制器。
@RestController
public class EmployeeRESTController
{
@RequestMapping(value = "/employees")
public EmployeeListVO getAllEmployees()
{
EmployeeListVO employees = new EmployeeListVO();
//Add employees
return employees;
}
}
阅读更多: Spring REST Hello World JSON 示例
我们可以有多个 Spring 配置文件吗?
是。 您可以有多个 spring 上下文文件。 有两种方法可以读取和配置 spring。
-
使用
contextConfigLocation
param-value
指定web.xml
文件中的所有文件。<servlet> <servlet-name>spring</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> WEB-INF/spring-dao-hibernate.xml, WEB-INF/spring-services.xml, WEB-INF/spring-security.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
或者,您可以将它们导入到已经配置的现有配置文件中。
<beans> <import resource="spring-dao-hibernate.xml"/> <import resource="spring-services.xml"/> <import resource="spring-security.xml"/> ... //Other configuration stuff </beans>
<context:annotation-config>
与<context:component-scan>
之间的区别?
- 这两个标签之间的第一个大区别是
<context:annotation-config>
用于在应用程序上下文中激活已注册 bean 中的应用注解。 请注意,bean 是否通过哪种机制注册都没有关系,例如使用<context:component-scan>
或在application-context.xml
文件本身中定义。 - 第二差异是由第一差异本身驱动的。 它将在配置文件中定义的 bean 注册到上下文中,并且还扫描 bean 内部的注解并激活它们。 因此,
<context:component-scan>
的作用与<context:annotation-config>
的作用相同,但它还会扫描软件包并在应用程序上下文中注册 Bean。
<context:annotation-config>
= 扫描和激活“已注册的 bean”中的注解。
<context:component-scan>
= Bean 注册 + 扫描并激活注解
阅读更多:注解配置和组件扫描之间的区别
Component
,@Controller
,@Repository
& @Service
注解之间的区别?
-
@Component
注解将 Java 类标记为 Bean,因此 spring 的组件扫描机制可以将其拾取并将其拉入应用程序上下文。 要使用此注解,请将其应用于类,如下所示:@Component public class EmployeeDAOImpl implements EmployeeDAO { ... }
-
@Repository
注解是@Component
注解的特化,具有相似的用途和功能。 除了将 DAO 导入 DI 容器之外,它还使未经检查的异常(从 DAO 方法抛出)有资格转换为 SpringDataAccessException
。 -
@Service
注解也是组件注解的特化。 目前,它没有提供@Component
注解以外的任何其他行为,但是最好在服务层类中的@Component
上使用@Service
,因为它可以更好地指定意图。 -
@Controller
注解将一个类标记为 Spring Web MVC 控制器。 它也是@Component
专长,因此标有它的 bean 将自动导入 DI 容器中。 将@Controller
注解添加到类时,可以使用另一个注解,即@RequestMapping
; 将 URL 映射到类的实例方法。
阅读更多:
@Component
,@Repository
,@Service
和@Controller
注解?
ViewResolver
类是什么?
ViewResolver
是要由可以通过名称解析视图的对象实现的接口。 有很多方法可以用来解析视图名称。 这些接口的各种内置实现都支持这些方式。 最常用的实现是InternalResourceViewResolver
类。 它定义prefix
和suffix
属性来解析视图组件。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
因此,使用上述视图解析器配置,如果控制器方法返回"login"
字符串,则将搜索并呈现“ /WEB-INF/views/login.jsp
”文件。
什么是MultipartResolver
?何时使用?
Spring 附带MultipartResolver
来处理 Web 应用程序中的文件上传。 Spring 包含两个具体的实现:
- 用于 Jakarta 常用文件上传的
CommonsMultipartResolver
- 用于 Servlet 3.0 Part API 的
StandardServletMultipartResolver
要定义实现,请在DispatcherServlet
的应用程序上下文中创建一个 ID 为multipartResolver
的 bean。 这样的解析器将应用于该DispatcherServlet
处理的所有请求。
如果DispatcherServlet
检测到多部分请求,它将通过配置的MultipartResolver
解析该请求,并传递已包装的HttpServletRequest
。 然后,控制器可以将给定的请求投射到MultipartHttpServletRequest
接口,该接口允许访问任何MultipartFiles
。
如何在 Spring MVC 应用程序中上传文件?
假设我们要使用CommonsMultipartResolver
,它使用 Apache Commons 上传库来处理表单中的文件上传。 因此,您将需要添加commons-fileupload.jar
和commons-io.jar
依赖项。
<!-- Apache Commons Upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- Apache Commons Upload -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
需要在应用程序上下文文件中进行以下声明以启用MultipartResolver
(以及在应用程序中包括必要的 jar 文件):
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
现在创建模型类FileUploadForm
,它将保存从 HTML 表单提交的多部分数据。
import org.springframework.web.multipart.MultipartFile;
public class FileUploadForm
{
private MultipartFile file;
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
}
}
现在创建FileUploadController
类,它将实际处理上传逻辑。
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import com.howtodoinjava.form.FileUploadForm;
@Controller
public class FileUploadController
{
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public String save(@ModelAttribute("uploadForm") FileUploadForm uploadForm, Model map) {
MultipartFile multipartFile = uploadForm.getFile();
String fileName = "default.txt";
if (multipartFile != null) {
fileName = multipartFile.getOriginalFilename();
}
//read and store the file as you like
map.addAttribute("files", fileName);
return "file_upload_success";
}
}
上传的 JSP 文件如下所示:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<h2>Spring MVC file upload example</h2>
<form:form method="post" action="save.html" modelAttribute="uploadForm" enctype="multipart/form-data">
Please select a file to upload : <input type="file" name="file" />
<input type="submit" value="upload" />
<span><form:errors path="file" cssClass="error" /></span>
</form:form>
</body>
</html>
Spring MVC 如何提供验证支持?
Spring 主要通过两种方式支持验证。
- 使用 JSR-303 注解和任何参考实现,例如 Hibernate 验证器
- 使用
org.springframework.validation.Validator
接口的自定义实现
在下一个问题中,您将看到一个如何在 Spring MVC 应用程序中使用验证支持的示例。
如何在 Spring Web MVC 框架中验证表单数据?
Spring MVC 通过实现Validator
接口的验证器对象来支持验证。 您需要创建一个类并实现Validator
接口。 在此定制验证器类中,您可以使用ValidationUtils
类中的实用程序方法,例如rejectIfEmptyOrWhitespace(
和rejectIfEmpty()
来验证所需的表单字段。
@Component
public class EmployeeValidator implements Validator
{
public boolean supports(Class clazz) {
return EmployeeVO.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors)
{
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "error.firstName", "First name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "error.lastName", "Last name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "error.email", "Email is required.");
}
}
如果任何表单字段为空,则这些方法将创建一个字段错误并将其绑定到该字段。 这些方法的第二个参数是属性名称,而第三个和第四个是错误代码和默认错误消息。
要将此自定义验证器激活为 spring 托管 bean,您需要执行以下操作之一:
-
将
@Component
注解添加到EmployeeValidator
类,并在包含此类声明的包上激活注解扫描。<context:component-scan base-package="com.howtodoinjava.demo" />
-
或者,您可以直接在上下文文件中注册验证器类 Bean。
<bean id="employeeValidator" class="com.howtodoinjava.demo.validator.EmployeeValidator" />
阅读更多:Spring MVC 自定义验证器和 JSR-303 注解示例
什么是 Spring MVC 拦截器以及如何使用?
如您所知,Servlet 过滤器可以在它们处理的每个 Web 请求之前和之后对它进行预处理和后处理。 以类似的方式,您可以在 spring mvc 应用程序中使用HandlerInterceptor
接口对由 Spring MVC 控制器处理的 Web 请求进行预处理和后处理。 这些处理器通常用于操作返回/提交的模型属性,并将它们传递给视图/控制器。
可以为特定的 URL 映射注册处理器拦截器,因此它仅拦截映射到某些 URL 的请求。 每个处理器拦截器都必须实现HandlerInterceptor
接口,该接口包含三种回调方法供您实现:preHandle()
,postHandle()
和afterCompletion()
。
HandlerInterceptor
接口的问题在于,无论是否需要,新类都必须实现所有三种方法。 为避免覆盖,可以使用HandlerInterceptorAdapter
类。 此类实现HandlerInterceptor
并提供默认的空白实现。
阅读更多: Spring MVC 拦截器示例
如何在 Spring MVC 框架中处理异常?
在 Spring MVC 应用程序中,可以在 Web 应用程序上下文中注册一个或多个异常解析器 bean,以解决未捕获的异常。 这些 Bean 必须为DispatcherServlet
实现HandlerExceptionResolver
接口才能自动检测它们。 Spring MVC 附带了一个简单的异常解析器,您可以将每种类别的异常映射到一个视图,即SimpleMappingExceptionResolver
以一种可配置的方式将每种类别的异常映射到一个视图。
假设我们有一个异常类,即AuthException
。 而且,我们希望每次将此异常从任何地方抛出到应用程序中时,我们都希望显示一个预定的视图页面/WEB-INF/views/error/authExceptionView.jsp
。
这样配置就可以了。
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.howtodoinjava.demo.exception.AuthException">
error/authExceptionView
</prop>
</props>
</property>
<property name="defaultErrorView" value="error/genericView"/>
</bean>
可以将defaultErrorView
属性配置为显示所有未在exceptionMappings
列表中配置的其他异常的通用消息 。
阅读更多: Spring MVC
SimpleMappingExceptionResolver
示例
如何在 Spring MVC 应用程序中实现本地化?
Spring 框架随LocaleResolver
一起提供,以支持国际化,从而也支持本地化。 为了使 Spring MVC 应用程序支持国际化,您将需要注册两个 bean。
-
SessionLocaleResolver
:它通过检查用户会话中的预定义属性来解析语言环境。 如果会话属性不存在,则此语言环境解析器从接受语言 HTTP 标头中确定默认语言环境。<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="en" /> </bean>
-
LocaleChangeInterceptor
:此拦截器检测当前 HTTP 请求中是否存在特殊参数。 可以使用此拦截器的paramName
属性自定义参数名称。 如果当前请求中存在此类参数,则此拦截器会根据参数值更改用户的语言环境。<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="lang" /> </bean> <!-- Enable the interceptor --> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="localeChangeInterceptor" /> </list> </property> </bean>
下一步是让每个特定于语言环境的属性文件都具有该特定于语言环境的语言的文本,例如 messages.properties
和messages_zh_CN.properties
等
阅读更多: Spring MVC 本地化(i10n)示例
如何在 Spring Bean 中获取ServletContext
和ServletConfig
对象?
只需实现ServletContextAware
和ServletConfigAware
接口并覆盖以下方法。
@Controller
@RequestMapping(value = "/magic")
public class SimpleController implements ServletContextAware, ServletConfigAware {
private ServletContext context;
private ServletConfig config;
@Override
public void setServletConfig(final ServletConfig servletConfig) {
this.config = servletConfig;
}
@Override
public void setServletContext(final ServletContext servletContext) {
this.context = servletContext;
}
//other code
}
如何在 Spring Web 应用中使用 Tomcat JNDI 数据源?
对于使用已配置 JNDI DataSource
的 servlet 容器,我们需要在 spring bean 配置文件中对其进行配置,然后将其作为依赖项注入 spring bean。 然后,我们可以将其与JdbcTemplate
一起使用来执行数据库操作。
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/MySQLDB"/>
</bean>
您如何将 Spring MVC 框架与三层架构联系起来?
3 层是体系结构样式,MVC 是设计模式。
在较大的应用程序中,MVC 仅是 3 层体系结构的表示层。 模型,视图和控制器仅与表示有关,并利用中间层用来自数据层的数据填充模型。
请分享您可能遇到的其他其他 Spring MVC 面试问题。 因此,我可以在本文中包括那些 Spring MVC 面试问题,以使其他人受益。
学习愉快!
Spring MVC InternalResourceViewResolver
配置示例
原文: https://howtodoinjava.com/spring-mvc/spring-mvc-internalresourceviewresolver-configuration-example/
了解如何在 Spring MVC 应用程序中配置InternalResourceViewResolver
,这有助于解析基于ViewResolver
类实现以及前缀和后缀属性的视图名称。
1. 什么是视图解析器?
在基于 Spring MVC 的应用程序中,请求处理的最后一步是返回逻辑视图名称。 这里DispatcherServlet
必须将控制委派给视图模板,以便呈现信息。 该视图模板根据返回的逻辑视图名称决定应呈现哪个视图。
这些视图模板是在 Web 应用程序上下文中声明的一个或多个视图解析器 bean。 这些 Bean 必须实现DispatcherServlet
的ViewResolver
接口才能自动检测它们。 Spring MVC 带有几个ViewResolver
实现。
在此示例中,我们将查看这种视图解析器模板,即InternalResourceViewResolver
。
2. Spring InternalResourceViewResolver
在大多数 Spring MVC 应用程序中,视图直接映射到模板的名称和位置。 InternalResourceViewResolver
有助于映射逻辑视图名称以直接查看特定预配置目录下的文件。
2.1. InternalResourceViewResolver
配置
要注册InternalResourceViewResolver
,可以在 Web 应用程序上下文中声明此类型的 Bean。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
可以通过以下方式在 Java 配置中编写类似的配置。 这也是 SpringBoot 的有效配置。
@Bean
public ViewResolver configureViewResolver() {
InternalResourceViewResolver viewResolve = new InternalResourceViewResolver();
viewResolve.setPrefix("/WEB-INF/jsp/");
viewResolve.setSuffix(".jsp");
return viewResolve;
}
2.2. 视图解析器示例
完成上述配置后,InternalResourceViewResolver
将以以下方式解析视图名称home
和admin/home
等。
逻辑视图名称 | 实际视图文件 |
---|---|
home |
/WEB-INF/jsp/home.jsp |
admin/home |
/WEB-INF/jsp/admin/home.jsp |
report/main |
/WEB-INF/jsp/report/main.jsp |
2.3. 映射不同的视图类型
默认情况下,如果在类路径中存在 JSTL 库(即jstl.jar
),则InternalResourceViewResolver
将视图名称解析为JstlView
类型的视图对象。 因此,如果您的视图是带有 JSTL 标签的 JSP 模板,则可以省略viewClass
属性。
否则,如果您的视图基于图块,则需要提供匹配的viewClass
,即org.springframework.web.servlet.view.tiles2.TilesView
。
3. 如何从控制器返回视图名称
控制器类应以字符串形式或<string>ModelAndView</string>
类的实例的形式返回视图名称。 例如,在给定的控制器类中,它的返回视图名称为"home"
。
public class HomepageController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception
{
ModelAndView model = new ModelAndView("home");
return model;
}
}
将您的查询添加到评论中 - 与 Spring MVC InternalResourceViewResolver
配置和示例有关。
学习愉快!
Spring MVC ResourceBundleViewResolver
配置示例
https://howtodoinjava.com/spring-mvc/spring-mvc-resourcebundleviewresolver-configuration-example/
在先前的示例中,我们了解了XmlViewResolver
和InternalResourceViewResolver
视图模板。 在这篇文章中,我们将学习ResourceBundleViewResolver
模板。 ResourceBundleViewResolver
从类路径根目录中的资源包中加载视图 bean。 请注意,ResourceBundleViewResolver
还可以利用资源包功能从不同的资源包中为不同的区域加载视图 Bean(这对于其他两个视图解析器是不可能的)。
默认情况下,ResourceBundleViewResolver
从存在的“ views.properties
”文件中将视图名称加载到类路径中,但是可以通过基本名称属性覆盖此位置。
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views" />
</bean>
在views.properties
资源包中,您可以以属性的格式声明视图 bean,并且它们的声明等效于XmlViewResolver
示例中看到的 XML bean 声明。
<!--beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="home" class="org.springframework.web.servlet.view.JstlView">
<property name="url" value="/WEB-INF/jsp/home.jsp" />
</bean>
<bean id="admin/home" class="org.springframework.web.servlet.view.JstlView">
<property name="url" value="/WEB-INF/jsp/admin/home.jsp" />
</bean>
<bean id="logOffRedirect" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="home" />
</bean>
</beans-->
//Equivalent resource bundle properites
home.(class)=org.springframework.web.servlet.view.JstlView
home.url=/WEB-INF/jsp/home.jsp
adminHome.(class)=org.springframework.web.servlet.view.JstlView
adminHome.url=/WEB-INF/jsp/admin/home.jsp
logOffRedirect.(class)=org.springframework.web.servlet.view.RedirectView
logOffRedirect.url=home
通过上述配置,当控制器返回逻辑视图名称
home
时,ResourceBundleViewResolver
将在views.properties
文件中找到以home
开头的密钥,并将相应视图的 URL 返回到DispatcherServlet
,即/WEB-INF/jsp/home.jsp
。
如果有什么需要更多的解释的话,请问我。
祝您学习愉快!
Spring MVC SimpleMappingExceptionResolver
示例
https://howtodoinjava.com/spring-mvc/spring-mvc-simplemappingexceptionresolver-example/
在某些编码错误的应用程序中,当发生未知异常时,应用程序服务器通常会在网页本身中向用户显示恶意异常堆栈跟踪。 在这种情况下,用户与此堆栈跟踪无关,并抱怨您的应用程序对用户不友好。 此外,当您向用户公开内部方法调用层次结构时,它还可能证明存在潜在的安全风险。 尽管可以将 Web 应用程序的web.xml
配置为在发生 HTTP 错误或类异常的情况下显示友好的 JSP 页面,但是 Spring MVC 支持一种更强大的方法来管理类异常的视图。
HandlerExceptionResolver
和SimpleMappingExceptionResolver
在 Spring MVC 应用程序中,可以在 Web 应用程序上下文中注册一个或多个异常解析器 bean,以解析未捕获的异常。 这些 Bean 必须实现DispatcherServlet
的HandlerExceptionResolver
接口才能自动检测它们。 Spring MVC 附带了一个简单的异常解析器,即SimpleMappingExceptionResolver
,用于以可配置的方式将每个类别的异常映射到一个视图。
假设我们有一个异常类,即AuthException
。 而且,我们希望每次将此异常从任何地方抛出到应用程序中时,我们都希望显示一个预定的视图页面/WEB-INF/views/error/authExceptionView.jsp
。 这样配置就可以了。
SimpleMappingExceptionResolver
配置
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.howtodoinjava.demo.exception.AuthException">
error/authExceptionView
</prop>
</props>
</property>
<property name="defaultErrorView" value="error/genericView"/>
</bean>
完整的上下文配置为:
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.howtodoinjava.demo.exception.AuthException">
error/authExceptionView
</prop>
</props>
</property>
<property name="defaultErrorView" value="error/genericView"/>
</bean>
</beans>
请注意最后的defaultErrorView
属性。 如果 spring 上下文检测到未从应用程序抛出的任何异常,但未在exceptionMappings
属性列表中列出,则它将呈现视图/WEB-INF/views/error/genericView.jsp
。
测试SimpleMappingExceptionResolver
配置的应用程序
为了测试目的,让我们创建AuthException.java
。
AuthException.java
package com.howtodoinjava.demo.exception;
import java.util.Date;
public class AuthException extends RuntimeException
{
private static final long serialVersionUID = 1L;
private Date date;
private String message;
public AuthException(Date date, String message) {
super();
this.date = date;
this.message = message;
}
public Date getDate() {
return date;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "AuthException [date=" + date + ", message=" + message + "]";
}
}
并从任何控制器抛出此异常。
EmployeeController.java
@Controller
@RequestMapping("/employee-module")
public class EmployeeController
{
@RequestMapping(value="/getAllEmployees", method = RequestMethod.GET)
public String welcome(Model model)
{
throw new AuthException(new Date(), "Something bad happened dude !! Run Away :-(");
}
}
并在路径/WEB-INF/views/error/
中创建两个 jsp 文件
authExceptionView.jsp
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Authentication Exception</title>
</head>
<body>
<h2>Exception occured at: </h2><fmt:formatDate value="${exception.date}" pattern="yyyy-MM-dd" />
<h2>Exception Message : </h2>${exception.message}
</body>
</html>
genericView.jsp
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<title>Generic Exception</title>
</head>
<body>
<h2>Some generic error message</h2>
</body>
</html>
现在点击 URL:http://localhost:8080/springmvcexample/employee-module/getAllEmployees
SimpleMappingExceptionResolver
示例
现在从控制器抛出任何其他异常,例如NullPointerException
,如下所示。
@Controller
@RequestMapping("/employee-module")
public class EmployeeController
{
@RequestMapping(value="/getAllEmployees", method = RequestMethod.GET)
public String welcome(Model model)
{
throw new NullPointerException();
}
}
然后再次点击以下网址:http://localhost:8080/springmvcexample/employee-module/getAllEmployees
SimpleMappingExceptionResolver
通用错误示例
显然,应用程序现在可以在出现异常的情况下找到正确的视图。 前视图中没有更多错误堆栈跟踪。
学习愉快!
Spring MVC:<context:annotation-config>
与<context:component-scan>
原文:https://howtodoinjava.com/spring-mvc/spring-mvc-difference-between-contextannotation-config-vs-contextcomponent-scan/
在先前的文章中,我们在 Spring MVC 中学到的东西很少。 在这些教程中,我确实使用了<context:annotation-config>
或<context:component-scan>
之类的标签,但是我没有对这些标签进行详细说明。 我正在写这篇文章,专门列出标签<context:annotation-config>
和<context:component-scan>
之间的区别,以便将来使用它们时,您将知道自己在做什么。
1)两个标签之间的第一个大区别是<context:annotation-config>
是用于在应用程序上下文中激活已注册 bean 中的应用注解。 请注意,bean 是否通过哪种机制注册都没有关系,例如使用<context:component-scan>
或在application-context.xml
文件本身中定义。
2)第二差异是由第一差异本身驱动的。 它确实在上下文中注册了 bean,并且还扫描了 bean 内部的注解并激活了它们。 所以<context:component-scan>
; 做<context:annotation-config>
的工作,但另外它会扫描软件包并在应用程序上下文中注册 bean。
<context:component-scan>
vs <context:annotation-config>
使用示例
我将通过一些示例详细说明这两个标签,这些示例对我们更有意义。 为了使示例简单,我仅创建 3 个 bean,然后尝试以各种方式在配置文件中对其进行配置,然后我们将看到控制台中各种配置之间的区别,其中将输出输出。
供参考,以下是 3 个 bean。 BeanA
另外引用了BeanB
和BeanC
。
package com.howtodoinjava.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@SuppressWarnings("unused")
@Component
public class BeanA {
private BeanB beanB;
private BeanC beanC;
public BeanA(){
System.out.println("Creating bean BeanA");
}
@Autowired
public void setBeanB(BeanB beanB) {
System.out.println("Setting bean reference for BeanB");
this.beanB = beanB;
}
@Autowired
public void setBeanC(BeanC beanC) {
System.out.println("Setting bean reference for BeanC");
this.beanC = beanC;
}
}
//Bean B
package com.howtodoinjava.beans;
import org.springframework.stereotype.Component;
@Component
public class BeanB {
public BeanB(){
System.out.println("Creating bean BeanB");
}
}
//Bean C
package com.howtodoinjava.beans;
import org.springframework.stereotype.Component;
@Component
public class BeanC {
public BeanC(){
System.out.println("Creating bean BeanC");
}
}
BeanDemo
类用于加载和初始化应用程序上下文。
package com.howtodoinjava.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanDemo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
}
}
现在,我们开始编写包含变体的配置文件"beans.xml"
。 在下面的示例中,我将省略模式声明,以继续关注配置本身。
a)仅定义bean
标签
<bean id="beanA" class="com.howtodoinjava.beans.BeanA"></bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Creating bean BeanC
在这种情况下,因为我们没有使用任何属性/引用属性,所以创建了所有 3 个 bean,并且没有在BeanA
中注入任何依赖项。
b)定义bean
标签和ref
属性
<bean id="beanA" class="com.howtodoinjava.beans.BeanA">
<property name="beanB" ref="beanB"></property>
<property name="beanC" ref="beanC"></property>
</bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Creating bean BeanC
Setting bean reference for BeanB
Setting bean reference for BeanC
现在,还创建并注入了 bean。 难怪。
c)仅使用<context:annotation-config/>
<context:annotation-config />
//No Output
正如我已经说过的,<context:annotation-config />
仅在已经发现并注册的 bean 上激活注解。 在这里,我们没有发现任何 Bean,因此什么也没发生。
d)将<context:annotation-config>
与bean
声明一起使用
<context:annotation-config />
<bean id="beanA" class="com.howtodoinjava.beans.BeanA"></bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Setting bean reference for BeanB
Creating bean BeanC
Setting bean reference for BeanC
在上面的配置中,我们使用<bean>
标签发现了 bean。 现在,当我们使用<context:annotation-config />
时,它只需激活@Autowired
注解,就会在BeanA
内部进行 bean 注入。
e)仅使用<context:component-scan/>
<context:component-scan base-package="com.howtodoinjava.beans" />
Output:
Creating bean BeanA
Creating bean BeanB
Setting bean reference for BeanB
Creating bean BeanC
Setting bean reference for BeanC
上面的配置完成了我之前在文章开头提到的两件事。 它会进行 Bean 发现(在基本包中搜索@Component
注解),然后激活其他注解(例如Autowired
)。
f)同时使用<context:component-scan>
和<context:annotation-config>
<context:annotation-config />
<context:component-scan base-package="com.howtodoinjava.beans" />
<bean id="beanA" class="com.howtodoinjava.beans.BeanA"></bean>
<bean id="beanB" class="com.howtodoinjava.beans.BeanB"></bean>
<bean id="beanC" class="com.howtodoinjava.beans.BeanC"></bean>
Output:
Creating bean BeanA
Creating bean BeanB
Setting bean reference for BeanB
Creating bean BeanC
Setting bean reference for BeanC
奇怪! 通过上述配置,我们两次发现了 bean,并且两次激活了注释。 但是输出只打印了一次。 为什么? 因为 spring 具有足够的智能,如果使用相同或不同的方式多次注册任何配置处理,则仅注册一次。酷!
因此,在这里,我将结束本教程,并希望当您在下一个项目中使用这些标签时,它们将为您提供更清晰的画面。 随意下载源代码并使用它。
下载源码
祝您学习愉快!
Spring 教程
原文: https://howtodoinjava.com/java-spring-framework-tutorials/
本页列出了所有 Spring 教程和 HowToDoInJava.com 上可用的示例。 每次都会更新此页面,我将在 Spring 框架上写任何文章。 随意建议您想阅读的主题。
Spring 核心教程
- Spring bean 范围
spring 容器中的 bean 可以在五个范围中创建。 单例,原型,请求,会话和全局会话。 详细了解它们。 - Spring bean 生命周期回调方法
Spring 框架提供了以下 4 种方法来控制 Bean 的生命周期事件:InitializingBean
和DisposableBean
回调接口- 其他用于特定行为的感知接口
- Bean 配置文件中的自定义
init()
和destroy()
方法 @PostConstruct
和@PreDestroy
注解
- Spring bean 自动装配
在 spring 框架中,遵循配置文件中设置 bean 的依赖关系是一个好的做法,但是 spring 容器也能够自动装配协作 bean 之间的关系。 这意味着可以通过检查BeanFactory
的内容来自动让 Spring 为您的 bean 解决协作者(其他 bean)。 了解如何做。 - Bean
byType
自动装配
如果容器中仅存在一个具有该属性类型的 Bean,则通过类型进行自动装配可以自动装配属性。 如果存在多个错误,则将引发致命异常,这表明您不能对该 bean 使用byType
自动装配。 详细了解。 - Bean
byName
自动装配
通过名称自动装配允许自动装配属性,以便它将检查容器并查找与需要自动装配的属性名称完全相同的 bean。 了解更多。 - Bean 的构造函数自动装配
构造函数的自动装配与byType
相似,但适用于构造函数参数。 在启用自动装配的 bean 中,它将查找构造函数参数的类类型,然后按类型对所有构造函数参数进行自动装配。 学到更多。 - 如何使用
FactoryBean
创建 bean
工厂 bean 是充当在 IoC 容器中创建其他 bean 的工厂的 bean。 从概念上讲,工厂 bean 与工厂方法非常相似,但是它是特定于 Spring 的 bean,可以在构造 bean 的过程中由 Spring IoC 容器识别,并且可以被容器用来实例化其他 bean。 了解如何使用 SpringFactoryBean
创建 bean。 - 如何使用静态工厂方法创建 bean
如果要在 Spring 中通过调用静态工厂方法创建 bean,其目的是将对象创建过程封装在静态方法中,那么您可以使用工厂方法属性。 - 如何使用
util:constant
从最终静态字段引用中声明 Bean
如果您在某个 Bean 中具有最终静态字段,并且希望将这些引用用作应用程序上下文文件中的 Bean 注入到另一个 Bean 中, 您可以使用<util:constant>
标签执行此操作。 - 如何将外部资源/文件加载到 Spring 上下文中?
很多时候,您想将外部资源或文件(例如,文本文件,XML 文件,属性文件或图像文件)加载到 Spring 应用程序上下文中。 Spring 的资源加载器提供了统一的getResource()
方法,可让您通过资源路径检索外部资源。 - 如何创建 bean 后处理器
bean 后处理器允许在 bean 初始化回调方法之前和之后进行其他处理。 Bean 后处理器的主要特征是它将一个接一个地处理 IoC 容器中的所有 Bean 实例,而不仅仅是单个 Bean 实例。 学习使用BeanPostProcessor
接口类创建此类后处理器。 - 如何解决文本消息:
ResourceBundleMessageSource
示例
对于支持国际化的应用程序,它需要能够解决不同语言环境的文本消息的功能。 Spring 的应用程序上下文可以通过其键解析目标区域设置的文本消息。 了解如何使用ResourceBundleMessageSource
类支持 i10n。 - 如何发布和监听应用程序事件
有时,在您的 spring 应用程序中,您可能希望添加监听特定事件的功能,以便可以按照应用程序逻辑处理这些事件。 让我们学习如何在 spring 应用程序中实现此发布和监听事件。 - 如何使用
@Component
,@Repository
,@Service
和@Controller
注解?
在具有@Component
,@Repository
,@Service
和@Controller
注解的情况下,启用自动组件扫描后,spring 会自动将 bean 导入容器,因此您不必定义它们来明确地使用 XML。 这些注解也称为构造型注解。 @Required
注解和RequiredAnnotationBeanPostProcessor
示例
在实际的应用程序中,您不会对检查上下文文件中配置的所有 Bean 属性感兴趣。 相反,您只想检查是否仅在某些特定的 Bean 中设置了特定的属性集。 Spring 使用dependency-check
属性的依赖项检查功能在这种情况下将无法为您提供帮助。 因此,要解决此问题,可以使用@Required
注解。
Spring 框架最佳实践
- 编写 Spring 配置文件的 13 个最佳实践
Spring Bean,依赖项以及 Bean 所需的服务在 xml 配置文件或注解中指定。 但是,XML 配置文件是冗长且更干净的。 如果未正确计划和编写,在大型项目中将变得非常难以管理。 在本文中,我将向您展示编写 Spring XML 配置的 13 种最佳实践。
控制反转和依赖注入
- 控制反转(IoC)和依赖注入(DI)模式
在传统编程中,业务逻辑流由静态分配给彼此的对象确定。 在控制反转的情况下,流程依赖于由汇编程序实例化的对象图,并且通过抽象定义对象交互来使流程成为可能。 绑定过程是通过依赖项注入实现的。 阅读这篇文章以获取详细信息。
Spring REST
- 使用 Spring 3 MVC 的 REST API
使用 Spring 3 mvc 创建 RESTful Web 服务应用程序的逐步指南。 这篇文章涵盖了所有需要的依赖项,应用的注解和有效的应用程序演示,您可以免费下载。 - Spring REST Hello World XML 示例
学习编写能够返回资源 XML 表示形式的 REST API。 - Spring REST Hello World JSON 示例
学习编写能够返回资源的 JSON 表示形式的 REST API。 - Spring RESTFul 客户端 –
RestTemplate
示例
了解如何构建 RESTFul 客户端以使用前面示例中编写的 REST API。
Spring ORM
- Spring 3 和 hibernate 集成教程,并带有示例
。本教程重点介绍将 Hibernate 与 Spring 3 框架一起使用。 我将展示这种集成的结果,一个基本的端到端应用程序流看起来如何。 - Spring 3.2.5.RELEASE 和 Hibernate 4 集成示例
在前面的示例中,很多人都在解决 Maven 依赖切面挣扎。 在此示例中,我在项目源代码本身中添加了所有必需的 jar 文件。 另外,我已经将 spring 的版本从 3.0.5 升级到 3.2.5.RELEASE。 - Spring
AbstractRoutingDataSource
示例
AbstractRoutingDataSource
是 Spring 框架中非常有用的功能,如果您的设计允许根据某些标准对多个数据库进行更改,那么每个用户的要求可能会有所变化。 一个例子可以是数据库的使用。 当用户属于某个语言环境时,可以使用特定的数据库;如果用户属于另一个语言环境,则可以切换到另一个语言环境。 - 在 Spring JDBC + JPA + HSQLDB 中使用 SQL 脚本
在应用程序启动时在 Spring 中使用自定义 SQL 脚本初始化数据库,并在其中填充适当的表和数据。
Spring 整合
- Spring 3 与 HornetQ 的独立集成
HornetQ 是一个开源项目,用于构建多协议,可嵌入式,非常高性能的集群异步消息传递系统。 HornetQ 具有很大的灵活性,可以通过一些现有的应用程序框架进行配置。 在这篇文章中,我将演示在 Spring 3 中使用 HornetQ。 - Spring 4 + Struts 2 + Hibernate 集成
当将 Spring 框架与 Struts 和 Hibernate 结合在一起时,要牢记所有重要点。 - Spring 3 与 Maven 的 JSTL 集成
如何使用 maven 构建工具向 Spring 3 添加 JSTL 支持。
杂项示例
- Spring Email:JavaMailSender 示例
一个简单的示例,展示了如何使用 Spring 框架发送电子邮件。
Spring 单元测试
- junit 中的单元测试授权
这篇文章的主要目的是描述如何以编程方式构建完全填充的身份验证对象,然后将其用于单元测试以及可能在应用程序代码本身中的方法。
Spring 事务
- 带有加载时织入的非公共方法上的 Spring 事务
了解如何在非公共方法上的任何 spring 应用程序中应用事务(通过默认 spring,AOP 只能建议 IoC 中声明的 bean 的公共方法容器)。 使用这种技术,您可以管理非公共方法的事务,也可以管理任何方法在 Spring IoC 外部创建的对象中的事务。
参考文献
Spring 3 文档
Spring 4 文档
ContextLoaderListener
与DispatcherServlet
原文: https://howtodoinjava.com/spring-mvc/contextloaderlistener-vs-dispatcherservlet/
在基于 XML 的 Spring MVC 配置中,您必须在web.xml
文件中看到两个声明,即ContextLoaderListener
和DispatcherServlet
。 让我们尝试了解它们在框架中的用途及其差异。
根和子上下文
在进一步阅读之前,请了解:
- Spring 一次可以有多个上下文。 其中之一将是根上下文,而所有其他上下文将是子上下文。
- 所有子上下文都可以访问在根上下文中定义的 Bean。 但事实并非如此。 根上下文无法访问子上下文 Bean。
DispatcherServlet
– 子应用程序上下文
DispatcherServlet
本质上是 Servlet(它扩展了HttpServlet
),其主要目的是处理与配置的 URL 模式匹配的传入 Web 请求。 它采用传入的 URI 并找到控制器和视图的正确组合。 因此它是前端控制器。
在 spring 配置中定义DispatcherServlet
时,您将使用contextConfigLocation
属性为 XML 文件提供控制器类,视图映射等条目。
web.xml
<servlet>
<servlet-name>employee-services</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:employee-services-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
如果不提供配置文件,则它将使用[servlet_name]-servlet.xml
加载其自己的配置文件。 Web 应用程序可以定义任意数量的DispatcherServlet
条目。 每个 servlet 将在其自己的名称空间中运行,并使用映射,处理器等加载其自己的应用程序上下文。
这意味着每个DispatcherServlet
都可以访问 Web 应用程序上下文。 在指定之前,每个DispatcherServlet
都会创建自己的内部 Web 应用程序上下文。
从Spring 3.x开始,方法DispatcherServlet(WebApplicationContext webApplicationContext)
使用给定的 Web 应用程序上下文创建一个新的DispatcherServlet
。 只有通过ServletContext.addServlet(java.lang.String, java.lang.String)
API支持,才可以在 Servlet 3.x 环境中使用。
ContextLoaderListener
– 根应用程序上下文
ContextLoaderListener
创建根应用程序上下文,并将与所有DispatcherServlet
上下文创建的子上下文共享。 在web.xml
中只能有一个条目。
web.xml
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>
ContextLoaderListener
的上下文包含全局可见的 Bean,例如服务,存储库,基础结构 Bean 等。创建根应用程序上下文后,它作为属性存储在ServletContext
中,名称为:
org/springframework/web/context/ContextLoader.java
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
//Where attibute is defined in /org/springframework/web/context/WebApplicationContext.java as
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
要在 Spring 控制器中获取根应用程序上下文,可以使用WebApplicationContextUtils
类。
Controller.java
@Autowired
ServletContext context;
ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(context);
if(ac == null){
return "root application context is null";
}
ContextLoaderListener
与DispatcherServlet
下图在单个视图中描述了整个关系。
ContextLoaderListener
vs DispatcherServlet
ContextLoaderListener
创建根应用程序上下文。DispatcherServlet
条目为每个 Servlet 条目创建一个子应用程序上下文。- 子上下文可以访问在根上下文中定义的 bean。
- 根上下文中的 Bean 无法(直接)访问子上下文中的 Bean。
- 所有上下文都添加到
ServletContext
中。 - 您可以使用
WebApplicationContextUtils
类访问根上下文。
总结
通常,您将在DispatcherServlet
上下文中定义所有与 MVC 相关的 bean(控制器和视图等),并在ContextLoaderListener
的根上下文中定义所有跨领域的 bean,例如安全性,事务,服务等。
通常,此设置可以正常工作,因为很少需要访问任何 MVC bean(从子上下文)到与安全相关的类(从根上下文)。 通常,我们在 MVC 类上使用安全 bean,并且他们可以通过上述设置来访问它。
学习愉快!
SpringSecurity
SpringSecurity 教程
原文: https://howtodoinjava.com/spring-security-tutorial/
Spring Security 是一个框架,致力于为基于 Java EE 的企业软件应用程序提供身份验证和授权。
Maven 依赖
要在基于 Maven 的项目中包含 spring security ,请包括以下依赖项:
pom.xml
<repositories>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>http://repo.spring.io/snapshot</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
</dependencies>
如果您正在使用 LDAP,OpenID 等其他功能,则还需要包括相应的模块。
如果您遇到任何导致运行时类路径问题的传递依赖问题,则可以考虑添加 spring security BOM 文件。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
Gradle 依赖
要在基于 gradle 的项目中包含 Spring Security,请包含以下依赖项:
build.gradle
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.security:spring-security-web:4.1.1.RELEASE'
compile 'org.springframework.security:spring-security-config:4.1.1.RELEASE'
}
SpringSecurity 实例
以下示例详细说明了 Spring Security 的各种功能。
Spring Security – 登录表单示例
本教程的范围是:
- 只有授权用户才能访问编辑员工屏幕。
- 未经授权的用户应显示登录屏幕。
- 成功的凭据应转发到编辑员工屏幕。
- 不成功的凭据应转发到拒绝访问屏幕。
- 应该有一个注销应用程序的链接。
Spring Security – JDBC 用户服务示例
<jdbc-user-service/>
的示例,用于从数据库中获取用户名和密码,以将用户验证为系统身份。
Spring Security – HTTP 基本认证示例
<http-basic/>
的示例,用于强制用户使用基本的 HTTP 身份验证来身份验证应用程序中的任何网页或任何其他资源。
Spring Security – 自定义UserDetailsService
示例
学习扩展和使用UserDetailsService
界面,该界面用于查找任何给定用户的用户名,密码和授予的权限。
Spring Security – 方法级别的安全性示例 – @PreAuthorize
和@Secured
了解在 Spring 应用程序中实现方法级别的安全性。
Spring Security – 使用 JSP Taglibs 的视图层安全性
学习在视图层中添加安全性。 当我们要根据用户的角色隐藏某些链接或按钮以使他将无法访问该功能时,通常需要使用该功能。
Spring Security – Spring Security 身份验证的单元测试
了解测试 Spring 身份验证技术的方法,以使系统免受外部攻击。
Spring Security – Siteminder 预身份验证示例
在其他任何应用程序中对用户进行预身份验证后,学习使用 Spring Security,并使用 siteminder 进入您的 Web 应用程序。
Spring Security – 具有保护切入点的方法级安全性
学习使用基于 XML 的安全性配置。
参考:
Spring Security 参考
具有保护切入点的 Spring 方法安全性
原文: https://howtodoinjava.com/spring-security/xml-config-based-method-level-spring-security-using-protect-pointcut/
在方法级别安全性的先前示例中,我们使用@PreAuthorize
注解添加了安全性。 注解也是在任何方法上增加安全性的好方法和快速方法。 但是有一个问题,它将您的应用程序代码紧紧地耦合到 Spring 上。 至少在理论上就最佳做法而言,这都是不推荐的。 推荐的方法是将所有此类安全性定义添加到 xml 配置文件中。 这样,您始终可以更改实现而无需触碰源代码。
总览
在此示例中,我使用了典型的员工管理屏幕。 有两个基本操作,ADD
和DELETE
。
- 添加要求经过身份验证的用户具有
"ROLE_USER"
或"ROLE_ADMIN"
。 - 删除受到更多保护,并且需要管理员访问权限,即仅允许
ROLE_ADMIN
删除用户。
我在应用程序中有两个用户,即admin
和lokesh
。 admin
用户同时具有“ ROLE_USER
”和“ ROLE_ADMIN
”角色,但另一个用户lokesh
仅具有“ ROLE_USER
”访问权限。
applicationContext.xml
<user-service>
<user name="lokesh" password="password" authorities="ROLE_USER" />
<user name="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN" />
</user-service>
上面的安全配置将允许两个用户都添加用户,但是只有
admin
才能够删除用户。
让我们看一下示例应用程序中的主要功能点。
使用保护切入点的基于 XML 的 Spring Security
完整的 XML 配置如下所示:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<global-method-security>
<protect-pointcut expression="execution(* com.howtodoinjava.service.*Impl.add*(..))" access="ROLE_USER"/>
<protect-pointcut expression="execution(* com.howtodoinjava.service.*Impl.delete*(..))" access="ROLE_ADMIN"/>
</global-method-security>
<http auto-config="false" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<intercept-url pattern="/accessdenied" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />
<logout logout-success-url="/logout" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="lokesh" password="password" authorities="ROLE_USER" />
<user name="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl" />
<beans:bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl" />
</beans:beans>
应用安全性的管理器类
EmployeeManagerImpl.java
package com.howtodoinjava.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.howtodoinjava.dao.EmployeeDAO;
import com.howtodoinjava.entity.EmployeeEntity;
@Service
public class EmployeeManagerImpl implements EmployeeManager {
@Autowired
private EmployeeDAO employeeDAO;
@Override
@Transactional
public void addEmployee(EmployeeEntity employee) {
employeeDAO.addEmployee(employee);
}
@Override
@Transactional
public List<EmployeeEntity> getAllEmployees() {
return employeeDAO.getAllEmployees();
}
@Override
@Transactional
public void deleteEmployee(Integer employeeId) {
employeeDAO.deleteEmployee(employeeId);
}
public void setEmployeeDAO(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
}
我跳过了其余代码,因为它与以前的使用注解的安全示例完全相同。 另外,如果需要引用任何内容,则可以下载源代码。
测试应用
1)使用用户"lokesh"
登录
2)将员工添加到列表中
3)用户添加成功
4)尝试删除员工。 访问被拒绝。
5)用管理员用户登录
6)将员工添加到列表中
7)用户添加成功
8)尝试删除员工。 员工已删除。
9)尝试删除其他员工。 员工已删除。
源代码下载
下载源码
学习愉快!
Spring Security Siteminder 预身份验证示例
原文: https://howtodoinjava.com/spring-security/spring-3-security-siteminder-pre-authentication-example/
到目前为止,我们已经了解了使用登录表单安全性 , 自定义用户详细信息安全性以及更多此类与安全性相关的概念。 在这篇文章中,我举了一个场景示例,其中已经通过任何第三方应用程序或工具对使用进行了身份验证,例如站点监视程序,这是组中多个应用程序之间非常常见的接口。
在这种情况下,用户已在任何其他应用程序中进行了预身份验证,并使用网站提示器进入您的 Web 应用程序。 网站管理员会发送有关预认证用户的请求标头,您可以利用该标头进一步授权应用程序内的用户。 您不需要进一步验证用户,只需从数据库验证用户角色并在应用程序内部提供适当的访问权限即可。
请记住,网站管理员仅是示例,实际上,您可以使用任何第三方
应用程序来获得预先认证的用户。 在每种情况下,仅请求标头都会更改。
让我们逐步学习本教程。
步骤 1)Maven 依赖
我使用 maven 作为运行时依赖项,因此提供pom.xml
。 如果使用的是 ANT,则下载相应的 JAR,并将其添加到类路径中。
pom.xml
<properties>
<spring.version>3.0.5.RELEASE</spring.version>
<jackson-mapper-asl.version>1.9.9</jackson-mapper-asl.version>
<jaxb-api.version>2.2.7</jaxb-api.version>
</properties>
<dependencies>
<!-- Spring 3 dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
步骤 2)更新web.xml
文件
web.xml
文件中没有太多内容。 只需添加上下文配置位置和 Spring Security 相关的过滤器映射即可。
web.xml
<web-app>
<display-name>www.howtodoinjava.com</display-name>
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-servlet.xml</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
步骤 3)Spring Security 配置
这是最重要的步骤,因为在这里我们将配置与验证前安全性相关的映射。 让我们看一下文件:
spring-mvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<!-- Annotation are configuring the application -->
<mvc:annotation-driven/>
<!-- Scan this package for all config annotations -->
<context:component-scan base-package="com.howtodoinjava.web" />
<security:http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
<!-- Additional http configuration omitted -->
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="customUserDetailsService"/>
</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
<bean id="customUserDetailsService" class="com.howtodoinjava.security.CustomUserDetailsService"></bean>
<bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"></bean>
</beans>
让我们了解以下配置:
mvc:annotation-driven
用于告诉 spring 它需要在context:component-scan
中指定的基本包中扫描注解,以搜索资源映射。security:http
配置指定与安全性相关的配置和选项。use-expressions
告诉您,与security: intercept-url
中的access
属性匹配时,允许使用表达式,并应对其进行解析。security:custom-filter
指定了自定义过滤器的定义,该过滤器将被调用以验证用户的有效性。PRE_AUTH_FILTER
确保在其他身份验证/授权处理之前将调用此过滤器。 我为此定义了一个siteminder
过滤器。 您可以将其命名为其他名称。principalRequestHeader
很重要,因为一旦用户从另一个应用程序进入应用程序,它将检查请求标头属性。 因此,请第三方供应器提供的此标头在此处集成。authenticationManager
最终使用了我在com.howtodoinjava.security.CustomUserDetailsService
类中编写的customUserDetailsService
。此类实现UserDetailsService
接口,并具有一种方法loadUserByUsername()
。 此方法必须返回类型为org.springframework.security.core.userdetails.UserDetails
的经过身份验证的用户界面。 该对象将具有其他授权详细信息,例如用户角色,将用于进一步的安全性。
步骤 4)编写自定义UserDetailsService
类
此类将获取从第三方应用程序传递的用户名,并将用户名作为请求标头传递,例如在我们的情况下为SM_USER
。
CustomUserDetailsService.java
package com.howtodoinjava.security;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class CustomUserDetailsService implements UserDetailsService
{
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException
{
System.out.println("username recieved :: " + username);
@SuppressWarnings("deprecation")
//Get this user details from database and set its roles also here
UserDetails user = new User(username, "password", true, true, true, true,
new GrantedAuthority[]{ new GrantedAuthorityImpl("ROLE_USER") });
return user;
}
}
步骤 5)编写安全资源以进行验证
为了简单起见,我编写了两个非常基本的类。 我将尝试在没有请求标头“ SM_USER
”的情况下访问它们。
DemoController.java
DemoController.java
package com.howtodoinjava.web;
@Controller
@RequestMapping("/users")
public class DemoController
{
@RequestMapping(method = RequestMethod.GET, value="/{id}", headers="Accept=application/xml")
public @ResponseBody User getUserById(@PathVariable String id)
{
User user = new User();
user.setFirstName("john");
user.setLastName("adward");
return user;
}
@RequestMapping(method = RequestMethod.GET, headers="Accept=application/xml")
public @ResponseBody Users getAllUsers()
{
User user1 = new User();
user1.setFirstName("john");
user1.setLastName("adward");
User user2 = new User();
user2.setFirstName("tom");
user2.setLastName("hanks");
Users users = new Users();
users.setUsers(new ArrayList<User>());
users.getUsers().add(user1);
users.getUsers().add(user2);
return users;
}
}
Users.java
Users.java
@XmlRootElement(name="users")
@XmlAccessorType(XmlAccessType.NONE)
public class Users
{
@XmlElement(name="user")
private Collection<User> users;
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
}
User.java
User.java
@XmlRootElement(name="user")
@XmlAccessorType(XmlAccessType.NONE)
public class User {
@XmlElement(name="first-name")
private String firstName;
@XmlElement(name="last-name")
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
步骤 6)示范
让我们将应用程序部署在 tomcat 服务器中并进行测试。
情况 1:不带SM_USER
请求头
这将引发以下异常:
org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: SM_USER header not found in request.
at org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter.getPreAuthenticatedPrincipal(RequestHeaderAuthenticationFilter.java:43)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doAuthenticate(AbstractPreAuthenticatedProcessingFilter.java:98)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:86)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:169)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
Spring security 预认证错误
情况 2:带SM_USER
请求标头
这次,用户将可以访问资源。
Spring security 预认证成功
要下载以上教程的源代码,请点击以下下载链接。
下载源码
学习愉快!
Spring Security 登录表单示例
原文: https://howtodoinjava.com/spring-security/login-form-based-spring-3-security-example/
学习使用 Spring Security 教程中讨论的详细信息,将 Spring Security 登录表单添加到任何 Spring Web 应用程序。
阅读更多: Spring Security 5 登录表单示例[为 Spring 5 更新]
1. 背景资料
我们学会了在链接的帖子中集成 Spring 3 和 Hibernate 之间。 该应用程序是简单的 Web 应用程序,它提供了一个视图,用户可以在其中添加/编辑员工。
使该应用程序安全。 本教程的范围是:
- 只有授权用户才能访问编辑员工屏幕。
- 未经授权的用户应显示登录屏幕。
- 成功的凭据应转发到编辑员工屏幕。
- 不成功的凭据应转发到拒绝访问屏幕。
- 应该有一个注销应用程序的链接。
2. Spring Security Maven 依赖项
让我们从第一步开始,即更新项目依赖项。 由于以下原因,它将在演示中添加以下four sub-modules
:
spring-security-core
:它包含核心身份验证以及访问控制类和接口。spring-security-web
:包含过滤器和相关的 Web 安全基础结构代码。 它还启用了基于 URL 的安全性,我们将在此演示中使用它。spring-security-config
:它包含安全名称空间解析代码。 如果您使用 Spring Security XML 文件进行配置,则需要它。spring-security-taglibs
:它为访问安全信息和在 JSP 中应用安全约束提供基本支持。
pom.xml
<properties>
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
</properties>
<!-- Spring Security -->
<dependency>
<groupid>org.springframework.security</groupid>
<artifactid>spring-security-core</artifactid>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupid>org.springframework.security</groupid>
<artifactid>spring-security-web</artifactid>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupid>org.springframework.security</groupid>
<artifactid>spring-security-config</artifactid>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupid>org.springframework.security</groupid>
<artifactid>spring-security-taglibs</artifactid>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
现在使用“ mvn compile
”命令更新项目中的依赖项。
3. 在web.xml
中配置DelegatingFilterProxy
Spring Security 的 Web 基础结构完全基于标准 Servlet 过滤器。 这些过滤器在web.xml
文件中定义,否则 servlet 容器将忽略它们。
在 Spring Security 中,过滤器类也是在应用程序上下文中定义的 Spring Bean,因此能够利用 Spring 丰富的依赖注入机制和生命周期接口。 Spring 的 DelegatingFilterProxy
提供了web.xml
和应用上下文之间的链接。
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如果您没有使用任何明确的过滤器定义,并且希望 spring 为您配置基本的基础架构,则如上例所示,将过滤器名称用作“ springSecurityFilterChain
”。 请注意,您不应自己使用此 bean 名称。 将其添加到web.xml
后,就可以开始编辑 Spring Security 配置文件了。 Web 安全服务是使用元素配置的。
同样不要忘记将安全配置文件放在上下文配置位置设置中。
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/employee-servlet.xml
/WEB-INF/application-security.xml
</param-value>
</context-param>
完整的web.xml
文件如下所示:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>Archetype Created Web Application</display-name>
<welcome-file-list>
<welcome-file>/WEB-INF/index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>employee</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>employee</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/employee-servlet.xml
/WEB-INF/application-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
4. 配置登录注销安全性
正如我们在上一节中了解到的那样,将过滤器名称用作springSecurityFilterChain
可以帮助您使用元素配置基本基础结构。 让我们先看看它是如何配置的? 我已经为这个演示编写了一个基本配置:
application-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<intercept-url pattern="/accessdenied" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />
<logout logout-success-url="/logout" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="lokesh" password="password" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
让我们看看这个配置的实际含义。
-
http
:包括与配置相关的网址级别安全性。 此元素是所有与 Web 相关的名称空间功能的父级。 -
auto-config
:包括一些基本服务。 它是<http> <form-login /> <http-basic /> <logout /> </http>
的简写
-
use-expressions
:这里使用表达式来保护单个 URL。 这些表达式可以是例如hasRole([role])
,hasAnyRole([role1,role2])
,permitAll
,denyAll
等。 -
intercept-url
:这将匹配请求中请求的网址格式,并根据访问值决定要采取的操作。 -
form-login
: 当用户尝试访问任何安全的 URL 时,这将成为图片。 映射到login-page
属性的登录页面将用于身份验证检查。 它是 spring securitylogin-processing-url
。如果未提供,spring 将为用户提供内置的登录页面。 如果登录成功或由于无效的用户/密码匹配而导致登录失败,它也包含默认目标的属性。
-
logout
:如果在应用程序中调用注销,这将有助于查找下一个视图。
我正在使用基于 XML 的用户服务,即我不会去数据库进行密码验证,而是将用户名/密码组合存储在配置文件本身中。 要使用此设置王,请使用内置的内置用户详细信息服务来设置authentication-manager
。 在更实时的应用程序中,这将是一些从远程数据库中获取数据的用户服务。
5. Spring 控制器
我将重用控制器,并将在控制器中添加其他映射和处理器方法。 这些其他 URL 是/login
,/logout
和/accessdenied
。 具有所有方法处理器的更新后的控制器如下所示:
EditEmployeeController.java
package com.howtodoinjava.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.howtodoinjava.entity.EmployeeEntity;
import com.howtodoinjava.service.EmployeeManager;
@Controller
public class EditEmployeeController {
@Autowired
private EmployeeManager employeeManager;
public void setEmployeeManager(EmployeeManager employeeManager) {
this.employeeManager = employeeManager;
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(ModelMap model) {
return "login";
}
@RequestMapping(value = "/accessdenied", method = RequestMethod.GET)
public String loginerror(ModelMap model) {
model.addAttribute("error", "true");
return "denied";
}
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout(ModelMap model) {
return "logout";
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public String defaultPage(ModelMap map) {
return "redirect:/list";
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listEmployees(ModelMap map) {
map.addAttribute("employee", new EmployeeEntity());
map.addAttribute("employeeList", employeeManager.getAllEmployees());
return "editEmployeeList";
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addEmployee(
@ModelAttribute(value = "employee") EmployeeEntity employee,
BindingResult result) {
employeeManager.addEmployee(employee);
return "redirect:/list";
}
@RequestMapping("/delete/{employeeId}")
public String deleteEmplyee(@PathVariable("employeeId") Integer employeeId) {
employeeManager.deleteEmployee(employeeId);
return "redirect:/list";
}
}
6. Spring 的视图
现在,我们已经使用安全配置和控制器处理器配置了我们的应用程序。 是时候编写本质上是 JSP 文件的视图了。 jsp 文件中最重要的附加内容是login.jsp
文件。
该文件的格式包含用户名和密码字段的文本框。 让我们看看它是怎么写的:
6.1. login.jsp
login.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<html>
<body>
<h1 id="banner">Login to Security Demo</h1>
<form name="f" action="<c:url value='j_spring_security_check'/>"
method="POST">
<table>
<tr>
<td>Username:</td>
<td><input type='text' name='j_username' /></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='j_password'></td>
</tr>
<tr>
<td colspan="2"> </td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"> <input name="reset" type="reset"></td>
</tr>
</table>
</form>
</body>
</html>
默认情况下,spring 会自动生成并配置一个UsernamePasswordAuthenticationFilter
bean。 默认情况下,此过滤器在处理 Web 表单中的登录 POST 时响应 URL /j_spring_security_check
。 用户名字段使用“ j_username
”,密码字段使用“ j_password
”。
提交此表单时,UsernamePasswordAuthenticationFilter
将匹配application-security.xml
中身份验证提供程序设置中配置的用户名和密码。
6.2. logout.jsp
logout.jsp
< % session.invalidate(); %>
You are now logged out!!
<a href="//howtodoinjava.com/spring/spring-security/login-form-based-spring-3-security-example/">go back</a>
该视图仅使会话无效,并提供一个链接以返回登录页面。
6.3. denied.jsp
当用户尝试使用无效的用户名和密码组合进行身份验证时,此 jsp 文件将出现在用户屏幕中。 它将在类路径中显示message.properties
中配置的相应消息。
denied.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<body>
<h1 id="banner">Unauthorized Access !!</h1>
<hr />
<c:if test="${not empty error}">
<div style="color:red">
Your fake login attempt was bursted, dare again !!<br />
Caused : ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
</c:if>
<p class="message">Access denied!</p>
<a href="//howtodoinjava.com/spring/spring-security/login-form-based-spring-3-security-example/">Go back to login page</a>
</body>
</html>
7. Spring Security 登录表单演示
是时候测试应用程序了。 只需将应用程序部署在任何服务器中,例如就我而言,我正在使用 Tomcat 7.0. 现在,执行以下步骤:
7.1. 在浏览器http://localhost:8080/Spring3HibernateIntegration
中输入 URL
除了/login
,/logout
和/accessdeni
之外,它将显示登录屏幕,所有其他 URL 都是受保护的 URL。
默认登录界面
7.2. 尝试使用用户名"demo"
和密码"1234"
进行身份验证
无效的用户名和密码的未经授权访问
由于用户名和密码无效,它将给出拒绝访问错误。
7.3. 尝试使用用户名"lokesh"
和密码"password"
进行身份验证
成功验证后的员工编辑界面
因为用户名和密码正确,它将显示员工管理屏幕。
7.4. 单击注销链接
注销消息
用户将被注销,并出现登录屏幕。
我希望这个 spring mvc 登录示例能够对使用 xml 配置的基本 Spring Security 机制有所启发。 如果您对此 Spring Security 登录表单示例有任何疑问,请给我评论。
下载源码
学习愉快!
使用 JSP Taglibs 的 Spring 视图层安全
原文: https://howtodoinjava.com/spring-security/spring-security-at-view-layer-using-jsp-taglibs/
到目前为止,在先前的教程中,我们已经了解了如何在登录表单,自定义用户详细信息服务甚至方法级安全性之后保护您的应用程序的安全。 所有这些安全性实现都在 MVC 的控制器或模型层上。 是时候在视图层中添加安全性了。 当我们要根据用户的角色隐藏某些链接或按钮以使他将无法访问该功能时,通常需要使用该功能。
Taglib 声明
为了确保以后查看应用程序的安全,spring Security 拥有自己的 taglib,它为访问安全信息和在 JSP 中应用安全约束提供了基本支持。 要在 jsp 文件中使用安全功能,需要添加以下标记库声明:
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
您可以选择自己的前缀,并且需要在 jsp 文件中使用该前缀。
安全标签
Spring 基本上提供了 3 个标签来保护视图层信息,即
- 授权标签
- 验证标签
accesscontrollist
标签
让我们一一看。
1)授权标签:
该标签用于确定是否应评估其内容。 此标记具有两种形式,即根据用户角色保护信息或根据用户访问特定 URL 的权限保护信息。
用法示例如下所示:
<security:authorize ifAnyGranted="ROLE_ADMIN">
<tr>
<td colspan="2">
<input type="submit" value="<spring:message code="label.add"/>"/>
</td>
</tr>
</security:authorize>
OR
<security:authorize url="/admin">
<tr>
<td colspan="2">
<input type="submit" value="<spring:message code="label.add"/>"/>
</td>
</tr>
</security:authorize>
2)验证标签
此标记允许访问存储在安全上下文中的当前身份验证对象。 它直接在 JSP 中呈现对象的属性。 因此,例如,如果Authentication
的主体属性是 Spring Security 的UserDetails
对象的实例,则使用 <sec:authentication property="principal.username"></sec:authentication>
将呈现当前用户的名称。
此标记不是直接出于安全目的,而是可用于访问可用于视图层安全性的信息。
<security:authentication property="principal.username" />
3)accesscontrollist
标记
该标签仅在与 Spring Security 的 ACL 模块一起使用时才有效。 它检查以逗号分隔的指定域对象的所需权限列表。 如果当前用户具有这些权限中的任何一个,则将评估标签正文。 如果他们不这样做,它将被跳过。
<sec:accesscontrollist hasPermission="1,2" domainObject="someObject">
This will be shown if the user has either of the permissions
represented by the values "1" or "2" on the given object.
</sec:accesscontrollist>
来源:http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
应用程序中的使用示例
我们已经在 Spring Security 登录表单教程的基础上开发了一个员工管理应用程序。 在此应用程序中,经过身份验证的用户可以添加/删除/列出员工。 现在,让我们修改应用程序,以便任何没有ROLE_ADMIN
权限的用户都看不到“添加”按钮。
以下是我在应用程序中所做的代码更改:
< %@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
< %@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
< %@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
< %@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
<html>
<head>
<title>Spring 3 hibernate integration example on www.howtodoinjava.com</title>
</head>
<body>
<h2>Employee Management Screen : <security:authentication property="principal.username"></security:authentication></h2>
<h6><a href="<c:url value='j_spring_security_logout'></a>">Click here to logout</h6>
<form:form method="post" action="add" commandName="employee">
<table>
<tr>
<td><form:label path="firstname"><spring:message code="label.firstname"></spring:message></form:label></td>
<td><form:input path="firstname"></form:input></td>
</tr>
<tr>
<td><form:label path="lastname"><spring:message code="label.lastname"></spring:message></form:label></td>
<td><form:input path="lastname"></form:input></td>
</tr>
<tr>
<td><form:label path="email"><spring:message code="label.email"></spring:message></form:label></td>
<td><form:input path="email"></form:input></td>
</tr>
<tr>
<td><form:label path="telephone"><spring:message code="label.telephone"></spring:message></form:label></td>
<td><form:input path="telephone"></form:input></td>
</tr>
<security:authorize ifAnyGranted="ROLE_ADMIN">
<tr>
<td colspan="2">
<input type="submit" value="<spring:message code="label.add"/>"/>
</td>
</tr>
</security:authorize>
</table>
</form:form>
<h3>Employees</h3>
<c:if test="${!empty employeeList}">
<table class="data">
<tr>
<th>Name</th>
<th>Email</th>
<th>Telephone</th>
<th></th>
</tr>
<c:foreach items="${employeeList}" var="emp">
<tr>
<td>${emp.lastname}, ${emp.firstname} </td>
<td>${emp.email}</td>
<td>${emp.telephone}</td>
<td>delete</td>
</tr>
</c:foreach>
</table>
</c:if>
</body>
</html>
以下是上述浏览器屏幕更改的结果。
员工管理界面
应用安全标签后,“添加”按钮将消失,并在屏幕上显示登录的用户名“ lokesh”。
Screen after applying security tags in JSP
让我知道是否仍然不清楚。
祝您学习愉快!
Spring Security – JDBC 用户服务示例
原文: https://howtodoinjava.com/spring-security/jdbc-user-service-based-spring-security-example/
在先前的与 spring 3 安全演示应用程序有关的帖子中,使用了配置文件中的默认用户服务,我们了解了如何在登录页面后面保护应用程序。 在那篇文章中,用户名和密码存储在application-security.xml
文件本身中。 现在是时候取消配置这些身份验证参数并将其存储在数据库中了。
有两种方法可以执行此操作,即使用自定义用户服务实现或 spring 提供的 jdbc 用户服务。 在这篇文章中,我将展示使用第二种方法的方法,即 jdbc 用户服务。
Sections in this post:
Create tables in database
Update security configuration to use jdbc user service
Test the application
背景信息
除了 spring 容器中其他内置的用户详细信息服务之外,JDBC 用户详细信息实现也是其中之一。 使用标签jdbc-user-service
进行配置。
就像其他 spring 功能一样,这也附带了可以使用的默认实现,例如
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource" />
</authentication-provider>
</authentication-manager>
以上定义将使用已配置数据库中的默认表。 它假定为:
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(50) not null,
enabled boolean not null);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
但是,在我的示例中,我根据自己的选择创建了两个表,并决定使用 sql 查询将数据提供给 jdbc 用户服务。
在数据库中创建表
因此,让我们在数据库中创建我们自己的表:
-- ----------------------------
-- Table structure for `tbl_users`
-- ----------------------------
DROP TABLE IF EXISTS `tbl_users`;
CREATE TABLE `tbl_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(100) NOT NULL,
`password` varchar(20) NOT NULL,
`enabled` int(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
用于存储角色和用户名映射的表。
-- ----------------------------
-- Table structure for `tbl_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `tbl_user_role`;
CREATE TABLE `tbl_user_role` (
`userid` int(11) NOT NULL,
`rolename` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
这些表具有所有必需的信息,例如用户名,密码,启用状态和分配的角色。 JDBC 用户服务仅对作为参数提供的用户名需要这些值。这些值将通过以下 sql 查询获取:
SELECT USERNAME, PASSWORD, CASE ENABLED WHEN 1 THEN 'true' ELSE 'false' END 'ENABLED' FROM TBL_USERS WHERE USERNAME=?;
+----------+----------+---------+
| USERNAME | PASSWORD | ENABLED |
+----------+----------+---------+
| lokesh | password | true |
+----------+----------+---------+
查询以选择角色名称。
SELECT u.USERNAME, r.ROLENAME
FROM TBL_USERS u, TBL_USER_ROLE r
WHERE u.ID = r.USERID
AND u.USERNAME=?;
+----------+-----------+
| USERNAME | ROLENAME |
+----------+-----------+
| lokesh | ROLE_USER |
+----------+-----------+
更新安全性配置以使用 jdbc 用户服务
可以按照以下步骤进行:
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
SELECT USERNAME, PASSWORD, CASE ENABLED WHEN 1 THEN 'true' ELSE 'false' END 'ENABLED'
FROM TBL_USERS
WHERE USERNAME=?;"
authorities-by-username-query="
SELECT u.USERNAME, r.ROLENAME
FROM TBL_USERS u, TBL_USER_ROLE r
WHERE u.ID = r.USERID
AND u.USERNAME=?;"
/>
</authentication-provider>
</authentication-manager>
JDBC 用户服务实现提供了这两个属性,以使用户名与密码匹配,然后使用户名与授予的角色或权限匹配。
- 用户按用户名查询
- 通过用户名查询权限
测试应用程序
现在,在应用程序服务器中再次构建并在应用程序之上运行。 它将完全符合先前教程 中 xml 配置的身份验证/授权的工作方式。
祝您学习愉快!
Spring Security UserDetailsService
示例
原文: https://howtodoinjava.com/spring-security/custom-userdetailsservice-example-for-spring-3-security/
学习在 Spring 应用程序的authentication-provider
中自定义UserDetailsService
实现,以获取自定义User
对象,以及如何在应用程序中使用它。
Spring UserDetailsService
接口
UserDetailsService
界面用于为任何给定用户查找用户名,密码和 GrantedAuthorities
。
此接口仅提供实现类需要实现的一种方法:
UserDetailsService.java
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
这里 UserDetails
是核心用户信息的容器。 根据文档,出于安全目的,Spring Security 不会直接使用其实现。 它们只是存储用户信息,这些信息随后封装到 Authentication
对象中。 这允许将与安全无关的用户信息(例如电子邮件地址,电话号码等)存储在方便的位置。 一个非常好的示例实现可以类似于 User
类。
在此示例中,
AuthenticationProvider
只需通过将UsernamePasswordAuthenticationToken
中提交的密码与UserDetailsService
加载的密码进行比较,即可对用户进行身份验证。
UserDetailsService
实现
1)配置身份验证供应器
我正在提出基于 Spring 登录表单的安全性中编写的代码库。 在application-security.xml
文件中,我将更新配置以将EmployeeDao
用作自定义用户详细信息服务。
application-security.xml
< ?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll"></intercept-url>
<intercept-url pattern="/logout" access="permitAll"></intercept-url>
<intercept-url pattern="/accessdenied" access="permitAll"></intercept-url>
<intercept-url pattern="/**" access="hasRole('ROLE_USER')"></intercept-url>
<form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied"></form-login>
<logout logout-success-url="/logout"></logout>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="employeeDAO" />
</authentication-manager>
</beans:beans>
2)配置数据源
另外,完整的employee-servlet.xml
文件如下所示:
employee-servlet.xml
< ?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop/ http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee/ http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang/ http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx/ http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util/ http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan base-package="com.howtodoinjava.controller" />
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages"></property>
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.properties"></bean>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
p:password="${jdbc.password}"></bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<value>
hibernate.connection.provider_class=org.hibernate.connection.C3P0ConnectionProvider
hibernate.dialect=org.hibernate.dialect.SQLServer2008Dialect
hibernate.default_schema=dbo
hibernate.show_sql=true
</value>
</property>
</bean>
<bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>
<bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl"></bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
3)在 Dao 中实现UserDetailsService
现在,我们必须更新EmployeeDaoImpl.java
以实现UserDetailsService
接口和重写方法loadUserByUsername()
。
EmployeeDaoImpl.java
EmployeeDaoImpl.java
package com.howtodoinjava.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.entity.EmployeeEntity;
@Repository
public class EmployeeDaoImpl implements EmployeeDAO, UserDetailsService {
@Autowired
private SessionFactory sessionFactory;
@Override
public void addEmployee(EmployeeEntity employee) {
this.sessionFactory.getCurrentSession().save(employee);
}
@SuppressWarnings("unchecked")
@Override
public List<EmployeeEntity> getAllEmployees() {
return this.sessionFactory.getCurrentSession().createQuery("from Employee").list();
}
@Override
public void deleteEmployee(Integer employeeId) {
EmployeeEntity employee = (EmployeeEntity) sessionFactory.getCurrentSession().load(
EmployeeEntity.class, employeeId);
if (null != employee) {
this.sessionFactory.getCurrentSession().delete(employee);
}
}
@SuppressWarnings("deprecation")
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException
{
System.out.println("Getting access details from employee dao !!");
// Ideally it should be fetched from database and populated instance of
// #org.springframework.security.core.userdetails.User should be returned from this method
UserDetails user = new User(username, "password", true, true, true, true, new GrantedAuthority[]{ new GrantedAuthorityImpl("ROLE_USER") });
return user;
}
}
在上面的 Dao 中,我使用了最少的代码来演示所涉及的类的用法,并且在企业应用程序中,应该对数据库进行适当的访问,并且应该设置用户的密码及其角色。
整个想法是用方法内部的填充值返回User
实例。 如果您还有其他要求,那么您也可以自由实现UserDetails
接口,并且 spring 不会阻止您使用它。
示例
要测试该应用程序,只需在浏览器窗口中单击 URL “http://localhost:8080/Spring3HibernateIntegration
”。 一个登录框将如下所示:
现在,使用正确的用户名和密码(即lokesh
和password
)登录,您可以进入应用程序,并出现员工管理屏幕。 否则,访问被拒绝的页面将显示如下:
下载源码
学习愉快!
Spring Security 基本身份验证示例
原文: https://howtodoinjava.com/spring-security/http-basic-authentication-example-using-spring-3/
基本身份验证通常用于无状态客户端,该客户端在每个请求上传递其凭据。 通常将其与基于表单的身份验证结合使用,在这种情况下,既可以通过基于浏览器的用户界面也可以通过网络服务来使用应用程序。 但是,基本身份验证以纯文本形式传输密码,因此,仅应在诸如 HTTPS 之类的加密传输层上实际使用该密码。
由于必须随每个 HTTP 请求一起发送基本身份验证标头,因此 Web 浏览器需要在合理的时间内缓存凭据,以避免不断提示用户输入用户名和密码。 缓存策略因浏览器而异。
Spring Security 中的BasicAuth
实现基本 http 身份验证的最简单解决方案是在 Spring Security配置文件中使用“ http-basic
”标签,如下所示:
<http>
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
</http>
应用程序中的上述设置将强制用户验证应用程序中的任何网页或任何其他资源。 有趣的是,您无需创建任何登录页面或会话管理机制。 浏览器将在用户代表您之前显示一个登录框。 并且因为每个请求都包含 http 无状态机制中的身份验证信息,所以您也不需要维护会话。
基本身份验证演示
在 Spring Security配置中配置basic-auth
在基于 Spring 登录表单的安全示例中创建的员工管理应用程序中,我们手动创建了登录表单并将其配置为各种 URL 模式。
让我们对其进行修改以使用 http 基本身份验证。 我们修改后的application-security.xml
现在看起来像这样。
application-security.xml
< ?xml version="1.0" encoding="UTF-8"?>
xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<intercept-url pattern="/accessdenied" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<logout logout-success-url="/logout" />
<http-basic />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user lokesh" password="password" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
验证BasicAuth
现在,让我们再次构建应用程序,然后在应用程序服务器中运行它。
1)在浏览器中点击网址http://localhost:8080/Spring3HibernateIntegration
出现登录窗口。 请注意,这是浏览器生成的登录框,应用程序仅向浏览器提供了相关的标头。
HTTP 基本认证窗口
2)输入错误的用户名和密码
这将使浏览器再次显示清除的登录框,或者在某些情况下将显示错误页面。
HTTP 基本认证中的登录错误
3)输入正确的用户名和密码
正确的用户名和密码:“lokesh
”和“password
”。 当您输入上述凭证时,员工管理屏幕将出现在浏览器屏幕中。
员工管理界面
如果您在运行上述配置时发现任何问题,请告诉我。
学习愉快!
Spring – IoC 容器
原文: https://howtodoinjava.com/spring-core/different-spring-ioc-containers/
Spring IoC 容器是 Spring 框架的核心。 容器将创建对象,将它们连接在一起,进行配置,并管理从创建到销毁的整个生命周期。 Spring 容器使用依赖项注入(DI)来管理组成应用程序的组件。
Spring 在提供两种类型的容器。
BeanFactory
容器ApplicationContext
容器
阅读更多:控制反转和依赖注入
1. BeanFactory
BeanFactory
本质上不过是一个高级工厂的接口,该工厂能够维护不同 Bean 及其依赖项的注册表。
BeanFactory
使我们能够读取 bean 定义并使用 bean 工厂访问它们。
1.1. 如何创建BeanFactory
仅使用BeanFactory
时,我们可以创建一个并以 XML 格式读取一些 bean 定义,如下所示:
How to Create XmlBeanFactory
InputStream is = new FileInputStream("beans.xml");
BeanFactory factory = new XmlBeanFactory(is);
//Get bean
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
创建 bean 工厂的其他方法如下:
How to Create XmlBeanFactory
Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
基本上就是全部。 使用getBean(String)
,您可以检索 bean 的实例; BeanFactory
的客户端视图非常简单。
1.2. BeanFactory
方法
BeanFactory
接口只有六个方法可用于调用客户端代码:
boolean containsBean(String)
:如果BeanFactory
包含与给定名称匹配的 bean 定义或 bean 实例,则返回true
Object getBean(String)
:返回以给定名称注册的 bean 的实例。 根据BeanFactory
配置对 bean 的配置方式,将返回单例(因此是共享实例)或新创建的 bean。 如果找不到该 bean(在这种情况下它将是NoSuchBeanDefinitionException
),或者在实例化和准备该 bean 时发生异常,则会抛出BeansException
Object getBean(String, Class)
:返回以给定名称注册的 bean。 返回的 bean 将被强制转换为给定的Class
。 如果无法投射 bean,则将引发相应的异常(BeanNotOfRequiredTypeException
)。 此外,getBean(String)
方法的所有规则均适用(请参见上文)Class getType(String name)
:返回具有给定名称的 bean 的Class
。 如果找不到与给定名称对应的 bean,将抛出NoSuchBeanDefinitionException
boolean isSingleton(String)
:确定以给定名称注册的 bean 定义或 bean 实例是否为单例。 如果找不到与给定名称相对应的 bean,将抛出NoSuchBeanDefinitionException
String[] getAliases(String)
:返回给定 bean 名称的别名(如果在 bean 定义中定义了别名)
2. ApplicationContext
ApplicationContext
容器添加了更多企业特定的功能,例如从属性文件解析文本消息的功能以及将应用程序事件发布到感兴趣的事件监听器的功能。 该容器由org.springframework.context.ApplicationContext
接口定义。
ApplicationContext
容器包含BeanFactory
容器的所有功能,因此通常建议在BeanFactory
上使用。 BeanFactory
仍可用于数据量和速度非常重要的轻量级应用程序,例如移动设备或基于 applet 的应用程序。
2.1. ApplicationContext
的类型
最常用的ApplicationContext
实现是:
FileSystemXmlApplicationContext
– 此容器从 XML 文件加载 Bean 的定义。 在这里,您需要向构造函数提供 XML bean 配置文件的完整路径。ClassPathXmlApplicationContext
– 此容器从 XML 文件加载 Bean 的定义。 在这里,您不需要提供 XML 文件的完整路径,但是需要正确设置 CLASSPATH,因为此容器将在 CLASSPATH 中查找 bean 配置 XML 文件。WebXmlApplicationContext
– 此容器从 Web 应用程序中加载带有所有 bean 定义的 XML 文件。
2.2. 如何创建ApplicationContext
用于应用程序上下文实例化的示例代码如下所示。
How to create ApplicationContext
ApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
将我的问题放在评论部分。
学习快乐!
参考文献:
BeanFactory
Java 文档
ApplicationContext
Java 文档
使用 JUnit 测试 Spring Security Auth
原文: https://howtodoinjava.com/junit/how-to-unit-test-spring-security-authentication-with-junit/
学习使用InMemoryDaoImpl
使用 JUnit 测试用例测试 Spring Security认证。 还学习以编程方式构建完全填充的authentication
对象,然后在应用程序中使用它。
SecurityContextHolder
Spring Security 基于安全性上下文,本质上是静态的。 从本质上讲,这意味着您无需将其引用注入到 spring 容器中的 bean 或类中。 您只需使用SecurityContextHolder.getContext()
方法即可随时访问 spring 上下文。
该上下文具有实际主体或用户的引用,我们必须对其访问权限进行验证。
Spring Security 单元测试
我正在创建一个简单的 Maven 项目,并且将编写最少的代码,以便我可以专注于仅测试此帖子范围内的内容,即身份验证。 然后,我将用一个需要“ ROLE_USER
”的单一方法编写一个演示服务类。 如果尝试访问此方法而没有“ ROLE_USER
”,则将获得预期的AccessDeniedException
。 很简单,不是吗?
步骤 1)创建项目
让我们使用以下命令创建 Java 项目:
Console
$ mvn archetype:generate -DgroupId=com.howtodoinjava
-DartifactId=SpringPasswordHashingDemo
-DarchetypeArtifactId=maven-archetype-quickstart
-DinteractiveMode=false
现在,使用以下依赖项更新pom.xml
,并运行命令mvn:eclipse:eclipse
以支持项目 Eclipse。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>SpringPasswordHashingDemo</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringPasswordHashingDemo</name>
<url>http://maven.apache.org</url>
<properties>
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
</properties>
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${org.springframework.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
步骤 2)创建安全配置文件
我创建了application-security.xml
文件,并将安全配置放入其中。
application-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<global-method-security secured-annotations="enabled" />
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="lokesh" password="password1" authorities="ROLE_USER" />
<user name="admin" password="password2" authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="demoService" class="com.howtodoinjava.DemoService"/>
</beans:beans>
步骤 3)建立受保护的方法
DemoService.java
package com.howtodoinjava;
import org.springframework.security.access.annotation.Secured;
public class DemoService
{
@Secured("ROLE_USER")
public void method()
{
System.out.println("Method called");
}
}
步骤 4)使用 JUnit 测试身份验证
在 junit 测试中,我们将以编程方式配置 spring 上下文,然后将通过默认用户详细信息服务中的用户名访问用户。 在极端情况下,它是内存中实现,在您的情况下,它可能与某些基于 jdbc 的用户详细信息服务或某些其他自定义用户详细信息服务不同。 因此,请相应地修改查找。
我们将测试各种方案,例如有效用户,无效用户,无效角色等。您可以根据选择添加/删除方案。
TestDemoService.java
package com.howtodoinjava;
import java.util.ArrayList;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.memory.InMemoryDaoImpl;
public class TestDemoService {
static ApplicationContext applicationContext = null;
static InMemoryDaoImpl userDetailsService = null;
/**
* Initialize the application context to re-use in all test cases
* */
@BeforeClass
public static void setup()
{
//Create application context instance
applicationContext = new ClassPathXmlApplicationContext("application-security.xml");
//Get user details service configured in configuration
userDetailsService = applicationContext.getBean(InMemoryDaoImpl.class);
}
/**
* Test the valid user with valid role
* */
@Test
public void testValidRole()
{
//Get the user by username from configured user details service
UserDetails userDetails = userDetailsService.loadUserByUsername ("lokesh");
Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
DemoService service = (DemoService) applicationContext.getBean("demoService");
service.method();
}
/**
* Test the valid user with INVALID role
* */
@Test (expected = AccessDeniedException.class)
public void testInvalidRole()
{
UserDetails userDetails = userDetailsService.loadUserByUsername ("lokesh");
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_INVALID"));
Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), authorities);
SecurityContextHolder.getContext().setAuthentication(authToken);
DemoService service = (DemoService) applicationContext.getBean("demoService");
service.method();
}
/**
* Test the INVALID user
* */
@Test (expected = AccessDeniedException.class)
public void testInvalidUser()
{
UserDetails userDetails = userDetailsService.loadUserByUsername ("admin");
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_INVALID"));
Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), authorities);
SecurityContextHolder.getContext().setAuthentication(authToken);
DemoService service = (DemoService) applicationContext.getBean("demoService");
service.method();
}
}
如您所见,所有测试用例均按预期通过。
学习快乐!
源码下载
使用@PreAuthorize
和@Secured
的 Spring 方法安全性
原文: https://howtodoinjava.com/spring-security/spring-3-method-level-security-example-using-preauthorize-and-secured/
Spring 框架使保护您的应用程序变得非常容易,您只需要正确地使用一些基本配置即可! 此安全性可以应用于 Web 应用程序中的多个级别。 Spring 对这些级别的基本支持:
- 网址级别的安全性
- 方法级别的安全性
- 实体或对象级别的安全性
在此 Spring Security 教程中,学习使用诸如@PreAuthorize
和@Secured
之类的注解来应用方法安全性。
启用@Secured
和@PreAuthorize
方法级别安全性的核心是配置元素<global-method-security/>
。 这需要在 spring 的配置文件中定义。 此元素用于在应用程序中启用基于注解的安全性(通过在元素上设置适当的属性)。 您只应声明一个<global-method-security/>
元素。 例如
<global-method-security pre-post-annotations="enabled" />
以上配置将在代码中启用 @PreAuthorize
和 @PostAuthorize
注解。
//要么
上述配置的另一种变化是:
<global-method-security secured-annotations="enabled" />
这将在您的代码中启用 @Secured
注解。
这些注解采用一个字符串参数,该参数可以是role-name
或表达式,并且取决于您对<http>
元素的use-expression
值的配置使用。
如果use-expression
设置为 true,则应在注解内使用表达式,否则应直接使用角色名称。
如果您需要定义简单的规则,而不是根据用户的权限列表检查角色名称,则基于表达式的注解是一个不错的选择。 您可以在同一应用程序中启用一种以上类型的注解,但应避免在同一接口或类中混合使用多种注解类型,以免造成混淆。
测试安全性注解
为了在运行的应用程序中测试以上注解,我使用的是先前教程的代码库,该代码库与基于登录表单的安全性相关。
此应用程序已经过 URL 级别安全保护。 现在,我们还将添加对方法级别安全性的支持。
修改application-security.xml
配置
为了启用对方法级别安全性的支持,我将使用<global-method-security>
标记更新application-security.xml
文件,如下所示:
application-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<global-method-security pre-post-annotations="enabled" />
<http auto-config="false" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<intercept-url pattern="/accessdenied" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" />
<logout logout-success-url="/logout" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="lokesh" password="password" authorities="ROLE_USER" />
<user name="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl" />
<beans:bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl" />
</beans:beans>
其余所有代码与以前的教程相同。
注解方法以确保安全
在本教程中,我希望具有角色admin
的用户只能将员工添加到员工集合。 像以前一样允许其他操作。 为此,我将在EmployeeDaoImpl.java
中注解add
方法,如下所示:
EmployeeDaoImpl.java
package com.howtodoinjava.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.entity.EmployeeEntity;
@Repository
public class EmployeeDaoImpl implements EmployeeDAO {
@Autowired
private SessionFactory sessionFactory;
@PreAuthorize("hasRole('ROLE_ADMIN')")
@Override
public void addEmployee(EmployeeEntity employee) {
//System.out.println(((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getAuthorities());
this.sessionFactory.getCurrentSession().save(employee);
}
@SuppressWarnings("unchecked")
@Override
public List<EmployeeEntity> getAllEmployees() {
return this.sessionFactory.getCurrentSession().createQuery("from Employee").list();
}
@Override
public void deleteEmployee(Integer employeeId) {
EmployeeEntity employee = (EmployeeEntity) sessionFactory.getCurrentSession().load(
EmployeeEntity.class, employeeId);
if (null != employee) {
this.sessionFactory.getCurrentSession().delete(employee);
}
}
}
@PreAuthorize
注解将测试使用的登录帐户是否具有“ ROLE_ADMIN
”授权。 如果用户没有此权限,将抛出拒绝访问的异常。
示例
我们的应用程序已配置完毕,可以部署了。 所以,让我们做吧!
1)在浏览器窗口中点击网址“ http://localhost:8080/Spring3HibernateIntegration/login
”
登录窗口
由于所有 URL 均受保护,因此将出现一个登录窗口。
2)使用用户名“ lokesh
”和密码“ password
”登录,然后尝试添加员工
拒绝访问消息
由于lokesh
没有管理员权限,将引发拒绝访问的异常。
3)使用用户名“admin
”和密码“password
”登录,然后尝试添加员工
员工管理界面
管理员可以添加员工,因为已为其分配了“ROLE_ADMIN
”。
如果您在运行该应用程序时遇到任何问题,请告诉我。
学习愉快!
下载源码
Spring ORM
Spring 3.2.5 AbstractRoutingDataSource
示例
原文: https://howtodoinjava.com/spring-orm/spring-3-2-5-abstractroutingdatasource-example/
AbstractRoutingDataSource
是 Spring 框架中非常有用的功能,如果您确实设计允许基于某些条件的多个数据库, 可能会针对每个用户请求更改。 一个例子可以是数据库的使用。 当用户属于某个语言环境时,可以使用特定的数据库;如果用户属于另一个语言环境,则可以切换到另一个语言环境。
根据定义,AbstractRoutingDataSource
是一种抽象数据源实现,它基于查找键将getConnection()
调用路由到各种目标DataSource
之一。 后者通常(但不是必须)通过某些线程绑定的事务上下文来确定。
在这篇文章中,我将提供此功能的示例。 我正在为使用路由数据源,根据用户的语言环境为用户选择适当的数据源。 在此示例中,我仅配置了两个语言环境“en
”和“es
”。 您可以配置任意多个。 此外,语言环境不仅是更改数据源的唯一标准。 如果您有其他要求,请随时添加自己的逻辑。
还请注意,我使用的是开发的源代码,例如在 Hibernate 4 和 Spring 3.2.5 之间集成。
下载源码
生成示例应用程序
步骤 1)扩展AbstractRoutingDataSource
类
这是必需的,因为在这里您将决定要使用的数据源。
package com.howtodoinjava.controller;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class MyRoutingDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
String language = LocaleContextHolder.getLocale().getLanguage();
System.out.println("Language obtained: "+ language);
return language;
}
}
步骤 2)在jdbc.properties
中配置两个数据源
这不是必需的,在您的实现中可能不是必需的。
jdbc.databaseurlOne=jdbc:mysql://127.0.0.1:3306/test
jdbc.databaseurlTwo=jdbc:mysql://127.0.0.1:3306/testTwo
步骤 3)在 spring 配置文件中配置多种类型的数据源
首先配置各种数据源,而不必担心如何访问它们。
<bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:username="${jdbc.username}"
p:password="${jdbc.password}" />
<bean id="concreteDataSourceOne"
parent="abstractDataSource"
p:url="${jdbc.databaseurlOne}"/>
<bean id="concreteDataSourceTwo"
parent="abstractDataSource"
p:url="${jdbc.databaseurlTwo}"/>
步骤 4)设置数据源的路由
在这里,您实际上将在上述步骤中为所有配置的数据源定义键值对targetDataSources
。 该值将是数据源 bean 名称,而键将是来自MyRoutingDataSource
中的defineCurrentLookupKey()
方法的结果。
如果对于任何用户请求都找不到任何内容,那么您还可以提及默认数据源。 如果将是默认值,并防止出现异常。
<bean id="dataSource" class="com.howtodoinjava.controller.MyRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="en" value-ref="concreteDataSourceOne"/>
<entry key="es" value-ref="concreteDataSourceTwo"/>
</map>
</property>
<!-- <property name="defaultTargetDataSource" ref="concreteDataSourceOne"/> -->
</bean>
步骤 5)现在使用数据源
这简单。 对?
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" /> <!-- HERE -->
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
完成所有更改后,您的 spring 配置将如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan base-package="com.howtodoinjava.controller" />
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.properties" />
<!-- Step 3 -->
<bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:username="${jdbc.username}"
p:password="${jdbc.password}" />
<bean id="concreteDataSourceOne"
parent="abstractDataSource"
p:url="${jdbc.databaseurlOne}"/>
<bean id="concreteDataSourceTwo"
parent="abstractDataSource"
p:url="${jdbc.databaseurlTwo}"/>
<!-- Step 4 -->
<bean id="dataSource" class="com.howtodoinjava.controller.MyRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="en" value-ref="concreteDataSourceOne"/>
<entry key="es" value-ref="concreteDataSourceTwo"/>
</map>
</property>
<!-- <property name="defaultTargetDataSource" ref="concreteDataSourceOne"/> -->
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
p:basenames="messages" />
<!-- Declare the Interceptor -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="locale" />
</mvc:interceptors>
<!-- Declare the Resolver -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>
<bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl"></bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
测试应用程序
在测试应用程序之前,我已经在其中创建了两个数据库模式和两个相似的表。 除了其中的数据外,这两个表完全相同。 我有意让它保持不变,以证明两个不同的请求实际上正在访问不同的数据库。
delimiter $$
CREATE DATABASE 'test' /*!40100 DEFAULT CHARACTER SET latin1 */$$
USE test$$
CREATE TABLE 'employee' (
'ID' int(11) NOT NULL AUTO_INCREMENT,
'FIRSTNAME' varchar(30) DEFAULT NULL,
'LASTNAME' varchar(30) DEFAULT NULL,
'TELEPHONE' varchar(15) DEFAULT NULL,
'EMAIL' varchar(30) DEFAULT NULL,
'CREATED' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ('ID')
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1$$
INSERT INTO 'test'.'employee' ('ID','FIRSTNAME','LASTNAME','TELEPHONE','EMAIL','CREATED')
VALUES (4,'Lokesh','Gupta','9811111111','howtodoinjava@gmail.com',CURRENT_TIMESTAMP);
CREATE DATABASE 'testtwo' /*!40100 DEFAULT CHARACTER SET latin1 */$$
USE testtwo$$
CREATE TABLE 'employee' (
'ID' int(11) NOT NULL AUTO_INCREMENT,
'FIRSTNAME' varchar(30) DEFAULT NULL,
'LASTNAME' varchar(30) DEFAULT NULL,
'TELEPHONE' varchar(15) DEFAULT NULL,
'EMAIL' varchar(30) DEFAULT NULL,
'CREATED' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ('ID')
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1$$
INSERT INTO 'testtwo'.'employee' ('ID','FIRSTNAME','LASTNAME','TELEPHONE','EMAIL','CREATED')
VALUES (1,'Rakesh','Shukla','1234','email',CURRENT_TIMESTAMP);
1)点击网址:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/?locale=zh-CN
2)点击网址:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/?locale=es
3)点击网址:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/?locale=fr
这将导致异常,因为我们既没有为此语言环境设置任何数据源,也没有任何默认数据源。
HTTP Status 500 - Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [fr]
type Exception report
message Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [fr]
下载源码
如果需要更多说明,请给我评论。
学习愉快!
Spring 3 和 Hibernate 4 集成示例教程
原文: https://howtodoinjava.com/spring-orm/spring3-hibernate4-integration-example/
如果您浏览了我以前的文章,该文章也是同一主题,即 Spring 3 + Hibernate 集成 。 我在那篇文章中收到了很多评论和反馈,其中大部分是因为人们试图在现有项目中使用代码/依赖关系,或者他们没有使用 Maven 来构建项目。
在这两种情况下,正确识别和使用项目依赖项仍然是主要挑战。 因此,我决定写另一篇根本不使用 Maven 的文章。 如果确实如此,我已经在项目源代码本身中添加了所有必需的 jar 文件。
此外,我具有 Spring 的最新版本,即 3.0.5 至 3.2.5.RELEASE。
下载源码
1. 开发环境
- Eclipse Juno RELEASE
- JDK 1.6
- Hibernate 4.0.1
- Spring 3.2.5.RELEASE
- MySQL 数据库
- Tomcat 7
对于那些直接来到本文的人,让我们逐步讲解 Spring Hibernate 集成示例。
2. 数据库架构
在开始采取行动之前,请确保您已经使用下表创建了数据库模式:
CREATE TABLE EMPLOYEE
(
ID INT PRIMARY KEY AUTO_INCREMENT,
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(30),
TELEPHONE VARCHAR(15),
EMAIL VARCHAR(30),
CREATED TIMESTAMP DEFAULT NOW()
);
3. 创建 Maven Eclipse 动态 Web 项目
这不是困难的一步,而是重要的一步。 请确保您正在创建“Web 应用程序”而不是简单的“java 项目”。 我已经创建了名称为Spring3.2.5Hibernate4.0.1Integration
的项目。
4. 创建配置
我不会在本节中强调更多,因为我没有使用上一篇文章中已经存在的新代码或函数集。 因此,请访问先前的示例 Hibernate+Spring 3 集成示例,以获取更多详细信息。
由于 Spring 版本的改进,我想在这里给出修改后的配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan base-package="com.howtodoinjava.controller" />
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.properties" />
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
p:password="${jdbc.password}" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>
<bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl"></bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
5. 下载并复制lib
文件夹中所需的 jar 文件
以前的帖子是关于 Maven 的,所以很多工作似乎都是多余的。 但是,对于使用 ANT 的用户,他们必须使用下载的 jar。 好吧,这一次,我已经为您完成了这项工作。 以下是此项目中使用的 jar 文件列表。
antlr-2.7.7.jar
aopalliance-1.0.jar
commons-collections-3.2.1.jar
commons-dbcp-1.4.jar
commons-lang-2.5.jar
commons-logging-1.1.1.jar
commons-pool-1.5.4.jar
dom4j-1.6.1.jar
hibernate-commons-annotations-3.2.0.Final.jar
hibernate-core-4.0.1.Final.jar
hibernate-jpa-2.1-api-1.0.0.Draft-16.jar
javassist-3.12.1.GA.jar
jboss-logging-3.1.1.GA.jar
jstl-1.2.jar
jta-1.1.jar
junit-4.11.jar
mysql-connector-java-5.1.9.jar
slf4j-api-1.6.1.jar
spring-aop-3.2.5.RELEASE.jar
spring-beans-3.2.5.RELEASE.jar
spring-context-3.2.5.RELEASE.jar
spring-context-support-3.2.5.RELEASE.jar
spring-core-3.2.5.RELEASE.jar
spring-expression-3.2.5.RELEASE.jar
spring-jdbc-3.2.5.RELEASE.jar
spring-orm-3.2.5.RELEASE.jar
spring-tx-3.2.5.RELEASE.jar
spring-web-3.2.5.RELEASE.jar
spring-webmvc-3.2.5.RELEASE.jar
standard-1.1.2.jar
将上述所有 jar 文件复制到lib
文件夹中。
完成此步骤后,您的项目应如下所示:
spring 3 项目层次结构
6. Spring Hibernate 集成示例演示
结果将类似于以前的帖子。 为了提醒您,屏幕将如下所示:
URL:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/
spring 3 + hibernate 集成示例
下载源码
学习愉快!
Spring Hibernate 集成示例
原文: https://howtodoinjava.com/spring-orm/spring-hibernate-integration-example/
此 Spring Hibernate 集成教程重点介绍 Hibernate 与 Spring 框架的结合使用。 我将展示这种集成的结果,一个基本的端到端应用程序流看起来如何。
1. 开发环境
- Eclipse Juno IDE
- JDK 1.7
- JBoss 7
- Maven
- Spring 3.0.5
- Hibernate 3.6.3
对于那些不使用 maven 进行依赖管理并且依赖下载的 jar 文件的用户。 我创建了另一个项目,其中包含要下载的所有必需 jar 文件。 另外,我已经使用 Spring 3.2.5 和 hibernate 4 完成了集成。
因此,如果您属于以上两个类别中的任何一个,请阅读以下链接文章,以防出现任何问题。 Spring + Hibernate 4 集成教程,包含所有 jar 文件
要构建此示例项目,我将逐步指导您 。 通过这种方式,我们还可以遍历一些概念。
2. 创建 Maven Web 项目
在命令提示符下使用以下命令创建 maven Web 项目。
$ mvn archetype:generate -DgroupId=com.howtodoinjava.app
-DartifactId=Spring3HibernateIntegration
-DarchetypeArtifactId=maven-archetype-webapp
-DinteractiveMode=false
现在,使用下面的 maven 命令将此 Web 项目转换为 eclipse 动态 Web 项目。
$ cd Spring3HibernateIntegration
$ mvn eclipse:eclipse -Dwtpversion=2.0
3. Maven 依赖
更新pom.xml
文件以包含 spring 和 hibernate 依赖项。 它还将包括在项目参考中添加的 mysql 驱动程序。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.app</groupId>
<artifactId>Spring3HibernateIntegration</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Spring3HibernateIntegration Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- JBoss repository for Hibernate -->
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<properties>
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.3.Final</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
</dependencies>
<build>
<finalName>Spring3HibernateIntegration</finalName>
</build>
</project>
4. 创建数据库架构
现在让我们决定数据库架构结构,因为在下一步中编写实体类时将需要它。
CREATE TABLE EMPLOYEE
(
ID INT PRIMARY KEY AUTO_INCREMENT,
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(30),
TELEPHONE VARCHAR(15),
EMAIL VARCHAR(30),
CREATED TIMESTAMP DEFAULT NOW()
);
5. 编写 Hibernate 实体类
现在该写EmployeeEntity
了。 此类将使用 Hibernate 模式映射到数据库中的Employee
表。 JPA 将在持久性管理设置中包括任何用@Entity
注解的类。 如果您使用注解,则不需要persistence.xml
。
package com.howtodoinjava.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="EMPLOYEE")
public class EmployeeEntity
{
@Id
@Column(name="ID")
@GeneratedValue
private Integer id;
@Column(name="FIRSTNAME")
private String firstname;
@Column(name="LASTNAME")
private String lastname;
@Column(name="EMAIL")
private String email;
@Column(name="TELEPHONE")
private String telephone;
//Setters and getters
}
6. 编写数据访问代码
让我们编写将与数据库交互负责的 DAO 类。 此类实际上将使用 Hibernate 会话工厂进行数据库交互。 会话工厂实现将在运行时使用 spring IoC 功能 注入参考变量。
EmployeeDao.java
package com.howtodoinjava.dao;
import java.util.List;
import com.howtodoinjava.entity.EmployeeEntity;
public interface EmployeeDAO
{
public void addEmployee(EmployeeEntity employee);
public List<EmployeeEntity> getAllEmployees();
public void deleteEmployee(Integer employeeId);
}
EmployeeDaoImpl.java
package com.howtodoinjava.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.entity.EmployeeEntity;
@Repository
public class EmployeeDaoImpl implements EmployeeDAO
{
@Autowired
private SessionFactory sessionFactory;
@Override
public void addEmployee(EmployeeEntity employee) {
this.sessionFactory.getCurrentSession().save(employee);
}
@SuppressWarnings("unchecked")
@Override
public List<EmployeeEntity> getAllEmployees() {
return this.sessionFactory.getCurrentSession().createQuery("from EmployeeEntity").list();
}
@Override
public void deleteEmployee(Integer employeeId) {
EmployeeEntity employee = (EmployeeEntity) sessionFactory.getCurrentSession().load(
EmployeeEntity.class, employeeId);
if (null != employee) {
this.sessionFactory.getCurrentSession().delete(employee);
}
}
}
我已经写了一个管理层,由于较少的复杂性,它在本演示中似乎是多余的,但是如果您编写此层,它通常会被视为最佳实践。 该层将仅接收来自控制器的调用,并将此调用传递到 dao 层。
EmployeeManager.java
package com.howtodoinjava.service;
import java.util.List;
import com.howtodoinjava.entity.EmployeeEntity;
public interface EmployeeManager {
public void addEmployee(EmployeeEntity employee);
public List<EmployeeEntity> getAllEmployees();
public void deleteEmployee(Integer employeeId);
}
EmployeeManagerImpl.java
package com.howtodoinjava.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.howtodoinjava.dao.EmployeeDAO;
import com.howtodoinjava.entity.EmployeeEntity;
@Service
public class EmployeeManagerImpl implements EmployeeManager
{
@Autowired
private EmployeeDAO employeeDAO;
@Override
@Transactional
public void addEmployee(EmployeeEntity employee) {
employeeDAO.addEmployee(employee);
}
@Override
@Transactional
public List<EmployeeEntity> getAllEmployees() {
return employeeDAO.getAllEmployees();
}
@Override
@Transactional
public void deleteEmployee(Integer employeeId) {
employeeDAO.deleteEmployee(employeeId);
}
public void setEmployeeDAO(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
}
7. Spring 控制器和视图文件
现在是时候编写 spring 控制器和处理器方法了,这些实际上将由 spring 框架的调度程序 servlet 调用以处理实际的应用程序逻辑。
EditEmployeeController.java
package com.howtodoinjava.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.howtodoinjava.entity.EmployeeEntity;
import com.howtodoinjava.service.EmployeeManager;
@Controller
public class EditEmployeeController
{
@Autowired
private EmployeeManager employeeManager;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String listEmployees(ModelMap map)
{
map.addAttribute("employee", new EmployeeEntity());
map.addAttribute("employeeList", employeeManager.getAllEmployees());
return "editEmployeeList";
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addEmployee(@ModelAttribute(value="employee") EmployeeEntity employee, BindingResult result)
{
employeeManager.addEmployee(employee);
return "redirect:/";
}
@RequestMapping("/delete/{employeeId}")
public String deleteEmplyee(@PathVariable("employeeId") Integer employeeId)
{
employeeManager.deleteEmployee(employeeId);
return "redirect:/";
}
public void setEmployeeManager(EmployeeManager employeeManager) {
this.employeeManager = employeeManager;
}
}
现在,我们将编写应用程序的视图层,该视图层实际上是.jsp
文件。
editEmployeeList.jsp
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Spring 3 hibernate integration example on www.howtodoinjava.com</title>
</head>
<body>
<h2>Employee Management Screen : Spring 3 hibernate integration example on www.howtodoinjava.com</h2>
<form:form method="post" action="add" commandName="employee">
<table>
<tr>
<td><form:label path="firstname"><spring:message code="label.firstname"/></form:label></td>
<td><form:input path="firstname" /></td>
</tr>
<tr>
<td><form:label path="lastname"><spring:message code="label.lastname"/></form:label></td>
<td><form:input path="lastname" /></td>
</tr>
<tr>
<td><form:label path="email"><spring:message code="label.email"/></form:label></td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td><form:label path="telephone"><spring:message code="label.telephone"/></form:label></td>
<td><form:input path="telephone" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="<spring:message code="label.add"/>"/>
</td>
</tr>
</table>
</form:form>
<h3>Employees</h3>
<c:if test="${!empty employeeList}">
<table class="data">
<tr>
<th>Name</th>
<th>Email</th>
<th>Telephone</th>
<th>Action</th>
</tr>
<c:forEach items="${employeeList}" var="emp">
<tr>
<td>${emp.lastname}, ${emp.firstname} </td>
<td>${emp.email}</td>
<td>${emp.telephone}</td>
<td>delete</td>
</tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
8. Spring 分派器 Servlet
我们的 Java 代码已经完成,现在可以配置应用程序了。 让我们从web.xml
开始。 在web.xml
中,我们将为 spring 框架配置前控制器,即DispatcherServlet
。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>Archetype Created Web Application</display-name>
<welcome-file-list>
<welcome-file>/WEB-INF/index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>employee</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>employee</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/employee-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
9. Spring Hibernate 集成配置
让我们为 Hibernate 数据源,消息资源,视图解析器和其他此类对象配置 spring 框架。
employee-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop/ http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee/ http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang/ http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx/ http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util/ http://www.springframework.org/schema/util/spring-util.xsd">
<context:annotation-config />
<context:component-scan base-package="com.howtodoinjava.controller" />
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages"></property>
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.properties"></bean>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
p:password="${jdbc.password}"></bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>
<bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl"></bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
使用注解时,Hibernate 配置变得简单。
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping class="com.howtodoinjava.entity.EmployeeEntity"></mapping>
</session-factory>
</hibernate-configuration>
10. JDBC 属性
让我们提到 jdbc 连接属性和消息资源属性。
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.dialect=org.hibernate.dialect.MySQLDialect
jdbc.databaseurl=jdbc:mysql://127.0.0.1:3306/test
jdbc.username=root
jdbc.password=password
messages_en.properties
label.firstname=First Name
label.lastname=Last Name
label.email=Email
label.telephone=Telephone
label.add=Add Employee
label.menu=Actions
label.title=Employee Form
label.footer=www.HowToDoInJava.com
而已。 您的应用程序已准备好部署在您选择的服务器上。 最后,您的项目结构应如下所示。
Spring + hibernate 集成项目结构
如果您在 Eclipse 逐步中发现构建或运行此 spring hibernate 集成示例时遇到任何问题,请发表评论,我将尽力帮助您。
下载源码
更新:
如果您遇到问题:java.lang.NoClassDefFoundError: Lorg/hibernate/cache/CacheProvider
,可能的解决方案:请尝试将org.springframework.orm.hibernate3.LocalSessionFactoryBean
更改为org.springframework.orm.hibernate4.LocalSessionFactoryBean
,hibernate3 到 hibernate4。
更多参考:
http://stackoverflow.com/questions/7528862/exception-noclassdeffounderror-for-cacheprovider
https://code.google.com/archive/p/jgk-spring-recipes/wikis/Migrating_Spring31_Hibernate4.wiki
java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor
可能的解决方案:下载并将com.springsource.org.aopalliance-1.0.0.jar
并添加到你的类路径
参考:
http://forum.spring.io/forum/spring-projects/aop/74011-java-lang-noclassdeffounderror-org-aopalliance-intercept-methodinterceptor
下载源码
学习愉快!
Spring REST
Spring REST JSON 响应示例
原文: https://howtodoinjava.com/spring-restful/spring-rest-hello-world-json-example/
在 Spring REST JSON 示例中,我们将学习编写能够返回资源的 JSON 表示形式的 RESTful Web 服务。 我们将使用MappingJackson2JsonView
将视图解析为 JSON 正文。
阅读更多: Spring REST XML 教程
1. Spring REST JSON – @ResponseBody
注解
此首要技术简单易行。 我们只需要在应用程序的类路径中包含 jackson 依赖项,spring 就会自动将Jackson2JsonMessageConverter
类注册到上下文中。 每当我们从 REST API 请求资源并提供 http 标头accept: application/json
时,我们都将取回资源的 json 表示形式。
1.1. JSON 运行时依赖
pom.xml
<!-- Jackson JSON Processor -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
1.2. @ResponseBody
支持
这里
@RestController = @Controller + @ResponseBody
EmployeeRESTController.java
@RestController
public class EmployeeRESTController
{
@RequestMapping(value = "/employees")
public EmployeeListVO getAllEmployees()
{
EmployeeListVO employees = new EmployeeListVO();
EmployeeVO empOne = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
EmployeeVO empTwo = new EmployeeVO(2,"Amit","Singhal","asinghal@yahoo.com");
EmployeeVO empThree = new EmployeeVO(3,"Kirti","Mishra","kmishra@gmail.com");
employees.getEmployees().add(empOne);
employees.getEmployees().add(empTwo);
employees.getEmployees().add(empThree);
return employees;
}
@RequestMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
{
if (id <= 3) {
EmployeeVO employee = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
}
2. Spring REST JSON – MappingJackson2JsonView
支持
这是第二技术。 MappingJackson2JsonView
类还取决于类路径中 Jackson JSON 处理器库的存在,因此您无需添加任何其他内容。 完整的pom.xml
看起来像这样。
2.1. Maven 依赖
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>springrestexample</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>springrestexample Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<!-- Jackson JSON Processor -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
</dependencies>
<build>
<finalName>springrestexample</finalName>
</build>
</project>
2.2. 添加MappingJackson2JsonView
视图
使用MappingJackson2JsonView
类时,您将需要返回MappingJackson2JsonView
类型的视图名称。 因此,您将需要更改两个位置。
2.2.1. 控制器变更
您将需要从控制器方法返回视图名称。在我们的例子中,视图名称是jsonTemplate
。
package com.howtodoinjava.demo.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.howtodoinjava.demo.model.EmployeeListVO;
import com.howtodoinjava.demo.model.EmployeeVO;
@Controller
public class EmployeeRESTController
{
private EmployeeListVO getEmployeesCollection()
{
EmployeeListVO employees = new EmployeeListVO();
EmployeeVO empOne = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
EmployeeVO empTwo = new EmployeeVO(2,"Amit","Singhal","asinghal@yahoo.com");
EmployeeVO empThree = new EmployeeVO(3,"Kirti","Mishra","kmishra@gmail.com");
employees.getEmployees().add(empOne);
employees.getEmployees().add(empTwo);
employees.getEmployees().add(empThree);
return employees;
}
@RequestMapping(value = "/employees")
public String getAllEmployeesJSON(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "jsonTemplate";
}
}
2.2.2. 配置变更
您将需要将视图名称jsonTemplate
配置为类型为MappingJackson2JsonView
的 bean。 并且您将需要配置类型为BeanNameViewResolver
的视图解析器。 这样,视图名称jsonTemplate
将与MappingJackson2JsonView
匹配,并且将已解析的 JSON 响应返回给客户端。
RESTConfiguration.java
@Configuration
public class RESTConfiguration
{
@Bean
public View jsonTemplate() {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
return view;
}
@Bean
public ViewResolver viewResolver() {
return new BeanNameViewResolver();
}
}
与上述 java 配置等效的 XML 配置如下。
spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:annotation-driven />
<!-- JSON Support -->
<bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</beans>
3. Spring REST JSON 示例 – 演示
现在,当您点击 URL:http://localhost:8080/springrestexample/employees
时,您将得到此结果。
Spring REST JSON 示例
4. Spring REST JSON 示例 – 项目结构
Spring REST JSON 示例 – 项目结构
这就是带有 spring mvc 的 spring restful web 服务 json 简单示例的全部内容。 将您的问题留在我的评论中。
学习愉快!
Spring REST XML 响应示例
原文: https://howtodoinjava.com/spring-restful/spring-rest-hello-world-xml-example/
在这个 Spring REST XML 示例中,我正在使用 Spring REST 功能编写 REST API 的世界示例。 在此示例中,我将创建两个 API,这些 API 将返回资源的 XML 表示形式。
下载源码
1. Maven 依赖
让我们从运行时依赖关系开始,您需要编写这些 REST API。 实际上,您只需要 Spring MVC 支持。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>springrestexample</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>springrestexample Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>springrestexample</finalName>
</build>
</project>
注意:如果您还计划包括 JSON 支持,那么您要做的就是将 Jackson 库包含到类路径中,并且相同的 API 也将适用于 jackson。
<!-- Jackson JSON Processor -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
2. Spring MVC 配置
为了创建 API,您需要像在 Spring MVC 中一样配置应用程序。
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context/
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:annotation-driven />
</beans>
3. JAXB 注解的模型对象
您将需要用 jaxb 注解来注解模型对象,以便 JAXB 可以将 Java 对象编组为 XML 表示形式,以发送给该 API 的客户端。
EmployeeVO.java
package com.howtodoinjava.demo.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement (name = "employee")
@XmlAccessorType(XmlAccessType.NONE)
public class EmployeeVO implements Serializable
{
private static final long serialVersionUID = 1L;
@XmlAttribute
private Integer id;
@XmlElement
private String firstName;
@XmlElement
private String lastName;
@XmlElement
private String email;
public EmployeeVO(Integer id, String firstName, String lastName, String email) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public EmployeeVO(){
}
//Setters and Getters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + ", email=" + email + "]";
}
}
EmployeeListVO.java
package com.howtodoinjava.demo.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement (name="employees")
public class EmployeeListVO implements Serializable
{
private static final long serialVersionUID = 1L;
private List<EmployeeVO> employees = new ArrayList<EmployeeVO>();
public List<EmployeeVO> getEmployees() {
return employees;
}
public void setEmployees(List<EmployeeVO> employees) {
this.employees = employees;
}
}
4. REST 控制器
这是主类,它将决定哪个 API 将以哪种方式运行。
EmployeeRESTController.java
package com.howtodoinjava.demo.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.howtodoinjava.demo.model.EmployeeListVO;
import com.howtodoinjava.demo.model.EmployeeVO;
@RestController
public class EmployeeRESTController
{
@RequestMapping(value = "/employees")
public EmployeeListVO getAllEmployees()
{
EmployeeListVO employees = new EmployeeListVO();
EmployeeVO empOne = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
EmployeeVO empTwo = new EmployeeVO(2,"Amit","Singhal","asinghal@yahoo.com");
EmployeeVO empThree = new EmployeeVO(3,"Kirti","Mishra","kmishra@gmail.com");
employees.getEmployees().add(empOne);
employees.getEmployees().add(empTwo);
employees.getEmployees().add(empThree);
return employees;
}
@RequestMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
{
if (id <= 3) {
EmployeeVO employee = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
}
让我们记下一些重要的事情。
-
我们使用了
@RestController
注解。直到 Spring 3,我们可以使用@Controller
注解,这种情况下使用@ResponseBody
注解也很重要。@Controller public class EmployeeRESTController { @RequestMapping(value = "/employees") public @ResponseBody EmployeeListVO getAllEmployees() { //API code } }
Spring 4 引入了
@RestController
,它是@Controller + @ResponseBody
的组合。 因此,在使用@RestController
时,无需使用@ResponseBody
。 它是可选的。 -
在这里,我们依靠 Spring MVC
HttpMessageConverter
将对象转换为用户请求的 xml 表示形式。@ResponseBody
注解(通过@RestController
包含)告诉 Spring MVC 应该将方法的结果用作响应的主体。正如我们想要的 XML 一样,这种封送处理由 Spring 提供的
Jaxb2RootElementHttpMessageConverter
完成,如果在类路径中找到 JAXB 库,则会在 Spring 上下文中自动注册。 由于我正在使用 JRE 7 运行此应用程序,并且它具有内置的 JAXB,因此不需要通过 maven 添加外部依赖项。 -
由于有了
@ResponseBody
注解,我们不再需要视图名称,而只需返回员工对象。 -
您可以将它们包装在
ResponseEntity
中,而不是直接返回java对象。ResponseEntity
是 Spring MVC 中的一个类,与 HTTP 状态代码一起用作结果主体的对象的包装。这样可以更好地控制您在各种用例中返回给客户端的内容。 例如如果找不到给定员工 ID 的员工,则返回 404 错误。
5. 项目结构
Spring REST XML 示例 – 项目结构
测试 API
让我们测试以上 REST API。
1)点击网址:http://localhost:8080/springrestexample/employees
您也可以传递接受标头“application/xml
”。
Spring REST XML 示例 – 用于获取所有员工的 REST API
2)点击网址:http://localhost:8080/springrestexample/employees/1
Spring REST XML 示例 – 根据 ID 获取员工的 REST API
3)点击网址:http://localhost:8080/springrestexample/employees/123
Status Code: 404 Not Found
Content-Length: 0
Date: Fri, 18 Feb 2015 07:01:17 GMT
Server: Apache-Coyote/1.1
这就是使用 spring mvc 的 REST API 的 hello world 简单应用程序的全部内容。
下载源码
学习愉快!
Spring REST 控制器示例
原文: https://howtodoinjava.com/spring-restful/how-to-write-restful-webservices-using-spring-3-mvc/
学习创建 Spring REST 控制器,该控制器可以处理任何 Spring MVC 应用程序中的 REST API 调用。 它邀请添加@Controller
和@RequestMapping
注解。
为了编写此应用程序,我正在修改 Spring MVC 示例中编写的源代码。 因此,如果需要,您可以从给定的链接下载源代码。
1. 更新 Maven 依赖项
更新pom.xml
以添加对 JAXB 和 Jackson 的支持(适用于 xml 和 json 格式)。
<dependency>
<groupid>org.codehaus.jackson</groupid>
<artifactid>jackson-mapper-asl</artifactid>
<version>${jackson-mapper-asl.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupid>javax.xml.bind</groupid>
<artifactid>jaxb-api</artifactid>
<version>${jaxb-api.version}</version>
<scope>runtime</scope>
</dependency>
2. 添加ContentNegotiatingViewResolver
更新视图解析器的 bean 配置文件,并添加ContentNegotiatingViewResolver
。
<mvc:annotation-driven />
<context:component-scan base-package="com.howtodoinjava.web" />
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"></entry>
<entry key="json" value="application/json"></entry>
<entry key="xml" value="application/xml"></entry>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</list>
</property>
</bean>
3. 在模型类中添加 JAXB 注解
我正在编写 2 个类,即Users.java
和User.java
。 这些类将具有 JAXB 注解,marshaller 将使用它们将其转换为适当的 xml 或 json 格式。
它们仅作为示例,您可以编写自己的类。
Users.java
package com.howtodoinjava.model;
import java.util.Collection;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="users")
@XmlAccessorType(XmlAccessType.NONE)
public class Users
{
@XmlElement(name="user")
private Collection<User> users;
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
}
User.java
package com.howtodoinjava.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="user")
@XmlAccessorType(XmlAccessType.NONE)
public class User {
@XmlElement(name="first-name")
private String firstName;
@XmlElement(name="last-name")
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
4. 创建 REST 控制器
DemoController.java
具有 REST 专用注解,用于请求参数映射中的路径映射。 另外,我们将为请求和响应指定头属性。
DemoController.java
@Controller
@RequestMapping("/users")
public class DemoController
{
@RequestMapping(method = RequestMethod.GET, value="/{id}", headers="Accept=*/*")
public @ResponseBody User getUserById(@PathVariable String id)
{
User user = new User();
user.setFirstName("john");
user.setLastName("adward");
return user;
}
@RequestMapping(method = RequestMethod.GET, headers="Accept=*/*")
public @ResponseBody Users getAllUsers()
{
User user1 = new User();
user1.setFirstName("john");
user1.setLastName("adward");
User user2 = new User();
user2.setFirstName("tom");
user2.setLastName("hanks");
Users users = new Users();
users.setUsers(new ArrayList<User>());
users.getUsers().add(user1);
users.getUsers().add(user2);
return users;
}
}
5. Spring REST 示例
现在,让我们在 tomcat 上重新部署应用程序,并在任何 REST 客户端上访问 URL。 我正在使用RESTClient
。 这是一个用于测试 RESTful Web 服务的 firefox 插件。
-
URL:
http://localhost:8080/firstSpringApplication/users
-
URL:
http://localhost:8080/firstSpringApplication/users/123
下载源码
如果确实有帮助,请给我评论,或者您有任何疑问。
学习快乐!
Spring – 控制反转与依赖注入
原文: https://howtodoinjava.com/spring-core/spring-ioc-vs-di/
在软件工程中,控制反转(IoC)是一种编程技术,其中对象耦合在运行时由汇编程序对象绑定,并且在编译时使用静态分析通常是未知的 。 在这个 Spring 教程中,通过示例了解 IoC 和依赖注入在 Spring 中的区别。
1. 什么是控制反转(IoC)
在传统编程中,业务逻辑的流程由静态分配给彼此的对象确定。 通过控制的反转,该流程取决于由汇编程序实例化的对象图,并且通过抽象定义对象交互使之成为可能。 绑定过程是通过依赖项注入实现的,尽管有些人认为使用服务定位器还可以提供控制反转。
控制反转作为设计准则具有以下目的:
- 某个任务的执行与实现之间存在脱钩。
- 每个模块都可以专注于其设计目的。
- 模块不假设其他系统在做什么,而是依赖其合同。
- 更换模块对其他模块没有副作用。
2. 什么是依赖注入(DI)
IoC 是一种设计示例,其目标是对应用程序的目标组件提供更多控制,使这些组件可以完成工作。 依赖注入是一种模式,用于创建其他对象依赖的对象实例,而在编译时不知道将使用哪个类来提供该功能。 IoC 依赖于依赖项注入,因为需要一种机制来激活提供特定功能的组件。
这两个概念以这种方式一起工作,以允许编写更加灵活,可重用和封装的代码。 因此,它们是设计面向对象解决方案中的重要概念。
3. 如何实现 IoC
在面向对象的编程中,有几种基本技术可以实现控制反转。 这些是:
- 使用工厂模式
- 使用服务定位器模式
- 使用以下任何给定类型的依赖项注入:
- 构造函数注入
- setter 注入
- 接口注入
4. Spring 的控制反转
org.springframework.beans
和org.springframework.context
软件包为 Spring Framework 的 IoC 容器提供了基础。 BeanFactory
接口提供了一种高级配置机制,能够管理任何性质的对象。 ApplicationContext
接口建立在BeanFactory
的基础上(它是一个子接口),并添加了其他功能,例如与 Spring 的 AOP 功能的集成更加容易,消息资源处理(用于国际化), 事件传播以及用于 Web 应用程序的特定于应用程序层的上下文,例如WebApplicationContext
。
BeanFactory
是 Spring IoC 容器的实际表示,该容器负责容纳和管理上述 bean。 BeanFactory
接口是 Spring 中的中央 IoC 容器接口。
BeanFactory
接口有多种实现。 最常用的BeanFactory
实现是XmlBeanFactory
类。 其他常用的类是XmlWebApplicationContext
。 根据 bean 的定义,工厂将返回所包含对象的独立实例(原型设计模式),或者返回单个共享实例(单例设计模式的替代方案,其中实例是作用域中的单例工厂)。将返回哪种类型的实例取决于 bean 工厂的配置:API 是相同的。
在深入研究依赖项注入类型之前,让我们首先确定在 spring 框架中创建 bean 的方法,因为它将有助于理解下一部分的内容。
5. 如何在 Spring 中创建 bean
Bean 定义可以视为创建一个或多个实际对象的方法。 询问时,容器将查看命名 bean 的配方,并使用该 bean 定义封装的配置元数据来创建(或获取)实际对象。
5.1. 使用构造函数
当使用构造函数方法创建 bean 时,所有普通类都可以被 Spring 使用并与之兼容。 也就是说,正在创建的类不需要实现任何特定的接口或以特定的方式进行编码。 仅指定 bean 类就足够了。 使用基于 XML 的配置元数据时,您可以像这样指定 bean 类:
beans.xml
<bean id="exampleBean"/>
5.2. 使用静态工厂方法
在定义要使用静态工厂方法创建的 bean 以及指定包含静态工厂方法的类的class
属性时,需要另一个名为factory-method
的属性来指定工厂方法本身的名称。
beans.xml
<bean id="exampleBean" factory-method="createInstance"/>
Spring 希望能够调用此方法并返回一个活动对象,从那时起,该对象将被视为通常是通过构造函数创建的。
5.3. 使用实例工厂方法
以类似于通过静态工厂方法进行实例化的方式,使用实例工厂方法进行实例化是调用容器中现有 bean 的工厂方法来创建新 bean。
beans.xml
<bean id="myFactoryBean" class="...">
<bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"></bean>
6. Spring 的依赖注入
依赖注入(DI)的基本原理是,对象只能通过构造函数参数,工厂方法的参数或在对象实例从工厂方法构造或返回后设置的属性来定义其依赖关系。 然后,容器的工作是在创建 bean 时实际注入那些依赖项。 从根本上讲,这是相反的概念,因此被称为控制反转(IoC)。
6.1. 二次注入
通过调用无参数构造函数或无参数静态工厂方法以实例化 bean 之后,在 bean 上调用 setter 方法,可以实现基于 setter 的 DI。
TestSetterDI.java
public class TestSetterDI {
DemoBean demoBean = null;
public void setDemoBean(DemoBean demoBean) {
this.demoBean = demoBean;
}
}
6.2. 构造函数注入
基于构造函数的 DI 是通过调用具有多个参数(每个参数代表一个协作者)的构造函数来实现的。 另外,调用带有特定参数的静态工厂方法来构造 Bean 几乎是等效的,本文的其余部分将类似地考虑构造函数的参数和静态工厂方法的参数。
ConstructorDI.java
public class ConstructorDI {
DemoBean demoBean = null;
public TestSetterDI (DemoBean demoBean) {
this.demoBean = demoBean;
}
}
6.3. 接口注入
在这种方法中,我们实现了 IOC 框架的接口。 IOC 框架将使用接口方法将对象注入到主类中。 当您需要某种不适用于放置在属性中的逻辑时,使用这种方法更为合适。 如日志支持。
public void SetLogger(ILogger logger)
{
_notificationService.SetLogger(logger);
_productService.SetLogger(logger);
}
7. 面试题
7.1. 组件和服务之间有什么区别?
组件是一整套软件,旨在由不受组件编写者控制的应用程序直接使用。 “不更改”表示使用中的应用程序不会更改组件的源代码,尽管它们可能会通过以组件编写者允许的方式扩展组件来更改组件的行为。
服务与组件相似,供外部应用程序使用。 主要区别在于要在本地使用的组件(请考虑 jar 文件,程序集,dll 或源导入)。 服务将通过同步或异步的某个远程接口(例如,Web 服务,消息系统,RPC 或套接字)远程使用。
7.2. DI 与服务定位器模式有何不同?
依赖项注入器的主要好处是,它允许根据环境和使用情况插入合适的服务实现。 注入不是打破这种依赖的唯一方法,另一种方法是使用服务定位器。 服务定位器的基本思想是拥有一个对象,该对象知道如何掌握应用程序可能需要的所有服务。 然后,它将扫描所有此类服务,并将它们存储为单例注册表。 当要求提供服务实现时,请求者可以使用令牌查询注册表并获取适当的实现。
通常,这些注册表是通过一些配置文件填充的。 关键区别在于,使用服务定位器时,服务的每个用户都对定位器具有依赖。 定位器可以隐藏对其他实现的依赖关系,但是您确实需要查看定位器。
7.3. 使用哪个服务更好(即服务定位器或依赖项注入)?
嗯,正如我已经说过的,关键区别在于,使用服务定位器,服务的每个用户都对定位器有依赖。 这意味着您必须在输入和输出切面了解服务定位器的详细信息。 因此,实际上成为选择哪种模式的决定因素。
如果维护注册表信息既简单又必要,则可以使用服务定位器,或者直接使用依赖项注入,因为它不会使服务用户感到任何先决条件。
7.4. 构造函数注入或 setter 注入哪个更好?
在 setter 和构造函数注入之间进行选择很有趣,因为它反映了面向对象编程的一个更普遍的问题 – 如果您在构造函数或 setter 中填充字段。
带参数的构造函数可让您清楚地说明在明显的位置创建有效对象的含义。 如果执行此操作的方法不止一种,请创建多个显示不同组合的构造函数。 构造函数初始化的另一个优点是,您可以通过不提供设置器来清楚地隐藏任何不可变的字段。 我认为这很重要-如果某些事情不应该改变,那么缺少二传手就可以很好地传达这一点。 如果使用 setter 进行初始化,则可能会很痛苦。
但是,如果您有很多构造函数参数,则看起来会很混乱,尤其是在没有关键字参数的语言中。 如果您有多种方法来构造有效的对象,则可能很难通过构造函数来显示它,因为构造函数只能在参数的数量和类型上有所不同。 如果您具有简单的参数(例如字符串),构造函数也会受到影响。 使用 setter 注入,您可以为每个 setter 命名,以指示该字符串应该执行的操作。 对于构造函数,您只是依靠位置,这很难遵循。
我的偏好是从构造函数注入开始,但是一旦我上面概述的问题开始成为问题,就可以准备切换到 setter 注入。
7.5. 什么是 Bean 工厂?
BeanFactory
就像一个工厂类,其中包含一系列 bean。BeanFactory
在其内部保存多个 Bean 的 Bean 定义,然后在每次客户端请求时实例化 Bean。
BeanFactory
能够在实例化协作对象之间创建关联。 这消除了 bean 本身和 bean 客户端的配置负担。BeanFactory
也参与了 bean 的生命周期,从而调用了自定义的初始化和销毁方法。
7.6. 什么是应用程序上下文?
Bean 工厂适合简单的应用程序,但是要利用 Spring 框架的全部功能,您可能需要升级到 Springs 更高级的容器即应用程序上下文。 从表面上看,应用程序上下文与 Bean 工厂相同,两者都加载 Bean 定义,将 Bean 绑定在一起并根据请求分配 Bean。 但它也提供:
- 解决文本消息的方法,包括对国际化的支持。
- 加载文件资源的通用方法。
- 注册为监听器的 bean 的事件。
7.7. 应用程序上下文的常见实现是什么?
ApplicationContext
的三种常用实现是:
-
ClassPathXmlApplicationContext
:它从位于类路径中的 XML 文件中加载上下文定义,将上下文定义视为类路径资源。 使用代码从应用程序的类路径中加载应用程序上下文。ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
-
FileSystemXmlApplicationContext
:它从文件系统中的 XML 文件加载上下文定义。 使用代码从文件系统中加载应用程序上下文。ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
-
XmlWebApplicationContext
:它从 Web 应用程序中包含的 XML 文件中加载上下文定义。
7.8. 最好使用BeanFactory
或ApplicationContext
?
BeanFactory
几乎只是实例化和配置 bean。 ApplicationContext
也这样做,它提供了支持基础结构,以支持许多企业特定的功能,例如事务和 AOP。
简而言之,赞成使用ApplicationContext
。
在本教程中,我们了解了 Spring 中 ioc 和 di 之间的区别。
学习愉快!
使用 JPA 配置的 Spring REST CRUD 示例
原文: https://howtodoinjava.com/spring-restful/spring-rest-crud-jpa-example/
学习使用 Spring REST 和 JPA 配置(以 H2 数据库为后端)创建 REST API 以进行分类操作,而无需 Spring boot 自动配置功能。
1. Maven 依赖
在此示例中,我们使用以下模块及其依赖项。
spring-webmvc
– 用于处理请求spring-data-jpa
– 为接口提供了支持针对后端数据存储读取,更新,删除和创建记录的方法。persistence-api
– JPA 规范javax.persistence
-JPA 实现- Hibernate 实体管理器 – 作为实体供体商
- Jackson – 用于 JSON 支持
- H2 – 作为数据库
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.rest</groupId>
<artifactId>SpringRestExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringRestExample</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.1.6.RELEASE</spring.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- spring webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring data jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<!-- persistence api -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<!-- persistence implementation -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<!-- entity manager provider -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.2.Final</version>
</dependency>
<!-- H2 database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
<!-- JSON support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
</dependencies>
</project>
2. DispatcherServlet
声明
我们已经在web.xml
文件中配置了DispatcherServlet
,该文件会将传入的请求路由到适当的控制器方法。
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Employee Management REST APIs</display-name>
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/api/rest/*</url-pattern>
</servlet-mapping>
</web-app>
3. Spring 和 JPA 配置
在rest-servlet.xml
文件中,我们已经配置了组件扫描和基于注解的配置支持。 随之,我们配置了实体管理器和数据源配置。
rest-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:annotation-driven />
<jpa:repositories base-package="com.howtodoinjava.demo.repository" entity-manager-factory-ref="emf"/>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="com.howtodoinjava.demo.model" />
<property name="dataSource" ref="dataSource" />
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
</props>
</property>
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
</property>
</bean>
<bean class="org.springframework.jdbc.datasource.SimpleDriverDataSource" id="dataSource">
<property name="driverClass" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT=2" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
4. 实体类
我们只有一个实体类,它将保留在数据库中,并将作为 REST API 的响应返回。
@Entity
– JPA 注解,以使对象准备好存储在基于 JPA 的数据存储区中。@Table
– 数据存储中将存储此实体的表的名称。
阅读更多: JPA 2 持久性注解
Employee.java
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "tbl_employee")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
public Employee() {
}
//Getters and setters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName + ",
lastName=" + lastName + ", email=" + email + "]";
}
}
5. 存储库
我们已将JpaRepository
实现创建为EmployeeRepository
,它提供了用于对员工实体执行搜索,获取,更新和删除操作的所有默认操作。
EmployeeRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.demo.model.Employee;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
6. REST 控制器
REST 控制器定义了可以调用以对员工资源执行操作的所有 CRUD 方法。
在给定的控制器中,@RestController
注解指示方法返回的数据将直接写入响应主体中,而不呈现模板。 其他注解(@GetMapping
,@PostMapping
,@PutMapping
和@DeleteMapping
)将 HTTP 请求映射到相应的方法。
EmployeeRESTController.java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.howtodoinjava.demo.model.Employee;
import com.howtodoinjava.demo.repository.EmployeeRepository;
@RestController
@RequestMapping(value = "/employee-management", produces = { MediaType.APPLICATION_JSON_VALUE })
public class EmployeeRESTController
{
@Autowired
private EmployeeRepository repository;
public EmployeeRepository getRepository() {
return repository;
}
public void setRepository(EmployeeRepository repository) {
this.repository = repository;
}
@GetMapping(value = "/employees")
public List<Employee> getAllEmployees() {
return repository.findAll();
}
@PostMapping("/employees")
Employee createOrSaveEmployee(@RequestBody Employee newEmployee) {
return repository.save(newEmployee);
}
@GetMapping("/employees/{id}")
Employee getEmployeeById(@PathVariable Long id) {
return repository.findById(id).get();
}
@PutMapping("/employees/{id}")
Employee updateEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) {
return repository.findById(id).map(employee -> {
employee.setFirstName(newEmployee.getFirstName());
employee.setLastName(newEmployee.getLastName());
employee.setEmail(newEmployee.getEmail());
return repository.save(employee);
}).orElseGet(() -> {
newEmployee.setId(id);
return repository.save(newEmployee);
});
}
@DeleteMapping("/employees/{id}")
void deleteEmployee(@PathVariable Long id) {
repository.deleteById(id);
}
}
7. Spring REST CRUD 操作演示
让我们测试一下 API 操作,以验证它们是否有效。
7.1. 创建员工
API Request
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees
{
"firstName": "lokesh",
"lastName": "gupta",
"email": "abc@gmail.com"
}
API Response
HTTP Response code : 200
{
"id": 1,
"firstName": "lokesh",
"lastName": "gupta",
"email": "abc@gmail.com"
}
创建更多的员工进行测试。
7.2. 获取员工集合
API Request
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees
API Response
HTTP Response code : 200
[
{
"id": 1,
"firstName": "lokesh",
"lastName": "gupta",
"email": "abc@gmail.com"
},
{
"id": 2,
"firstName": "Amit",
"lastName": "Sharma",
"email": "xyz@gmail.com"
}
]
7.3. 按编号获取员工
API Request
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
API Response
HTTP Response code : 200
{
"id": 1,
"firstName": "lokesh",
"lastName": "gupta",
"email": "abc@gmail.com"
}
7.4. 更新员工
API Request
HTTP PUT : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
{
"firstName": "Lucky",
"lastName": "Gupta",
"email": "abc@gmail.com"
}
API Response
HTTP Response code : 200
{
"id": 1,
"firstName": "Lucky",
"lastName": "Gupta",
"email": "abc@gmail.com"
}
7.5. 删除员工
API Request
HTTP DELETE : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
API Response
HTTP Response code : 200
学习愉快!
下载源码
Spring REST 异常处理示例
原文: https://howtodoinjava.com/spring-restful/exception-handling-example/
在使用 Spring REST 模块创建的 REST API 中学习处理异常(请求验证,错误数据或其他请求处理错误)。 我们将研究一种使用@ControllerAdvice
和@ExceptionHandler
的方法。
要使用@ControllerAdvice
全局处理 REST 异常,我们需要遵循以下步骤。
1. 使用@ControllerAdvice
和@ExceptionHandler
创建处理器
@ControllerAdvice
注解是@Component
注解的特化,其方法(以@ExceptionHandler
注解)在全球范围内跨多个@Controller
类共享。- 通过类路径扫描自动检测带有
@ControllerAdvice
的类。 - 使用选择器
annotations()
,basePackageClasses()
和basePackages()
定义目标控制器的更窄子集。 - 我们可以在选择器中应用 OR 运算符,即如果遇到给定异常中的任何一个,则将执行给定方法。
请注意,ResponseEntityExceptionHandler
是@ControllerAdvice
类的便捷基类,这些类希望通过@ExceptionHandler
方法跨所有@RequestMapping
方法提供集中的异常处理 。
CustomExceptionHandler.java
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
private String INCORRECT_REQUEST = "INCORRECT_REQUEST";
private String BAD_REQUEST = "BAD_REQUEST";
@ExceptionHandler(RecordNotFoundException.class)
public final ResponseEntity<ErrorResponse> handleUserNotFoundException
(RecordNotFoundException ex, WebRequest request)
{
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse(INCORRECT_REQUEST, details);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(MissingHeaderInfoException.class)
public final ResponseEntity<ErrorResponse> handleInvalidTraceIdException
(MissingHeaderInfoException ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse(BAD_REQUEST, details);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
2. 创建异常模型类
我们需要识别业务异常用例,并用异常类来表示它们。 这些类将扩展RuntimeException
类。 也可以根据需要随意创建更多错误响应表示。
MissingHeaderInfoException.java
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class MissingHeaderInfoException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public MissingHeaderInfoException(String message) {
super(message);
}
}
RecordNotFoundException.java
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class RecordNotFoundException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public RecordNotFoundException(String message) {
super(message);
}
}
ErrorResponse.java
import java.util.List;
public class ErrorResponse
{
public ErrorResponse(String message, List<String> details) {
super();
this.message = message;
this.details = details;
}
private String message;
private List<String> details;
//getters and setters
}
3. 配置视图解析器
如果尚未完成,我们需要配置视图解析器以将异常消息转换为 XML 或 JSON 形式。
在 Spring Boot 中,此配置是自动完成的。 没有 SpringBoot,我们需要像下面这样。
重要说明:确保已启用
mvc: annotation-driven
配置,或已使用@EnableWebMvc
注解。
rest-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:view-resolvers>
<mvc:content-negotiation>
<mvc:default-views>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</mvc:default-views>
</mvc:content-negotiation>
</mvc:view-resolvers>
<!-- JPA Config -->
</beans>
4. REST 控制器更改
从 rest 控制器处理器方法中,我们需要抛出要转换并作为响应发送给 API 使用者的异常。 在这种情况下,如果id
搜索了一个雇员并且该雇员在数据库中不存在,我们将发送RecordNotFoundException
。
EmployeeRESTController.java
@GetMapping("/employees/{id}")
Employee getEmployeeById(@PathVariable Long id)
{
return repository.findById(id)
.orElseThrow(() -> new RecordNotFoundException("Employee id '" + id + "' does no exist"));
}
5. Spring REST 异常处理演示
尝试按 ID(在数据库中不存在 ID)获取员工。
API Request
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/101
API Response
{
"message": "INCORRECT_REQUEST",
"details": [
"Employee id '101' does no exist"
],
}
请把关于 Spring Rest API 中的异常处理的问题交给我。
学习愉快!
下载源码
Spring REST 请求主体和参数验证示例
https://howtodoinjava.com/spring-restful/request-body-parameter-validation/
学习验证请求正文(发布到 Spring REST API 的 JSON)。 还使用 Hibernate 验证器 2.x 验证资源 URI 中的@PathVariable
和@RequestParam
参数。
在此 SpringRest 验证示例中,我们将在为 CRUD 示例创建的 REST API 中添加验证。
1. 使用 Hibernate 验证器的请求正文验证
1.1. Maven 依赖
pom.xml
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
这可传递地将对 Bean 验证 API 的依赖关系拉到javax.validation:validation-api: 2.0.1.Final
。
1.2. 启用 bean 验证
在 SpringBoot 中,如果类路径上有任何 JSR-303 实现(例如Hibernate 验证器 2.0 ),则将自动启用 Bean 验证。
如果不使用 Spring Boot,则需要添加LocalValidatorFactoryBean
。
Java 配置
@Bean
public javax.validation.Validator localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
XML 配置
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
1.3. 添加 bean 验证注解
在模型类中添加 Bean 验证注解,该注解将存储请求正文数据,例如@NotEmpty
和@Email
。
Java 配置
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
@Entity
@Table(name = "tbl_employee")
public class Employee implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotEmpty(message = "First name must not be empty")
private String firstName;
@NotEmpty(message = "Last name must not be empty")
private String lastName;
@NotEmpty(message = "Email must not be empty")
@Email(message = "Email must be a valid email address")
private String email;
public Employee() {
}
//setters and getters
@Override
public String toString() {
return "EmployeeVO [id=" + id + ", firstName=" + firstName + ",
lastName=" + lastName + ", email=" + email + "]";
}
}
1.4. 处理ConstraintViolationException
如果发生任何验证失败,Spring 将抛出ConstraintViolationException
。 我们可以使用@ExceptionHandler
处理任何返回有意义的 JSON 错误响应的事件。
CustomExceptionHandler.java
@ControllerAdvice
@ResponseBody
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
@ExceptionHandler(ConstraintViolationException.class)
public final ResponseEntity<ErrorResponse> handleConstraintViolation(
ConstraintViolationException ex,
WebRequest request)
{
List<String> details = ex.getConstraintViolations()
.parallelStream()
.map(e -> e.getMessage())
.collect(Collectors.toList());
ErrorResponse error = new ErrorResponse(BAD_REQUEST, details);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
2. 查询和路径参数验证
在 Spring REST 中,通过@PathVariable
捕获请求 URI 中的参数,并通过@RequestParam
捕获所有查询参数。
请注意,必须添加 maven 依赖项,并且应按上述说明处理ConstraintViolationException
。
2.1. 启用验证
查询和路径参数验证并不简单。 我们需要显式创建 bean MethodValidationPostProcessor
,它将处理@Validated
注解。
XML 配置
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
Java 配置
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
2.2. 向参数添加验证
- 使用如上所述的 JSR-303 注解。
- 在控制器顶部使用
@Validated
注解,因此适用于其中的所有方法。
@RestController
@RequestMapping(value = "/employee-management",
produces = { MediaType.APPLICATION_JSON_VALUE })
@Validated
public class EmployeeRESTController
{
@GetMapping("/employees/{id}")
Employee getEmployeeById(@PathVariable
@Min(value = 1, message = "id must be greater than or equal to 1")
@Max(value = 1000, message = "id must be lower than or equal to 1000") Long id)
{
return repository.findById(id)
.orElseThrow(() -> new RecordNotFoundException("Employee id '" + id + "' does no exist"));
}
}
3. 演示
3.1. 请求正文验证
API 请求 1
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
Headers:
AUTH_API_KEY: abcd123456
Content-Type: application/json
Body:
{
"firstName": "",
"lastName": "Gupta",
"email": "abc@gmail.com"
}
API 响应 1
{
"message":"BAD_REQUEST",
"details":["First name must not be empty"]
}
API 请求 2
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
Headers:
AUTH_API_KEY: abcd123456
Content-Type: application/json
Body:
{
"firstName": "",
"email": "abc@gmail.com"
}
API 响应 2
{
"message":"BAD_REQUEST",
"details":
[
"First name must not be empty",
"Last name must not be empty"
]
}
3.2. 路径参数验证
API 请求 1
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/0
Headers:
AUTH_API_KEY: abcd123456
Content-Type: application/json
API 响应 1
{
"message":"BAD_REQUEST",
"details":["id must be greater than or equal to 1"]
}
API 请求 2
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/5000
Headers:
AUTH_API_KEY: abcd123456
Content-Type: application/json
API 请求 2
{
“message”:”BAD_REQUEST”,
“details”:[“id must be lower than or equal to 1000”]
}
下载源码
学习愉快!
Spring REST 自定义令牌认证示例
原文: https://howtodoinjava.com/spring-restful/custom-token-auth-example/
通过使用 Spring REST 和 Spring Security 5 创建的方法,学习将 基于自定义令牌的身份验证 添加到 REST API。 将通过。 所有其他请求将返回HTTP 403
响应。
1. Spring Security 依赖
包括以下依赖项以使用 Spring Security 类和接口。
pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
2. 扩展AbstractPreAuthenticatedProcessingFilter
创建一个类并扩展AbstractPreAuthenticatedProcessingFilter
。 它是用于处理过滤器的基类,这些过滤器处理预认证的认证请求,其中假定主体已经由外部系统认证。
默认情况下,当身份验证尝试失败时,过滤器链将继续进行,以允许其他身份验证机制处理请求。 如果发现令牌无效,它将有助于将请求传递给其他安全过滤器(例如,表单登录名)。
getPreAuthenticatedPrincipal()
方法有助于从当前请求中读取auth
标头值。
PreAuthTokenHeaderFilter.java
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication
.preauth.AbstractPreAuthenticatedProcessingFilter;
public class PreAuthTokenHeaderFilter
extends AbstractPreAuthenticatedProcessingFilter {
private String authHeaderName;
public PreAuthTokenHeaderFilter(String authHeaderName) {
this.authHeaderName = authHeaderName;
}
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(authHeaderName);
}
@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
这是可选方法。 应用程序可能会决定立即简单地返回认证失败错误。
3. 配置AuthenticationManager
并添加到HttpSecurity
我们需要设置身份验证管理器,它将处理身份验证过程并决定如何处理成功和失败方案。
添加身份验证管理器后,我们可以将PreAuthTokenHeaderFilter
添加到HttpSecurity
。
如果出现任何身份验证错误,则默认情况下将处理该错误ExceptionTranslationFilter
,该错误会在 Spring 转发到默认身份验证错误页面。 如果要以不同方式显示认证错误响应,则需要创建自定义ExceptionTranslationFilter
类。
AuthTokenSecurityConfig.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
@Order(1)
public class AuthTokenSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${howtodoinjava.http.auth.tokenName}")
private String authHeaderName;
//TODO: retrieve this token value from data source
@Value("${howtodoinjava.http.auth.tokenValue}")
private String authHeaderValue;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
PreAuthTokenHeaderFilter filter = new PreAuthTokenHeaderFilter(authHeaderName);
filter.setAuthenticationManager(new AuthenticationManager()
{
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException
{
String principal = (String) authentication.getPrincipal();
if (!authHeaderValue.equals(principal))
{
throw new BadCredentialsException("The API key was not found "
+ "or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.
antMatcher("/api/**")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.addFilterBefore(new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint()),
filter.getClass()
)
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
4. 注册安全过滤器
传统上,spring security 在DelegatingFilterProxy
基于 XML 的配置中以web.xml
文件为起点。
web.xml
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在 Java 配置中,我们可以通过删除类AbstractSecurityWebApplicationInitializer
来实现相同的效果。
SpringSecurityInitializer.java
import org.springframework.security.web.context
.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer
extends AbstractSecurityWebApplicationInitializer {
//no code needed
}
4. Spring REST 自定义令牌认证演示
4.1. 标头中没有身份验证令牌
API 请求
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
API 响应
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.
4.2. 标头中的身份验证令牌不正确
API 请求
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
AUTH_API_KEY: xyz123
API 响应
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.
4.2. 标头中的身份验证令牌有效
API 请求
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
AUTH_API_KEY: abcd123456
API 响应
HTTP Status - 200 OK
{
//response body
}
下载源码
学习愉快!
Spring REST – 多部分上传和下载示例
原文: https://howtodoinjava.com/spring-restful/multipart-upload-download-example/
了解如何使用接受MultipartFile
请求的 Spring REST API 上传多部分二进制文件(例如 jpeg 图像)。 还学习使用FileSystemResource
使用另一个 REST API 下载文件。
1. Maven 依赖
除了 spring webmvc 之外,我们在类路径中还需要commons-fileupload
和commons-io
。
pom.xml
dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2. 配置CommonsMultipartResolver
它是commons-fileupload
的基于 Servlet 的MultipartResolver
实现。 它提供maxUploadSize
,maxInMemorySize
和defaultEncoding
设置作为 bean 属性。
此类的目的是将临时文件保存到 Servlet 容器的临时目录中。
rest-servlet.xml
<beans>
...
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000"/>
</bean>
...
</beans>
3. 创建多部分处理器 API
创建两个 REST API ,它们将负责处理上载和下载请求以及响应。 在给出的示例中,我在路径/employee-management/employees/1/photo
处创建了 API。
我假设 ID 为'1'
的员工存在于数据库中。 随时更改资源路径和实现。
EmployeeImageController.java
package com.howtodoinjava.demo.controller;
import static org.springframework.web.servlet
.support.ServletUriComponentsBuilder.fromCurrentRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.concurrent.Callable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.howtodoinjava.demo.exception.RecordNotFoundException;
import com.howtodoinjava.demo.model.Employee;
import com.howtodoinjava.demo.repository.EmployeeRepository;
@RestController
@RequestMapping(value = "/employee-management/employees/{id}/photo")
@PropertySource("classpath:application.properties")
public class EmployeeImageController {
@Autowired
private EmployeeRepository repository;
private File uploadDirRoot;
@Autowired
EmployeeImageController(@Value("${image.upload.dir}") String uploadDir,
EmployeeRepository repository) {
this.uploadDirRoot = new File(uploadDir);
this.repository = repository;
}
@GetMapping
ResponseEntity<Resource> read(@PathVariable Long id)
{
return this.repository.findById(id)
.map(employee ->
{
File file = fileFor(employee);
Resource fileSystemResource = new FileSystemResource(file);
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(fileSystemResource);
})
.orElseThrow(() -> new RecordNotFoundException("Image for available"));
}
@RequestMapping(method = { RequestMethod.POST, RequestMethod.PUT },
consumes = { "multipart/form-data" })
Callable<ResponseEntity<?>> write(@PathVariable Long id,
@RequestParam("file") MultipartFile file) throws Exception
{
return () -> this.repository.findById(id)
.map(employee ->
{
File fileForEmployee = fileFor(employee);
try (InputStream in = file.getInputStream();
OutputStream out = new FileOutputStream(fileForEmployee))
{
FileCopyUtils.copy(in, out);
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
URI location = fromCurrentRequest().buildAndExpand(id).toUri();
return ResponseEntity.created(location).build();
})
.orElseThrow(() -> new RecordNotFoundException("Employee id is not present in database"));
}
private File fileFor(Employee e) {
return new File(this.uploadDirRoot, Long.toString(e.getId()));
}
}
上面的 REST 控制器依赖于上传文件夹即image.upload.dir
配置在属性文件中的存在。
application.properties
image.upload.dir=c:/temp/images
同样,控制器返回可调用的 ,这意味着该方法将在 IO 操作可能运行时立即返回。 上传过程完成后,API 将返回响应。 要启用异步支持,请在DispatcherServlet
中配置异步支持的。
web.xml
<web-app>
<display-name>Employee Management REST APIs</display-name>
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/api/rest/*</url-pattern>
</servlet-mapping>
</web-app>
4. 多部分上传请求演示
出于演示目的,我创建了一个 JSP 页面,其中仅包含一个类型的文件字段。 我们将从计算机浏览图像,并将其上传到服务器。
singleFileUpload.jsp
<html>
<head>
<title>Spring REST File Upload</title>
</head>
<body>
<form method="POST"
action="/SpringRestExample/api/rest/employee-management/employees/1/photo"
enctype="multipart/form-data">
<table>
<tr>
<td>Select a file to upload</td>
<td><input type="file" name="file" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
</body>
</html>
现在启动服务器并在 URL http://localhost:8080/SpringRestExample/singleFileUpload.jsp
中打开上传页面。 浏览文件,然后单击提交按钮。 图像文件将被上传到配置的上传目录中的服务器。
要下载文件,请在浏览器中输入 URL /employee-management/employees/1/photo
,然后将显示图像。
请问您有关创建 Spring MVC REST API 来处理多部分文件上传和下载的问题。
下载源码
学习愉快!
Spring REST Multipart – 多部分上传示例
原文: https://howtodoinjava.com/spring-restful/multipart-multiple-uploads-example/
了解如何使用 Spring REST API 接受上传多个多部分二进制文件(例如 jpeg 图像),这些文件接受MultipartFile
请求的数组。
1. Maven 依赖
除了 spring webmvc 之外,我们还将在类路径中需要commons-fileupload
和commons-io
。
pom.xml
dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2. 配置CommonsMultipartResolver
它是commons-fileupload
的基于 Servlet 的MultipartResolver
实现。 它提供maxUploadSize
,maxInMemorySize
和defaultEncoding
设置作为 bean 属性。
此类的目的是将临时文件保存到 Servlet 容器的临时目录中。
rest-servlet.xml
<beans>
...
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1000000"/>
</bean>
...
</beans>
等效的 Java 注解配置为:
@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver()
{
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(20848820);
return multipartResolver;
}
3. 创建多部分上载 Rest API
创建给定的 REST API ,该 API 将负责处理发布到服务器的多个上载。 在给出的示例中,我在路径/employee-management/employees/1/photo/multiple
处创建了 API。
我假设 ID 为'1'
的员工存在于数据库中。 随时更改资源路径和实现。
EmployeeImageController.java
package com.howtodoinjava.demo.controller;
import static org.springframework.web.servlet
.support.ServletUriComponentsBuilder.fromCurrentRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.concurrent.Callable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.howtodoinjava.demo.exception.RecordNotFoundException;
import com.howtodoinjava.demo.model.Employee;
import com.howtodoinjava.demo.repository.EmployeeRepository;
@RestController
@RequestMapping(value = "/employee-management/employees/{id}/photo")
@PropertySource("classpath:application.properties")
public class EmployeeImageController {
@Autowired
private EmployeeRepository repository;
private File uploadDirRoot;
@Autowired
EmployeeImageController(@Value("${image.upload.dir}") String uploadDir,
EmployeeRepository repository) {
this.uploadDirRoot = new File(uploadDir);
this.repository = repository;
}
@PostMapping(value = "/multiple", consumes = { "multipart/form-data" })
Callable<ResponseEntity<?>> writeMultiple(@PathVariable Long id,
@RequestParam("files") MultipartFile[] files) throws Exception
{
return () -> this.repository.findById(id).map(employee ->
{
Arrays.asList(files).stream().forEach(file ->
{
File fileForEmployee;
try {
fileForEmployee = uploadPath(employee, file);
} catch (IOException e) {
throw new RuntimeException(e);
}
try (InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileForEmployee)) {
FileCopyUtils.copy(in, out);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
return ResponseEntity.ok().build();
}).orElseThrow(() -> new RecordNotFoundException("Employee id is not present in database"));
}
private File uploadPath(Employee e, MultipartFile file) throws IOException
{
File uploadPath = Paths.get(this.uploadDirRoot.getPath(), e.getId().toString()).toFile();
if(uploadPath.exists() == false) {
uploadPath.mkdirs();
}
return new File(uploadPath.getAbsolutePath(), file.getOriginalFilename());
}
}
如果文件系统中尚不存在上述 REST 控制器,则会创建一个上载文件夹(名称为员工 ID)。 仍然需要定义以下属性。
application.properties
image.upload.dir=c:/temp/images
同样,控制器返回可调用的 ,这意味着该方法将在 IO 操作可能运行时立即返回。 上传过程完成后,API 将返回响应。 要启用异步支持,请在DispatcherServlet
中配置异步支持的。
web.xml
<web-app>
<display-name>Employee Management REST APIs</display-name>
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/api/rest/*</url-pattern>
</servlet-mapping>
</web-app>
4. 多部分上传请求演示
出于演示目的,我创建了一个 JSP 页面,其中仅包含文件类型的多个字段。 我们将从计算机浏览一些图像,并将其上传到服务器。
multipleFileUpload.jsp
<html>
<head>
<title>Spring REST File Upload</title>
</head>
<body>
<form method="POST" action="/SpringRestExample/api/rest/employee-management/employees/1/photo/multiple"
enctype="multipart/form-data">
<table>
<tr>
<td>Select first file to upload</td>
<td><input type="file" name="files" /></td>
</tr>
<tr>
<td>Select second file to upload</td>
<td><input type="file" name="files" /></td>
</tr>
<tr>
<td>Select third file to upload</td>
<td><input type="file" name="files" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
</body>
</html>
现在启动服务器并在 URL http://localhost:8080/SpringRestExample/multipleFileUpload.jsp
中打开上传页面。 浏览图像,然后单击提交按钮。 图像文件将被上传到配置的上传目录中的服务器。
要下载文件,请在浏览器中输入 URL /employee-management/employees/1/photo
,然后将显示图像。 下载 API 已包含在附件中。
请问您有关创建 Spring MVC REST API 来处理文件(例如图像)的多个上传问题。
下载源码
学习愉快!
Spring REST – HTTP OPTIONS 请求处理器示例
原文: https://howtodoinjava.com/spring-restful/http-options-request-handler/
通过为RequestMethod.OPTIONS
添加特定的处理器方法,学习在 Spring webmvc 应用程序中处理特定于 REST 资源的 HTTP 选项请求。
1. HTTP OPTIONS 方法
HTTP OPTIONS 方法用于描述目标资源的通信选项。 此方法允许客户端确定与资源相关联的选项和/或要求,或者服务器的功能,而无需暗示资源操作或启动资源检索。
- 如果 Request-URI 是星号(
*
),则 OPTIONS 请求通常适用于服务器而不是特定资源。 - 如果 Request-URI 不是星号,则 OPTIONS 请求仅适用于与该资源进行通信时可用的选项。
- 此方法的响应是不可缓存。
2. 将选项处理器方法添加到 REST 控制器
下一步是添加处理器方法以处理 OPTIONS 请求。
EmployeeRESTController.java
@RestController
@RequestMapping(value = "/employee-management", produces = { MediaType.APPLICATION_JSON_VALUE })
public class EmployeeRESTController
{
@RequestMapping(value="/employees", method = RequestMethod.OPTIONS)
ResponseEntity<?> collectionOptions()
{
return ResponseEntity
.ok()
.allow(HttpMethod.GET, HttpMethod.POST, HttpMethod.OPTIONS)
.build();
}
@RequestMapping(value="/employees/{id}", method = RequestMethod.OPTIONS)
ResponseEntity<?> singularOptions()
{
return ResponseEntity
.ok()
.allow(HttpMethod.GET, HttpMethod.DELETE, HttpMethod.PUT, HttpMethod.OPTIONS)
.build();
}
//Other APIs
}
3. 演示
要测试 OPTIONS 请求是否被正确处理,请在任何 REST 客户端插件或 SoapUI 中进行测试。
点击 URL:HTTP 选项
http://localhost:8080/api/rest/employee-management/employees/1
在 cURL 中,我们可以通过点击以下 URL 进行测试。
Option 验证响应 - 1
curl -i -X OPTIONS http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
Response:
#status# HTTP/1.1 200 OK
Server Apache-Coyote/1.1
Content-Length 0
Date Thu, 02 May 2019 10:41:02 GMT
Allow GET,POST,OPTIONS
Option 验证响应 - 2
curl -i -X OPTIONS http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
Response:
#status# HTTP/1.1 200 OK
Server Apache-Coyote/1.1
Content-Length 0
Date Thu, 02 May 2019 10:43:15 GMT
Allow GET,DELETE,PUT,OPTIONS
学习愉快!
Spring REST – 访问被拒绝请求的 JSON 响应
原文: https://howtodoinjava.com/spring-restful/access-denied-json-response/
在应预先验证主体但已在AbstractPreAuthenticatedProcessingFilter
中收到null
的情况下,从 REST API 学习以 jSON 格式返回带有错误详细信息的拒绝访问的响应,并包含错误详细信息。
1. 添加自定义AuthenticationEntryPoint
ExceptionTranslationFilter
使用此类来启动身份验证方案。 如果您正在使用AbstractPreAuthenticatedProcessingFilter
处理预先认证的认证请求,如自定义令牌认证示例所示,则默认情况下,所有未认证的用户都将重定向到默认访问被拒绝的页面,即Http403ForbiddenEntryPoint
。
默认行为显示拒绝访问的 HTML 页面。
我们可以更改定义自己的自定义AuthenticationEntryPoint
的行为。 此身份验证入口点已添加到exceptionHandling()
配置中。
ExceptionTranslationFilter
用于捕获任何 Spring Security 异常,以便可以返回 HTTP 错误响应或启动适当的AuthenticationEntryPoint
。- 如果用户请求安全的 HTTP 资源但未通过身份验证,则将调用
AuthenticationEntryPoint
。 这样做的工作是向用户呈现适当的响应,以便可以开始身份验证。
在给定的配置中,我们保护所有以"/api"
开头的 URL。
Spring security 配置
httpSecurity.
antMatcher("/api/**")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
/*.addFilterBefore(new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint()),
filter.getClass()
)*/
.authorizeRequests()
.anyRequest()
.authenticated();
//Exception handling configuration
httpSecurity
.exceptionHandling()
.authenticationEntryPoint((request, response, e) ->
{
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write(new JSONObject()
.put("timestamp", LocalDateTime.now())
.put("message", "Access denied")
.toString());
});
请不要使用
AccessDeniedHandler
,因为它仅适用于不需要角色的经过身份验证的用户。
2. 演示
2.1. 没有身份验证令牌
API 请求
HTTP POST http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
Headers:
Content-Type application/json
API 响应
Response code - 403 Forbidden
{
"message" : "Access denied",
"timestamp" : "2019-05-03T23:59:52.103"
}
2.2. 使用不正确的身份验证令牌
API 请求
HTTP POST http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
Headers:
Content-Type application/json
AUTH_API_KEY 123456
API 响应
Response code - 403 Forbidden
{
"message" : "Access denied",
"timestamp" : "2019-05-04T00:04:15.457"
}
2.2. 使用正确的身份验证令牌
API 请求
HTTP POST http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/1
Headers:
Content-Type application/json
AUTH_API_KEY abcd123456
API 响应
Response code - 200 OK
{
//API response body
}
请问您有关使用自定义authenticationEntryPoint
将拒绝访问的响应从 Spring REST API 转换为 JSON 响应的问题。
下载源码
学习愉快!
Spring RestTemplate
– Spring REST 客户端示例
原文: https://howtodoinjava.com/spring-boot2/resttemplate/spring-restful-client-resttemplate-example/
在学习为 XML 表示形式和 JSON 表示形式构建 Spring REST API 之后,让我们构建 Spring REST 客户端以使用我们编写的 API 在链接的示例中。
在 Spring 应用程序内部访问第三方 REST 服务的过程涉及 Spring RestTemplate
类的使用。 RestTemplate
类的设计原理与许多其他 Spring *Template
类(例如JdbcTemplate
和JmsTemplate
)相同,为执行复杂任务提供了具有默认行为的简化方法。
鉴于RestTemplate
类是为调用 REST 服务设计的,因此它的主要方法与 REST 的基础紧密结合在一起就不足为奇了,REST 的基础是 HTTP 协议的方法: HEAD, GET , POST , PUT , DELETE 和 OPTION。 例如,RestTemplate
类具有方法headForHeaders()
,getForObject()
,postForObject()
,put()
和delete()
等。
阅读更多和源代码: Spring REST 示例 – JSON
阅读更多:带有
HttpClient
的RestTemplate
Java 配置
1. Spring RestTemplate
– HTTP GET 方法示例
1.1. XML 响应
REST API 代码
用于 HTTP GET 方法的 Spring REST API。
@RequestMapping(value = "/employees", produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public String getAllEmployeesXML(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "xmlTemplate";
}
REST 客户端代码
Spring REST 客户端使用RestTemplate
访问 HTTP GET api 请求。
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees.xml";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}
1.2. JSON 响应
REST API 代码
@RequestMapping(value = "/employees", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String getAllEmployeesJSON(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "jsonTemplate";
}
REST 客户端代码
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees.json";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}
1.3. 使用RestTemplate
的自定义 HTTP 标头
REST API 代码
@RequestMapping(value = "/employees", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String getAllEmployeesJSON(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "jsonTemplate";
}
REST 客户端代码
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println(result);
}
1.4. 获取响应作为对象
REST API 代码
@RequestMapping(value = "/employees", produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public String getAllEmployeesXML(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "xmlTemplate";
}
REST 客户端代码
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees";
RestTemplate restTemplate = new RestTemplate();
EmployeeListVO result = restTemplate.getForObject(uri, EmployeeListVO.class);
System.out.println(result);
}
1.5. URL 参数
REST API 代码
@RequestMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
{
if (id <= 3) {
EmployeeVO employee = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
REST 客户端代码
private static void getEmployeeById()
{
final String uri = "http://localhost:8080/springrestexample/employees/{id}";
Map<String, String> params = new HashMap<String, String>();
params.put("id", "1");
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.getForObject(uri, EmployeeVO.class, params);
System.out.println(result);
}
2. Spring RestTemplate
– HTTP POST 方法示例
REST API 代码
用于 HTTP POST 方法的 Spring REST API。
@RequestMapping(value = "/employees", method = RequestMethod.POST)
public ResponseEntity<String> createEmployee(@RequestBody EmployeeVO employee)
{
System.out.println(employee);
return new ResponseEntity(HttpStatus.CREATED);
}
REST 客户端代码
Spring REST 客户端使用RestTemplate
访问 HTTP POST api 请求。
private static void createEmployee()
{
final String uri = "http://localhost:8080/springrestexample/employees";
EmployeeVO newEmployee = new EmployeeVO(-1, "Adam", "Gilly", "test@email.com");
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.postForObject( uri, newEmployee, EmployeeVO.class);
System.out.println(result);
}
3. Spring RestTemplate
– HTTP PUT 方法示例
REST API 代码
用于 HTTP PUT 方法的 Spring REST API。
@RequestMapping(value = "/employees/{id}", method = RequestMethod.PUT)
public ResponseEntity<EmployeeVO> updateEmployee(@PathVariable("id") int id, @RequestBody EmployeeVO employee)
{
System.out.println(id);
System.out.println(employee);
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
REST 客户端代码
Spring REST 客户端使用RestTemplate
访问 HTTP PUT api 请求。
private static void updateEmployee()
{
final String uri = "http://localhost:8080/springrestexample/employees/{id}";
Map<String, String> params = new HashMap<String, String>();
params.put("id", "2");
EmployeeVO updatedEmployee = new EmployeeVO(2, "New Name", "Gilly", "test@email.com");
RestTemplate restTemplate = new RestTemplate();
restTemplate.put ( uri, updatedEmployee, params);
}
4. Spring RestTemplate
– HTTP DELETE 方法示例
REST API 代码
用于 HTTP DELETE 方法的 Spring REST API。
@RequestMapping(value = "/employees/{id}", method = RequestMethod.DELETE)
public ResponseEntity<String> updateEmployee(@PathVariable("id") int id)
{
System.out.println(id);
return new ResponseEntity(HttpStatus.OK);
}
REST 客户端代码
Spring REST 客户端使用RestTemplate
访问 HTTP DELETE api 请求。
private static void deleteEmployee()
{
final String uri = "http://localhost:8080/springrestexample/employees/{id}";
Map<String, String> params = new HashMap<String, String>();
params.put("id", "2");
RestTemplate restTemplate = new RestTemplate();
restTemplate.delete ( uri, params );
}
随意复制和修改以上 Spring RestTemplate
示例,以在您的 MVC 应用程序中构建 Spring REST 客户端
5. 更多的RestTemplate
示例
Spring RestTemplate
基本身份验证示例
Spring RestTemplate
超时配置示例
Spring RestTemplateBuilder
示例
Spring RestTemplate
– HttpClient
配置示例
Spring Boot RestTemplate
GET 示例
Spring Boot RestTemplate
POST 示例
带有RestTemplate
的 Spring Boot JUnit 示例
带有标题示例的 Spring boot TestRestTemplate
POST 示例
带有RestTemplate
的 Spring ClientHttpRequestInterceptor
学习愉快!
Spring WebFlux
Spring 5 – Bean 范围
原文: https://howtodoinjava.com/spring-core/spring-bean-scopes/
在 Spring 框架中,我们可以在 6 个内置的 spring bean 范围中创建 bean,还可以定义自定义 bean 作用域。 在这六个范围中,只有当您使用网络感知ApplicationContext
时,四个才可用。 singleton
和prototype
范围在任何类型的 IOC 容器中都可用。
1. Spring Bean 范围类型
在 Spring 中,可以使用 spring bean @Scope
注解定义范围。 让我们快速列出所有六个可在 Spring 应用程序上下文中使用的内置 bean 范围。 这些相同的范围也适用于 SpringBoot bean 范围。
范围 | 描述 |
---|---|
singleton (默认) |
每个 spring IoC 容器一个 bean 对象实例 |
prototype |
与单例相反,每次请求 bean 时,它都会产生一个新实例。 |
request |
将创建一个单例,并在 HTTP 请求的整个生命周期中可用。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session |
一个单例将在 HTTP 会话的整个生命周期中创建并可用。仅在可感知网络的 Spring ApplicationContext 中有效。 |
application |
一个单例将在ServletContext 的整个生命周期中创建并可用。仅在可感知网络的 Spring ApplicationContext 中有效。 |
websocket |
一个单例将在WebSocket 的整个生命周期中创建并可用。仅在可感知网络的 Spring ApplicationContext 中有效。 |
1.1. 单例范围
singleton
是 spring 容器中的默认 bean 作用域。 它告诉容器每个容器仅创建和管理一个 bean 类实例。 该单个实例存储在此类单例 Bean 的缓存中,并且对该命名 Bean 的所有后续请求和引用都返回该缓存的实例。
使用 Java 配置的单例作用域 bean 的示例:
@Component
//This statement is redundant - singleton is default scope
@Scope("singleton") //This statement is redundant
public class BeanClass {
}
使用 XML 配置的单例作用域 bean 的示例:
<!-- To specify singleton scope is redundant -->
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="singleton" />
//or
<bean id="beanId" class="com.howtodoinjava.BeanClass" />
1.2. 原型范围
每当应用程序代码对 Bean 进行请求时,prototype
范围都会导致创建新的 Bean 实例。
您应该知道,销毁 bean 生命周期方法没有被称为prototype
作用域 bean,仅调用了初始化回调方法。 因此,作为开发人员,您需要负责清理原型作用域的 Bean 实例以及其中包含的所有资源。
原型 bean 范围的 Java 配置示例:
@Component
@Scope("prototype")
public class BeanClass {
}
原型 bean 范围的 XML 配置示例:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="prototype" />
通常,您应该对所有有状态 Bean 使用prototype
范围,而对无状态Bean使用singleton
范围。要在请求,会话,应用程序和 Websocket 范围中使用 Bean,您需要注册RequestContextListener
或者RequestContextFilter
。
1.3. 请求范围
在request
范围内,容器为每个 HTTP 请求创建一个新实例。 因此,如果服务器当前正在处理 50 个请求,则容器最多可以具有 50 个单独的 Bean 类实例。 对一个实例的任何状态更改,对其他实例都不可见。 请求完成后,这些实例将被销毁。
请求 bean 范围的 Java 配置示例:
@Component
@Scope("request")
public class BeanClass {
}
//or
@Component
@RequestScope
public class BeanClass {
}
请求 bean 范围的 XML 配置示例:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="request" />
阅读更多: Web 服务器如何工作?
1.4. 会话范围
在session
范围内,容器为每个 HTTP 会话创建一个新实例。 因此,如果服务器有 20 个活动会话,则容器最多可以具有 20 个 Bean 类的单独实例。 单个会话生存期内的所有 HTTP 请求都可以访问该会话范围内的同一单个 Bean 实例。
对一个实例的任何状态更改,对其他实例都不可见。 一旦会话在服务器上销毁/结束,这些实例将被销毁。
会话 bean 范围的 Java 配置示例:
@Component
@Scope("session")
public class BeanClass {
}
//or
@Component
@SessionScope
public class BeanClass {
}
会话 bean 范围的 XML 配置示例:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="session" />
1.5. 应用范围
在application
范围内,容器为每个 Web 应用程序运行时创建一个实例。 它几乎与singleton
示波器相似,只有两个区别,即
application
范围的 bean 是每个ServletContext
单身,而singleton
范围的 bean 是每个ApplicationContext
单身。 请注意,单个应用程序可以有多个应用程序上下文。application
范围内的 bean 作为ServletContext
属性可见。
应用程序 bean 范围的 Java 配置示例:
@Component
@Scope("application")
public class BeanClass {
}
//or
@Component
@ApplicationScope
public class BeanClass {
}
应用程序 bean 范围的 XML 配置示例:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="application" />
1.6. websocket 范围
WebSocket 协议允许客户端与已选择与客户端通信的远程主机之间进行双向通信。 WebSocket 协议为双向通信提供单个 TCP 连接。 这对于同时编辑和多用户游戏的多用户应用程序特别有用。
在这种类型的 Web 应用程序中,HTTP 仅用于初始握手。 如果服务器同意,服务器可以以 HTTP 状态 101(交换协议)进行响应。 如果握手成功,则 TCP 套接字保持打开状态,客户端和服务器都可以使用该套接字向彼此发送消息。
websocket bean 作用域的 Java 配置示例:
@Component
@Scope("websocket")
public class BeanClass {
}
Websocket bean 作用域的 XML 配置示例:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="websocket" />
请注意,websocket
范围内的 bean 通常是单例的,并且比任何单独的 WebSocket 会话的生存期都更长。
2. 自定义线程范围
Spring 还使用类SimpleThreadScope
提供了非默认thread
范围。 要使用此作用域,必须使用CustomScopeConfigurer
类将其注册到容器。
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
对 bean 的每个请求都将在同一线程中返回相同的实例。
线程 bean 范围的 Java 配置示例:
@Component
@Scope("thread")
public class BeanClass {
}
线程 bean 范围的 XML 配置示例:
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="thread" />
3. 总结
Spring 框架提供了六个 Spring bean 作用域,并且实例在每个作用域内具有不同的生命周期跨度。 作为开发人员,我们必须明智地选择任何容器管理的 bean 的范围。 同样,当具有不同作用域的 bean 相互引用时,我们必须做出明智的决定。
试着记住上面给出的所有信息,以回答任何 SpringBean 范围面试问题。
学习愉快!