• [技术干货] SpringBoot常用注解
    1 @SpringBootApplication @SpringBootApplication是 Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上;可以把 @SpringBootApplication看作是@EnableAutoConfiguration、@ComponentScan、@Configuration注解的集合; @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制; @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类; @Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类; 2 Spring Bean相关 2.1 @Autowired 自动导入对象到类中,被注入进的类同样要被 Spring 容器管理。比如:Service 类注入到 Controller 类中。  2.2 @Component/@Controller/@Service/@Repository 一般使用 @Autowired 让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 @Autowired 注解自动装配的 bean 的类,可以采用以下注解实现:  @Component :通用注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component注解标注; @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面; @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层; @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作; 2.3 @RestController @RestController是@Controller和@ResponseBody的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器; 单独使用@Controller不加@ResponseBody一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC的应用,对应于前后端不分离的情况; @Controller+@ResponseBody 返回JSON或XML形式数据; 2.4 @Scope 声明 Spring Bean 的作用域。  @Bean @Scope("singleton") public Person personSingleton() {     return new Person(); } 四种常见的 Spring Bean 的作用域: singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的; prototype : 每次请求都会创建一个新的 bean 实例; request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效; session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。 2.5 @Configuration 一般用来声明配置类,可以使用@Component注解替代,不过使用@Configuration注解声明配置类更加语义化。  @Configuration public class AppConfig {     @Bean     public TransferService transferService() {         return new TransferServiceImpl();     } } 3 处理常见的 HTTP 请求类型 GET :请求从服务器获取特定资源。举个例子:GET /users(获取所有学生) POST :在服务器上创建一个新的资源。举个例子:POST /users(创建学生) PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /users/12(更新编号为 12 的学生) DELETE :从服务器删除特定的资源。举个例子:DELETE /users/12(删除编号为 12 的学生) 3.1 @GetMapping @GetMapping(“users”) 等价于@RequestMapping(value=“/users”,method=RequestMethod.GET) @GetMapping("/users") public ResponseEntity<List<User>> getAllUsers() {  return userRepository.findAll(); } 3.2 @PostMapping @PostMapping(“users”) 等价于@RequestMapping(value=“/users”,method=RequestMethod.POST) @PostMapping("/users") public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {  return userRespository.save(user); } 3.3 @PutMapping @PutMapping(“/users/{userId}”) 等价于@RequestMapping(value=“/users/{userId}”,method=RequestMethod.PUT)  @PutMapping("/users/{userId}") public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId,   @Valid @RequestBody UserUpdateRequest userUpdateRequest) {   ...... } 3.3 @DeleteMapping @DeleteMapping(“/users/{userId}”)等价于@RequestMapping(value=“/users/{userId}”,method=RequestMethod.DELETE)  @DeleteMapping("/users/{userId}") public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){   ...... } 4 前后端传值 4.1 @PathVariable和@RequestParam @PathVariable用于获取路径参数,@RequestParam用于获取查询参数。 例:  @GetMapping("/klasses/{klassId}/teachers") public List<Teacher> getKlassRelatedTeachers(          @PathVariable("klassId") Long klassId,          @RequestParam(value = "type", required = false) String type ) { ... } 如果我们请求的 url 是:/klasses/{123456}/teachers?type=web,那么我们服务获取到的数据就是:klassId=123456,type=web。 4.2 @RequestBody 用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter或者自定义的HttpMessageConverter将请求的 body 中的 json 字符串转换为 java 对象。 例: @PostMapping("/sign-up") public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) {   userService.save(userRegisterRequest);   return ResponseEntity.ok().build(); } UserRegisterRequest对象:  @Data @AllArgsConstructor @NoArgsConstructor public class UserRegisterRequest {     @NotBlank     private String userName;     @NotBlank     private String password;     @NotBlank     private String fullName; } 我们发送 post 请求到这个接口,并且 body 携带 JSON 数据: {"userName":"coder","fullName":"shuangkou","password":"123456"} 这样后端就可以直接把 json 格式的数据映射到UserRegisterRequest类上。 一个请求方法只可以有一个@RequestBody,但是可以有多个@RequestParam和@PathVariable。 5 读取配置信息 很多时候我们需要将一些常用的配置信息比如阿里云 oss、发送短信、微信认证的相关配置信息等等放到配置文件中。 Spring 为我们提供了以下几种方式帮助我们从配置文件中读取这些配置信息。 数据源application.yml: wuhan:     2020: 2020年初武汉爆发了新型冠状病毒,疫情严重,但是,我相信一切都会过去!武汉加油!中国加油! my-profile:   name: Guide哥   email: koushuangbwcx@163.com library:   location: 湖北武汉加油中国加油   books:     - name: 天才基本法       description: 二十二岁的林朝夕在父亲确诊阿尔茨海默病这天,得知自己暗恋多年的校园男神裴之即将出国深造的消息——对方考取的学校,恰是父亲当年为她放弃的那所。     - name: 时间的秩序       description: 为什么我们记得过去,而非未来?时间“流逝”意味着什么?是我们存在于时间之内,还是时间存在于我们之中?卡洛·罗韦利用诗意的文字,邀请我们思考这一亘古难题——时间的本质。     - name: 了不起的我       description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻? 5.1 @value(常用) 使用 @Value(“${property}”) 读取比较简单的配置信息: @Value("${wuhan.2020}") String wuhan2020; 5.2 @ConfigurationProperties(常用) 通过@ConfigurationProperties读取配置信息并与 bean 绑定。 @Data @Component @ConfigurationProperties(prefix = "library") public class LibraryProperties {     @NotEmpty     private String location;     private List<Book> books;     @Setter     @Getter     @ToString     public static class Book {         String name;         String description;     } } 可以像使用普通的 Spring bean 一样,将其注入到类中使用。 5.3 @PropertySource(不常用) @PropertySource读取指定 properties 文件 @Data @Component @PropertySource("classpath:website.properties") class WebSite {     @Value("${url}")     private String url; } 6 参数校验 数据的校验非常重要,即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据; JSR(Java Specification Requests) 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面,这样就可以在需要校验的时候进行校验了; 校验的时候我们实际用的是 Hibernate Validator 框架。Hibernate Validator 是 Hibernate 团队最初的数据校验框架,Hibernate Validator 4.x 是 Bean Validation 1.0(JSR 303)的参考实现,Hibernate Validator 5.x 是 Bean Validation 1.1(JSR 349)的参考实现,目前最新版的 Hibernate Validator 6.x 是 Bean Validation 2.0(JSR 380)的参考实现; SpringBoot 项目的 spring-boot-starter-web 依赖中已经有 hibernate-validator 包,不需要引用相关依赖。如下图所示(通过 idea 插件—Maven Helper 生成): 非 SpringBoot 项目需要自行引入相关依赖包; 所有的注解,推荐使用 JSR 注解,即javax.validation.constraints,而不是org.hibernate.validator.constraints; 6.1 一些常用的字段验证的注解 @NotEmpty(message=):被注释的字符串的不能为 null 也不能为空,即长度必须大于0; @NotBlank(message=):被注释的字符串非 null,并且必须包含一个非空白字符,即调用trim()后,长度必须大于0; @Null(message=):被注释的元素必须为 null; @NotNull(message=):被注释的元素必须不为 null; @AssertTrue:被注释的元素必须为 true; @AssertFalse:被注释的元素必须为 false; @Pattern(regexp=,message=):被注释的元素必须符合指定的正则表达式; @Email:被注释的元素必须是 Email 格式; @Min(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值; @Max(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值; @DecimalMin(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值; @DecimalMax(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值; @Size(max=, min=):被注释的字符串、Collection、Map、数组等的大小必须在指定的范围内; @Range(min=最小值, max=最大值):被注释的BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型在最小值和最大值之间; @Digits (integer=整数位数, fraction=小数位数):被注释的元素必须是一个数字,其值必须在可接受的范围内; @Past:被注释的元素必须是一个过去的日期; @Future:被注释的元素必须是一个将来的日期; 6.2 验证请求体(RequestBody) @Data @AllArgsConstructor @NoArgsConstructor public class Person {     @NotNull(message = "classId不能为空")     private String classId;     @Size(max=33)     @NotNull(message = "name不能为空")     private String name;     @Pattern(regexp ="((^Man$|^Woman$|^UGM$))", message = "sex值不在可选范围")     @NotNull(message = "sex不能为空")     private String sex;     @Email(message = "email格式不正确")     @NotNull(message = "email不能为空")     private String email;  } 在需要验证的参数上加上@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException。 @RestController @RequestMapping("/api") public class PersonController {      @PostMapping("/person")     public ResponseEntity<Person> getPerson(@RequestBody @Valid Person person) {         return ResponseEntity.ok().body(person);     } } 6.3 验证请求参数(Path Variables 和 Request Parameters) 需要在类上加上 Validated 注解,它可以告诉 Spring 去校验方法参数。 @RestController @RequestMapping("/api") @Validated public class PersonController {      @GetMapping("/person/{id}")     public ResponseEntity<Integer> getPersonByID(@Valid @PathVariable("id") @Max(value = 5,message = "超过 id 的范围了") Integer id) {         return ResponseEntity.ok().body(id);     } } 7 全局处理 Controller 层异常 @ControllerAdvice :注解定义全局异常处理类 @ExceptionHandler :注解声明异常处理方法 拿参数校验来举例。如果方法参数不对的话就会抛出MethodArgumentNotValidException。 @ControllerAdvice @ResponseBody public class GlobalExceptionHandler {      /**      * 请求参数异常处理      */     @ExceptionHandler(MethodArgumentNotValidException.class)     public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {        ......     } } 8 json 数据处理 8.1 过滤 json 数据 @JsonIgnoreProperties 作用在类上用于过滤掉特定字段不返回或者不解析; //生成json时将userRoles属性过滤 @JsonIgnoreProperties({"userRoles"}) public class User {      private String userName;     private String fullName;     private String password;     private List<UserRole> userRoles = new ArrayList<>(); } @JsonIgnore一般用于类的属性上,作用和上面的@JsonIgnoreProperties 一样; public class User {     private String userName;     private String fullName;     private String password;    //生成json时将userRoles属性过滤     @JsonIgnore     private List<UserRole> userRoles = new ArrayList<>(); } 8.2 格式化 json 数据 @JsonFormat一般用来格式化 json 数据。 例 @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT") private Date date; 8.3 扁平化对象 @Data public class Account {     private Location location;     private PersonInfo personInfo;    @Data   public static class Location {      private String provinceName;      private String countyName;   }   @Data   public static class PersonInfo {     private String userName;     private String fullName;   } } 未扁平化之前:  {     "location": {         "provinceName":"湖北",         "countyName":"武汉"     },     "personInfo": {         "userName": "coder1234",         "fullName": "shaungkou"     } } 使用@JsonUnwrapped 扁平对象之后: @Data public class Account {     @JsonUnwrapped     private Location location;     @JsonUnwrapped     private PersonInfo personInfo;     ...... } {   "provinceName":"湖北",   "countyName":"武汉",   "userName": "coder1234",   "fullName": "shaungkou" } ———————————————— 原文链接:https://blog.csdn.net/sc179/article/details/128376337 
  • [技术干货] SpringBoot 常用注解汇总
    Spring Boot 常用注解汇总 一、启动注解 @SpringBootApplication 查看源码可发现,@SpringBootApplication是一个复合注解,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解 @SpringBootConfiguration 注解,继承@Configuration注解,主要用于加载配置文件 @SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类, 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。 @EnableAutoConfiguration 注解,开启自动配置功能 @EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成 @ComponentScan 注解,主要用于组件扫描和自动装配 @ComponentScan的功能其实就是自动扫描并加载符合条件的组件或bean定义,最终将这些bean定义加载到容器中。我们可以通过basePackages等属性指定@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现从声明@ComponentScan所在类的package进行扫描,默认情况下是不指定的,所以SpringBoot的启动类最好放在root package下。 二、Controller 相关注解 @Controller 控制器,处理http请求。 @RestController 复合注解 查看@RestController源码 从源码我们知道,@RestController注解相当于@ResponseBody+@Controller合在一起的作用,RestController使用的效果是将方法返回的对象直接在浏览器上展示成json格式. @RequestBody 通过HttpMessageConverter读取Request Body并反序列化为Object(泛指)对象 @RequestMapping @RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一。这个注解会将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上 @GetMapping用于将HTTP get请求映射到特定处理程序的方法注解 GetMapping源码 是@RequestMapping(method = RequestMethod.GET)的缩写 @PostMapping用于将HTTP post请求映射到特定处理程序的方法注解 是@RequestMapping(method = RequestMethod.POST)的缩写 三、取请求参数值 @PathVariable:获取url中的数据 请求示例:http://localhost:8080/User/getUser/123 @RequestParam:获取请求参数的值 请求示例:http://localhost:8080/User/getUser?uid=123 @RequestHeader 把Request请求header部分的值绑定到方法的参数上 @CookieValue 把Request header中关于cookie的值绑定到方法的参数上 四、注入bean相关 @Repository DAO层注解,DAO层中接口继承JpaRepository<T,ID extends Serializable>,需要在build.gradle中引入相关jpa的一个jar自动加载。 Repository注解源码 @Service是@Component注解的一个特例,作用在类上 @Service注解作用域默认为单例 使用注解配置和类路径扫描时,被@Service注解标注的类会被Spring扫描并注册为Bean @Service用于标注服务层组件,表示定义一个bean @Service使用时没有传参数,Bean名称默认为当前类的类名,首字母小写 @Service(“serviceBeanId”)或@Service(value=”serviceBeanId”)使用时传参数,使用value作为Bean名字 @Scope作用域注解 @Scope作用在类上和方法上,用来配置 spring bean 的作用域,它标识 bean 的作用域 @Scope源码 属性介绍 @Entity实体类注解 @Table(name =“数据库表名”),这个注解也注释在实体类上,对应数据库中相应的表。 @Id、@Column注解用于标注实体类中的字段,pk字段标注为@Id,其余@Column。 @Bean产生一个bean的方法 @Bean明确地指示了一种方法,产生一个bean的方法,并且交给Spring容器管理。支持别名@Bean(“xx-name”) @Autowired 自动导入 @Autowired注解作用在构造函数、方法、方法参数、类字段以及注解上 @Autowired注解可以实现Bean的自动注入 @Component 把普通pojo实例化到spring容器中,相当于配置文件中的 虽然有了@Autowired,但是我们还是要写一堆bean的配置文件,相当麻烦,而@Component就是告诉spring,我是pojo类,把我注册到容器中吧,spring会自动提取相关信息。那么我们就不用写麻烦的xml配置文件了 五、导入配置文件 @Import 导入额外的配置信息 功能类似XML配置的,用来导入配置类,可以导入带有@Configuration注解的配置类或实现了ImportSelector/ImportBeanDefinitionRegistrar。 使用示例 六、事务注解 @Transactional 在Spring中,事务有两种实现方式,分别是编程式事务管理和声明式事务管理两种方式 编程式事务管理: 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。 声明式事务管理: 建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务,通过@Transactional就可以进行事务操作,更快捷而且简单。推荐使用 七、全局异常处理 @ControllerAdvice 统一处理异常 @ControllerAdvice 注解定义全局异常处理类 @ExceptionHandler 注解声明异常处理方法 ———————————————— 原文链接:https://blog.csdn.net/m0_67401153/article/details/125243438 
  • [技术干货] SpringBoot最常用的50个注解
    SpringBoot最常用的50个注解 ​ SpringBoot提供了很多注解,可以帮助我们快速构建应用程序。以下是SpringBoot最常用的50个注解: (1)@SpringBootApplication 作用:这是一个组合注解,包括了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解。用于标识SpringBoot应用程序的入口类。 @Configuration:指示这个类是一个配置类,它定义了一个或多个@Bean方法,用于创建和配置Spring应用程序上下文中的Bean。 @EnableAutoConfiguration:启用Spring Boot的自动配置机制,它会自动添加所需的依赖项和配置,以使应用程序能够运行。 @ComponentScan:指示Spring Boot扫描当前包及其子包中的所有@Component、@Service、@Repository和@Controller注解的类,并将它们注册为Spring Bean。 @SpringBootApplication注解通常被用于Spring Boot应用程序的入口类上,用于启动Spring Boot应用程序。它可以简化Spring应用程序的配置和启动过程。 用例: @SpringBootApplication public class MyApplication {      public static void main(String[] args) {         SpringApplication.run(MyApplication.class, args);     } } (2)@RestController 作用:与@Controller类似,但是@RestController会自动将返回值转换为JSON格式。 @RestController是Spring Framework 4.0版本引入的一个注解,它是@Controller和@ResponseBody的组合注解。它用于标注一个类,表示这个类是一个RESTful风格的控制器,可以处理HTTP请求并返回JSON/XML格式的响应。 @RestController注解用于替代原来的@Controller注解,它默认情况下会将控制器方法的返回值转换为JSON格式,并以HTTP响应的方式返回给客户端。如果需要返回XML格式的响应,可以使用其他注解,如@Produces和@Consumes。 用例: @RestController public class HelloController {      @GetMapping("/hello")     public String hello() {         return "Hello, World!";     } } (3)@RequestMapping 作用:用于映射请求URL和处理方法。@RequestMapping是Spring MVC框架中的一个核心注解,它用于映射HTTP请求和控制器方法之间的关系。它可以用于类级别和方法级别,用于指定请求URL和HTTP方法(GET、POST、PUT、DELETE等)。 用例: @RestController @RequestMapping("/api") public class UserController {      @GetMapping("/users")     public List<User> getUsers() {         // 获取用户列表     }      @PostMapping("/users")     public void createUser(@RequestBody User user) {         // 创建新用户     }      @GetMapping("/users/{id}")     public User getUserById(@PathVariable Long id) {         // 根据ID获取用户信息     }      @PutMapping("/users/{id}")     public void updateUser(@PathVariable Long id, @RequestBody User user) {         // 更新用户信息     }      @DeleteMapping("/users/{id}")     public void deleteUser(@PathVariable Long id) {         // 根据ID删除用户     } } (4)@GetMapping 作用:用于映射HTTP GET请求。 用例: @RestController @RequestMapping("/api") public class UserController {      @GetMapping("/users")     public List<User> getUsers() {         // 获取用户列表     }      @GetMapping("/users/{id}")     public User getUserById(@PathVariable Long id) {         // 根据ID获取用户信息     } } (5)@PostMapping 作用:用于映射HTTP POST请求。 用例: @RestController @RequestMapping("/api") public class UserController {      @PostMapping("/users")     public void createUser(@RequestBody User user) {         // 创建新用户     } } (6)@PutMapping 作用:用于映射HTTP PUT请求。 用例: @RestController @RequestMapping("/api") public class UserController {      @PutMapping("/users/{id}")     public void updateUser(@PathVariable Long id, @RequestBody User user) {         // 更新用户信息     } } (7)@DeleteMapping 作用:用于映射HTTP DELETE请求。 用例: @RestController @RequestMapping("/api") public class UserController {      @DeleteMapping("/users/{id}")     public void deleteUser(@PathVariable Long id) {         // 根据ID删除用户     } } (8)@RequestParam 作用:用于获取请求参数的值。 用例: @RestController @RequestMapping("/api") public class UserController {      @GetMapping("/users")     public List<User> getUsers(@RequestParam("page") int page, @RequestParam("size") int size) {         // 分页获取用户列表     } } (9)@PathVariable 作用:用于获取URL中的参数值。@PathVariable是Spring MVC框架中的一个注解,用于将HTTP请求路径中的变量绑定到控制器方法的参数上。 用例:  @RestController @RequestMapping("/api") public class UserController {      @GetMapping("/users/{id}")     public User getUser(@PathVariable Long id) {         // 根据ID获取用户信息     } } (10)@RequestBody 作用:用于将HTTP请求的主体转换为方法的参数。@RequestBody是Spring MVC框架中的一个注解,用于将HTTP请求体中的数据绑定到控制器方法的参数上。 用例: @RestController @RequestMapping("/api") public class UserController {      @PostMapping("/users")     public User createUser(@RequestBody User user) {         // 创建用户     } } (11)@ResponseBody 作用:用于将方法的返回值转换为HTTP响应的主体。@ResponseBody是Spring MVC框架中的一个注解,用于将控制器方法的返回值转换为HTTP响应体中的数据。 用例: @RestController public class UserController {      @GetMapping("/users/{id}")     @ResponseBody     public User getUser(@PathVariable int id) {         // 从数据库或其他地方获取用户数据         User user = userService.getUserById(id);         return user;     } } (12)@Autowired 作用:用于自动装配Spring容器中的Bean。 用例: @Service public class UserServiceImpl implements UserService {      @Autowired     private UserRepository userRepository;      // 实现UserService接口中的方法 } (13)@Component 作用:用于标识一个类是Spring容器中的组件。@Component是Spring框架中的一个通用注解,用于标注一个类作为Spring Bean。 用例: @Component public class UserServiceImpl implements UserService {      // 实现UserService接口中的方法 } (14)@Service 作用:用于标识一个类是Spring容器中的服务组件。@Service是Spring框架中的一个注解,用于标注一个类作为服务类(Service)。 用例: @Service public class UserServiceImpl implements UserService {      // 实现UserService接口中的方法 } (15)@Repository 作用:用于标识一个类是Spring容器中的数据访问组件。@Repository是Spring框架中的一个注解,用于标注一个类作为数据访问对象(DAO)。 用例: @Repository public class UserRepositoryImpl implements UserRepository {      // 实现UserRepository接口中的方法 } (16)@Configuration 作用:用于标识一个类是Spring的配置类。@Configuration是Spring框架中的一个注解,用于标注一个类作为配置类。 用例: @Configuration public class AppConfig {      @Bean     public UserService userService() {         return new UserServiceImpl();     }      @Bean     public UserRepository userRepository() {         return new UserRepositoryImpl();     } } (17)@Value 作用:用于获取配置文件中的属性值。@Value是Spring框架中的一个注解,用于将配置文件中的属性值注入到Bean对象中。 用例: @Component public class MyComponent {      @Value("${my.property}")     private String myProperty;      // 其他方法 } 这个类使用@Component注解标注,表示这个类是一个Spring Bean,可以被其他的Spring Bean自动装配。 在属性级别上,@Value注解指定了需要注入的属性值,这个属性值可以通过${...}的方式引用配置文件中的属性值。 在这个例子中,MyComponent类中的myProperty属性使用@Value注解指定了需要注入的属性值,Spring会自动将配置文件中名为my.property的属性值注入到这个属性中。 @Value注解用于注入配置文件中的属性值,使得开发者可以方便地从配置文件中获取属性值,并将其注入到Bean对象中。同时,使用@Value注解还可以方便地处理不同环境下的配置文件,如开发环境和生产环境的配置文件。 @Value注解是Spring框架中比较常用的注解之一,可以让开发者更加专注于业务逻辑的实现,而不必关心属性值的获取和注入细节。 (18)@Bean 作用:用于将一个方法返回的对象注册到Spring容器中。@Bean是Spring框架中的一个注解,用于将一个方法返回的对象注册为一个Spring Bean。 用例: @Configuration public class AppConfig {      @Bean     public UserService userService() {         return new UserServiceImpl();     }      @Bean     public UserRepository userRepository() {         return new UserRepositoryImpl();     } } (19)@Import 作用:用于导入其他配置类或Bean。 用例: @Configuration @Import({AppConfig1.class, AppConfig2.class}) public class AppConfig {      // 其他方法 } (20)@Conditional 作用:用于根据条件判断是否创建Bean或执行配置。 用例: @Configuration public class AppConfig {      @Bean     @Conditional(DatabaseTypeCondition.class)     public UserRepository userRepository() {         return new UserRepositoryImpl();     }      // 其他方法 } (21)@Profile 作用:用于指定配置的环境,如开发环境、测试环境或生产环境。 用例:  @Configuration public class AppConfig {      @Bean     @Profile("dev")     public UserService userServiceDev() {         return new UserServiceDevImpl();     }      @Bean     @Profile("prod")     public UserService userServiceProd() {         return new UserServiceProdImpl();     }      // 其他方法 } (22)@PropertySource 作用:用于指定配置文件的位置。@PropertySource是Spring框架中的一个注解,用于指定一组属性文件的位置,从而可以在Spring应用程序中使用这些属性。 用例:  @Configuration @PropertySource("classpath:application.properties") public class AppConfig {      @Autowired     private Environment environment;      @Bean     public UserService userService() {         return new UserServiceImpl(environment.getProperty("userService.name"));     }      // 其他方法 } 这个类使用@Configuration注解标注,表示这个类是一个配置类,用于配置应用程序的Bean对象。 在类级别上,使用@PropertySource注解可以指定一个属性文件的位置。在这个例子中,使用@PropertySource注解指定了一个名为application.properties的属性文件,它位于classpath下。 在方法级别上,使用@Bean注解标注方法,表示这个方法返回一个Bean对象。在这个例子中,使用Environment对象从属性文件中读取属性值,并将这些属性值传递给UserService实例的构造方法。 @PropertySource注解用于指定一组属性文件的位置,使得开发者可以在Spring应用程序中使用这些属性。同时,使用Environment对象可以方便地读取属性文件中的属性值,并将这些属性值传递给Bean对象的构造方法或属性。 @PropertySource注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地管理和配置Spring Bean。 (23)@Qualifier 作用:用于指定注入的Bean的名称。 用例:  @Component public class UserServiceImpl implements UserService {      @Autowired     @Qualifier("userRepositoryImpl")     private UserRepository userRepository;      // 其他方法 } (24)@ExceptionHandler 作用:用于处理异常。 用例: @ControllerAdvice public class GlobalExceptionHandler {      @ExceptionHandler(Exception.class)     public ModelAndView handleException(Exception ex) {         ModelAndView modelAndView = new ModelAndView();         modelAndView.addObject("errorMessage", ex.getMessage());         modelAndView.setViewName("error");         return modelAndView;     } }  这个类使用@ControllerAdvice注解标注,表示这个类是一个全局异常处理器。在方法级别上,使用@ExceptionHandler注解可以指定一个方法来处理控制器中抛出的异常。 在这个例子中,使用@ExceptionHandler注解指定了一个名为handleException的方法,它处理所有类型的异常。当控制器中抛出异常时,会调用这个方法,并将异常对象作为参数传递给这个方法。 在这个方法中,使用ModelAndView对象来封装错误信息,并将视图名称设置为error。最后,返回这个ModelAndView对象,将错误信息显示到用户界面上。 @ExceptionHandler注解用于处理控制器中抛出的异常,使得开发者可以根据需要灵活地处理异常。同时,使用@ControllerAdvice注解可以将这个异常处理器应用于所有的控制器中。 @ExceptionHandler注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地处理控制器中的异常。 (25)@ResponseStatus 作用:用于指定异常的HTTP响应状态码。 用例: @Controller public class UserController {      @GetMapping("/user/{id}")     @ResponseBody     @ResponseStatus(HttpStatus.OK)     public UserDetails getUserDetails(@PathVariable("id") Long id) {         // 查询用户信息         UserDetails userDetails = userService.getUserDetails(id);         if (userDetails == null) {             throw new UserNotFoundException("User not found");         }         return userDetails;     }      @ExceptionHandler(UserNotFoundException.class)     @ResponseStatus(HttpStatus.NOT_FOUND)     @ResponseBody     public String handleUserNotFoundException(UserNotFoundException ex) {         return ex.getMessage();     } } (26)@ControllerAdvice 作用:用于全局处理异常。 @ControllerAdvice是Spring框架中的一个注解,用于定义全局控制器通知。 在Spring MVC框架中,控制器通知是一些特殊的组件,它们可以在控制器方法执行前、执行后或抛出异常时执行一些额外的逻辑处理。使用@ControllerAdvice注解可以定义全局控制器通知,它可以应用于所有的控制器。 用例:  @ControllerAdvice public class GlobalControllerAdvice {      @ModelAttribute("currentUser")     public User getCurrentUser() {         // 获取当前登录用户信息         User currentUser = userService.getCurrentUser();         return currentUser;     }      @InitBinder     public void initBinder(WebDataBinder binder) {         // 注册自定义的属性编辑器         binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));     }      @ExceptionHandler(Exception.class)     public ModelAndView handleException(Exception ex) {         ModelAndView modelAndView = new ModelAndView();         modelAndView.addObject("errorMessage", ex.getMessage());         modelAndView.setViewName("error");         return modelAndView;     } }  这个类使用@ControllerAdvice注解标注,表示这个类是一个全局控制器通知。在方法级别上,使用@ModelAttribute注解标注方法,表示这个方法会在所有控制器方法执行前执行,用于将当前登录用户信息添加到模型中。 使用@InitBinder注解标注方法,表示这个方法会在所有控制器方法执行前执行,用于注册自定义的属性编辑器。 使用@ExceptionHandler注解标注方法,表示这个方法会在控制器中抛出异常时执行,用于处理控制器方法中抛出的异常。 @ControllerAdvice注解用于定义全局控制器通知,使得开发者可以在所有控制器方法执行前、执行后或抛出异常时执行一些额外的逻辑处理。同时,使用@ModelAttribute注解可以将一些公共的模型数据添加到模型中,使用@InitBinder注解可以注册自定义的属性编辑器,使用@ExceptionHandler注解可以处理控制器方法中抛出的异常。 @ControllerAdvice注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地定义全局控制器通知。 (27)@CrossOrigin 作用:用于解决跨域问题。 @CrossOrigin是Spring框架中的一个注解,用于解决跨域资源共享(CORS)问题。 跨域资源共享是浏览器安全策略的一部分,它限制了浏览器在不同域名之间发送和接收HTTP请求。使用@CrossOrigin注解可以指定允许跨域访问的域名和HTTP方法。 用例:  @RestController @RequestMapping("/api") @CrossOrigin(origins = "http://localhost:8080", methods = {RequestMethod.GET, RequestMethod.POST}) public class ApiController {      @GetMapping("/users")     public List<User> getUsers() {         // 查询用户信息         List<User> users = userService.getUsers();         return users;     } }  这个类使用@RestController注解标注,表示这个类是一个RESTful风格的控制器。在类级别上,使用@RequestMapping注解指定控制器处理的请求路径为/api。同时,使用@CrossOrigin注解可以指定允许跨域访问的域名和HTTP方法。 在这个例子中,使用@CrossOrigin注解指定允许来自http://localhost:8080域名的GET和POST请求访问该控制器中的方法。这意味着,在http://localhost:8080域名下的网页可以通过XMLHttpRequest对象发送GET和POST请求,访问该控制器中的方法。 @CrossOrigin注解用于解决跨域资源共享(CORS)问题,使得开发者可以更加灵活地控制允许跨域访问的域名和HTTP方法。它是一种简单但非常有效的解决方案,可以使得前端开发者更加轻松地开发跨域应用程序。 @CrossOrigin注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地解决跨域资源共享(CORS)问题。 (28)@Async 作用:用于将方法标记为异步执行。 在Spring框架中,如果一个方法需要执行一些比较耗时的操作,如果这个方法是在主线程中执行,就会导致主线程被阻塞,用户界面无法响应用户的操作。使用@Async注解可以将这个方法的执行异步化,让主线程继续执行其他任务,提高应用程序的响应性能。 用例:  @Service public class UserService {      @Async     public CompletableFuture<UserDetails> getUserDetailsAsync(Long id) {         // 查询用户信息         UserDetails userDetails = userRepository.getUserDetails(id);         return CompletableFuture.completedFuture(userDetails);     } } 这个类使用@Service注解标注,表示这个类是一个服务。在方法级别上,使用@Async注解标注方法,表示这个方法需要异步执行。 在这个例子中,getUserDetailsAsync方法使用@Async注解标注,表示这个方法需要异步执行。查询用户信息的操作在异步线程中执行,不会阻塞主线程。同时,这个方法返回一个CompletableFuture对象,表示异步执行的结果。 @Async注解用于异步执行方法,可以提高应用程序的响应性能。它是一种简单但非常有效的解决方案,可以使得开发者更加轻松地编写并发应用程序。 @Async注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地异步执行方法。需要注意的是,异步执行的方法必须在一个独立的线程中执行,因此需要使用线程池来管理异步线程的执行。 (29)@Cacheable 作用:用于缓存方法的返回值。 在Spring框架中,如果一个方法的返回结果是固定的,而且这个方法的执行比较耗时,我们可以使用@Cacheable注解将这个方法的返回结果缓存起来,下次执行这个方法时直接从缓存中获取结果,避免重复执行。  用例: @Service public class UserService {      @Cacheable("userCache")     public User getUser(Long id) {         // 查询用户信息         User user = userRepository.getUser(id);         return user;     } } 这个类使用@Service注解标注,表示这个类是一个服务。在方法级别上,使用@Cacheable注解标注方法,表示这个方法返回的结果可以被缓存起来。 在这个例子中,getUser方法使用@Cacheable注解标注,表示这个方法的返回结果可以被缓存起来。查询用户信息的操作在第一次执行时会被执行,返回结果会被缓存到名为"userCache"的缓存中。下次执行getUser方法时,如果缓存中已经存在这个结果,就直接从缓存中获取结果,不需要再次执行查询操作。 @Cacheable注解用于缓存方法的返回结果,可以提高应用程序的执行效率。它是一种简单但非常有效的解决方案,可以使得开发者更加灵活地使用缓存来优化应用程序的性能。 @Cacheable注解是Spring框架中比较常用的注解之一,可以让开发者更加轻松地使用缓存来提高应用程序的性能。需要注意的是,使用缓存需要考虑缓存的生命周期和缓存的一致性,必要时需要使用缓存失效机制和缓存更新机制来维护缓存的一致性。 (30)@CacheEvict 作用:用于清除缓存。 @CacheEvict是Spring框架中的一个注解,用于清空缓存中的数据。 在Spring框架中,如果一个方法的执行会导致缓存数据的失效,我们可以使用@CacheEvict注解将这个方法的缓存数据清空,这样下次执行这个方法时就会重新查询数据并缓存起来。 用例: @Service public class UserService {      @Cacheable("userCache")     public User getUser(Long id) {         // 查询用户信息         User user = userRepository.getUser(id);         return user;     }          @CacheEvict("userCache")     public void clearCache() {         // 清空缓存     } } 这个类使用@Service注解标注,表示这个类是一个服务。在方法级别上,使用@Cacheable注解标注getUser方法,表示这个方法的返回结果可以被缓存起来。同时,使用@CacheEvict注解标注clearCache方法,表示这个方法会清空名为"userCache"的缓存。 在这个例子中,getUser方法使用@Cacheable注解标注,表示这个方法的返回结果可以被缓存起来。查询用户信息的操作在第一次执行时会被执行,返回结果会被缓存到名为"userCache"的缓存中。下次执行getUser方法时,如果缓存中已经存在这个结果,就直接从缓存中获取结果,不需要再次执行查询操作。 当调用clearCache方法时,@CacheEvict注解会清空名为"userCache"的缓存,下次执行getUser方法时,就需要重新查询数据并缓存起来。 @CacheEvict注解用于清空缓存中的数据,可以使得开发者更加灵活地控制缓存的生命周期和缓存的一致性。它是一种简单但非常有效的解决方案,可以使得开发者更加轻松地使用缓存来提高应用程序的性能。 @CacheEvict注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地控制缓存的生命周期和缓存的一致性。需要注意的是,清空缓存需要谨慎操作,必要时需要考虑缓存的失效机制和缓存更新机制来维护缓存的一致性。 (31)@CachePut 作用:用于更新缓存中的数据。 @CachePut是Spring框架中的一个注解,用于更新或添加缓存中的数据。 在Spring框架中,如果一个方法的执行会导致缓存数据的更新或添加,我们可以使用@CachePut注解将这个方法的返回结果更新或添加到缓存中。 用例: @Service public class UserService {      @Cacheable("userCache")     public User getUser(Long id) {         // 查询用户信息         User user = userRepository.getUser(id);         return user;     }          @CachePut("userCache")     public User updateUser(Long id, User user) {         // 更新用户信息         User updatedUser = userRepository.updateUser(id, user);         return updatedUser;     } } 这个类使用@Service注解标注,表示这个类是一个服务。在方法级别上,使用@Cacheable注解标注getUser方法,表示这个方法的返回结果可以被缓存起来。同时,使用@CachePut注解标注updateUser方法,表示这个方法会更新或添加名为"userCache"的缓存。 在这个例子中,getUser方法使用@Cacheable注解标注,表示这个方法的返回结果可以被缓存起来。查询用户信息的操作在第一次执行时会被执行,返回结果会被缓存到名为"userCache"的缓存中。下次执行getUser方法时,如果缓存中已经存在这个结果,就直接从缓存中获取结果,不需要再次执行查询操作。 当调用updateUser方法时,@CachePut注解会更新或添加名为"userCache"的缓存,下次执行getUser方法时,就可以从缓存中获取更新后的用户信息。 @CachePut注解用于更新或添加缓存中的数据,可以使得开发者更加灵活地控制缓存的生命周期和缓存的一致性。它是一种简单但非常有效的解决方案,可以使得开发者更加轻松地使用缓存来提高应用程序的性能。 @CachePut注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地控制缓存的生命周期和缓存的一致性。需要注意的是,更新或添加缓存需要谨慎操作,必要时需要考虑缓存的失效机制和缓存更新机制来维护缓存的一致性。 (32)@Transactional 作用:用于指定事务的范围。 用例: (33)@EnableTransactionManagement 作用:用于启用事务管理功能。 @Transactional是Spring框架中的一个注解,用于标识一个方法或类需要使用事务进行操作。 在Spring框架中,如果一个方法需要对数据库进行操作,我们可以使用@Transactional注解来确保这个操作在一个事务中进行,从而保证操作的原子性、一致性、隔离性和持久性。 用例:  @Service @Transactional public class UserService {      @Autowired     private UserRepository userRepository;      public void createUser(User user) {         userRepository.save(user);     }          public void updateUser(Long id, User user) {         User existingUser = userRepository.findById(id);                  if (existingUser != null) {             existingUser.setName(user.getName());             existingUser.setEmail(user.getEmail());             userRepository.save(existingUser);         }     } }  这个类使用@Service注解标注,表示这个类是一个服务。同时,在类级别上使用@Transactional注解标注,表示这个类中的所有方法都需要使用事务进行操作。 在这个例子中,createUser和updateUser方法都需要对数据库进行操作,因此使用userRepository来保存或更新用户信息。由于这个类使用了@Transactional注解来标识,因此userRepository的操作都在一个事务中进行,从而保证操作的原子性、一致性、隔离性和持久性。 @Transactional注解用于标识一个方法或类需要使用事务进行操作,可以使得开发者更加灵活地控制事务的使用。它是一种简单但非常有效的解决方案,可以使得开发者更加轻松地使用事务来提高应用程序的性能和数据一致性。 @Transactional注解是Spring框架中比较常用的注解之一,可以让开发者更加灵活地控制事务的使用。需要注意的是,事务的使用需要谨慎操作,必要时需要考虑事务的隔离级别、超时时间和回滚机制等来维护数据的一致性和应用程序的性能。 (34)@EnableAspectJAutoProxy 作用:用于启用AOP功能。 @EnableAspectJAutoProxy是Spring框架中的一个注解,用于启用自动代理功能,以便使用AOP(面向切面编程)进行编程。 在Spring框架中,如果需要使用AOP来实现某些功能,我们可以使用@EnableAspectJAutoProxy注解来启用自动代理功能,从而在运行时自动为我们生成代理对象,以便进行切面编程。 用例:  @Configuration @EnableAspectJAutoProxy public class AppConfig {      @Bean     public MyAspect myAspect() {         return new MyAspect();     }          @Bean     public UserService userService() {         return new UserService();     } }  这个类使用@Configuration注解标注,表示这个类是一个配置类。同时,在类级别上使用@EnableAspectJAutoProxy注解标注,表示这个配置类需要启用自动代理功能。 在这个例子中,我们定义了一个MyAspect类来实现某些功能的切面编程。为了让Spring框架能够自动为我们生成代理对象,我们需要将MyAspect类加入到Spring容器中,并且使用@Bean注解标注。另外,我们还定义了一个UserService类来实现某些业务功能。 @EnableAspectJAutoProxy注解用于启用自动代理功能,可以使得开发者更加方便地使用AOP来实现某些功能。它是一种简单但非常有效的解决方案,可以让开发者更加轻松地使用切面编程来提高应用程序的性能和可维护性。  @EnableAspectJAutoProxy注解是Spring框架中比较常用的注解之一,可以让开发者更加方便地使用AOP来实现某些功能。需要注意的是,AOP的使用需要谨慎操作,必要时需要考虑AOP的切面逻辑、切入点和通知类型等来维护应用程序的性能和可维护性。 (35)@Aspect 作用:用于定义切面。 @Aspect是Spring框架中的一个注解,用于标识一个类为切面类,从而可以在该类中定义切面逻辑以实现AOP(面向切面编程)。 在Spring框架中,如果需要使用AOP来实现某些功能,我们可以使用@Aspect注解来标识一个类为切面类。在切面类中,我们可以定义切面逻辑,包括切入点、通知类型和切面顺序等,从而实现AOP编程的功能。 用例:  @Aspect @Component public class MyAspect {      @Before("execution(* com.example.UserService.*(..))")     public void beforeAdvice() {         System.out.println("Before advice is executed.");     }          @After("execution(* com.example.UserService.*(..))")     public void afterAdvice() {         System.out.println("After advice is executed.");     } }  这个类使用@Aspect注解标识,表示这个类是一个切面类。同时,我们还使用@Component注解标识这个类,以便Spring框架能够自动将它加入到Spring容器中。 在这个例子中,我们定义了一个MyAspect类来实现某些功能的切面编程。在这个类中,我们定义了两个通知类型,即@Before和@After,分别表示在目标方法执行前和执行后执行某些操作。这些通知类型的执行条件是通过切入点表达式来定义的。 @Aspect注解用于标识一个类为切面类,可以使得开发者更加方便地使用AOP来实现某些功能。它是一种简单但非常有效的解决方案,可以让开发者更加轻松地使用切面编程来提高应用程序的性能和可维护性。 @Aspect注解是Spring框架中比较常用的注解之一,用于标识一个类为切面类。需要注意的是,AOP的使用需要谨慎操作,必要时需要考虑切入点、通知类型和切面顺序等来维护应用程序的性能和可维护性。 (36)@Pointcut 作用:用于定义切点。 @Pointcut是Spring框架中的一个注解,用于定义一个切入点,从而可以在该切入点上定义通知类型以实现AOP(面向切面编程)。 在Spring框架中,如果需要使用AOP来实现某些功能,我们可以使用@Pointcut注解来定义一个切入点。在切入点上,我们可以定义切面逻辑,包括通知类型和切面顺序等,从而实现AOP编程的功能。 用例:  @Aspect @Component public class MyAspect {      @Pointcut("execution(* com.example.UserService.*(..))")     public void userServicePointcut() {}          @Before("userServicePointcut()")     public void beforeAdvice() {         System.out.println("Before advice is executed.");     }          @After("userServicePointcut()")     public void afterAdvice() {         System.out.println("After advice is executed.");     } }  这个类使用@Aspect注解标识,表示这个类是一个切面类。同时,我们还使用@Component注解标识这个类,以便Spring框架能够自动将它加入到Spring容器中。 在这个例子中,我们定义了一个MyAspect类来实现某些功能的切面编程。在这个类中,我们使用@Pointcut注解定义了一个切入点,即userServicePointcut()方法。在这个切入点上,我们定义了两个通知类型,即@Before和@After,分别表示在目标方法执行前和执行后执行某些操作。 @Pointcut注解用于定义一个切入点,可以使得开发者更加方便地使用AOP来实现某些功能。它是一种简单但非常有效的解决方案,可以让开发者更加轻松地使用切面编程来提高应用程序的性能和可维护性。  @Pointcut注解是Spring框架中比较常用的注解之一,用于定义一个切入点。需要注意的是,AOP的使用需要谨慎操作,必要时需要考虑切入点、通知类型和切面顺序等来维护应用程序的性能和可维护性。 (37)@Before 作用:用于在方法执行前执行通知。 @Before是Spring框架中的一个注解,用于定义在目标方法执行前执行的通知类型,以实现AOP(面向切面编程)。 在Spring框架中,如果需要在目标方法执行前执行某些操作,我们可以使用@Before注解来定义一个通知类型。在这个通知类型中,我们可以编写自己的逻辑代码,从而实现AOP编程的功能。 用例:  @Aspect @Component public class MyAspect {      @Before("execution(* com.example.UserService.*(..))")     public void beforeAdvice() {         System.out.println("Before advice is executed.");     } } (38)@After 作用:用于在方法执行后执行通知。 @After是Spring框架中的一个注解,用于定义在目标方法执行后执行的通知类型,以实现AOP(面向切面编程)。 在Spring框架中,如果需要在目标方法执行后执行某些操作,我们可以使用@After注解来定义一个通知类型。在这个通知类型中,我们可以编写自己的逻辑代码,从而实现AOP编程的功能。 用例:  @Aspect @Component public class MyAspect {      @After("execution(* com.example.UserService.*(..))")     public void afterAdvice() {         System.out.println("After advice is executed.");     } } (39)@Around 作用:用于在方法执行前后执行通知。 @Around是Spring框架中的一个注解,用于定义在目标方法执行前后执行的通知类型,以实现AOP(面向切面编程)。 在Spring框架中,如果需要在目标方法执行前后执行某些操作,我们可以使用@Around注解来定义一个通知类型。在这个通知类型中,我们可以编写自己的逻辑代码,从而实现AOP编程的功能。 用例: @Aspect @Component public class MyAspect {      @Around("execution(* com.example.UserService.*(..))")     public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {         System.out.println("Before advice is executed.");         Object result = joinPoint.proceed();         System.out.println("After advice is executed.");         return result;     } } (40)@AfterReturning 作用:用于在方法返回结果后执行通知。  @AfterReturning是Spring框架中的一个注解,用于定义在目标方法返回结果后执行的通知类型,以实现AOP(面向切面编程)。 在Spring框架中,如果需要在目标方法返回结果后执行某些操作,我们可以使用@AfterReturning注解来定义一个通知类型。在这个通知类型中,我们可以编写自己的逻辑代码,从而实现AOP编程的功能。 用例:  @Aspect @Component public class MyAspect {      @AfterReturning(pointcut = "execution(* com.example.UserService.*(..))", returning = "result")     public void afterReturningAdvice(Object result) {         System.out.println("After returning advice is executed. Result is " + result);     } } (41)@AfterThrowing 作用:用于在方法抛出异常后执行通知。 @AfterThrowing是Spring框架中的一个注解,用于定义在目标方法抛出异常后执行的通知类型,以实现AOP(面向切面编程)。 在Spring框架中,如果需要在目标方法抛出异常后执行某些操作,我们可以使用@AfterThrowing注解来定义一个通知类型。在这个通知类型中,我们可以编写自己的逻辑代码,从而实现AOP编程的功能。 用例:  @Aspect @Component public class MyAspect {      @AfterThrowing(pointcut = "execution(* com.example.UserService.*(..))", throwing = "ex")     public void afterThrowingAdvice(Exception ex) {         System.out.println("After throwing advice is executed. Exception is " + ex);     } } (42)@Order 作用:用于指定切面的执行顺序。 @Order是Spring框架中的一个注解,用于定义切面的执行顺序。 在Spring框架中,如果有多个切面类需要对同一个方法进行切面处理,那么这些切面类的执行顺序可能会影响到最终的结果。为了控制这些切面类的执行顺序,我们可以使用@Order注解来定义它们的执行顺序。 @Order注解可以应用在切面类上,用于指定切面执行的顺序。它的参数为一个整数,数值越小表示优先级越高,数值相同时按照类名的自然顺序进行排序。 用例:  @Aspect @Component @Order(1) public class MyAspect1 {      @Before("execution(* com.example.UserService.*(..))")     public void beforeAdvice() {         System.out.println("Before advice from MyAspect1 is executed.");     } }  @Aspect @Component @Order(2) public class MyAspect2 {      @Before("execution(* com.example.UserService.*(..))")     public void beforeAdvice() {         System.out.println("Before advice from MyAspect2 is executed.");     } } (43)@Slf4j 作用:用于简化日志记录。 @Slf4j是Lombok框架中的一个注解,用于在Java类中自动生成日志记录器。 在Java开发中,日志记录是非常重要的一环,可以帮助我们更好地了解程序的运行情况,从而更好地进行调试和优化。通常情况下,我们需要手动引入日志框架(如Log4j、SLF4J等)并编写相应的日志记录代码。这些代码可能会比较繁琐,而且容易出现错误。为了简化这个过程,Lombok框架提供了一个@Slf4j注解,可以在Java类中自动生成日志记录器。 使用@Slf4j注解非常简单,只需要在Java类中添加这个注解即可。在使用时,我们可以直接使用log变量来记录日志,而不需要再引入其他的日志框架 用例:  @Slf4j public class MyService {      public void doSomething() {         log.debug("This is a debug message.");         log.info("This is an info message.");         log.error("This is an error message.");     } }  在这个例子中,我们定义了一个MyService类,并使用@Slf4j注解来自动生成日志记录器。然后,在doSomething()方法中,我们直接使用log变量来记录日志,而不需要再引入其他的日志框架。 需要注意的是,使用@Slf4j注解需要在编译器中安装Lombok插件,否则可能会出现编译错误。另外,虽然@Slf4j注解非常方便,但在实际应用中,我们还需要根据实际情况选择合适的日志框架,并编写相应的日志记录代码。 总之,@Slf4j是Lombok框架中的一个注解,可以在Java类中自动生成日志记录器,从而简化日志记录的过程。它是一种极为方便的解决方案,可以提高应用程序的可维护性和可读性。 (44)@Data 作用:用于自动生成JavaBean的getters、setters、toString、hashCode和equals方法。 @Data是Lombok框架中的一个注解,可以自动生成Java类的getter、setter、equals、hashCode和toString等方法。 在Java开发中,我们经常需要编写一些POJO类来表示数据结构。这些类通常包含一些成员变量,并且需要编写相应的getter、setter、equals、hashCode和toString等方法。这些方法通常是相似的,而且比较繁琐。为了简化这个过程,Lombok框架提供了一个@Data注解,可以自动生成这些方法。 使用@Data注解非常简单,只需要在Java类上添加这个注解即可。在使用时,我们可以直接访问类的成员变量,并且可以自动生成相应的getter、setter、equals、hashCode和toString等方法。 用例:  @Data public class User {     private Long id;     private String name;     private Integer age; } (45)@NoArgsConstructor 作用:用于生成无参构造函数。 @NoArgsConstructor是Lombok框架中的一个注解,用于自动生成一个无参构造方法。  在Java开发中,我们经常需要编写一些POJO类来表示数据结构。这些类通常包含一些成员变量,并且需要编写相应的构造方法。在某些情况下,我们可能需要编写一个无参构造方法,用于创建一个对象的实例。这个构造方法通常是简单的、无需参数的。为了简化这个过程,Lombok框架提供了一个@NoArgsConstructor注解,可以自动生成一个无参构造方法。 使用@NoArgsConstructor注解非常简单,只需要在Java类上添加这个注解即可。在使用时,我们可以直接创建对象的实例,而不需要手动编写无参构造方法。 用例:  @NoArgsConstructor public class User {     private Long id;     private String name;     private Integer age; } 在这个例子中,我们定义了一个User类,并使用@NoArgsConstructor注解来自动生成一个无参构造方法。然后,在其他的Java类中,我们可以直接创建User对象的实例,而不需要手动编写无参构造方法。  需要注意的是,使用@NoArgsConstructor注解需要在编译器中安装Lombok插件,否则可能会出现编译错误。另外,虽然@NoArgsConstructor注解非常方便,但在实际应用中,我们还需要根据实际情况选择合适的构造方法,并编写相应的代码。 总之,@NoArgsConstructor是Lombok框架中的一个注解,用于自动生成一个无参构造方法,从而简化Java开发的过程。它是一种极为方便的解决方案,可以提高应用程序的可维护性和可读性。 (46)@AllArgsConstructor 作用:用于生成全参构造函数。 @AllArgsConstructor是Lombok框架中的一个注解,用于自动生成一个全参构造方法。 在Java开发中,我们经常需要编写一些POJO类来表示数据结构。这些类通常包含一些成员变量,并且需要编写相应的构造方法。在某些情况下,我们可能需要编写一个全参构造方法,用于初始化所有成员变量。这个构造方法通常包含所有成员变量作为参数。为了简化这个过程,Lombok框架提供了一个@AllArgsConstructor注解,可以自动生成一个全参构造方法。 使用@AllArgsConstructor注解非常简单,只需要在Java类上添加这个注解即可。在使用时,我们可以直接创建对象的实例,并传入相应的参数,而不需要手动编写全参构造方法。 用例:  @AllArgsConstructor public class User {     private Long id;     private String name;     private Integer age; }  在这个例子中,我们定义了一个User类,并使用@AllArgsConstructor注解来自动生成一个全参构造方法。然后,在其他的Java类中,我们可以直接创建User对象的实例,并传入相应的参数,而不需要手动编写全参构造方法。  需要注意的是,使用@AllArgsConstructor注解需要在编译器中安装Lombok插件,否则可能会出现编译错误。另外,虽然@AllArgsConstructor注解非常方便,但在实际应用中,我们还需要根据实际情况选择合适的构造方法,并编写相应的代码。 总之,@AllArgsConstructor是Lombok框架中的一个注解,用于自动生成一个全参构造方法,从而简化Java开发的过程。它是一种极为方便的解决方案,可以提高应用程序的可维护性和可读性。 (47)@Builder 作用:用于生成Builder模式的构造函数。 @Builder是Lombok框架中的一个注解,用于自动生成一个Builder模式的构造器。 在Java开发中,我们经常需要编写一些POJO类来表示数据结构。这些类通常包含一些成员变量,并且需要编写相应的构造方法。在某些情况下,我们可能需要编写一个Builder模式的构造器,用于方便地创建对象实例。Builder模式是一种创建对象的设计模式,它可以通过链式调用的方式设置对象的属性,并最终创建一个不可变的对象。为了简化这个过程,Lombok框架提供了一个@Builder注解,可以自动生成一个Builder模式的构造器。 使用@Builder注解非常简单,只需要在Java类上添加这个注解即可。在使用时,我们可以使用链式调用的方式设置对象的属性,并最终创建一个不可变的对象。 用例: @Builder public class User {     private Long id;     private String name;     private Integer age; } 在这个例子中,我们定义了一个User类,并使用@Builder注解来自动生成一个Builder模式的构造器。然后,在其他的Java类中,我们可以使用链式调用的方式设置User对象的属性,并最终创建一个不可变的对象。 需要注意的是,使用@Builder注解需要在编译器中安装Lombok插件,否则可能会出现编译错误。另外,虽然@Builder注解非常方便,但在实际应用中,我们还需要根据实际情况选择合适的构造方法,并编写相应的代码。 总之,@Builder是Lombok框架中的一个注解,用于自动生成一个Builder模式的构造器,从而简化Java开发的过程。它是一种极为方便的解决方案,可以提高应用程序的可维护性和可读性。 (48)@EqualsAndHashCode 作用:用于生成hashCode和equals方法。 @EqualsAndHashCode是Lombok框架中的一个注解,用于自动生成equals()和hashCode()方法。 在Java开发中,我们经常需要比较两个对象是否相等,并且需要根据对象的属性生成一个hashCode值。为了简化这个过程,Lombok框架提供了一个@EqualsAndHashCode注解,可以自动生成equals()和hashCode()方法。 使用@EqualsAndHashCode注解非常简单,只需要在Java类上添加这个注解即可。在使用时,Lombok会根据类的属性自动生成equals()和hashCode()方法。如果两个对象的所有属性都相等,那么它们的equals()方法返回true,并且它们的hashCode()方法返回相同的值。 用例:  @EqualsAndHashCode public class User {     private Long id;     private String name;     private Integer age; } (49)@ToString 作用:用于生成toString方法。 @ToString是Lombok框架中的一个注解,用于自动生成toString()方法。 在Java开发中,我们经常需要将对象转换为字符串,以便于输出或日志记录。为了简化这个过程,Lombok框架提供了一个@ToString注解,可以自动生成toString()方法。 使用@ToString注解非常简单,只需要在Java类上添加这个注解即可。在使用时,Lombok会根据类的属性自动生成toString()方法,这个方法将输出类的名称和所有属性的名称和值。如果需要排除某些属性,可以使用exclude属性来指定排除的属性。 用例:  @ToString(exclude = "password") public class User {     private Long id;     private String name;     private String password; } (50)@Getter 作用:用于生成getters方法。 @Getter是Lombok框架中的一个注解,用于自动生成getter方法。 在Java开发中,我们经常需要为类的属性编写getter和setter方法。为了简化这个过程,Lombok框架提供了一个@Getter注解,可以自动生成getter方法。 使用@Getter注解非常简单,只需要在Java类上添加这个注解即可。在使用时,Lombok会根据类的属性自动生成对应的getter方法。如果需要生成setter方法,可以使用@Setter注解。 用例: @Getter public class User {     private Long id;     private String name;     private Integer age; } ———————————————— 原文链接:https://blog.csdn.net/qq_46138492/article/details/129476788 
  • [技术干货] Java 算法篇-深入理解递归(递归实现:青蛙爬楼梯)-转载
       1.0 递归的说明         递归就是在一个函数中调用自身。这样做可以让我们解决一些问题,比如计算斐波那契数列、阶乘等。          递归函数一般包括两部分:基本情况和递归情况。基本情况是指当问题变得很小,可以直接得到答案时,递归就可以停止了。递归情况是指在解决问题的过程中,需要不断地调用自身来解决更小规模的问题。          对于递归这个算法,简单的来说,方法自身调用自身的时候,需要有终止的条件,在运行过程中不断的趋向终止条件。还有递归总的来说有两个动作:第一个动作是递出,方法不断的在栈区中创建出来,直到达到了条件就会停止。第二个动作,达到条件停止了,就会回归,指方法在栈区中依次执行完后就销毁。          2.0 用递归来实现相关问题         以下的问题都较为简单,采取直接用代码来演示。          2.1 递归 - 阶乘 代码如下:      //阶乘     public static void main(String[] args) {         System.out.println(fun(5));     }          public static int fun(int n) {         if (n == 1) {             return 1;         }         return n * fun(n-1);     } } 运行结果为:           2.2 递归 - 反向打印字符串 代码如下:      //反向打印字符串     public static void main(String[] args) {         String str = "lisi";         fun2(str,0);     }       public static void fun2 (String s, int n) {           if (n == s.length()) {             return;         }         fun2(s,n + 1);         System.out.println(s.charAt(n));     } 运行结果:           2.3 递归 - 二分查找 代码如下:      //二分查找     public static void main(String[] args) {         int[] arr = {1,3,5,7,9,10,13};         System.out.println(fun3(arr, 0, arr.length - 1, 4));     }       public static int fun3 (int[] arr, int left, int right, int target) {         int mid = (left + right) >>> 1;         if (left > right) {             return -1;         }         if(arr[mid] < target) {             return fun3(arr, mid + 1,right,target);         } else if (target < arr[mid]) {             return fun3(arr,left,right - 1,target);         }else {             return mid;         }     }    运行结果如下:           没有找到就返回 - 1          2.4 递归 - 冒泡排序 代码如下:      //冒泡排序     public static void main(String[] args) {         int[] arr = {1,5,2,4,9,1,3};         fun4(arr, arr.length - 1);         System.out.println(Arrays.toString(arr));     }     public static void fun4 (int[] arr, int n) {         if (n == 0) {             return;         }         for (int i = 0; i < n; i++) {             if (arr[i] > arr[i + 1]) {                 int temp = arr[i];                 arr[i] = arr[i+1];                 arr[i+1] = temp;             }         }         fun4(arr,n-1);     }  运行结果如下:           2.5 递归 - 冒泡排序2.0         对冒泡排序进行升级,假如 int[] arr = {2,1,1,3,4,5,9},这种只需要遍历一遍即可,但是对与已经用递归实现的冒泡不止遍历一次。因此,需要得到升级版冒泡排序。          思路为:对于后续的元素已经是排好序了,就不用再遍历了。每一次交换完元素之后记下来 i 索引,i 之后的元素已经是排好序的,i 之前的元素还需要继续遍历,看是否还需要交换。  代码如下:      //冒泡排序升级版     public static void main(String[] args) {         int[] arr = {1,3,2,4,9,10,13};         fun4(arr, arr.length - 1);         System.out.println(Arrays.toString(arr));     }     public static void fun4 (int[] arr, int n) {         if (n == 0) {             return;         }         int j = 0;         for (int i = 0; i < n; i++) {             if (arr[i] > arr[i + 1]) {                 int temp = arr[i];                 arr[i] = arr[i+1];                 arr[i+1] = temp;                 j = i;             }         }         fun4(arr,j);     }          如果还不是很清晰的话,可以一步步来调试一下,来对比两种冒泡的执行过程。          2.6 递归 - 插入排序         思路:假设第一个元素已经排序好了的,在已经排好的元素的后一个元素记录为 low,这个 low 索引对应的元素需要用临时变量来接受,只要找到比这个索引对应的元素小的值,就可以插入到比它小的值的后一个索引位置了,当然,每一次对比之后,都需要往后移一个位置,以便直接插入。当 low 一直每一个加 1 ,当 low 等于数组的长度时,就该停止了继续递归下去了。  代码如下:  public class Recursion {     // 插入排序     public static void main(String[] args) {         int[] arr = {1,3,2,4,9,10,13};         fun5(arr,1);         System.out.println(Arrays.toString(arr));     }       public static void fun5 (int[] arr,int low) {         if (low == arr.length) {             return;         }         int temp = arr[low];         int i = low - 1;         while (arr[i] > temp) {             arr[i + 1] = arr[i];             i--;         }         arr[i + 1] = temp;         fun5(arr,low + 1);     }  运行结果如下:           2.7 递归 - 斐波那契 代码如下:      //斐波那契     public static void main(String[] args) {         System.out.print(fun6(1) +" ");         System.out.print(fun6(2) +" ");         System.out.print(fun6(3) +" ");         System.out.print(fun6(4) +" ");         System.out.print(fun6(5) +" ");         System.out.print(fun6(6) +" ");     }       public static int fun6 (int n) {         if (n == 0) {             return 0;         }         if (n == 1 || n == 2) {             return 1;         }           return fun6(n-1) + fun6(n - 2);     }  运行结果如下:           2.8 递归 - 兔子问题         一个斐波那契的变体问题。          思路:观察第六个月的兔子个数,是否等于第四个月的兔子的总数加上第五个月的兔子总数;类推,第五个月的兔子个数,是否等于第四个月的兔子的总数加上第三个月的兔子总数;以此类推,是符合斐波那契逻辑的。   代码如下:      //兔子问题     public static void main(String[] args) {         System.out.print(fun7(1) + " ");         System.out.print(fun7(2) + " ");         System.out.print(fun7(3) + " ");         System.out.print(fun7(4) + " ");         System.out.print(fun7(5) + " ");     }          public static int fun7 (int n) {         if (n == 1) {             return 1;         }         if (n == 0) {             return 0;         }         return fun7(n -1) + fun7(n - 2);     }  运行结果如下:           2.9 递归 - 青蛙爬楼梯         一个斐波那契的变体问题。  题目如下:   列举一下:           实现思路: 一个阶梯一种跳法,两个阶梯两种跳法。重点,如果有四个阶梯,从后往前分析,分两种情况;第一种,从第二个台阶直接一下子跳两阶上来。第二种,从第三个台阶跳一阶上来。那么从考虑第一种情况,前面两阶是不是就是只有两种方法。考虑第二种情况,前面的三个台阶是不是就是前面已经算出来的方式跳法个数了。因此,这就是一个斐波那契的变体问题。  代码如下:      //青蛙问题     public static void main(String[] args) {         System.out.print(fun8(1) + " ");         System.out.print(fun8(2) + " ");         System.out.print(fun8(3) + " ");         System.out.print(fun8(4) + " ");         System.out.print(fun8(5) + " ");         System.out.print(fun8(6) + " ");     }       public static int fun8 (int n) {         if (n == 1) {             return 1;         }         if (n == 2) {             return 2;         }         return fun8(n-1) +fun8(n-2);     }  运行结果如下:  ———————————————— 版权声明:本文为CSDN博主「小扳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Tingfeng__/article/details/134325079 
  • JAVA面向对象之封装&amp;多态
    一、封装性.1.1封装概述1、是面向对象三大特征之一(封装,继承,多态)2、是面向对象编程语言对现实世界的模拟,现实世界里成员变量和对象都是一个整体,归属者是对象,外界需要通过对象的同意才能对其进行相应操作1.2封装原则1.2.1.封装提高了数据的安全性      将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问            成员变量private,提供对应的getXxx()/setXxx()方法比如:public class student{        private string name;        public string getName(){        }               pub1ic void setName(string name)i           this.name=name;       }      private int age;        public int getAge(){       }          public void setAge(int age){              this.age=age;       } }1.2.2把代码用方法进行封装(可维护性和条理性)      把代码按照功能模块抽取成一个个方法,然后在main方法里调用就可以了,这样做,我们main方法里的代码实现了哪些功能一眼就能看出来,都是一个个功能的调用,没有冗余的代码,比较直观,方便阅读和后期的维护1.2.3隐藏了实现,操作简单(复用性)      在其他也有用到这个功能的地方,可以不用在写,直接调用方法,不用过于关注过程,达到了复用的效果,让事情变得更简单二、多态.2.1多态的概念多态:同一个事物,表现出来的多种形态:多态需要满足的前提条件:1、要有继承或实现关系2、要有方法的重写3、要有父类接收子类对象比如:public class Car {      public void weight(){            system.out.print1n("普通车重量...");       } } class Big extends Car { //重写父类的方法         pub1ic void weight(){             system. out.println("大车又高又大,而且重量.....");         } } class Small extends Car{ //重写父类的方法          public void weight({                 system. out.println("小车又小又快,而且重量.....");           } } /** 1、要有继承或实现关系 2、要有方法的重写 3、要有父类接收子类对象 **/ public class BigDemo {       public static void main(string[] args) {           //多态的表达形式:向上造型           Car c = new Big();        } }2.2 多态的好处与弊端好处: 提高代码的扩展性; 定义一个方法的时候,以父类作为参数,在调用时,传入子类进行 具体的操作;弊端:不能访问子类特有的方法;(可以通过类型强转来实现)2.3 多态的转型1、向上转型:参考多态的优点扩展---设计中的一个基本准则:里氏代换原则      在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果 一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。      里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在 程 序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。2、向下转型         子类 子类名称 = (子类)父类名称;
  • SSH 组合框架模式小知识分享
    SSH=Struts+Spring+Hibernate      集成SSH框架的系统从职责上分为四层:表示层、业务逻辑层、数据持久层和域模块层,以帮助开发人员在短期内搭建结构清晰、可复用性好、维护方便的Web应用程序。其中使用Struts作为系统的整体基础架构,负责MVC的分离,在Struts框架的模型部分,控制业务跳转,利用Hibernate框架对持久层提供支持,Spring做管理,管理struts和hibernate。      Struts 是一个很好的MVC框架,主要技术是Servlet和Jsp。Struts的MVC设计模式可以使我们的逻辑变得很清晰,让我们写的程序层次分明。基于Struts开发可以简化开发难度,提高开发效率。  Spring 提供了管理业务对象的一致方法,并鼓励注入对接口编程而不是对类编程的良好习惯,使我们的产品在最大程度上解耦。Hibernate 是用来持久化数据的,提供了完全面向对象的数据库操作。Hibernate对JDBC进行了非常轻量级的封装,它使得与关系型数据库打交道变得非常轻松。在Struts+Spring+Hibernate系统中,对象之间的调用流程如下: Struts——>Spring——>Hibernate  JSP——>Action——>Service——>DAO——>Hibernate比如:1.Spring的配置文件bean.xml<?xml version="1.0" encoding="UTF-8"?><beans         xmlns="http://www.springframework.org/schema/beans"         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"         xmlns:tx="http://www.springframework.org/schema/tx">     <bean id="dataSource"           class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">         <property name="jdbcUrl"                   value="jdbc:mysql://localhost:3306/samblog?useUnicode=true&amp;characterEncoding=UTF-8&amp;autoReconnect=true">         </property>         <property name="user" value="root"></property>         <property name="password" value="123456"></property>         <property name="driverClass" value="org.gjt.mm.mysql.Driver"/>     </bean>     <bean id="sessionFactory"           class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">         <property name="dataSource">             <ref bean="dataSource"/>         </property>         <property name="hibernateProperties">             <value>                 hibernate.dialect=org.hibernate.dialect.MySQL5Dialect                 hibernate.hbm2ddl.auto=update                 hibernate.show_sql=false                 hibernate.format_sql=false            </value>         </property>         <property name="mappingResources">             <list>                 <value>site/sambloger/domain/Users.hbm.xml</value>                 <value>site/sambloger/domain/Blog.hbm.xml</value>                 <value>site/sambloger/domain/Category.hbm.xml</value>                 <value>site/sambloger/domain/Comment.hbm.xml</value>             </list>         </property>     </bean>     <bean id="transactionManager"           class="org.springframework.orm.hibernate5.HibernateTransactionManager">         <property name="sessionFactory" ref="sessionFactory"/>     </bean>     <tx:annotation-driven transaction-manager="transactionManager"/>     <!-- 配置Blog  spring进行管理  服务层直接调用实现与数据库的CRUD-->     <bean id="blogDao" class="site.sambloger.dao.impl.BlogDAOImpl">         <property name="sessionFactory" ref="sessionFactory"/>     </bean>     <bean id="blogService" class="site.sambloger.service.impl.BlogServiceImpl" scope="prototype">         <property name="blogDao" ref="blogDao"/>     </bean>     <bean id="blogAction" class="site.sambloger.action.BlogAction">         <property name="blogService" ref="blogService"/>         <property name="commentService" ref="commentService"/>     </bean>     <!-- 配置Comment -->     <bean id="commentDao" class="site.sambloger.dao.impl.CommentDAOImpl">         <property name="sessionFactory" ref="sessionFactory"/>     </bean>     <bean id="commentService" class="site.sambloger.service.impl.CommentServiceImpl" scope="prototype">         <property name="commentDao" ref="commentDao"/>     </bean>     <bean id="commentAction" class="site.sambloger.action.CommentAction">         <property name="commentService" ref="commentService"/>         <property name="blogService" ref="blogService"/>     </bean>     <!-- 配置Users -->     <bean id="usersDao" class="site.sambloger.dao.impl.UsersDAOImpl">         <property name="sessionFactory" ref="sessionFactory"></property>     </bean>     <bean id="usersService" class="site.sambloger.service.impl.UsersServiceImpl" scope="prototype">         <property name="usersDao" ref="usersDao"/>     </bean>     <bean id="usersAction" class="site.sambloger.action.UsersAction">         <property name="userService" ref="usersService"></property>     </bean></beans>2.Struts的配置文件 struts.xml<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"><struts>     <package name="samblog" extends="struts-default" namespace="/">              <action name="init" class="blogAction" method="init">                     <result name="success">/bloglist.jsp</result>             </action>             <action name="getBlog" class="blogAction" method="getBlog">                     <result name="success">/displayBlog.jsp</result>             </action>              <action name="getAllNote" class="blogAction" method="getAllNote">                 <result name="success">/notelist.jsp</result>             </action>             <action name="addComment" class="commentAction" method="addComment">                 <result name="success"  type="redirect">/getBlog</result>             </action>     </package></struts>3.Hibernate其中的一个配置文件:<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><!--      Mapping file autogenerated by MyEclipse Persistence Tools--><hibernate-mapping>     <class name="site.sambloger.domain.Blog" table="blog">         <!--id标签表示映射到数据库中是作为主键 其他property表示普通键-->         <id name="id" type="java.lang.Integer">             <column name="id" />             <generator class="increment" />         </id><!--该标签加N方 会有一个字段叫category_id作为外键参照1(Category)的主键字段 并且用来存储这个主键的信息-->         <many-to-one name="category" class="site.sambloger.domain.Category"  lazy="false" cascade="all">             <column name="category_id" not-null="true" />         </many-to-one>         <property name="title" type="java.lang.String">             <column name="title" length="400" not-null="true" />         </property>         <property name="content" type="java.lang.String">             <column name="content" length="4000" not-null="true" />         </property>         <property name="createdTime" type="java.util.Date">             <column name="created_time" length="10" not-null="true" />         </property><!--在一对多的关联中,在一的一方(Blog)设置inverse=”true”让多的一方来维护关联关系更有助于优化,因为可以减少执行update语句-->         <set name="comments" inverse="true">             <key>                 <column name="blog_id" not-null="true" />             </key>             <one-to-many class="site.sambloger.domain.Comment" />         </set>     </class></hibernate-mapping>Spring框架的作用和好处:    Spring框架提供了一个容器,该容器可以管理应用程序的组件,还提供了IoC和AoP机制,实现组件之间解耦,提高程序结构的灵活性,增强系统的可维护和可扩展性。     在SSH整合开发中,利用Spring管理Service、DAO等组件,利用IoC机制实现Action和Service,Service和DAO之间低耦合调用。利用AoP机制实现事务管理、以及共通功能的切入等。     功能是整合,好处是解耦。Hibernate中操作并发处理(乐观锁和悲观锁)    Hibernate框架可以使用锁的机制来解决操作并发。    a.悲观锁         在数据查询出来时,就给数据加一个锁,锁定。这样其他用户再执行删、改操作时不允许。当占用着事务结束,锁会自动解除。          Hibernate采用的是数据库锁机制实现悲观锁控制。        缺点:将并发用户操作同步开,一个一个处理。当一个用户处理时间比较长时,效率会比较低。      b.乐观锁         允许同时更新提交,但是最快的会成功,慢的失败。         在记录中追加一个字段值,用该字段值当做版本。当最先提交者提交后,会自动将版本字段值提升,这样其他用户提交,会发现版本低于数据库记录目前版本,因此抛出异常提示失败。    特点:允许用户同时处理,但只能有一个成功,其他失败,以异常方式提示。SSH工作流程       a.启动服务器,加载工程以及web.xml.           (实例化Lisener,Filter等组件,将Spring容器和Struts2控制创建)       b.客户端发送请求,所有请求进入Struts2控制器。控制器根据请求类型不同,分别处理。           (action请求,*.action会进入struts.xml寻找<action>配置.            其他请求,*.jsp会直接调用请求资源,生成响应信息)       c.Struts2控制器根据<action>配置调用一个Action对象处理。         整合方法一:将Action交给Spring容器          (Action对象由struts2-spring-plugin.jar插件提供的                      StrutsSpringObjectFactory负责去Spring容器获取)         整合方法二:将Action置于Spring容器之外          (Action对象由struts2-spring-plugin.jar插件提供的                      StrutsSpringObjectFactory负责创建,然后到Spring容器中寻找与Action属性匹配的Bean对象,给Action对象注入。(默认采用名称匹配规则)       d.Struts2控制器执行defaultStack拦截器、Action对象、Result等组件处理.       e.执行Action的execute业务方法时,如果使用Service或DAO采用Spring的IoC机制调用。       f.执行Result生成响应信息,执行后续拦截器处理       g.将响应信息输出。
  • [技术干货] 如何动态更新SpringBoot中的yml文件
    前言在系统运行过程中,可能由于一些配置项的简单变动需要重新打包启停项目,这对于在运行中的项目会造成数据丢失,客户操作无响应等情况发生,针对这类情况对开发框架进行升级提供yml文件实时修改更新功能。项目依赖项目基于的是2.0.0.RELEASE版本,所以snakeyaml需要单独引入,高版本已包含在内。 codeduidaima.com<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.23</version></dependency>网上大多数方法是引入spring-cloud-context配置组件调用ContextRefresher的refresh方法达到同样的效果,考虑以下两点未使用:1.开发框架使用了logback日志,引入spring-cloud-context会造成日志配置读取错误。2.引入spring-cloud-context会同时引入spring-boot-starter-actuator组件,会开放一些健康检查路由及端口,需要对框架安全方面进行额外控制。YML文件内容获取读取resource文件下的文件需要使用ClassPathResource获取InputStream。 codeduidaima.compublic String getTotalYamlFileContent() throws Exception {// 堆代码 duidaima.comString fileName = "application.yml";return getYamlFileContent(fileName);}public String getYamlFileContent(String fileName) throws Exception {ClassPathResource classPathResource = new ClassPathResource(fileName);return onvertStreamToString(classPathResource.getInputStream());}public static String convertStreamToString(InputStream inputStream) throws Exception{return IOUtils.toString(inputStream, "utf-8");}YML文件内容更新我们获取到yml文件内容后可视化显示到前台进行展示修改,将修改后的内容通过yaml.load方法转换成Map结构,再使用yaml.dumpAsMap转换为流写入到文件。 codeduidaima.compublic void updateTotalYamlFileContent(String content) throws Exception {String fileName = "application.yml";updateYamlFileContent(fileName, content);}public void updateYamlFileContent(String fileName, String content) throws Exception {Yaml template = new Yaml();Map<String, Object> yamlMap = template.load(content);ClassPathResource classPathResource = new ClassPathResource(fileName);Yaml yaml = new Yaml();//字符输出FileWriter fileWriter = new FileWriter(classPathResource.getFile());//用yaml方法把map结构格式化为yaml文件结构fileWriter.write(yaml.dumpAsMap(yamlMap));//刷新fileWriter.flush();//关闭流fileWriter.close();}YML属性刷新yml属性在程序中读取使用一般有三种:1.使用Value注解 codeduidaima.com@Value("${system.systemName}")private String systemName;2.通过enviroment注入读取 codeduidaima.com@Autowiredprivate Environment environment;environment.getProperty("system.systemName")3.使用ConfigurationProperties注解读取 codeduidaima.com@Component@ConfigurationProperties(prefix = "system")public class SystemConfig {private String systemName;}Property刷新我们通过environment.getProperty方法读取的配置集合实际是存储在PropertySources中的,我们只需要把键值对全部取出存储在propertyMap中,将更新后的yml文件内容转换成相同格式的ymlMap,两个Map进行合并,调用PropertySources的replace方法进行整体替换即可。但是yaml.load后的ymlMap和PropertySources取出的propertyMap两者数据解构是不同的,需要进行手动转换。propertyMap集合就是单纯的key,value键值对,key是properties形式的名称,例如system.systemName=>xxxxx集团管理系统。ymlMap集合是key,LinkedHashMap的嵌套层次结构,例如system=>(systemName=>xxxxx集团管理系统)。转换方法如下: codeduidaima.compublic HashMap<String, Object> convertYmlMapToPropertyMap(Map<String, Object> yamlMap) {HashMap<String, Object> propertyMap = new HashMap<String, Object>();for (String key : yamlMap.keySet()) {String keyName = key;Object value = yamlMap.get(key);if (value != null && value.getClass() == LinkedHashMap.class) {convertYmlMapToPropertyMapSub(keyName, ((LinkedHashMap<String, Object>) value), propertyMap);} else {propertyMap.put(keyName, value);}}return propertyMap;}private void convertYmlMapToPropertyMapSub(String keyName, LinkedHashMap<String, Object> submMap, Map<String, Object> propertyMap) {for (String key : submMap.keySet()) {String newKey = keyName + "." + key;Object value = submMap.get(key);if (value != null && value.getClass() == LinkedHashMap.class) {convertYmlMapToPropertyMapSub(newKey, ((LinkedHashMap<String, Object>) value), propertyMap);} else {propertyMap.put(newKey, value);}}}刷新方法如下 codeduidaima.comString name = "applicationConfig: [classpath:/" + fileName + "]";MapPropertySource propertySource = (MapPropertySource) environment.getPropertySources().get(name);Map<String, Object> source = propertySource.getSource();Map<String, Object> map = new HashMap<>(source.size());map.putAll(source);Map<String, Object> propertyMap = convertYmlMapToPropertyMap(yamlMap);for (String key : propertyMap.keySet()) {Object value = propertyMap.get(key);map.put(key, value);}environment.getPropertySources().replace(name, new MapPropertySource(name, map));注解刷新不论是Value注解还是ConfigurationProperties注解,实际都是通过注入Bean对象的属性方法使用的,我们先自定注解RefreshValue来修饰属性所在Bean的class。通过实现InstantiationAwareBeanPostProcessorAdapter接口在系统启动时过滤筛选对应的Bean存储下来,在更新yml文件时通过spring的event通知更新对应bean的属性即可。注册事件使用EventListener注解: codeduidaima.com@EventListenerpublic void updateConfig(ConfigUpdateEvent configUpdateEvent) {if(mapper.containsKey(configUpdateEvent.key)){List<FieldPair> fieldPairList = mapper.get(configUpdateEvent.key);if(fieldPairList.size()>0){for (FieldPair fieldPair:fieldPairList) {fieldPair.updateValue(environment);}}}}通知触发事件使用ApplicationContext的publishEvent方法: codeduidaima.com@Autowiredprivate ApplicationContext applicationContext;for (String key : propertyMap.keySet()) {applicationContext.publishEvent(new YamlConfigRefreshPostProcessor.ConfigUpdateEvent(this, key));}YamlConfigRefreshPostProcessor的完整代码如下: codeduidaima.com@Componentpublic class YamlConfigRefreshPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements EnvironmentAware {private Map<String, List<FieldPair>> mapper = new HashMap<>();private Environment environment;@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {processMetaValue(bean);return super.postProcessAfterInstantiation(bean, beanName);}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}private void processMetaValue(Object bean) {Class clz = bean.getClass();if (!clz.isAnnotationPresent(RefreshValue.class)) {return;}if (clz.isAnnotationPresent(ConfigurationProperties.class)) {//@ConfigurationProperties注解ConfigurationProperties config = (ConfigurationProperties) clz.getAnnotation(ConfigurationProperties.class);for (Field field : clz.getDeclaredFields()) {String key = config.prefix() + "." + field.getName();if(mapper.containsKey(key)){mapper.get(key).add(new FieldPair(bean, field, key));}else{List<FieldPair> fieldPairList = new ArrayList<>();fieldPairList.add(new FieldPair(bean, field, key));mapper.put(key, fieldPairList);}}} else {//@Valuez注解try {for (Field field : clz.getDeclaredFields()) {if (field.isAnnotationPresent(Value.class)) {Value val = field.getAnnotation(Value.class);String key = val.value().replace("${", "").replace("}", "");if(mapper.containsKey(key)){mapper.get(key).add(new FieldPair(bean, field, key));}else{List<FieldPair> fieldPairList = new ArrayList<>();fieldPairList.add(new FieldPair(bean, field, key));mapper.put(key, fieldPairList);}}}} catch (Exception e) {e.printStackTrace();System.exit(-1);}}}public static class FieldPair {private static PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}",":", true);private Object bean;private Field field;private String value;public FieldPair(Object bean, Field field, String value) {this.bean = bean;this.field = field;this.value = value;}public void updateValue(Environment environment) {boolean access = field.isAccessible();if (!access) {field.setAccessible(true);}try {if (field.getType() == String.class) {String updateVal = environment.getProperty(value);field.set(bean, updateVal);}else if (field.getType() == Integer.class) {Integer updateVal = environment.getProperty(value,Integer.class);field.set(bean, updateVal);}else if (field.getType() == int.class) {int updateVal = environment.getProperty(value,int.class);field.set(bean, updateVal);}else if (field.getType() == Boolean.class) {Boolean updateVal = environment.getProperty(value,Boolean.class);field.set(bean, updateVal);}else if (field.getType() == boolean.class) {boolean updateVal = environment.getProperty(value,boolean.class);field.set(bean, updateVal);}else {String updateVal = environment.getProperty(value);field.set(bean, JSONObject.parseObject(updateVal, field.getType()));}} catch (IllegalAccessException e) {e.printStackTrace();}field.setAccessible(access);}public Object getBean() {return bean;}public void setBean(Object bean) {this.bean = bean;}public Field getField() {return field;}public void setField(Field field) {this.field = field;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}}public static class ConfigUpdateEvent extends ApplicationEvent {String key;public ConfigUpdateEvent(Object source, String key) {super(source);this.key = key;}}@EventListenerpublic void updateConfig(ConfigUpdateEvent configUpdateEvent) {if(mapper.containsKey(configUpdateEvent.key)){List<FieldPair> fieldPairList = mapper.get(configUpdateEvent.key);if(fieldPairList.size()>0){for (FieldPair fieldPair:fieldPairList) {fieldPair.updateValue(environment);}}}}}转载自cid:link_0Group/Topic/JAVA/18012
  • [技术干货] 如何使用Spring Validation实现数据校验功能
    背景Spring 框架,广泛应用于 JAVA 企业级开发中,包含了一套实用的字段校验机制: Spring Validation。这个机制融合了 JSR380 规范,即 Bean Validation 2.0。本文将介绍 Spring Validation 的使用方法,包括基础注解的应用以及进阶使用技巧。常用注解Bean Validation 2.0 注解校验空值@Null:验证对象是否为 null@NotNull:验证对象是否不为 null@NotEmpty:验证对象不为 null,且长度(数组、集合、字符串等)大于 0@NotBlank:验证字符串不为 null,且去除两端空白字符后长度大于 0校验大小@Size(min=, max=):验证对象(数组、集合、字符串等)长度是否在给定的范围之内@Min(value):验证数值(整数或浮点数)是否大于等于指定的最小值@Max(value):验证数值是否小于等于指定的最大值校验布尔值@AssertTrue:验证 Boolean 对象是否为 true@AssertFalse:验证 Boolean 对象是否为 false校验日期和时间@Past:验证 Date 和 Calendar 对象是否在当前时间之前@Future:验证 Date 和 Calendar 对象是否在当前时间之后@PastOrPresent:验证日期是否是过去或现在的时间@FutureOrPresent:验证日期是否是现在或将来的时间正则表达式@Pattern(regexp=, flags=):验证 String 对象是否符合正则表达式的规则Hibernate Validation 拓展@Length(min=, max=):验证字符串的大小是否在指定的范围内@Range(min=, max=):验证数值是否在合适的范围内@UniqueElements:校验集合中的值是否唯一,依赖于 equals 方法@ScriptAssert:利用脚本进行校验@Valid 和 @Validated这两个注解是校验的入口,作用相似但用法上存在差异。  codeduidaima.com@Validated// 用于类/接口/枚举,方法以及参数@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Validated {// 校验时启动的分组Class<?>[] value() default {};}@Valid// 用于方法,字段,构造函数,参数,以及泛型类型@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Documentedpublic @interface Valid {// 未提供其他属性}作用范围不同:@Validated 无法作用在于字段, @Valid 无法作用于类;注解中的属性不同:@Validated 中提供了指定校验分组的属性,而 @Valid 没有这个功能,因为 @Valid 不能进行分组校验。字段校验场景及使用示例常见的校验场景有三种: Controller 层的校验、编程式校验、 Dubbo 接口校验。Controller层 的校验使用方式当方法入参为 @RequestBody 注解的 JavaBean,可在入参前使用 @Validated 或 @Valid 注解开启校验。 codeduidaima.com@PostMapping("/save")public Response<Boolean> saveNotice(@Validated @RequestBody NoticeDTO noticeDTO) {// noticeDTO中各字段校验通过,才会执行后续业务逻辑return Response.ok(true);}当方法入参为 @PathVariable、 @RequestParam 注解的简单参数时,需要在 Controller 加上 @Validated 注解开启校验。 codeduidaima.com@RequestMapping("/notice")@RestController// 必须加上该注解@Validatedpublic class UserController {// 路径变量@GetMapping("{id}")public Reponse<NoticeDTO> detail(@PathVariable("id") @Min(1L) Long noticeId) {// 参数noticeId校验通过,执行后续业务逻辑return Reponse.ok();}// 请求参数@GetMapping("getByTitle")public Result getByTitle(@RequestParam("title") @Length(min = 1, max = 20) String title) {// 参数title校验通过,执行后续业务逻辑return Result.ok();}}原理Spring 框架中的 HandlerMethodArgumentResolver 策略接口,负责将方法参数解析为特定请求中的参数值。 codeduidaima.compublic interface HandlerMethodArgumentResolver {// 判断当前解析器是否支持给定的方法参数boolean supportsParameter(MethodParameter var1);// 堆代码 duidaima.com@Nullable// 实际解析参数的方法Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;}上述接口针对 @RequestBody 的实现类 RequestResponseBodyMethodProcessor 中,存在字段校验逻辑,调用 validateIfApplicable 方法校验参数。// RequestResponseBodyMethodProcessorpublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 前置处理// 校验逻辑if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);if (arg != null) {//调用校验函数this.validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}// 数据绑定逻辑}// 返回处理结果return this.adaptArgumentIfNecessary(arg, parameter);}validateIfApplicable 方法中,根据方法参数上的注解,决定是否进行字段校验:当存在 @Validated 或以 Valid 开头的注解时,进行校验。 codeduidaima.comprotected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {// 获取参数上的注解Annotation[] annotations = parameter.getParameterAnnotations();Annotation[] var4 = annotations;int var5 = annotations.length;// 遍历注解for(int var6 = 0; var6 < var5; ++var6) {Annotation ann = var4[var6];// 获取 @Validated 注解Validated validatedAnn = (Validated)AnnotationUtils.getAnnotation(ann, Validated.class);// 或者注解以 Valid 开头if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {// 开启校验Object hints = validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann);Object[] validationHints = hints instanceof Object[] ? (Object[])((Object[])hints) : new Object[]{hints};binder.validate(validationHints);break;}}}@PathVariable 和 @RequestParam 对应的实现类中,则没有相应字段校验逻辑,因此需要在 Controller 上使用 @Validated,开启字段校验。编程式校验1.配置 Validator codeduidaima.com@Configurationpublic class ValidatorConfiguration {@Beanpublic Validator validator() {ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class).configure()// 设置是否开启快速失败模式//.failFast(true).buildValidatorFactory();return validatorFactory.getValidator();}}2.获取 validator 并校验 codeduidaima.compublic class TestValidator {// 注入验证器@Resourceprivate javax.validation.Validator validator;public String testMethod(TestRequest request) {// 进行校验,获取校验结果Set<ConstraintViolation<TestRequest>> constraintViolations = validator.validate(request);// 组装校验信息并返回return res;}}Dubbo 接口校验可在 @DubboService 注解中,设置 validation 参数为 true,开启生产者的字段验证。 codeduidaima.com@DubboService(version = "1.0.0", validation="true")public class DubboApiImpl implements DubboApi {....}该方式返回的信息对使用者不友好,可通过 Dubbo 的 filter 自定义校验逻辑和返回信息。需要注意的是,在 Dubbo 中有自己的 IOC 实现来控制容器,因此需提供 setter 方法,供 Dubbo 调用。 codeduidaima.com@Activate(group = {"provider"},value = {"customValidationFilter"},order = 10000)@Slf4jpublic class CustomValidationFilter implements Filter {private javax.validation.Validator validator;// duubo会调用setter获取beanpublic void setValidator(javax.validation.Validator validator) {this.validator = validator;}public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {if (this.validator != null && !invocation.getMethodName().startsWith("$")) {// 补充字段校验,返回信息的组装以及异常处理}return invoker.invoke(invocation);}}进阶使用分组校验对于同一个 DTO, 不同场景下对其校验规则可能不同, @Validted 支持按照分组分别验证,示例代码如下:1.校验注解的 groups 属性中添加分组 codeduidaima.com@Datapublic class NoticeDTO {@Min(value = 0L, groups = Update.class)private Long id;@NotNull(groups = {Save.class, Update.class})@Length(min = 2, max = 10, groups = {Save.class, Update.class})private String title;// 保存的时候校验分组public interface Save {}// 更新的时候校验分组public interface Update {}}2.@Validted 上指定分组 codeduidaima.com@PostMapping("/save")public Response<Boolean> saveNotice(@RequestBody @Validated(NoticeDTO.Save.class) NoticeDTO noticeDTO) {// 分组为Save.class的校验通过,执行后续逻辑return Response.ok();}@PostMapping("/update")public Response<Boolean> updateNotice(@RequestBody @Validated(NoticeDTO.Update.class) NoticeDTO noticeDTO) {// 分组为Update.class的校验通过,执行后续逻辑return Response.ok();}自定义校验注解如果我们想自定义实现一些验证逻辑,可以使用自定义注解,主要包括两部分:实现自定义注解,实现对应的校验器 validator。下面尝试实现一个注解,用于校验集合中的指定属性是否存在重复,代码如下:实现校验注解,主要需要包含 message()、 filed()、 groups() 三个方法,功能如注释所示。 codeduidaima.com@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documented// 指定校验器@Constraint(validatedBy = UniqueValidator.class)public @interface Unique {// 用于自定义验证信息String message() default "字段存在重复";// 指定集合中的待校验字段String[] field();// 指定分组Class<?>[] groups() default {};}实现对应的校验器,主要校验逻辑在 isValid 方法:获取集合中指定字段,并组装为 set,比较 set 和集合的长度,以判断集合中指定字段是否存在重复。 codeduidaima.com// 实现ConstraintValidator<T, R>接口,T为注解的类型,R为注解的字段类型public class UniqueValidator implements ConstraintValidator<Unique, Collection<?>> {private Unique unique;@Overridepublic void initialize(Unique constraintAnnotation) {this.unique = constraintAnnotation;}@Overridepublic boolean isValid(Collection collection, ConstraintValidatorContext constraintValidatorContext) {// 集合为空直接校验通过if (collection == null || collection.size() == 0) {return Boolean.TRUE;}// 从集合中获取filed中指定的待校验字段,看是否存在重复return Arrays.stream(unique.field()).filter(fieldName -> fieldName != null && !"".equals(fieldName.trim())).allMatch(fieldName -> {// 收集集合collection中字段为fieldName的值,存入set并计算set的元素个数countint count = (int) collection.stream().filter(Objects::nonNull).map(item -> {Class<?> clazz = item.getClass();Field field;try {field = clazz.getField(fieldName);field.setAccessible(true);return field.get(item);} catch (Exception e) {return null;}}).collect(Collectors.collectingAndThen(Collectors.toSet(), Set::size));// set中元素个数count与集合长度比较,若不相等则说明collection中字段存在重复,校验不通过if (count != collection.size()) {return false;}return true;});}}总结通过本文我们得以了解 Spring Validation 的机理及其在实际项目中的应用。无论是标准的校验注解,还是自定义的校验逻辑, Spring Validation 都为开发者提供了高效且强大的校验工具。总的来说, Spring Validation 是任何 Spring 应用不可或缺的一部分,对于追求高质量代码的 JAVA 开发者而言,掌握其用法和最佳实践至关重要。转载自cid:link_0Group/Topic/JAVA/18022
  • [技术干货] 前端如何发送date类型的参数给后端
    @DateTimeFormat第一次:Get方式传参-成功 这个时候是用的get请求方式,get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应。/** * http://localhost:8080/intoParam?date=2023-01-18 11:11:11*/ @RequestMapping(value = "/intoParam",method = RequestMethod.GET) @ResponseBody public void intoParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date){ System.out.println(date);//Fri Jan 18 08:00:00 CST 2023 }第二次:Post方式传参-失败/** * http://localhost:8080/intoParam * 请求体 * { * "date":"2023-01-18 11:11:11" * } */ @RequestMapping(value = "/intoParam",method = RequestMethod.POST) @ResponseBody public void intoParam2(@RequestBody DateVo dateVo){ System.out.println(dateVo.getDate());//Fri Jan 18 08:00:00 CST 2023 }@Data@AllArgsConstructor@NoArgsConstructorpublic class DateVo { @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") private Date date;}错误信息{ "timestamp": "2023-10-19T07:05:22.407+0000", "status": 400, "error": "Bad Request", "message": "JSON parse error: Cannot deserialize value of type `java.util.Date` from String \"2023-01-18 11:11:11\": not a valid representation (error: Failed to parse Date value '2023-01-18 11:11:11': Cannot parse date \"2023-01-18 11:11:11\": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String \"2023-01-18 11:11:11\": not a valid representation (error: Failed to parse Date value '2023-01-18 11:11:11': Cannot parse date \"2023-01-18 11:11:11\": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null))\n at [Source: (PushbackInputStream); line: 2, column: 12] (through reference chain: com.mye.hl20springbootdataparam.vo.DateVo[\"date\"])", "path": "/intoParam"}第三次:post传参-成功/** * http://localhost:8080/intoParam * 請求體是: * { * "date":"2023-01-18" * } */ @RequestMapping(value = "/intoParam",method = RequestMethod.POST) @ResponseBody public void intoParam2(@RequestBody DateVo dateVo){ System.out.println(dateVo.getDate());//Fri Jan 18 08:00:00 CST 2023 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String format = sdf.format(dateVo.getDate()); System.out.println(format);//2023-01-18 }@Data@AllArgsConstructor@NoArgsConstructorpublic class DateVo { @DateTimeFormat(pattern="yyyy-MM-dd") private Date date;}原因springboot默认采用jackson,而jackson只能识别以下几种日期格式"yyyy-MM-dd'T'HH:mm:ss.SSSZ";"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";"yyyy-MM-dd"; "EEE, dd MMM yyyy HH:mm:ss zzz"; long类型的时间戳(毫秒时间戳)解决方法采用long时间戳,如:1537191968000或者在传参的对象上加上@JsonFormat注解并且指定时区。@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")第四次:post传参-成功/** * http://localhost:8080/intoParam * 請求體是: * { * "date":"2019-01-18 11:11:11" * } */ @RequestMapping(value = "/intoParam",method = RequestMethod.POST) @ResponseBody public void intoParam2(@RequestBody DateVo dateVo){ System.out.println(dateVo.getDate());//Fri Jan 18 11:11:11 CST 2019 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String format = sdf.format(dateVo.getDate()); System.out.println(format);//2019-01-18 11:11:11 }@Data@AllArgsConstructor@NoArgsConstructorpublic class DateVo { @DateTimeFormat(pattern="yyyy-MM-dd") @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date date;}@jsonFormat@JsonFormat(pattern=“yyyy-MM-dd”,timezone = “GMT+8”)pattern:是你需要转换的时间日期的格式timezone:是时间设置为东八区,避免时间在转换中有误差@JsonFormat注解可以在属性的上方,同样可以在属性对应的get方法上,两种方式没有区别展示结果{ "date": "2023-10-19 15:44:51"}总结 前端Content-Type 为application/json的请求时,我们使用@JsonFormat来进行转化,如果为表单,则应该使用@DateTimeFormat。转载自https://www.duidaima.com/Group/Topic/JAVA/18030
  • [技术干货] cpu和内存的查看命令
    查看CPU相关命令#限制某个线程的cpu使用率sudo cpulimit -p pid -l 50ps -eo %cpu,args | grep -m1 PROCESS | awk '{print $1}'#将当前进程按照memory和cpu排序ps aux --sort=%mem,%cpu#按照cpu使用率排序ps -e -o pcpu,cpu,nice,state,cputime,args --sort pcpu | sed "/^ 0.0 /d"#查看当前系统的物理cpu个数grep "processor" /proc/cpuinfo | wc -l grep -c -e '^cpu[0-9]\+' /proc/stat#查看当前cpu型号grep "model name" /proc/cpuinfo#查看当前cpu信息cat /proc/cpuinfo#查看当前系统的位数grep -q '\<lm\>' /proc/cpuinfo && echo 64 bits || echo 32 bitsgetconf LONG_BIT | grep '64'java -version#查看当前系统的cpu频率awk -F": " '/cpu MHz\ */ { print "Processor (or core) running speed is: " $2 }' /proc/cpuinfo ; dmidecode | awk -F": " '/Current Speed/ { print "Processor real speed is: " $2 }'#查看每个cpu每个进程的cpu使用率ps ax -L -o pid,tid,psr,pcpu,args | sort -nr -k4| head -15 | cut -c 1-90#查看当前中断cat /proc/interrupts#查看多个处理器的使用率相关信息mpstat –P ALL 1#每个物理CPU中Core的个数:cat /proc/cpuinfo | grep "cpu cores" | uniq | awk -F: '{print $2}'#是否为超线程?#如果有两个逻辑CPU具有相同的”core id”,那么超线程是打开的。#每个物理CPU中逻辑CPU(可能是core, threads或both)的个数:cat /proc/cpuinfo | grep "siblings"#/proc/stat 文件中有一行记录的机器从启动依赖,各个中断序号发生中断的次数。#这一行以intr开头,接下来的第一个数字是总的中断数目,之后就是分别的中断数目,从0开始。cat /proc/stat | grep intr查找文件相关命令#按照目录大小排序战士最前面15个目录或者文件du -xB M --max-depth=2 /var | sort -rn | head -n 15#列出当前所有子目录的文件大小du -h --max-depth=1#列出当前文件或者目录最大的10个du -s * | sort -n | tail#按照目录大小从大到小排序du -b --max-depth 1 | sort -nr | perl -pe 's{([0-9]+)}{sprintf "%.1f%s", $1>=2**30? ($1/2**30, "G"): $1>=2**20? ($1/2**20, "M"): $1>=2**10? ($1/2**10, "K"): ($1, "")}e'#列出path这个目录的文件树du -h /path | sort -h#每隔60s监控对应目录的文件大小变化watch -n60 du /var/log/messages#递归删除当前目录下所有子目录中的.svn目录find . -type d -name '.svn' -print0 | xargs -0 rm -rdf#列出当前磁盘的使用情况df -P | column -t#监控磁盘的使用情况watch -d -n 5 df#列出当前inode的使用情况df -i <partition>#按照每个磁盘使用量从高到低排序df -h | grep -v ^none | ( read header ; echo "$header" ; sort -rn -k 5)#查看物理磁盘的使用情况df -x tmpfs | grep -vE "(gvfs|procbususb|rootfs)"#查看当前所有磁盘的大小和使用量df -H#查看所有分区使用情况fdisk -l /dev/sda# 显示系统所有的分区或给定的分区fdisk -l # 显示时,显示的是扇区数不是柱面数 fdisk -u # 显示指定partition的block数 fdisk -s partition #查看磁盘的读写容量iostat -m -d /dev/sda1#测试磁盘的读写速度hdparm -t /dev/sda#查看某个文件的所有链接find -L / -samefile /path/to/file -exec ls -ld {} +#查看最大的5个文件find . -type f -exec ls -s {} \; | sort -n -r | head -5#查看365天前的文件并删除find ./ -type f -mtime +365 -exec rm -f {} \;#查看大于100M的文件find . -type f -size +100M
  • [技术干货] 几种常见工具的功能对比和应用场景
    Flume是一种可靠、可扩展、分布式的日志收集、聚合和传输系统。它主要用于以下几个应用场景:1、日志收集:Flume能够从多个源头(例如服务器、应用程序、设备等)收集大量的日志数据,并将其中转到集中式存储系统(如Hadoop HDFS)或消息队列系统(如Kafka)中。这种集中式收集机制可以帮助企业对日志数据进行集中管理和分析。2、数据聚合:Flume可以将多个源头的数据聚合到一起,并将其传输到统一的目标系统。例如,在分布式计算中,可以使用Flume将不同节点上的数据聚合并传输到计算节点或结果节点。3、流式数据传输:Flume支持实时的、可靠的数据传输,因此被广泛用于构建数据流水线,以实现流数据的实时处理和分析。4、网络日志分析:Flume可以用于抓取并分析网络设备、服务器和应用程序产生的日志数据。通过配置适当的源和目标,Flume能够将日志数据从各个设备中收集、存储并进行分析。DataX CDC(Change Data Capture)的主要应用场景有以下几个:1、数据同步:将源数据库中的数据变更(增、删、改)实时同步到目标数据库,确保两个数据库中的数据保持一致。2、数据仓库加载:将源数据库中的增量数据加载到数据仓库或数据湖中,用于数据分析、报表生成等业务需求。3、数据备份与恢复:通过记录数据变更,实时备份源数据库中的增量数据,以防止数据库故障或数据丢失,同时可以快速恢复到指定的时间点。4、实时数据分析:将源数据库中的增量数据实时传输给大数据平台,用于实时数据分析、实时监控等业务场景。5、数据集成和ETL:将多个数据源中的数据变更集成到一个目标数据库中,实现数据的统一管理和分析。需要注意的是,DataX CDC是一款开源数据同步工具,能够实现增量数据的抽取和传输,但并不负责处理数据转换和清洗的任务。Flink CDC(Change Data Capture)的主要应用场景有以下几个:1、实时数据分析:Flink CDC可以将源数据库中的增量数据实时传输到Flink流处理引擎中,使得实时数据分析和实时计算成为可能。通过实时处理增量数据,可以进行实时指标计算、实时报表生成、实时业务监控等。2、数据协同与集成:Flink CDC可以将多个不同数据源的增量数据集成到一个目标系统中,实现不同数据源之间的数据协同和集成。比如将多个数据库中的数据变更实时同步到数据仓库或数据湖,以实现统一的数据分析和报表生成。3、数据流转与传输:Flink CDC可以将源数据库中的增量数据转换成数据流,并实时传输到指定的目标位置。这在数据实时传输和数据交换场景中非常有用,比如将数据传输到消息队列、实时推送数据给其他系统等。4、实时数据湖建设:通过Flink CDC将源数据库中的增量数据实时写入数据湖中,可以建立起一个实时的数据湖,进而支持实时分析、实时机器学习等高价值数据应用。5、实时数据缓存和缓冲:Flink CDC可以实时捕获和缓冲源数据库中的增量数据,并加快其它系统对数据的访问速度,减少对数据库的直接查询和压力。Kettle(也称为Pentaho Data Integration)是一个开源的ETL工具,主要用于数据抽取(Extract)、转换(Transform)和加载(Load)操作。下面是Kettle主要的应用场景:1、数据同步:Kettle可以将不同数据源(如关系型数据库、文件、Web服务等)中的数据进行抽取和同步,实现数据的一致性和更新。2、数据仓库加载:Kettle可以将数据从各种数据源加载到数据仓库(如数据仓库、数据湖等),并进行数据清洗、转换和映射操作,以支持后续的分析和报告需求。3、实时数据分析:Kettle可以实时抽取和转换数据,将其加载到实时数据分析平台中,实现实时监控、实时分析和实时决策。4、数据质量管理:Kettle提供了各种数据清洗和校验的功能,可以帮助用户识别和修复数据质量问题,提高数据的准确性和完整性。  以下是相关同步工具的功能对比:转载自https://www.studyjava.cn/post/2029
  • [技术干货] Kettle 的工作原理和优势
    Kettle,也被称为Pentaho Data Integration(PDI),是一款开源的ETL工具,用于提取、转换和加载数据。它的工作原理可以简单概括为以下几个步骤:1、设计和配置:在Kettle的图形化界面中,通过拖拽和连接各个组件,设计数据转换和加载的流程。每个组件代表不同的任务,如数据抽取、数据清洗、数据转换、数据加载等。为每个组件配置相应的参数,定义数据输入输出的连接和转换规则。2、读取数据:Kettle支持多种数据来源,如关系型数据库、文件、Web服务等。根据配置的数据源和查询条件,Kettle会读取数据源中的数据。3、数据转换和清洗:通过在转换和清洗组件中定义规则和函数,对读取的数据进行处理和转换。可以进行各种操作,如过滤、排序、合并、聚合、计算等,以满足数据处理的需求。4、加载数据:将经过转换和清洗的数据加载到目标数据源中。可以是关系型数据库、文件、数据仓库等。Kettle提供了各种输出组件,用于将数据写入到目标数据源中。5、执行和调度:完成数据处理流程的配置后,可以手动执行该流程。也可以使用Kettle内置的调度器,按照预定的时间和频率自动运行任务。总的来说,Kettle通过可视化的方式,串联和配置多个数据处理和转换组件,实现数据的提取、转换和加载。它具有强大的扩展性和灵活性,能够满足各种数据集成和处理的需求。优势:1、强大的可视化设计和配置:Kettle提供了一个易于使用的图形化界面,使用户可以轻松设计和配置ETL工作流程,而无需编写复杂的代码。2、多种数据源和目标的支持:Kettle可以与各种数据源(例如数据库、文件、Web服务等)以及数据目标(例如数据库、文件、Data Warehouse等)进行无缝集成,方便数据的抽取和加载。3、强大的数据转换和清洗功能:Kettle提供了丰富的数据转换和清洗步骤,如字段映射、数据过滤、数据合并、数据拆分、数据排序等,可以灵活地处理和转换数据。4、插件扩展机制:Kettle支持插件机制,用户可以通过自定义插件扩展Kettle的功能,满足特定需求。5、支持任务调度和并行处理:Kettle可以根据需求进行任务调度和并行处理,提高ETL工作的效率和可靠性。劣势:1、学习曲线较陡峭:尽管Kettle提供了图形化界面,但对于没有经验的用户来说,学习和掌握Kettle的各种功能和操作仍然需要一定的时间和精力。2、对大规模数据处理的限制:由于Kettle是基于Java开发的,对于大规模数据的处理和性能可能存在一些限制,特别是在并行处理和集群环境下。3、对于复杂数据处理场景的限制:尽管Kettle提供了丰富的数据转换和清洗步骤,但在处理复杂数据处理场景时,可能需要编写自定义的脚本或插件来实现。Kettle 支持的实时采集数据源类型:转载自https://www.studyjava.cn/post/2029
  • [技术干货] DataX CDC的工作原理和优势
    DataX CDC基于DataX框架,为用户提供了一种灵活、高效的数据同步解决方案。它通过监视源数据库的事务日志或数据库增量日志来捕获源数据库中的变更操作,并将这些操作应用于目标数据库,以保持两者之间的数据同步。这种增量方式可以大大减少数据传输的时间和成本,并提供更及时的数据更新。DataX CDC 是基于 CDC(Change Data Capture)技术实现的数据同步工具,其工作原理如下:1、数据源监控:DataX CDC 首先会监控数据源(如 Oracle 数据库)的事务日志。事务日志是数据库记录每个操作(如插入、更新、删除)的日志文件。2、数据解析:一旦有新的事务日志生成,DataX CDC 会解析事务日志,提取出新增、更新和删除的数据。3、数据同步:经过解析后,DataX CDC 将提取到的数据进行转换和转发,将其同步到目标数据源(如 HDFS、MySQL 等)。4、数据应用:目标数据源接收到同步的数据后,可以被应用程序直接使用或者进行进一步的处理和分析。具体举例来说,DataX CDC 监控 Oracle 事务日志的步骤如下:1、配置数据库连接:首先,需要在 DataX CDC 中配置 Oracle 数据库的连接信息,包括数据库地址、用户名、密码等。2、开启 CDC 日志模式:在 Oracle 数据库中,需要将数据库的日志模式设置为 CDC 日志模式。这可以通过在 Oracle 数据库中执行相应的 SQL 命令来完成,例如执行 ALTER DATABASE ADD SUPPLEMENTAL LOG DATA 命令。3、配置 Change Data Capture:在 DataX CDC 中,需要配置相应的 Change Data Capture 模块,以便能够捕获 Oracle 数据库中的变更事件。这包括指定表的 CDC 规则、列的映射关系等。4、监控事务日志:一旦配置完成,DataX CDC 将自动监控 Oracle 数据库的事务日志,捕获其中的数据变更事件。5、数据同步:捕获到的数据变更事件将会被传递给目标系统或者工具,以完成数据同步操作。注意,在使用 DataX CDC 监控 Oracle 事务日志时,需要确保数据库的参数正确配置,并且有足够的权限执行相关的 SQL 命令。此外,由于监控事务日志可能会带来额外的性能开销,因此需要根据实际情况进行调整和优化。优势:1、实时同步:DataX CDC 可以实时监控和同步数据源的变更,能够及时将数据更新到目标数据源,提供高实时性的数据同步。2、高性能:DataX CDC 使用 CDC 技术,只同步变更数据,避免了全量数据的传输和处理,能够提供高效的数据同步性能。3、精确同步:DataX CDC 可以精确捕获和同步数据源的每一次变更操作,保证了数据的一致性和准确性。4、灵活配置:DataX CDC 提供了灵活的配置选项,可以根据具体需求选择要同步的数据源、目标数据源等,并支持多种数据源类型。劣势:1、复杂性:DataX CDC 的配置和使用相对复杂,需要熟悉 CDC 技术和相关的配置知识。2、依赖数据库事务日志:DataX CDC 的工作原理是基于数据库事务日志的,因此需要确保数据源开启了事务日志功能,并且保证事务日志的稳定性和完整性。3、对数据库性能有影响:在进行实时同步的过程中,DataX CDC 需要读取和解析数据库的事务日志,可能会对源数据库的性能产生一定影响。DataX CDC 支持的实时采集数据源类型:转载自https://www.studyjava.cn/post/2029
  • [技术干货] Flink CDC的工作原理和优势
    Flink CDC通过与数据库进行交互,实时捕获数据库中的变更操作。它的工作原理可以分为以下几个步骤:1. 数据库连接和监控:首先,Flink CDC需要与目标数据库建立连接,并监控数据库的变更操作。它可以通过监听数据库的事务日志或者使用数据库引擎的内部机制来实现。2. 变更事件解析:一旦数据库发生变更操作,Flink CDC会解析这些变更事件。它会将变更事件转化为对应的数据结构,例如INSERT、UPDATE或DELETE操作。3. 数据转换和格式化:解析后的变更事件需要经过数据转换和格式化,以便能够被Flink进行处理。Flink CDC会将变更事件转化为Flink支持的数据格式,例如JSON、Avro等。4. 事件流生成:经过转换和格式化后,Flink CDC会将变更事件转化为数据流。这个数据流可以被Flink的流处理任务进行消费和处理。5. 数据同步和传输:生成的数据流可以被传输到不同的目的地,例如Flink的流处理任务、消息队列或者其他外部系统。这样,我们就可以对变更事件进行实时分析和处理。需要注意的是,Flink CDC并非直接支持所有数据库。它的可用性取决于数据库本身是否提供了事务日志的访问接口。目前,Flink CDC支持的数据库包括MySQL、PostgreSQL、Oracle等。优势:1. 实时性:Flink CDC能够实时捕获数据库中的变更操作,并将其转化为实时的数据流。这使得我们能够及时地对数据库中的数据变动进行响应和处理。2. 精确性:Flink CDC通过解析数据库的事务日志或者内部机制,能够准确地捕获数据库中的变更操作。这保证了数据的准确性和一致性。3. 可靠性:Flink CDC能够处理数据库中的变更操作,并将其转化为可靠的数据流。它具有容错机制,能够处理故障和数据丢失的情况。4. 扩展性:Flink CDC支持水平扩展,能够处理大规模的数据变更。它可以与Flink的流处理任务无缝集成,实现高效的数据处理和分析。劣势:1.对于非关系型数据库(如MongoDB、HBase等)的支持相对较弱。Flink CDC主要面向关系型数据库,对于非关系型数据库的支持相对有限。2.对于大规模数据变更的处理可能存在延迟。由于Flink CDC通过读取事务日志来捕获数据变化,如果有大量的数据变更发生,可能会造成读取和处理的延迟。3.需要访问数据库的主从部署。为了保持数据一致性,Flink CDC需要访问数据库的主库以读取事务日志,这可能会对数据库的性能产生一定影响,尤其是在高并发的情况下。4.需要数据库的binlog开启。Flink CDC依赖数据库的binlog来捕获数据变化,如果数据库的binlog没有开启,就无法正常使用Flink CDC。需要注意的是,这些劣势并不是Flink CDC本身的问题,而是基于流式数据捕获的一般限制。如果你的应用场景不适合流式数据捕获,可能需要考虑其他的数据同步方案。Flink CDC 支持的实时采集数据源类型:转载自https://www.studyjava.cn/post/2029
  • [技术干货] Flume的工作原理和优势
    Flume的原理是将数据从源头(source)采集,经过一个或多个中间节点(channel)进行传输,最终发送到目的地(sink)。下面是Flume的工作原理的详细解释:1、Source(数据源):Source负责从数据源(如日志文件、网络流、消息队列等)采集数据,并将数据发送到Channel。2、Channel(通道):Channel是数据传输的中间节点,它负责暂存Source采集到的数据。Channel可以是内存、磁盘或者其他存储介质,可以按照事务或批量的方式传输数据。3、Sink(数据目的地):Sink从Channel中获取数据,并将数据发送到指定的目的地(如HDFS、关系型数据库、消息队列等)进行存储和分析。4、Agent(代理):Agent是Flume的一个独立运行实例,它由Source、Channel和Sink组成,负责管理整个数据流的采集、传输和存储。Flume的工作流程如下:1、Source采集数据,将数据发送到Channel。2、Channel将数据暂存,并等待Sink的消费。3、Sink从Channel中获取数据,发送到目的地进行存储和分析。Flume的可靠性和容错性体现在以下几个方面:1、消息确认:Channel会对消息进行确认,确保数据在传输过程中不会丢失。2、事务机制:Flume使用事务机制来确保数据的可靠传输,如果发送失败,会进行回滚和重试操作。3、失败处理:Flume提供了失败处理机制,可以配置重试策略、错误日志记录等来处理发送失败的情况。总的来说,Flume通过源头采集数据,经过中间节点传输,最后发送到目的地实现数据的收集和传输。通过可靠的机制和丰富的配置选项,可以保证数据的安全和可靠传输。优势:1、可靠性高:Flume 采用了可靠性机制,包括数据重传、事件推送确认机制等,确保数据不会丢失。2、扩展性强:Flume 可以通过添加多个代理节点实现横向扩展,使其可以处理大规模的数据流。3、灵活性:Flume 可以根据不同的需求进行配置,支持多种收集器、聚合器和传输器,可以满足不同场景的需求。4、实时数据传输:Flume 支持实时数据传输,可以以流式方式处理和传输数据,适用于需要实时数据处理和分析的场景。5、分布式流处理:Flume 支持分布式架构,可以将数据流分发到多个节点上进行并行处理和传输。劣势:1、复杂性:Flume 的配置和部署相对复杂,需要一定的技术功底和经验。2、依赖性:Flume 依赖于其他组件,比如 Hadoop、HBase 等,如果没有这些组件的支持,应用可能会受到限制。3、部署和维护成本:由于 Flume 的复杂性和依赖性,部署和维护的成本可能较高。Flume支持的实时采集数据源类型:转载自https://www.studyjava.cn/post/2029
总条数:696 到第
上滑加载中