本章主要学习了spring的一些核心组件、初始化一个Spring项目以及spring概览。
快速开始
spring是什么
Spring的核心是提供一个容器,通常称为Spring应用程序上下文,用于创建和管理应用程序组件。
初始化Spring应用程序
使用Spring Initializr的几种方法如下:
- http://start.spring.io
- curl 命令
- 使用Spring引导命令行界面
- Spring Tool Suite new project
- IntelliJ IDEA new project
- NetBeans new project
spring项目架构图:
写一个helloword
定义controller层
定义view层
测试controller类
构建和运行spring应用
Spring概览
Spring核心框架是Spring领域中其他一切的基础。它提供了核心容器和依赖注入框架。
- Spring Boot
优点:
- 启动依赖项和自动配置
- 执行器提供对应用程序内部工作方式的运行时洞察
- 环境属性的灵活规范
- 额外的测试支持
Spring Data
Spring Data提供了一些非常惊人的功能:将应用程序的数据存储库定义为简单的Java接口,在定义驱动如何存储和检索数据的方法时使用命名约定。Spring Security
Spring Security解决了广泛的应用程序安全性需求,包括身份验证、授权和API安全性。Spring Integration和Spring Batch
Spring Integration解决了实时集成,即数据在可用时进行处理。
Spring Batch解决了成批集成的问题,允许在一段时间内收集数据,直到某个触发器(可能是一个时间触发器)发出信号,表示该处理一批数据了。Spring Cloud
Spring Cloud是一组用Spring开发云本地应用程序的项目。
部署web应用
信息展示
定义domain类
创建controller类
设计页面
1 | 1. 添加依赖包 |
form表单提交
页面
[案例] 1
2
3
4
5<form method="POST" th:action="@{/orders}" th:object="${order}">
Name:<input type="text" th:field="*{name}"/>
Street address:<input type="text" th:field="*{street}"/>
...
</form>业务端代码
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// controller类
4j
"/orders") (
public class OrderController {
public String processOrder(Order order) {
log.info("Order submitted: " + order);
return "redirect:/";
}
}
// domain类
public class Order {
private String name;
private String street;
...
}
form表单验证
定义验证规则
1 |
|
在表单绑定时执行验证(@Valid)
1 |
|
显示验证错误信息
使用视图控制器
1 |
|
选择视图模板库
使用data
使用JDBC读写数据
添加依赖
[案例] 1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>定义JDBC存储库
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52public interface IngredientRepository {
Iterable<Ingredient> findAll();
Ingredient findOne(String id);
Ingredient save(Ingredient ingredient);
}
public class JdbcIngredientRepository implements IngredientRepository {
private JdbcTemplate jdbc;
public JdbcIngredientRepository(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
...
// 使用JdbcTemplate查询数据库
public Iterable<Ingredient> findAll() {
return jdbc.query("select id, name, type from Ingredient", this::mapRowToIngredient);
}
private Ingredient mapRowToIngredient(ResultSet rs, int rowNum) throws SQLException {
return new Ingredient(
rs.getString("id"),
rs.getString("name"),
Ingredient.Type.valueOf(rs.getString("type")));
}
// 查询单条记录
public Ingredient findOne(String id) {
return jdbc.queryForObject(
"select id, name, type from Ingredient where id=?",
new RowMapper<Ingredient>() {
public Ingredient mapRow(ResultSet rs, int rowNum)
throws SQLException {
return new Ingredient(
rs.getString("id"),
rs.getString("name"),
Ingredient.Type.valueOf(rs.getString("type")));
};
}, id);
}
// 保存
public Ingredient save(Ingredient ingredient) {
jdbc.update(
"insert into Ingredient (id, name, type) values (?, ?, ?)",
ingredient.getId(),
ingredient.getName(),
ingredient.getType().toString());
return ingredient;
}
}定义controller类
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"/design") (
"order") (
public class DesignTacoController {
private final IngredientRepository ingredientRepo;
public DesignTacoController(IngredientRepository ingredientRepo) {
this.ingredientRepo = ingredientRepo;
}
public String showDesignForm(Model model) {
List<Ingredient> ingredients = new ArrayList<>();
ingredientRepo.findAll().forEach(i -> ingredients.add(i));
Type[] types = Ingredient.Type.values();
for (Type type : types) {
model.addAttribute(type.toString().toLowerCase(),
filterByType(ingredients, type));
}
return "design";
}
...
}
使用Spring data JPA持久化数据
添加依赖
[案例] 1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>定义实体bean
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Taco {
(strategy=GenerationType.AUTO)
private Long id;
5, message="Name must be at least 5 characters long") (min=
private String name;
private Date createdAt;
.class) (targetEntity=Ingredient
@Size(min=1, message="You must choose at least 1 ingredient")
private List<Ingredient> ingredients;
void createdAt() {
this.createdAt = new Date();
}
}定义JPA存储库
[案例] 1
public interface IngredientRepository extends CrudRepository<Ingredient, String> { }
自定义JPA存储库
一些关键语法:
- IsAfter, After, IsGreaterThan, GreaterThan
- IsNull, Null
- IsIn, In
- IsBetween, Between
…
OrderRepository:[案例] 1
2
3
4
5
6// 方式一:根据deliveryZip字段查询
List<Order> findByDeliveryZip(String deliveryZip);
// 方式二:自定义sql
"Order o where o.deliveryCity='Seattle'") (
List<Order> readOrdersDeliveredInSeattle();
...
使用Security
集成spring security
添加依赖
1 | <dependency> |
配置Spring Security
1 |
|
基于内存的用户存储
1 |
|
基于jdbc的用户存储
1 |
|
passwordEncoder()有几种加密方式:
- BCryptPasswordEncoder
- NoOpPasswordEncoder
- Pbkdf2PasswordEncoder
- SCryptPasswordEncoder
- StandardPasswordEncoder
LDAP-backed用户存储
1 |
|
定制用户身份验证
定义用户域和持久性
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
true) (access=AccessLevel.PRIVATE, force=
public class User implements UserDetails {
private static final long serialVersionUID = 1L;
(strategy=GenerationType.AUTO)
private Long id;
private final String username;
private final String password;
private final String fullname;
...
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return true;
}
}创建用户详细信息服务
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UserRepositoryUserDetailsService implements UserDetailsService {
private UserRepository userRepo;
public UserRepositoryUserDetailsService(UserRepository userRepo) {
this.userRepo = userRepo;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepo.findByUsername(username);
if (user != null) {
return user;
}
throw new UsernameNotFoundException(
"User '" + username + "' not found");
}
}修改配置类configure()方法
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
private UserDetailsService userDetailsService;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder()); // 密码加密
}
public PasswordEncoder encoder() {
return new StandardPasswordEncoder("53cr3t");
}注册用户
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"/register") (
public class RegistrationController {
private UserRepository userRepo;
private PasswordEncoder passwordEncoder;
public RegistrationController(
UserRepository userRepo, PasswordEncoder passwordEncoder) {
this.userRepo = userRepo;
this.passwordEncoder = passwordEncoder;
}
public String registerForm() {
return "registration";
}
public String processRegistration(RegistrationForm form) {
userRepo.save(form.toUser(passwordEncoder));
return "redirect:/login";
}
}
request请求安全验证
配置request请求规则
1 |
|
创建自定义登录页面
配置configure()
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/design", "/orders")
.access("hasRole('ROLE_USER')")
.antMatchers(“/”, "/**").access("permitAll")
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate")
.usernameParameter("username") // 表单字段:user
.passwordParameter("password"); // 表单字段:pwd
}在WebConfig中声明它为一个视图控制器
[案例] 1
2
3
4
5
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/login");
}登录页面
[案例] 1
2
3
4
5
6
7<form method="POST" th:action="@{/login}" id="loginForm">
<label for="username">Username: </label>
<input type="text" name="username" id="username" /><br/>
<label for="password">Password: </label>
<input type="password" name="password" id="password" /><br/>
<input type="submit" value="Login"/>
</form>防止跨站请求伪造
(1)配置configure()[案例] 1
2
3http.and()
.csrf()
.disable()(2)前端页面加
[案例] 1
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
使用配置属性
微调自动配置
理解Spring的环境抽象
application.properties
[案例] 1
server.port=9090
application.yml
[案例] 1
2server:
port: 9090命令行
[案例] 1
$ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090
配置数据源
application.yml
1 | spring: |
配置嵌入式服务器
JDK的keytool命令行实用工具:
1 | $ keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA |
启用HTTPS:application.yml
1 | server: |
配置日志记录
1 | <configuration> |
1 | logging: |
使用特殊的属性值
在设置属性时,您不仅可以将它们的值声明为硬编码的字符串和数值。相反,可以从其他配置属性派生它们的值。
welcome: You are using ${spring.application.name}.
创建自己的配置属性
定义一个属性类
[案例] 1
2
3
4
5
6
7
"taco.orders") (prefix=
public class OrderProps {
private int pageSize = 20;
}引用,以controller为例
[案例] 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"/orders") (
"order") (
public class OrderController {
private OrderRepository orderRepo;
private OrderProps props; // 属性类
public OrderController(OrderRepository orderRepo,
OrderProps props) {
this.orderRepo = orderRepo;
this.props = props;
}
...
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, props.getPageSize());
model.addAttribute("orders",
orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}
...
}
配置profiles
激活profile=prod环境的配置文件。
1 | spring: |