一、Spring Web MVC注解
@RequestMapping
注解说明:主要用途是将Web请求与请求处理类中的方法进行映射,可以作用到类或者方法上
属性说明
name:相当于方法的注释,使方法更易理解
java@RequestMapping(value = "/index",name = "第一个接口") @ResponseBody public String login() { return "hello world"; }value:默认属性(如果只填一个属性时,属性名可以省略),属性支持通配符匹配
java// 效果一样 @RequestMapping(value="/login") @RequestMapping("/login")java// http://localhost:8080/login/test和http://localhost:8080/login/1都能访问该端口 @RequestMapping("/login/*")path:与value同义,也支持通配符匹配
method:指定请求类型,值为枚举类RequestMethod一个或者多个,如果不指定,说明该方法支持全部HTTP请求
java// 接口只支持 GET 请求 @RequestMapping(value = "/login",method = RequestMethod.GET) @ResponseBody public String login() { return "success"; }java// 接口同时支持 GET和POST 请求 @RequestMapping(value = "/login",method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody public String login() { return "success"; }params:该属性指定请求中必须包含params指定的一个或者多个参数时,才能执行该请求
java// http://localhost:8080/login?flag=0能访问 // http://localhost:8080/login无法访问 @RequestMapping(value = "/login",method = RequestMethod.GET, params = "flag") @ResponseBody public String login() { return "success"; }java// http://localhost:8080/login?flag=0能访问 // http://localhost:8080/login?flag=1无法访问 // http://localhost:8080/login无法访问 @RequestMapping(value = "/login",method = RequestMethod.GET, params = "flag=0") @ResponseBody public String login() { return "success"; }headers:该属性指定请求中必须包含headers指定的一个或者多个请求头时,才能执行该请求
java// 请求的header中包含Referer请求头并且值为http://localhost:80才能访问 @RequestMapping(path = "/login", headers="Referer=http://localhost:80") public String login() { return "success"; }consumes:该属性指定请求的提交内容类型(Content-Type)必须包含consumes指定的一个或者多个请求头时,才能执行该请求
java@RequestMapping(value = "/login", consumes = "application/json") @ResponseBody public String login() { return "success"; }produces:该属性指定返回值的内容类型,注意:返回的内容类型必须是request请求头Accept中所包含的类型
java@RequestMapping(value = "login", produces = "application/json") @ResponseBody public String login() { return "success"; }java// 还可以指定返回值的编码 @RequestMapping(value = "login", produces = "application/json;charset=utf-8") @ResponseBody public String login() { return "success"; }
注意:在使用 @RequestMapping 之前,请求处理类还需要使用
@Controller或@RestController进行标记
@RequestBody
- 注解说明:将请求主题中的参数绑定到一个对象中,通常用于处理
POST、PUT请求,该注解的实现依赖于Spring MVC的HttpMessageConverter接口,该接口定义了将请求体中的 JSON、XML等格式的数据转换成Java对象的逻辑
@RequestParam
注解说明:用于将指定的请求参数赋值给方法中的形参
属性说明
value:默认参数,请求参数名,如果和形参一致,不需要设置
java// http://localhost:8080/test?id=123 名称一致,都为id,可省略属性值的指定 @GetMapping("/test") public String test(@RequestParam String id) { return "success"; }java// http://localhost:8080/test?id=123 将请求中的id参数赋值给userId @GetMapping("/test") public String test(@RequestParam("id") String userId) { return "success"; }name:与value同义
required:表示请求中是否包含该参数,默认为true,如果设置成true并且请求没有此参数,则抛出异常
java// http://localhost:8080/test // @RequestParam默认required为true,请求中没有参数,报错 Required String parameter 'userId' is not present @GetMapping("/test") public String test(@RequestParam("id") String userId) { return "success"; }defaultValue:为属性设置默认值,当设置默认值时,
required属性会被设置成falsejava// http://localhost:8080/test 后台拿到的是默认值456 // http://localhost:8080/test?id=123 后台拿到的是123 @GetMapping("/test") public String test(@RequestParam(defaultValue = "456") String id) { return "success"; }
代码示例:除了上面的属性用法以外,还有下面的扩展用法
映射所有参数:使用
Map封装多个参数(无需定义参数的名称和数量)java// http://localhost:8080/test?id=123&name=zhangsan @GetMapping("/test") public String test(@RequestParam Map<String, String> params) { System.out.println("Parameters are: " + allParams.entrySet()); return "success"; }多值参数
java// http://localhost:8080/test?id=1,2,3 可以映射 // http://localhost:8080/test?id=1&id=2&id=3 可以映射 @GetMapping("/test") public String test(@RequestParam List<String> idList) { System.out.println("idList: " + idList); return "success"; }
@PathVariable
注解说明:将方法的参数与请求URL中的模板变量相绑定
属性说明
value:指定URL中的模板变量,如果方法参数和模板变量一样,那么value可以省略
java// http://localhost:8080/test/user/info/1 @GetMapping("/test/{system}/info/{id}") public String test(@PathVariable(name = "system") String system, @PathVariable(value = "id") String id) { System.out.println("system: " + system); System.out.println("id: " + id); return "success"; }name:与value同义
required:是否必需,默认为true
@GetMapping
- 注解说明:用于处理HTTP的
GET请求,并将请求映射到具体的处理方法中,该注解相当于@RequestMapping(method=RequestMethod.GET)的快捷方式
@PostMapping
- 注解说明:用于处理HTTP的
POST请求,并将请求映射到具体的处理方法中,该注解相当于@RequestMapping(method=RequestMethod.POST)的快捷方式
@PutMapping
- 注解说明:用于处理HTTP的
PUT请求,并将请求映射到具体的处理方法中,该注解相当于@RequestMapping(method=RequestMethod.PUT)的快捷方式
@DeleteMapping
- 注解说明:用于处理HTTP的
DELETE请求,并将请求映射到具体的处理方法中,该注解相当于@RequestMapping(method=RequestMethod.DELETE)的快捷方式
@PatchMapping
- 注解说明:用于处理HTTP的
PATCH请求,并将请求映射到具体的处理方法中,该注解相当于@RequestMapping(method=RequestMethod.PATCH)的快捷方式
@Controller
- 注解说明:@Controller是@Component注解的一个延伸注解,此注解用于标注Spring MVC的控制器
- 属性说明
- value:通过此属性显式定义Bean的名称,该属性也仅用于定义Bean的名称,与请求路径无关
@ResponseBody
- 注解说明:该注解会自动将控制器中的方法返回值写入到HTTP响应中
- 注意:@ResponseBody注解只能用在被@Controller注解标记的类中
@RestController
- 注解说明:该注解相当于
@ResponseBody+@Controller的快捷方式
@ControllerAdvice
注解说明:@ControllerAdvice是@Component注解的一个延伸注解,Spring会自动扫描并检测被@ControllerAdvice所标注的类,在该类中,定义一个用于处理具体异常的方法,并使用
@ExceptionHandler注解进行标记,此外,在有必要的时候,可以使用@InitBinder在类中进行全局的配置,还可以使用@ModelAttribute配置与视图相关的参数属性说明
basePackages:增强指定包下的所有Controller,如果不指定,默认增强所有包下的Controller
java@ControllerAdvice(basePackages = {"com.example.user", "com.example.order"})value:与basePackages同义
basePackageClasses:增强指定类所在包以及其子包下的所有Controller
java// 增强的是UserController类所在的包下的所有组件 @ControllerAdvice(basePackageClasses = {com.example.user.UserController.class})assignableTypes:增强指定的Controller,其他的Controller不会被增强
java// 增强的只有UserController和OrderController类 @ControllerAdvice(assignableTypes = {com.example.user.UserController.class, com.example.order.OrderController.class})annotations:增强标记了指定注解的所有Controller
java// 自定义注解 @Documented @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ControllerMarkAnnotation { }java// 指定增强的标注了自定义注解的Controller @ControllerAdvice(annotations = ControllerMarkAnnotation.class)java// 被自定义注解标注了,被增强了 @ControllerMarkAnnotation @RestController public class UserController { } // 未被自定义注解标注了,未被增强 @RestController public class OrderController { }
代码示例
@ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理
java@RestControllerAdvice public class GlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); // 处理@Validated @Valid校验表单时抛出的异常 @ExceptionHandler(BindException.class) public AjaxResult handleBindException(BindException e) { List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); List<String> msgList = fieldErrors.stream() .map(o -> o.getDefaultMessage()) .collect(Collectors.toList()); String messages = StringUtils.join(msgList.toArray(), ";"); log.error("Validation格式校验异常:-------------->{}", messages); return AjaxResult.error(messages); } // 处理@Validated @Valid校验json格式时抛出的异常 @ExceptionHandler(MethodArgumentNotValidException.class) public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get(); String messages = objectError.getDefaultMessage(); log.error("MethodArgumentNotValidException异常:-------------->{}", messages); return AjaxResult.error(messages); } // 处理@NotBlank @NotNull @NotEmpty校验失败抛出的异常 @ExceptionHandler(ConstraintViolationException.class) public AjaxResult handleConstraintViolationException(ConstraintViolationException e) { List<String> msgList = new ArrayList<>(); for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) { msgList.add(constraintViolation.getMessage()); } String messages = StringUtils.join(msgList.toArray(), ";"); log.error("entity格式校验异常:-------------->{}", messages); return AjaxResult.error(messages); } // 处理自定义异常... // 处理其他异常... }@ControllerAdvice 配合 @ModelAttribute 预设全局数据
方式一
java@ControllerAdvice public void GlobalControllerAdvice { @ModelAttritbute public void presetParam(Model model) { model.addAttribute("flag", "Y"); } }方式二
java@ControllerAdvice public void GlobalControllerAdvice { @ModelAttritbute() public Map<String, String> presetParam() { Map<String, String> result = new HashMap<String, String>(); result.put("key1", "value1"); result.put("key2", "value2"); result.put("key3", "value3"); return result; } } // @ModelAttritbute()会将返回值的变量名作为key,例如上面return result,所以key则是"result",但是!不支持字符串作为key // @ModelAttritbute("myMap") 如果显式定义了key名,则key是"myMap"全局参数的使用
java@RestController public class UserController { @GetMapping("/test1") public String test01(Model model) { Map<String, Object> modelMap = model.asMap(); return (String) modelMap.get("flag"); } @GetMapping("/test2") public String test02(@ModelAttribute("flag") String flag) { return flag; } @GetMapping("test3") public String test03(ModelMap modelMap) { return (String) modelMap.get("flag"); } }
@ControllerAdvice 配合 @InitBinder 实现对请求参数的预处理
java@ControllerAdvice public class GlobalControllerAdvice { @InitBinder public void processParams(WebDataBinder webDataBinder) { // 创建一个字符串微调编辑器,去掉字符串前后的空格 StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true); /* * 注册自定义编辑器 * 第一个参数requiredType: 需要处理的类型 * 第二个参数propertyEditor: 属性编辑器,PropertyEditor的实现类 */ webDataBinder.registerCustomEditor(String.class, stringTrimmerEditor); } }java// http://localhost:8080/test?name=%20%20zhangsan%20 @RestController public class UserController { @GetMapping("/test") public String test(@RequestParam String name) { System.out.println("name: " + name); // 去掉前后空格的值zhangsan return "success"; } }
@RestControllerAdvice
- 注解说明:该注解相当于
@ResponseBody+@ControllerAdvice的快捷方式
@ExceptionHandler
- 注解说明:@ExceptionHandler用于标注处理特定类型异常类所抛出异常的方法,当控制器中的方法抛出异常时,Spring会自动捕获异常,并将捕获的异常信息传递给被@ExceptionHandler标注的方法
- 属性说明
- value:方法处理的异常类型,Throwable的子类
@ResponseStatus
注解说明:@ExceptionHandler用于改变HTTP响应的状态码
属性说明
- code:http状态码,值为枚举类HttpStatus的属性,如HttpStatus.OK、HttpStatus.CREATED
- value:与code同义
- reason:错误信息
代码示例
用法一:@ResponseStatus加在自定义异常类上
java@RestControllerAdvice public class GlobalExceptionHandler { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(Exception.class) public AjaxResult handleGlobalExceptionHandler(Exception e) { return AjaxResult.error(); } }用法二:@ResponseStatus加在控制层方法上
java@RestController public class UserController { @ResponseStatus(HttpStatus.BAD_REQUEST) @GetMapping("/test") public String test() { return "success"; } }
@CrossOrigin
注解说明:为请求处理类或者请求处理方法提供跨域调用支持
属性说明
- *origins:允许跨域的域名,如果想向所有域名开放,直接填 **
- value:与origins同义
- maxAge:预检请求的缓存时间,减少重复预检
- originPatterns:允许跨域的域名通配符
- allowedHeaders:服务端通过此项告知浏览器客户端可以携带哪些请求头
- exposedHeaders:允许客户端访问的响应头
- methods:允许客户端使用的HTTP方法
- allowCredentials:是否允许客户端携带凭据
代码示例
作用到处理类上(生效的是类中的所有方法)
java@CrossOrigin @RestController public class UserController { @GetMapping("/test") public String test() { return "success"; } }作用到处理方法上
java@RestController public class UserController { @CrossOrigin @GetMapping("/test") public String test() { return "success"; } }
@ModelAttribute
注解说明:@ModelAttribute将方法参数或者方法返回值绑定到命名的模型属性,并将其暴露给web视图
代码示例*(数据预处理的使用在@ControllerAdvice已经演示过了,这里只对此注解在SpringMVC+模板引擎这种数据驱动视图的场景进行演示)*
@ModelAttribute作用到void返回值的方法上
java// http://localhost:8080/info?username=zhangsan, 当用户在请求这个接口时,populateModel方法会在goToInfo方法前被调用,它将请求参数加入到了model属性username中,在此之后,goToInfo被调用 @Controller public class UserController { @ModelAttribute public void populateModel(@RequestParam String username, Model model) { model.addAttribute("username", username); } @GetMapping("/info") public String goToInfo() { return "info"; } }@ModelAttribute作用到返回具体类的方法上
java// http://localhost:8080/info?username=zhangsan, 当用户在请求这个接口时,populateModel方法会在goToInfo方法前被调用,model属性的名称没有指定,它由返回类型隐含表示,如此方法返回的是Account类 ,那么model属性的名称就是account @Controller public class UserController { @ModelAttribute public Account populateModel(@RequestParam String username) { return new Account(username); } @GetMapping("/info") public String goToInfo() { return "info"; } }@ModelAttribute(value="xxx")作用到返回具体类的方法上
java// http://localhost:8080/info?username=zhangsan, 当用户在请求这个接口时,populateModel方法会在goToInfo方法前被调用,使用@ModelAttribute注释的value属性作为model属性的名称 @Controller public class UserController { @ModelAttribute("username") public String populateModel(@RequestParam String username) { return username; } @GetMapping("/info") public String goToInfo() { return "info"; } }@ModelAttribute和@RequestMapping作用到同一个方法
java// 此时方法的返回值就不是一个视图的名称了,而是model属性的值,属性的名称是由@ModelAttribute(value="")指定,视图名称由RequestToViewNameTranslator根据请求"/info.do"转换为逻辑视图info @Controller public class UserController { @ModelAttribute("username") @GetMapping("/info.do") public String goToInfo() { return "zhangsan"; } }@ModelAttribute作用到一个方法的参数上
java@PostMapping("/saveProduct") public String saveProduct(@ModelAttribute Product product) { // product 对象已通过请求参数自动填充(如 name, price 等字段) productService.save(product); return "redirect:/products"; }
@InitBinder
注解说明:该注解用于标注初始化WebDataBinider 的方法,该方法用于对Http请求传递的表单数据进行处理,如时间格式化、字符串处理等
java@ControllerAdvice public class GlobalControllerAdvice { @InitBinder public void initBinder(WebDataBinder webDataBinder) { // 将请求中的所有的字符串去掉前后空格 StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true); webDataBinder.registerCustomEditor(String.class, stringTrimmerEditor); } }java.beans.PropertyEditor接口实现类说明:PropertyEditor使得开发者能够轻易地调用扩展类进行对象与String类型的转换
类名 描述 BooleanEditor 处理布尔类型( Boolean)的属性,将字符串"true"或"false"转换为Boolean对象ByteEditor 处理字节类型( Byte)的属性,将字符串转换为Byte对象ColorEditor 处理颜色类型( Color)的属性,将十六进制颜色代码(如#RRGGBB)转换为Color对象DoubleEditor 处理双精度浮点数( Double)的属性,将字符串解析为Double类型FloatEditor 处理单精度浮点数( Float)的属性,将字符串解析为Float类型IntegerEditor 处理整数类型( Integer)的属性,将字符串解析为Integer类型LongEditor 处理长整型类型( Long)的属性,将字符串解析为Long类型ShortEditor 处理短整型类型( Short)的属性,将字符串解析为Long类型StringEditor 处理字符串类型( String)的属性,直接返回或设置字符串,无需额外解析EnumEditor 处理枚举类型( Enum)的属性,将字符串转换成枚举常量或将枚举常量转换成字符串FontEditor 处理字体类型( Font类)的属性,将字符串(例如"Arial,PLAIN,12")转换为Font对象自定义PropertyEditor扩展类:java提供了一个默认的实现类
java.beans.PropertyEditorSupport,开发者只需要重写setAsText和getAsText方法就可以实现String类型到特定类型的转换了java// 例如: String转成Integer的扩展类 public class String2IntegerPropertyEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { this.setValue(Integer.valueOf(text)); } } public class App { public static void main(String[] args) { String text = "520"; String2IntegerPropertyEditor editor = new String2IntegerPropertyEditor(); editor.setAsText(text); Object resultVal = editor.getValue(); System.out.println("值是:"); // 520 System.out.println(resultVal); System.out.println("类型是:"); // java.lang.Integer System.out.println(resultVal.getClass()); } }
二、Spring Bean注解
@ComponentScan
注解说明:@ComponentScan指定Spring容器自动扫描组件并注册组件(被
@Component、@Service、@Repository、@Controller标注的组件)属性说明
java@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) // 可重复注解 public @interface ComponentScan { @AliasFor("basePackages") String[] value() default {}; // 基础包名,等同于basePackages @AliasFor("value") String[] basePackages() default {}; // 基础包名,value Class<?>[] basePackageClasses() default {}; // 扫描的类,会扫描该类所在包及其子包的组件。 Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; // 注册为BeanName生成策略 默认BeanNameGenerator,用于给扫描到的Bean生成BeanName Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; // 用于解析bean的scope的属性的解析器,默认是AnnotationScopeMetadataResolver ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; // scoped-proxy 用来配置代理方式 // no(默认值):如果有接口就使用JDK代理,如果没有接口就使用CGLib代理 interfaces: 接口代理(JDK代理) targetClass:类代理(CGLib代理) String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; // 配置要扫描的资源的正则表达式的,默认是"**/*.class",即配置类包下的所有class文件。 boolean useDefaultFilters() default true; // useDefaultFilters默认是true,扫描带有@Component ro @Repository ro @Service ro @Controller 的组件 Filter[] includeFilters() default {}; // 包含过滤器 Filter[] excludeFilters() default {}; // 排除过滤器 boolean lazyInit() default false; // 是否是懒加载 @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter { // 过滤器注解 FilterType type() default FilterType.ANNOTATION; // 过滤判断类型 @AliasFor("classes") Class<?>[] value() default {}; // 要过滤的类,等同于classes @AliasFor("value") Class<?>[] classes() default {}; // 要过滤的类,等同于value String[] pattern() default {}; // 正则化匹配过滤 } }代码示例
@ComponentScan的基本用法
java// 使用@ComponentScan注解指定扫描com.example包中的组件 @Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { }java// 在Spring应用中通过AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext来加载配置类 public class Application { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = context.getBean(MyService.class); myService.doSomething(); } }@ComponentScan指定扫描多个包路径
java@Configuration @ComponentScan(basePackages = {"com.example.service", "com.example.repository"}) public class AppConfig { }@ComponentScan指定扫描的类以及类所在包及其子包的所有组件
java@Configuration @ComponentScan(basePackageClasses = {MyService.class, MyRepository.class}) public class AppConfig { }@ComponentScan排除特定的组件
java// 排除了com.example下的service包的所有组件 @Configuration @ComponentScan(basePackages = "com.example", excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\\.example\\.service\\..*")) public class AppConfig { }@ComponentScan指定扫描的类带有自定义注解的所有组件
java// @ComponentScan扫描包含了所有带有MyCustomAnnotation注解的组件 @Configuration @ComponentScan(basePackages = "com.example", includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyCustomAnnotation.class)) public class AppConfig { }
@Component
- 注解说明:@Component注解用于标注一个普通的组件类,它没有明确的业务范围,只是通知Spring被此注解的类需要被纳入到Spring Bean容器中并进行管理
@Service
- 注解说明:@Service注解是@Component的一个延伸,用于标注业务逻辑类
@Repository
- 注解说明:@Repository注解是@Component的一个延伸,用于标注dao层数据持久化的类
三、Spring Dependency Inject注解
@DependsOn
注解说明:@DependsOn注解可以定义在类和方法上,意思是我这个组件要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中
代码演示
没使用@DependsOn时,因为spring默认扫描包时会根据文件在文件夹的位置先后顺序扫描加载,而EventSource 文件位置在EventTListener前面,所以会先加载EventSource 事件源组件
java/** * 事件源 */ @Component public class EventSource { public EventSource(){ System.out.println("事件源创建"); } } /** * 监听类 */ @Component public class EventListener { public EventListener(){ System.out.println("监听器创建"); } } @Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { } // 事件源创建 // 监听器创建使用@DependsOn,value属性必填!
java/** * 事件源 */ @Component @DependsOn(value = {"eventListener"}) public class EventSource { public EventSource(){ System.out.println("事件源创建"); } } /** * 监听类 */ @Component public class EventListener { public EventListener(){ System.out.println("监听器创建"); } } @Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { } // 监听器创建 // 事件源创建
注意事项
依赖一个bean
java@DependsOn("b") public class A { public A() { System.out.println("a创建"); } } public class B { public B() { System.out.println("b创建"); } } // b创建 // a创建依赖多个bean
java@DependsOn({"b", "c"}) public class A { public A() { System.out.println("a创建"); } } public class B { public B() { System.out.println("b创建"); } } public class C { public C() { System.out.println("C创建"); } } // b创建 // c创建 // a创建
@Bean
注解说明
- @Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。 产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中
- @Bean是一个方法级别的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里
为什么要使用@Bean?
- 类似@Compnent、@Repository、@Controller、@Service这些注册Bean的注解局限性,只能局限作用于自己编写类,如果是一个jar包的第三方库的类对象要加入IOC容器的话,这些注解就心有余力不足了,这时候就轮到@Bean注解登场了
属性说明
value:bean的名称,如果没有指定,默认是注解所标注的方法的名
name:与value同义
autowire:自动装配,默认不开启,建议尽量不要开启,因为自动装配不能装配基本数据类型、字符串、数组等,这是自动装配设计的局限性,并且自动装配不如依赖注入精确
initMethod:bean初始化之前的执行方法,一般也不怎么用,初始化前的逻辑也完全可以在方法中编写
destroyMethod:bean销毁时执行的方法,如果存在该方法但是不想执行该方法,可以使用@Bean(destroyMethod="")阻止销毁方法的执行
javapublic class Person { public Person() { System.out.println("执行Person构造器创建Person对象"); } public void init() { System.out.println("initMethod"); } public void destroy() { System.out.println("destroyMethod"); } }java@Configuration public class BeanConfig { // 可以自己指定方法名,也可以不指定,使用Spring默认提供的方法 @Bean(initMethod = "init", destroyMethod = "destroy") // singleton单例,在IOC容器销毁时,就会调用 destroyMethod() 方法来将 Bean对象销毁;prototype多例,它的Bean实例对象则不受IOC容器的管理,最终由GC来销毁 @Scope(value = "singleton") public Person person(){ return new Person(); } }
@Bean注解与其他注解一起使用
@Import
注解说明:
- 该注解允许将一个或多个配置类、组件类导入到当前Spring容器中
- 该注解允许将动态注册逻辑导入到当前Spring容器中
代码示例
导入一个或多个配置类(如将数据库配置、安全配置等模块拆分到不同类中,通过@Import统一加载)
java@Configuration @Import({DatabaseConfig.class, SecurityConfig.class}) // 导入多个配置类 public class AppConfig { // 主配置类 }导入普通类作为Bean(如导入第三方库的类(无法修改源码添加
@Component注解)作为Bean)java@Configuration @Import(ExternalService.class) // 将第三方库的类 ExternalService 注册为 Bean public class AppConfig { }使用
ImportSelector动态选择配置(如根据条件(如环境变量、系统属性)动态决定导入哪些配置)java// step1: 实现ImportSelector接口 // step2: 返回需要导入的配置类的全限定名数组 public class EnvBasedImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata metadata) { String env = System.getProperty("spring.profiles.active", "dev"); if ("prod".equals(env)) { return new String[] {ProdConfig.class.getName()}; } else { return new String[] {DevConfig.class.getName()}; } } } @Configuration @Import(EnvBasedImportSelector.class) // 根据环境动态导入配置 public class AppConfig { }使用
ImportBeanDefinitionRegistrar编程式注册Bean(需要完全控制 Bean 的注册逻辑时)javapublic class CustomBeanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) { // 动态注册一个 ExternalService类的Bean GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClassName(ExternalService.class.getName()); registry.registerBeanDefinition("externalService", beanDefinition); } } @Configuration @Import(CustomBeanRegistrar.class) // 编程式注册 Bean public class AppConfig { }组合注解简化 @Import
java@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import({DatabaseConfig.class, CacheConfig.class}) // 隐藏导入细节 public @interface EnableInfrastructure { } @Configuration @EnableInfrastructure // 等效于 @Import({DatabaseConfig.class, CacheConfig.class}) public class AppConfig { }
四、Bean Scops注解
@Scope
注解说明:@Scope注解的作用是用来配置IOC容器中Bean实例的作用域
属性说明
value:指定作用域名称
作用域名称 描述、 singleton 默认作用域,整个IOC容器中只存在一个实例,所有依赖共享该实例
采用的是饿汉加载(容器启动,Bean实例就创建好了),如果想让bean变成懒汉加载,使用@Lazy注解prototype 每次获取(如getBean() 或者 注入)都会创建一个新实例
采用的是懒汉加载(IOC容器启动的时候,并不会创建Bean实例,而是在第一次使用的时候才会创建)request 每个 HTTP 请求生成一个新实例(仅 Web 应用有效) session 每个用户会话生成一个实例(仅 Web 应用有效) application 整个 Web 应用生命周期内共享一个实例(类似 ServletContext) websocket 每个 WebSocket 会话生成一个实例(Spring 5+) scopeName:与value同义
proxyMode:指定作用域代理模式,用于解决作用域生命周期不一致的问题
代码示例
在
@Component类上指定作用域java@Component @Scope("prototype") public class ShoppingCart { }在
@Bean方法上指定作用域java@Configuration public class AppConfig { @Bean @Scope("prototype") public UserService userService() { return new UserService(); } }
快捷注解
@RequestScope<=>@Scope("request")@SessionScope<=>@Scope("session")@ApplicationScope<=>@Scope("application")
五、容器配置注解
@Autowired和@Resource
@Autowired和@Resource的对比
@Autowired @Resource 来源 Spring框架 Java EE 规范(JSR-250),Spring 兼容支持 默认匹配策略 按 类型 匹配 按 名称 匹配(若未指定名称,则回退到类型匹配) 名称指定方式 需配合 @Qualifier 注解指定名称 直接通过name属性指定名称 依赖查找循序 1. 按类型 → 2. 按名称(需 @Qualifier) 1. 按名称 → 2. 按类型 是否支持required=false 支持 不支持(默认必须存在) 支持位置 字段、构造函数、Setter 方法 字段、Setter 方法 @Autowired的代码示例
字段、构造函数、Setter 方法注入
java@Service public class OrderService { // 字段注入(按类型匹配) @Autowired private PaymentService paymentService; // 构造函数注入(推荐) @Autowired public OrderService(PaymentService paymentService) { this.paymentService = paymentService; } // Setter 方法注入 @Autowired public void setPaymentService(PaymentService paymentService) { this.paymentService = paymentService; } }允许依赖不存在
java@Autowired(required = false) // 若找不到 Bean,注入 null private Optional<SomeService> someService;
@Resource的代码示例
字段、Setter 方法注入
java@Service public class OrderService { // 字段注入(默认按字段名 "paymentService" 匹配) @Resource private PaymentService paymentService; // 显式指定名称注入 @Resource(name = "alipayService") private PaymentService paymentService; // Setter 方法注入 @Resource public void setPaymentService(PaymentService paymentService) { this.paymentService = paymentService; } }
@Lazy
注解说明:标明一个bean是否懒汉加载(第一次用的时候才会创建实例),当作用在被
@Component标注的类上或@Component 的延伸注解(如 @Controller、@Service、@Repository等)时,表明该类中的所有bean都延迟加载代码示例
@Lazy作用在组件类上
java@Lazy @Configuration @ComponentScan(basePackages = "com.example") public class AppConfigWithLazy { @Bean public MyBean myBean(){ System.out.println("myBean Initialized"); return new MyBean(); } @Bean public MyBean myBean2(){ System.out.println("myBean2 Initialized"); return new MyBean(); } }java// 上面的方法也相当于 @Configuration @ComponentScan(basePackages = "com.example") public class AppConfigWithLazy { @Bean @Lazy public MyBean myBean(){ System.out.println("myBean Initialized"); return new MyBean(); } @Bean @Lazy public MyBean myBean2(){ System.out.println("myBean2 Initialized"); return new MyBean(); } }@Lazy与@Autowired一起使用:当@Lazy注解与@Autowired一起使用时,Spring会为依赖项生成一个代理对象,而非立即初始化真实Bean,真实Bean的初始化会延迟到第一次实际使用该依赖时(如调用其的方法)
java@Service public class ServiceA { private final ServiceB serviceB; @Autowired public ServiceA(@Lazy ServiceB serviceB) { // 延迟注入 ServiceB this.serviceB = serviceB; } }java// bean的注入也可以写成下面的方式 @Service public class ServiceA { @Lazy @Autowired private ServiceB serviceB; }
@Lazy的使用场景
解决循环依赖:若两个单例Bean相互依赖,可以通过@Lazy打破初始化顺序导致的循环依赖问题
java@Service public class ServiceA { private final ServiceB serviceB; @Autowired public ServiceA(@Lazy ServiceB serviceB) { // 延迟注入 ServiceB this.serviceB = serviceB; } } @Service public class ServiceB { @Autowired private ServiceA serviceA; // 正常注入 ServiceA }优化启动性能:避免加载启动时不需要的“重型”Bean(如数据库连接池、第三方服务客户端)
java@Service public class ReportService { @Autowired @Lazy // 延迟初始化大型报表生成器 private ReportGenerator heavyReportGenerator; public void generateReport() { heavyReportGenerator.generate(); // 第一次调用时初始化 } }
@Primary
注解说明:该注解是Spring框架中用于解决多个同类型Bean的依赖注入冲突的注解,通过标记某个Bean为“首选”,使得Bean在自动装配时,Spring会优先选择该Bean,避免因存在多个候选Bean而抛出异常
代码示例
@Primary作用在组件类上
java@Component @Primary public class PrimaryDataSource implements DataSource { // 主数据源实现 } @Component public class BackupDataSource implements DataSource { // 备份数据源实现 } // 使用时,优先装配PrimaryDataSource private DataSource dataSource;@Primary作用在@Bean方法上
java@Configuration public class AppConfig { @Bean @Primary public DataSource primaryDataSource() { return new PrimaryDataSource(); } @Bean public DataSource backupDataSource() { return new BackupDataSource(); } } // 使用时,优先装配PrimaryDataSource private DataSource dataSource;
@Primary 和 @Qualifier 的区别
@Primary @Qualifier 优先级 低 高,和@Primary同时使用时,会覆盖@Primary 使用场景 定义全局默认的Bean 按名称精确指定Bean 是否需要指定名称属性 否 是 作用范围 影响所有注入点 仅影响当前注入点
@Qualifier
注解说明:当容器中存在多个相同类型的 Bean 时,通过
@Qualifier可以显式指定要注入的具体 Bean,避免因类型冲突导致NoUniqueBeanDefinitionException异常代码示例
按照Bean名称注入
javapublic interface PaymentService { void pay(); } @Component("alipayService") // 指定 Bean 名称 public class AlipayService implements IPaymentService { @Override public void pay() { /* 支付宝逻辑 */ } } @Component("wechatPayService") // 指定 Bean 名称 public class WechatPayService implements IPaymentService { @Override public void pay() { /* 微信支付逻辑 */ } } @Service public class OrderService { @Autowired @Qualifier("alipayService") // 按名称注入 private IPaymentService paymentService; }结合@Bean方法
java@Configuration public class PaymentConfig { @Bean("alipay") public PaymentService alipayService() { return new AlipayService(); } @Bean("wechat") public PaymentService wechatPayService() { return new WechatPayService(); } } @Service public class OrderService { @Autowired @Qualifier("wechat") // 注入指定 Bean private PaymentService paymentService; }用于构造函数或Setter方法
java// 用于构造函数 @Service public class OrderService { private final PaymentService paymentService; @Autowired public OrderService(@Qualifier("wechatPayService") PaymentService paymentService) { this.paymentService = paymentService; } }java// 用于Setter方法 @Service public class OrderService { private PaymentService paymentService; @Autowired public void setPaymentService(@Qualifier("wechatPayService") PaymentService paymentService) { this.paymentService = paymentService; } }使用组合注解标注Bean和注入Bean
java@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier("alipay") // 组合注解 public @interface Alipay { } @Component @Alipay // 使用自定义注解标记 Bean public class AlipayService implements PaymentService { /* ... */ } @Service public class OrderService { @Autowired @Alipay // 通过注解注入 private PaymentService paymentService; }
@PostConstruct
注解说明:用于标记一个Bean的方法依赖注入(如构造器注入、Setter注入、字段注入等)完成后,Bean正式投入使用前被调用,经典使用场景如初始化资源(如数据库连接)、加载配置、预缓存数据
添加@PostConstruct注解的方法需要满足以下几点
- 访问权限:非
private(通常为public) - 参数列表:无参数
- 返回值:
void - 静态性:非
static方法
- 访问权限:非
代码示例
javaimport javax.annotation.PostConstruct; @Service public class UserServiceImpl implements IUserService { @Autowired private IOrderService orderService; @PostConstruct public void init() { // @PostConstruct方法中,该类的所有依赖都已注入完成,可安全使用 orderService.getAllOrder(); System.out.println("数据初始化完成"); } }注意
- 通常一个Bean定义一个 @PostConstruct 方法,若存在多个 @PostConstruct 方法,执行顺序不确定(依赖方法声明顺序,但不同 JVM 可能表现不同),所以不建议使用多个
- 单例Bean@PostConstruct仅执行一次,原型Bean每次创建新实例都会执行一次
@PreDestroy
注解说明:用于标记一个方法在 Bean 被容器销毁之前自动执行,在 Spring 框架中,它常用于执行资源清理、关闭连接等收尾工作
添加@PostConstruct注解的方法需要满足以下几点
- 访问权限:非
private(通常为public) - 参数列表:无参数
- 返回值:
void - 静态性:非
static方法
- 访问权限:非
代码示例
java@Component public class DatabaseConnection { private Connection connection; @PostConstruct public void init() { // 初始化数据库连接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password"); } @PreDestroy public void cleanup() { // 容器销毁前关闭连接 if (connection != null) { try { connection.close(); System.out.println("数据库连接已关闭"); } catch (SQLException e) { e.printStackTrace(); } } } }注意
单例Bean(singleton),容器关闭时,@PreDestroy方法会被调用
原型Bean(prototype),默认不会触发@PreDestroy,因为Spring不管理原型Bean的生命周期,若需清理,需要手动调用销毁方法
javapublic class PrototypeBean { public void destroy() { // 手动清理逻辑 } } // 使用方手动调用 context.getBean(PrototypeBean.class).destroy();
六、Spring/SpringBoot注解
@Order
- 注解说明:@Order的作用是定义Spring IOC容器中Bean的
执行顺序优先级,而不是Bean的加载顺序 - 底层原理
- @Order 底层是在 Bean 注入 IOC 容器之后执行的,所以无法控制 Bean 的加载顺序
- @Order 底层是通过 List.sort(Comparator) 实现的,AnnotationAwareOrderComparator 类集成 OrderComparator 类,通过获取注解的 value 值实现了比对逻辑
- 代码示例
- 注意
- @Order注解只能用于Spring IOC容器中的Bean,普通的类不适用
@SpringBootApplication
@EnableAutoConfiguration
@ConfigurationProperties
注解说明:
使用场景之一:多层嵌套参数的加载方式
ymlspring: kafka: bootstrap-servers: xxx.xxx.xxx.xxx:9092 producer: retries: 0 batch-size: 16384 buffer-memory: 33554432 key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer consumer: group-id: views.invoke auto-offset-reset: earliest enable-auto-commit: true max-poll-records: 100 max-poll-interval-ms: 60000 auto-commit-interval: 100 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.apache.kafka.common.serialization.StringDeserializerjava@Data @Component @ConfigurationProperties(prefix = "spring.kafka") public class KafkaConfig { private String bootstrapServers; // prefix只截止到spring.kafka,所以自此往下,每新增一层,就用一个对象代替 // 比如spring.kafka.producer,就需要用一个属性名producer接收,属性的类型可以自定义,为了统一,我也写成Producer // 同理,如果producer下还有多层,就需要在Producer类中再定义属性接收 private Producer producer; private Consumer consumer; @Data @NoArgsConstructor @AllArgsConstructor public static class Producer { private String retries; private String batchSize; private String bufferMemory; private String keySerializer; private String valueSerializer; } @Data @NoArgsConstructor @AllArgsConstructor public static class Consumer { private String groupId; private String autoOffsetReset; private String enableAutoCommit; private String maxPollRecords; private String maxPollIntervalMs; private String autoCommitInterval; private String keyDeserializer; private String valueDeserializer; } }
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnProperty
@ConditionalOnResource
@ConditionalOnWebApplication
@ConditionalOnNotWebApplication
@ConditionalExpression
@Conditional
@Profile
注解说明:@Profile注解的作用是指定类或者方法在特定的profile环境生效,任何
@Component或者@Configuration注解的类都可以使用@Profile注解,环境是由spring.profiles.active控制代码示例
@Profile修饰类
java@Configuration @Profile("prod") public class JndiDataConfig { @Bean(destroyMethod="") public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } }@Profile修饰方法
java@Configuration public class AppConfig { @Bean("dataSource") @Profile("dev") public DataSource standaloneDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build(); } @Bean("dataSource") @Profile("prod") public DataSource jndiDataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } }
@Profile修饰注解:@Profile支持定义到其他注解之上,例如下面自定义的注解就可以使注入的bean使用dev的场景
java@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Profile("prod") public @interface Production { }java// 使用场景一: 标注配置类,只有profile激活为prod时,ProdDatabaseConfig才生效 @Production @Configuration public class ProdDatabaseConfig { // 生产环境的数据库配置(如连接池、集群地址) @Bean public DataSource dataSource() { return new HikariDataSource(...); } }java// 使用场景二: 标注组件类,只有profile激活为prod时,才会创建ProdEmailService的Bean @Production @Component public class ProdEmailService implements EmailService { // 生产环境的邮件发送实现(如使用企业 SMTP) public void sendEmail() { ... } }java// 使用场景三: 标注Bean方法,根据 Profile 动态选择缓存实现 @Configuration public class AppConfig { @Production @Bean public CacheManager prodCacheManager() { return new RedisCacheManager(...); // 生产环境用 Redis 缓存 } @Profile("dev") // 这里也可以定义一个@Development注解,仅做对比演示所以就不定义了 @Bean public CacheManager devCacheManager() { return new SimpleCacheManager(); // 开发环境用简单缓存 } }@Profile的匹配规则(支持逻辑运算符)
java// 非开发环境生效 @Profile("!dev")java// 开发环境和生产环境同时激活时生效 @Profile("dev & prod")java// 开发环境或生产环境任一激活时生效 @Profile("dev & prod")
@ConfigurationProperties
注解说明:该注解用于将外部配置属性绑定到Java对象上。通过使用这个注解,开发者可以将配置文件(如
application.properties或application.yml)中的属性值自动映射到Java类的字段上,从而实现配置的集中管理和类型安全代码示例
基本使用
properties# properties文件 esign.v3.appId=3213213 esign.v3.appSecret=4352hjvhgvn3w132123 esign.v3.hostAddress=https://openapi.esign.cnyml# yml文件 esign: v3: appId: 3213213 appSecret: 4352hjvhgvn3w132123 hostAddress: https://openapi.esign.cnjava@Data @Configuration @ConfigurationProperties(prefix = "esign.v3") public class EsignConfig { private String appId; private String appSecret; private String hostAddress; }嵌套属性(类)
properties# properties文件 esign.v3.appId=3213213 esign.v3.appSecret=4352hjvhgvn3w132123 esign.v3.hostAddress=https://openapi.esign.cn esign.v3.callback.host=http://111.222.333.444:8000 esign.v3.callback.signNotifyUrl=/api/esign/v3/callback/signNotifyyml# yml文件 esign: v3: appId: 3213213 appSecret: 4352hjvhgvn3w132123 hostAddress: https://openapi.esign.cn callback: host: http://111.222.333.444:8000 signNotifyUrl: /api/esign/v3/callback/signNotifyjava@Data @Configuration @ConfigurationProperties(prefix = "esign.v3") public class EsignConfig { private String appId; private String appSecret; private String hostAddress; private Callback callback; @Data public static class Callback { private String host; private String signNotifyUrl; } }嵌套属性(List)
properties# properties文件 org.userList[0]=zhangsan org.userList[1]=lisiyml# yml文件 org: userList: - zhangsan -lisijava@Data @Configuration @ConfigurationProperties(prefix = "org") public class OrgConfig { private List<String> userList; }嵌套属性(Map)
properties# properties文件 org.credentials.appId=3213213 org.credentials.appSecret=4352hjvhgvn3w132123yml# yml文件 org: credentials: appId: 3213213 appSecret: 4352hjvhgvn3w132123java@Data @Configuration @ConfigurationProperties(prefix = "org") public class OrgConfig { private Map<String, String> credentials; }
@Value
注解说明:该注解用于将外部配置属性绑定到Java对象的属性上
代码示例
字符串注入
propertiesapp.name=zhangsanjava@Data @Component public class AppConfig { @Value("${app.name}") private String name; }数字注入
propertiesapp.count=200java@Data @Component public class AppConfig { // 设置默认值100 @Value("${app.count:100}") private Integer count; }布尔值注入
propertiesapp.enabled=falsejava@Data @Component public class AppConfig { // 设置默认值true @Value("${app.enabled:true}") private Boolean enabled; }数组注入
propertiesapp.roles=ROLE_GUEST,ROLE_USERjava@Data @Component public class AppConfig { // 设置默认值 @Value("${app.roles:ROLE_USER,ROLE_ADMIN}") private String[] roles; }列表注入(使用SpEL表达式)
propertiesapp.roles=ROLE_GUEST,ROLE_USERjava@Data @Component public class AppConfig { @Value("#{'${app.roles}'.split(',')}") private List<String> roles; }Map注入
propertiesapp.credentials={key1:value1,key2:value2}ymlapp: credentials: key1: value1 key2: value2java@Data @Component public class AppConfig { @Value("#{${app.credentials}}") private Map<String, String> credentials; }
@PropertySource
注解说明:用于指定资源文件读取的位置,可以读取properties文件,也能xml文件,通过YAML解析器并且配合自定义PropertySourceFactory能够实现读取yml文件,只能作用到类上
属性说明
java//只能作用在类上 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(PropertySources.class) public @interface PropertySource { /** * 指定资源名称,如果为空,就根据基础资源的描述生成。 */ String name() default ""; /** * 指定资源路径。 * 可以是 classpath:/xxx/xxxx * 也可以是 file:/xxx/xxx/xx */ String[] value(); /** * 是否忽略资源不存在的情况,如果不忽略,当资源不存在时就报错。默认不忽略。 * 此属性是spring4.0以后出现的。 */ boolean ignoreResourceNotFound() default false; /** * 指定资源文件的编码格式。如果不指定就使用文件默认的。 * 此属性是spring4.3以后出现的。 */ String encoding() default ""; /** * 指定资源工厂,如果不指定,就使用默认的资源工厂。 */ Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; }注意:该注解在spring4.3之前与spring4.3及之后使用的方式不一样
注解使用
spring4.3之前读取properties文件
java@Configuration @ComponentScan(basePackages = "propertysourcedemo") @PropertySource(value = "classpath:daoconfig/datasource-config.properties") public class SpringConfig { // 通过SPEL表达式注入文件中的属性 @Value("${druid.driverClassName}") private String driverClassName; @Value("${druid.url}") private String url; @Value("${druid.username}") private String username; @Value("${druid.password}") private String password; // 创建资源文件解析器,spring4.3之前必须要的,不要就无法解析。 @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){ return new PropertySourcesPlaceholderConfigurer(); } //注册Druid数据源连接池 @Bean public DruidDataSource druidDataSource(){ System.out.println("driverClassName====> " + driverClassName); System.out.println("url====> " + url); System.out.println("username====> " + username); System.out.println("username====> " + username); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driverClassName); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }spring4.3之后读取properties文件
java@Configuration @ComponentScan(basePackages = "propertysourcedemo") // 这次使用file协议的url路径来解析 @PropertySource(value = "file:///D:/spring-high-level-study/src/main/resources/daoconfig/datasource-config.properties") public class SpringConfig { // 通过SPEL表达式注入文件中的属性 @Value("${druid.driverClassName}") private String driverClassName; @Value("${druid.url}") private String url; @Value("${druid.username}") private String username; @Value("${druid.password}") private String password; // 注册Druid数据源连接池 @Bean public DruidDataSource druidDataSource(){ System.out.println("driverClassName====> " + driverClassName); System.out.println("url====> " + url); System.out.println("username====> " + username); System.out.println("username====> " + username); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driverClassName); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }解析yml文件的时候,@PropertySource默认实现的PropertySourceFactory是解析不了的,如果要解析,就要自定义实现
引入第三方jar包
xml<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.23</version> </dependency>自定义yml解析工厂
javapublic class YAMLPropertySourceFactory implements PropertySourceFactory { @Override public org.springframework.core.env.PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException { //创建一个YAML解析工厂。 YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); //设置资源。 factory.setResources(encodedResource.getResource()); //获取解析后的Properties对象 Properties properties = factory.getObject(); //返回。此时不能像默认工厂那样返回ResourcePropertySource对象 ,要返回他的父类PropertiesPropertySource对象。 return name != null ? new PropertiesPropertySource(name, properties) : new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties); } }
spring4.3之前读取yml文件
java@Configuration @ComponentScan(basePackages = "propertysourcedemo") // 指定PropertySourceFactory读取yml文件 @PropertySource(value = "classpath:daoconfig/datasource-config.yml", factory = YAMLPropertySourceFactory.class) public class SpringConfig { // 通过SPEL表达式注入文件中的属性 @Value("${druid.driverClassName}") private String driverClassName; @Value("${druid.url}") private String url; @Value("${druid.username}") private String username; @Value("${druid.password}") private String password; // 创建资源文件解析器,spring4.3之前必须要的,不要就无法解析。 @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){ return new PropertySourcesPlaceholderConfigurer(); } //注册Druid数据源连接池 @Bean public DruidDataSource druidDataSource(){ System.out.println("driverClassName====> " + driverClassName); System.out.println("url====> " + url); System.out.println("username====> " + username); System.out.println("username====> " + username); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driverClassName); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }spring4.3之后读取yml文件和spring4.3之前读取yml文件的区别就是去掉了资源文件解析器,这里就不赘述代码了
参考文档
- @RequestMapping属性详解 - SpringMVC高手进阶-腾讯云开发者社区-腾讯云
- Spring 中的 @RequestParam 注解 - spring 中文网
- @RequestParam注解的详细介绍 - tomingto - 博客园
- SpringBoot 增强Controller方法,@ControllerAdvice注解的使用-CSDN博客
- @ControllerAdvice 的介绍及三种用法-CSDN博客
- 浅析@ResponseStatus的作用、属性、用法、底层原理及使用注意事项 - 古兰精 - 博客园
- spring学习之@ModelAttribute运用详解 - 靳哲 - 博客园
- java - 探秘 Spring 的 PropertyEditor - 个人文章 - SegmentFault 思否
- Spring 核心特性之类型转换(PropertyEditor、ConversionService)-阿里云开发者社区
- JDK的PropertyEditor简单使用_propertyeditor使用-CSDN博客
- Spring注解扫描:ComponentScan使用及原理详解 - 知乎
- Spring的@ComponentScan注解:深入解析与实战指南_spring componentscan-CSDN博客
- @Repository注解的作用和用法,以及和@Mapper的区别-CSDN博客
- Spring高级之注解@DependsOn详解(超详细)-CSDN博客
- 使用Spring @DependsOn控制bean加载顺序,依赖多个bean_dependson 多个-CSDN博客
- 大白话讲解Spring的@bean注解 - 知乎
- @Bean注解的使用和详解_bean注解的作用-CSDN博客
- Springboot中的@Profile注解_profile注解的作用-CSDN博客
- SpringBoot@Profile注解和Spring EL(多环境注入)_java_脚本之家
- @Scope注解 详细讲解及示例-CSDN博客
- Spring Bean Scope 指南 - spring 中文网
- 深入解析Spring Boot中的@ConfigurationProperties注解-CSDN博客
- Spring Boot 中的 @ConfigurationProperties 注解 - spring 中文网
- Spring注解
@Value实战之各种数据类型注入(Array、List、Map等)_value注解注入list-CSDN博客 - Spring 中的 @Value 注解 - spring 中文网
- Spring高级之注解@PropertySource详解(超详细)_propetysource-CSDN博客
- Spring(18) @Order注解介绍、使用、底层原理_spring order-CSDN博客