Bean
初始化调用
Spring
容器底层使用反射机制创建Bean
实例,因此可在Bean
的空参构造器中执行某些初始化操作,但如果在空参构造器中调用属性注入相关的内容,将出现异常:
@RestController
public class UserController {
/* Spring容器先创建Bean实例,再使用Bean的实例进行注入 */
@Autowired
private UserService userService;
/* Bean实例的创建在调用空参构造器之后完成 */
public UserController() {
/* 空参构造器中不可调用属性注入相关的内容 */
userService.service();
}
}
实际上Bean
的空参构造器中不可调用任何与属性注入相关的内容,因为Spring
容器必须先创建Bean
实例,才能使用Bean
的实例进行属性注入,而Bean
实例的创建在调用空参构造器之后完成。
Spring
提供了Bean
的初始化调用方式:
/* InitializingBean接口中提供了Bean初始化调用的方法,
实现此接口后,Spring容器将在Bean初始化后调用该方法 */
@RestController
public class UserController implements InitializingBean {
@Autowired
private UserService userService;
/* 在afterPropertiesSet()中调用属性注入相关的内容 */
@Override
public void afterPropertiesSet() throws Exception {
userService.service();
}
}
自定义SpringBoot
配置属性
SpringBoot
的配置文件支持自定义属性,作用是可以通过Spring
容器将配置文件中的属性值注入到Bean
类的指定属性中,因此需要有对应的Bean
类加载自定义属性,并指定Bean
类与配置文件中的属性对应关系。
自定义配置属性
application.yml
# ...
# 自定义配置属性
com.xxx.user:
# 注意":"与值之间必须有空格!
username: Tony
password: 123456
# ...
自定义Bean
类
Bean
类需要使用@Component
等注解将实例交由Spring
容器创建。
package com.xxx;
/* 使用相应的注解修饰类,声明类为Bean类 */
@Component
public class User {
/* Bean的属性应与配置属性对应一致 */
private String username;
private String password;
public String getUserName() {return username;}
public void setUserName(String username) {this.username = username;}
public String getPassword() {return password;}
public void setPassword() {this.password = password;}
}
加载配置属性
SpringBoot
不能自动将配置文件中的属性值注入到对应的Bean
属性中,需要使用额外的注解实现。
@Value
@Value
是Spring
提供的属性注入注解,作用是在Spring
容器创建Bean
类实例时,将配置文件中的指定属性值注入到Bean
的属性中,@Value
注解的定义为:
@Target({ElementType.FIELD,ElementType.METHOD,
ElementType.PARAMETER,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
String value();
}
@Value
修饰属性:
package com.xxx;
@Component
public class User {
/* "${}"中传入配置文件中的属性 */
@Value("${com.xxx.user.username}")
private String username;
@Value("${com.xxx.user.password}")
private String password;
public String getUserName() {return username;}
public void setUserName(String username) {this.username = username;}
public String getPassword() {return password;}
public void setPassword() {this.password = password;}
}
@Value
修饰方法:
package com.xxx;
@Component
public class User {
private String username;
private String password;
public String getUserName() {return username;}
/* @Value修饰setter时将读取参数列表,并将指定的属性注入给参数(参数名不一定与属性名相同) */
@Value("${com.xxx.user.username}")
public void setUserName(String username) {this.username = username;}
@Value("${com.xxx.user.password}")
public String getPassword() {return password;}
public void setPassword() {this.password = password;}
}
在其它类中注入Bean
:
@Component
public class Test {
private User user;
/* 自动装配Bean */
@Autowired
public void setUser(User user) {
this.user = user;
}
}
@ConfigurationProperties
@ConfigurationProperties
是Spring
提供的注解,用于使用前缀查找配置文件中的自定义对象(属性集),并将配置文件中的同名配置属性注入到Bean
的属性中。@ConfigurationProperties
的定义为:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
/* 与prefix属性作用一致 */
@AliasFor("prefix")
String value() default "";
/* 前缀,与value属性作用一致 */
@AliasFor("value")
String prefix() default "";
/* 属性忽略 */
boolean ignoreInvalidFields() default false;
boolean ignoreUnknownFields() default true;
}
@ConfigurationProperties
修饰类:
package com.xxx;
@Component
/* 通过前缀与Bean类中的属性名查找配置文件中的配置属性 */
@Configuration("com.xxx.user")
public class User {
/* 属性名应与配置文件中的配置属性名相同 */
private String username;
private String password;
public String getUserName() {return username;}
public void setUserName(String username) {this.username = username;}
public String getPassword() {return password;}
public void setPassword() {this.password = password;}
}
在其它类中注入Bean
:
@Component
public class Test {
private User user;
/* 自动装配Bean */
@Autowired
public void setUser(User user) {
this.user = user;
}
}
前后端数据交互
简单对象类型
示例
SpringBoot
使用JSON
字符串接收对象类型的数据:
/* 必须将JSON对象字符串化 */
var user = JSON.stringify({username:"Tony",password:"123456"});
$.ajax({
type:"POST",
url:"http://localhost:8080/test",
async:true,
/* 数据类型必须与后台一致 */
dataType:"json",
/* 必须指定此请求头 */
headers:{"Content-Type":"application/json;charset=utf-8"},
data:user
});
后台接收数据:
@Controller
public class UserController {
/* consumes属性指定请求体格式,例如数据类型和编码格式等 */
@PostMapping(value="test",consumes="application/json;charset=utf-8")
/* 使用@RequestBody修饰参数用于接收对象 */
public void test(@RequestBody User user) {
System.out.println(user);
}
}
@RequestBody
@RequestBody
用于接收JSON
字符串并将其转换为实体类对象,定义为:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
/* 指定是否必须提供此请求参数 */
boolean required() default true;
}
List
类型
前台示例:
/* 必须将JSON对象字符串化 */
var user1 = {username:"Tony",password:"123456"};
var user2 = {username:"Jack",password:"abcded"};
var user3 = {username:"Luis",password:"abc123"};
/* JSON数组的格式是[json1,json2,...],并非{json1,json2,...} */
var users = [user1,user2,user3];
$.ajax({
type:"POST",
url:"http://localhost:8080/test",
async:true,
/* 数据类型必须与后台一致 */
dataType:"json",
/* 必须指定此请求头 */
headers:{"Content-Type":"application/json;charset=utf-8"},
/* 必须将JSON对象字符串化 */
data:JSON.stringify(users)
});
或:
/* 必须将JSON对象字符串化 */
var user1 = {username:"Tony",password:"123456"};
var user2 = {username:"Jack",password:"abcded"};
var user3 = {username:"Luis",password:"abc123"};
var users = [];
/* 直接使用数组存储 */
users[0] = user1;
users[1] = user2;
users[2] = user3;
$.ajax({
type:"POST",
url:"http://localhost:8080/test",
async:true,
/* 数据类型必须与后台一致 */
dataType:"json",
/* 必须指定此请求头 */
headers:{"Content-Type":"application/json;charset=utf-8"},
/* 必须将JSON对象字符串化 */
data:JSON.stringify(users)
});
后台示例:
@Controller
public class UserController {
/* consumes属性指定请求体格式,例如数据类型和编码格式等 */
@PostMapping(value="test",consumes="application/json;charset=utf-8")
/* 使用@RequestBody修饰参数用于接收对象 */
public void test(@RequestBody List<User> users) {
System.out.println(users);
}
}
Map
类型
前台示例:
/* 必须将JSON对象字符串化 */
var user1 = {username:"Tony",password:"123456"};
var user2 = {username:"Jack",password:"abcded"};
var user3 = {username:"Luis",password:"abc123"};
/* 将需要作为Map的key的值作为JSON的key */
var users = {"Tony":user1,"Jack":user2,"Luis":user3};
$.ajax({
type:"POST",
url:"http://localhost:8080/test",
async:true,
/* 数据类型必须与后台一致 */
dataType:"json",
/* 必须指定此请求头 */
headers:{"Content-Type":"application/json;charset=utf-8"},
/* 必须将JSON对象字符串化 */
data:JSON.stringify(users)
});
注意事项
JSON
字符串
SpringBoot
接收JSON
数据时,只能接收JSON
字符串而不能接收JSON
对象,例如:
$.ajax({
type:"POST",
url:"http://localhost:8080/test",
async:true,
/* 数据类型必须与后台一致 */
dataType:"json",
/* 必须指定此请求头 */
headers:{"Content-Type":"application/json;charset=utf-8"},
/* 不能接收JSON对象 */
data:{username:"Tony",password:"123456"}
});
多个@RequestBody
SpringBoot
不支持多个@RequestBody
修饰的参数,仅转换第一个参数:
/* 仅转换第一个参数 */
public void test(@RequestBody Type1 obj1,
@RequestBody Type2 obj2,
@RequestBody Type3 obj3) {
// ...
}
@RequestBody
修饰的参数代表http
请求的请求体,而一次请求中只有一个请求体,多个@RequestBody
参数不符合实际意义。
请求体类型
前后台必须明确指定请求体的格式要求,后台:
/* consumes属性指定请求体的格式要求,例如请求体的数据类型和编码格式 */
@PostMapping(value="test",consumes="application/json;charset=utf-8")
public void test(@RequestBody User user) {
// ...
}
前台:
$.ajax({
type:"POST",
url:"http://localhost:8080/test",
async:true,
/* 数据类型必须与后台一致 */
dataType:"json",
/* 必须指定此请求头 */
headers:{"Content-Type":"application/json;charset=utf-8"},
/* 必须将JSON对象字符串化 */
data:JSON.stringify(users)
});
@Transactional
@Transactional
注解用于数据库访问的事务管理,其定义为:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/* 指定事务管理器名 */
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
/* 事务的传播行为,取值有:
Propagation.REQUIRED - 如果有事务则加入事务,没有则新建
Propagation.NOT_SUPPORTED - 容器不为这个方法开启事务
Propagation.REQUIRES_NEW - 不管是否存在事务都创建新的事务,原来的事务挂起,
新事务执行完成后继续执行原来的事务
Propagation.MANDATORY - 必须在一个已有的事务中执行,否则抛出异常
propagation=Propagation.NEVER - 必须在一个没有的事务中执行,否则抛出异常
propagation=Propagation.SUPPORTS - 如果其他bean调用这个方法,
在其他bean中声明事务,那就用事务,如果其他bean没有声明事务,那就不用事务. */
Propagation propagation() default Propagation.REQUIRED;
/* 用于设置事务的隔离级别 */
Isolation isolation() default Isolation.DEFAULT;
/* 用于设置事务的超时时间,为-1表示永不超时 */
int timeout() default -1;
/* 是否为只读事务 */
boolean readOnly() default false;
/* 指定需要进行回滚的异常数组,当事务方法抛出包含的异常时将执行回滚 */
Class<? extends Throwable>[] rollbackFor() default {};
/* 指定需要进行回滚的异常名称数组,当事务方法抛出名称包含的异常时将执行回滚 */
String[] rollbackForClassName() default {};
/* 指定不需要进行回滚的异常数组,当事务方法抛出包含的异常时不执行回滚 */
Class<? extends Throwable>[] noRollbackFor() default {};
/* 指定不需要进行回滚的异常名称数组,当事务方法抛出名称包含的异常时不执行回滚 */
String[] noRollbackForClassName() default {};
}
@Transactional
默认只回滚RuntimeException
和Error
,设置rollbackFor=Exception.class
可使事务在任何异常发生时都回滚。@Transactional
不可修饰接口,但可修饰设置了代理的接口,只能修饰权限为public
的方法。一般@Transactional
用在Service
层的方法,因为Service
层的方法通常包含一组数据访问操作,符合事务处理的需求。示例:
@Service
public class UserService {
@Autowired
private UserDao userDao;
/* 对于没有添加事务管理的方法,出现异常时将不会回滚 */
/* 重复添加字段要求为unique的值时,第一次操作成功,第二次不成功 */
public addUser1(User user) {
userDao.add(user);
userDao.add(user);
}
/* rollbackFor=Exception.class指定任何异常发生都将事务回滚 */
@Transactional(timeout=10,rollbackFor=Exception.class)
/* 重复添加字段要求为unique的值时,两个操作都不成功,因为添加了事务管理 */
public addUser2(User user) {
userDao.add(user);
userDao.add(user);
}
}
普通类中获取HttpSession
获取方式:
/* 获取ServletRequestAttributes */
ServletRequestAttributes requestAttributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
/* 获取HttpServletRequest */
HttpServletRequest request = requestAttributes.getRequest();
/* 获取HttpSession */
HttpSession session = request.getSession();
/* 获取HttpServletResponse */
HttpServletResponse response = requestAttributes.getResponse();