1. 演示一下 Spring 管理类的模式
下面代码在第一次打印 Person 对象的时候,pid = 0,name = null,
第二次打印的时候,pid 有值,name 依旧是 null
第三次打印的时候,pid 和 name 都有值
而 pid 和 name 是由不同的两个类赋值的,说明这两个类被注入了同一个 Bean,由此可知 Spring 管理对象采用单例模式
一般在 Java 中,表过程的对象总是以单例的方式出现,表数据的对象无法使用单例管理,所以,一般让 Spring 管理的对象大多是表过程的对象(不是绝对的)
@Component public class Person { int pid; String name; @Override public String toString() { return "Person{" + "pid=" + pid + ", name='" + name + '\'' + '}'; } }
@Component public class Person1 { @Autowired public void usePerson1(Person person) { System.out.println("usePerson1(), person = " + person); person.pid = 20221024; } }
@Component public class Person2 { @Autowired public void usePerson2(Person person) { System.out.println("usePerson2(), person = " + person); person.name = "hsq"; } }
@SpringBootApplication public class DemoApplication { public static void main(String[] args) throws Exception { ApplicationContext context = SpringApplication.run(DemoApplication.class, args); Person bean = context.getBean(Person.class); System.out.println(bean); } }
2. 用户登录案例
没有用到 Web 的形式,仅仅只是从控制台输入输出,然后将数据存入数据库
2.1 准备的对象和其功能
在 IOC 之外的场景下,Bean 注入的注释标签就要有一些区分了
2.1.1 User
表示用户本身(属性,构造方法,toString)
public class User { public Integer uid; public String username; public String password; public User() {} public User(String username, String password) { this.username = username; this.password = password; } public User(Integer uid, String username, String password) { this.uid = uid; this.username = username; this.password = password; } @Override public String toString() { return "User{" + "uid=" + uid + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(uid, user.uid); } @Override public int hashCode() { return Objects.hash(uid); } }
2.1.2 UserController
进行用户输入、输出的操作(不同于 Web 的模式,这里的输入输出通过 Scanner(System.in) 和 System.out 控制)
@Controller public class UserController { // 依赖输入、输出,我们自己不强调一定是标准输入输出 private final Scanner scanner; private final PrintWriter writer; private final UserService userService; // 全部让 Spring 帮我们注入 @Autowired public UserController(Scanner scanner, PrintWriter writer, UserService userService) { this.scanner = scanner; this.writer = writer; this.userService = userService; } public void run() throws Exception { while (true) { writer.print("请选择是注册还是登录:"); writer.flush(); String func = scanner.nextLine(); if (func.equals("注册")) { writer.println("您选择了【注册】功能,接下来请输入用户名和密码"); writer.print("请输入用户: "); writer.flush(); String username = scanner.nextLine(); writer.print("请输入密码: "); writer.flush(); String password = scanner.nextLine(); User user = userService.register(username, password); writer.println("注册完成,您的用户信息是: " + user); } else if (func.equals("登录")) { writer.println("您选择了【登录】功能,接下来请输入用户名和密码"); writer.print("请输入用户: "); writer.flush(); String username = scanner.nextLine(); writer.print("请输入密码: "); writer.flush(); String password = scanner.nextLine(); User user = userService.login(username, password); if (user == null) { writer.println("登录失败"); } else { writer.println("登录成功,您的用户信息是: " + user); } } } } }
2.1.3 UserService
进行必要的数据操作,进行密码的加密和解密\
@Service public class UserService { // UserService 对象依赖 UserDao 对象 // 直接使用注入的方式 private final UserDao userDao; // 构造方法注入 @Autowired public UserService(UserDao userDao) { this.userDao = userDao; } public User register(String username, String password) throws Exception { // 1. 密码的 hash 加密 String salt = BCrypt.gensalt(); password = BCrypt.hashpw(password, salt); // 2. 进行插入 User user = new User(username, password); userDao.insert(user); // 3. 返回构造完成的用户对象 return user; } public User login(String username, String password) throws Exception { // 1. 先查询用户 User user = userDao.selectOneByUsername(username); if (user == null) { return null; } // 2. 进行密码的比较 if (!BCrypt.checkpw(password, user.password)) { return null; } // 3. 返回构造完成的用户对象 return user; } }
上面代码中用到的 BCrypt 算法是一种密码加密算法,代码篇幅过长,有需要的大佬可以去自行下载 —— BCrypt 加密算法下载
2.1.4 UserDao
对数据库进行操作
@Repository public class UserDao { private static final Logger log = LoggerFactory.getLogger(UserDao.class); // 依赖 DataSource 对象才能完成 // 需要 Spring 帮我们注入 DataSource 对象 private final DataSource dataSource; // 使用构造方法注入(依赖注入) @Autowired public UserDao(DataSource dataSource) { this.dataSource = dataSource; } public void insert(User user) throws Exception { try (Connection c = dataSource.getConnection()) { String sql = "insert into users (username, password) values (?, ?)"; try (PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, user.username); ps.setString(2, user.password); log.info("执行的 SQL = {} ", ps); ps.executeUpdate(); try (ResultSet rs = ps.getGeneratedKeys()) { rs.next(); user.uid = rs.getInt(1); } } } } public User selectOneByUsername(String username) throws Exception { try (Connection c = dataSource.getConnection()) { String sql = "select uid, username, password from users where username = ?"; try (PreparedStatement ps = c.prepareStatement(sql)) { ps.setString(1, username); log.info("执行的 SQL = {} ", ps); try (ResultSet rs = ps.executeQuery()) { if (!rs.next()) { return null; } return new User( rs.getInt("uid"), rs.getString("username"), rs.getString("password") ); } } } } }
2.1.5 AppConfig
因为案例只是我自己写的,所以前面代码中所注入的 Bean,我需要自行注册进 Spring,这里采用方法注册。并且在这里配置数据库
@Configuration public class AppConfig { @Bean public Scanner scanner() { return new Scanner(System.in); } @Bean public PrintWriter writer() { // PrintStream -> PrintWriter PrintStream printStream = System.out; return new PrintWriter(printStream, true); } @Bean public DataSource dataSource() { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"); dataSource.setUser(" "); // 数据库用户名 dataSource.setPassword(" "); // 数据库密码 return dataSource; } }
2.1.6 DemoApplication
主类,程序的入口,调用 UserController 中的 run() 方法开始程序
@SpringBootApplication public class DemoApplication { public static void main(String[] args) throws Exception { ApplicationContext context = SpringApplication.run(DemoApplication.class, args); UserController userController = context.getBean(UserController.class); userController.run(); } }
2.2 各对象之间的依赖关系
2.3 IOC 带来的一个好处
我们去依赖抽象的概念,不需要具体的实现
好处是,我们可以很方便的替换背后的依赖对象。比如:刚才我们依赖的 Scanner 对象是从标准输入读取的,我们可以很方便的替换成读取文件的方式
只需修改 AppConfig 对象中的 Scanner 方法为以下即可
public Scanner scanner() throws FileNotFoundException { // return new Scanner(System.in); File file = new File("input.txt"); return new Scanner(file, "UTF-8"); }
3. Spring 中的日志打印
3.1 Spring 官方提供的方式
先得到 log 对象,再调用其中方法打印日志即可
private static final Logger log = LoggerFactory.getLogger(UserDao.class); log.info("执行的 SQL = {} ", ps);
3.2 lombok 提供的方法
使用 @Slf4j 注释修饰此类,然后使用 log 调用方法打印即可,前提是必须要有 lombok 插件
3.2 日志打印级别
比当前日志级别低的打印方式不会有结果
如:当前日志级别为 info 时,那么用 debug 打印不会有结果
3.3 修改当前日志级别
默认日志级别是 info ,我们可以在配置文件中修改当前日志级别
在配置文件中写入如下代码,即可将 com.hsq.demo 包下的日志文件修改为 debug
logging.level.com.hsq.demo=debug