SpringBoot2--一些关键知识点

整理一下重要的前置知识。

1 SpringBoot的注解

1.1 Java的注解

内置注解

@Override
@Deprecated
@SuppressWarnings

主要是给编译器用的,好像用处也不是很大。

元注解

就是注解的注解

@Target
@Retention
@Documented
@Inherited
@Repeatable (Java 8+)
@NonNull

自定义注解

AnnotationProcessor.java

import java.lang.reflect.Method;

public class AnnotationProcessor {
    public static void processAnnotations(Object obj) {
        Class<?> clazz = obj.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Loggable.class)) {
                Loggable loggable = method.getAnnotation(Loggable.class);
                String description = loggable.value().isEmpty()
                    ? method.getName() : loggable.value();
                Loggable.LogLevel level = loggable.level();
                boolean trackTime = loggable.trackTime();

                System.out.printf("[%s] 开始执行: %s%n", level, description);
                long startTime = System.currentTimeMillis();

                try {
                    method.invoke(obj);
                } catch (Exception e) {
                    System.err.printf("[ERROR] 方法执行失败: %s%n", e.getCause());
                }

                if (trackTime) {
                    long duration = System.currentTimeMillis() - startTime;
                    System.out.printf("[%s] 执行完成, 耗时: %dms%n", level, duration);
                } else {
                    System.out.printf("[%s] 执行完成%n", level);
                }
            }
        }
    }

    public static void main(String[] args) {
        BusinessService service = new BusinessService();
        processAnnotations(service);
    }
}

BusinessService.java

public class BusinessService {
    @Loggable("执行用户查询操作")
    public void queryUsers() {
        System.out.println("查询用户数据...");
    }

    @Loggable(level = Loggable.LogLevel.WARN, trackTime = true)  // 修正这里
    public void processOrder() {
        System.out.println("处理订单中...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Loggable(value = "危险操作", level = Loggable.LogLevel.ERROR)  // 修正这里
    public void dangerousOperation() {
        System.out.println("执行危险操作...");
    }
}

 Loggable.java

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
    String value() default "";
    LogLevel level() default LogLevel.INFO;
    boolean trackTime() default false;

    // 将枚举定义在注解内部
    enum LogLevel {
        DEBUG, INFO, WARN, ERROR
    }
}

编译:

javac Loggable.java BusinessService.java AnnotationProcessor.java

运行:

hp@DESKTOP-430500P:~/test$ java AnnotationProcessor
[INFO] 开始执行: 执行用户查询操作
查询用户数据...
[INFO] 执行完成
[WARN] 开始执行: processOrder
处理订单中...
[WARN] 执行完成, 耗时: 1000ms
[ERROR] 开始执行: 危险操作
执行危险操作...
[ERROR] 执行完成

这里可以看出,一个自定义注解的流程。

1 在@interface中声明要处理的注解。这里要用@Target和@Retentio做限定。

2 在AnnotationProcessor的processAnnotations中处理注解的具体流程。

3 在要使用的地方增加注解,类似这样@Loggable。。。

简化流程:

// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
    String value() default "";
}

// 使用注解
class Service {
    @LogExecution("耗时监控")
    public void process() { /*...*/ }
}

// 处理注解(需手动通过反射)
Method method = Service.class.getMethod("process");
if (method.isAnnotationPresent(LogExecution.class)) {
    LogExecution anno = method.getAnnotation(LogExecution.class);
    System.out.println("注解值: " + anno.value());
}

最后,感觉Java的注解和python的装饰器真的挺类似的。

# 定义装饰器
def log_execution(desc=""):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"开始执行: {desc}")
            result = func(*args, **kwargs)
            print("执行结束")
            return result
        return wrapper
    return decorator

# 使用装饰器(直接生效)
@log_execution("耗时监控")
def process():
    print("正在处理...")

# 调用时自动执行装饰逻辑
process()

从使用上来,唯一的区别就是Java需要手动处理反射,在那个位置处理注解的流程。Python是直接在装饰器里面。

特性Java 注解Python 装饰器
本质元数据(附加信息)高阶函数(可执行代码)
运行时行为被动(需要反射/APT处理)主动(直接修改函数/类行为)
实现方式通过 @interface 定义通过普通函数实现
作用阶段编译时/运行时代码加载时立即执行

1.2 Srpingboot的注解

类型常用注解
启动入口@SpringBootApplication
定义组件@Component, @Service, @Repository
配置管理@Configuration, @Bean, @Value
Web 控制器@RestController, @RequestMapping, @GetMapping, @RequestBody
调度与异步@Scheduled, @Async, @EnableScheduling
测试注解@SpringBootTest, @WebMvcTest

2 Bean

通过上面的注解,可以将类直接包装成Bean。

这个Bean主要用在一个服务中使用另一个类的服务,其实不用也可以。

// EmailService 实现
public class EmailService {
    public void sendEmail(String to, String content) {
        System.out.println("Sending email to " + to + ": " + content);
        // 实际发送邮件的逻辑
    }
}

// UserService 实现
public class UserService {
    private EmailService emailService;
    
    // 需要手动创建依赖
    public UserService() {
        this.emailService = new EmailService();
    }
    
    public void registerUser(String username) {
        System.out.println("Registering user: " + username);
        emailService.sendEmail(username, "Welcome to our platform!");
    }
}

// 使用方式
public class Application {
    public static void main(String[] args) {
        // 需要手动创建和管理所有对象
        UserService userService = new UserService();
        userService.registerUser("test@example.com");
    }
}

使用

// EmailService 作为 Bean
@Component
public class EmailService {
    public void sendEmail(String to, String content) {
        System.out.println("Sending email to " + to + ": " + content);
    }
}

// UserService 作为 Bean 并自动注入 EmailService
@Service
public class UserService {
    private final EmailService emailService;
    
    // 通过构造器自动注入
    @Autowired
    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }
    
    public void registerUser(String username) {
        System.out.println("Registering user: " + username);
        emailService.sendEmail(username, "Welcome to our platform!");
    }
}

// 配置类
@Configuration
@ComponentScan
public class AppConfig {
}

// 使用方式
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.registerUser("test@example.com");
    }
}

在类的前面加一个@Component/@Service/@Controller/@Repository...就可以标注为bean。

@Autowired实现自动注入。

看起来就是将一些服务类集中管理了,需要的时候直接用单例方式传入。不用再自己去new了。。。

3 面向切面编程AOP

一个例子,请求拦截器。

public class AuthInterceptor implements HandlerInterceptor {
    
    // 在Controller方法执行前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (!validateToken(token)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "无效的token");
            return false; // 中断请求
        }
        return true; // 继续执行
    }
    
    // 在Controller方法执行后,视图渲染前调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        // 可以修改ModelAndView
    }
    
    // 在整个请求完成后调用
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 可以进行资源清理
    }
}

在这里注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/api/**") // 拦截路径
                .excludePathPatterns("/api/login"); // 排除路径
    }
}

可以在这个切面上针对具体场景进行控制,就是访问api接口时,/api/**。大概是这个意思吧。

当然,除此之外的场景还有很多,比如日志,性能,事务等等。

4 DAO

也就是(Data Access Object)。说真的,看了下感觉这个感觉没啥特别的。

有个接口:

public interface UserDao {
    // 添加用户
    void addUser(User user);
}

有个实现:

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserDaoImpl implements UserDao {
    // 假设使用JDBC连接
    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/mydb", 
            "username", 
            "password");
    }

    @Override
    public void addUser(User user) {
        String sql = "INSERT INTO users(username, email) VALUES(?, ?)";
        try (Connection conn = getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, user.getUsername());
            ps.setString(2, user.getEmail());
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

用的时候直接:

public class Main {
    public static void main(String[] args) {
        UserDao userDao = new UserDaoImpl();
        
        // 添加用户
        User newUser = new User();
        newUser.setUsername("john_doe");
        newUser.setEmail("john@example.com");
        userDao.addUser(newUser);
}

就是将数据库操作做了一个封装。然后就说是DAO。。。Are you kidding me?

5 事务

就是@Transactional关键字,这个是spring框架所提供。在springboot中做了增强。

例子

@Service
public class BankService {
    
    @Autowired
    private AccountRepository accountRepository;
    
    // 最简单的转账事务方法
    @Transactional
    public void transferMoney(Long fromId, Long toId, Double amount) {
        // 转出账户扣款
        accountRepository.updateBalance(fromId, -amount);
        
        // 转入账户加款
        accountRepository.updateBalance(toId, amount);
    }
}

增加Transactional注解后,如果第二个update失败,第一个update也会跟着回滚。

WebFlux

 更高性能的API处理架构。。。这个有时间再看吧,估计一下也用不到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值