Spring框架基础知识(02)

举报
海拥 发表于 2021/09/28 17:25:39 2021/09/28
【摘要】 🌊 作者主页:海拥🌊 简介:🏆CSDN全栈领域优质创作者、🥇HDZ核心组成员、🥈蝉联C站周榜前十 9. 由Spring管理的对象的生命周期如果需要管理Bean的生命周期,可以在对应的类中自定义生命周期的初始化方法和销毁方法,关于这2个方法的声明:应该使用public权限;使用void表示返回值类型;方法名称可以自定义;参数列表为空。例如:package cn.tedu.spring...

🌊 作者主页:海拥
🌊 简介:🏆CSDN全栈领域优质创作者、🥇HDZ核心组成员、🥈蝉联C站周榜前十

9. 由Spring管理的对象的生命周期

如果需要管理Bean的生命周期,可以在对应的类中自定义生命周期的初始化方法和销毁方法,关于这2个方法的声明:

  • 应该使用public权限;
  • 使用void表示返回值类型;
  • 方法名称可以自定义;
  • 参数列表为空。

例如:

package cn.tedu.spring;

public class User {
	
	public User() {
		System.out.println("User.User()");
	}
	
	public void init() {
		System.out.println("User.init()");
	}
	
	public void destroy() {
		System.out.println("User.destroy()");
	}

}

在配置Spring管理对象的@Bean注解中,配置注解参数,以指定以上2个方法分别是初始化方法和销毁方法:

package cn.tedu.spring;

@Configuration
public class BeanFactory {

	@Bean(initMethod = "init", destroyMethod = "destroy")
	public User user() {
		return new User();
	}
	
}

最终,可以看到:

  • 初始化方法会在构造方法之后执行,且只执行1次;
  • 销毁方法会在Spring容器被销毁之前执行,且只执行1次。

10. 使用组件扫描使得Spring管理类的对象

首先,自定义某个类(类名、包名均没有要求),在类的声明之前添加@ComponentScan注解,该注解用于配置组件扫描,注解的参数是String类型的,表示“被扫描的根包”:

package cn.tedu.spring;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan("cn.tedu.spring")
public class SpringConfig {

}

在组件扫描的包下创建类,该类的声明之前需要添加@Component注解,以表示这个类是一个“组件类”,后续,当Spring扫描时,会自动创建所有组件类的对象:

package cn.tedu.spring;

import org.springframework.stereotype.Component;

@Component
public class User {

}

当完成以后配置后,后续,程序执行时,只要加载了SpringConfig类,由于类之前配置了组件扫描,Spring框架就会扫描对应的包下所有的类,并逐一检查是否为“组件类”,如果是,则创建对象,如果不是,则不创建!

使用@ComponentScan时,配置的是需要扫描的“根包”,假设需要扫描的是cn.tedu.spring,在配置时,配置为cn.tedu甚至配置为cn都是可用的,但是,强烈不推荐使用过于简单的设置,避免出现扫描范围过多而导致的浪费资源!

另外,在@ComponentScan注解的源代码中:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

	@AliasFor("basePackages")
	String[] value() default {};

	@AliasFor("value")
	String[] basePackages() default {};
    
}

可以看出,配置的值可以是String[],也就是可以指定多个包名。

在使用这种做法时,必须保证被Spring管理的对象所归属的类存在无参数构造方法!

在使用这种做法时,Spring创建对象后,默认会使用以下原则作为Bean的名称:

  • 如果类名的第1个字母是大写的,第2个字母是小写的(不关心其它字母的大小写),则会把类名的第1个字母改为小写,其它不变,作为Bean的名称,例如类名是User时,Bean的名称就是user,类名是UserDao时,Bean的名称就是userDao
  • 如果不满足以上条件,则类名就是Bean的名称。

如果希望使用自定义的名称作为Bean的名称,可以在@Component注解中配置参数,例如:

package cn.tedu.spring;

import org.springframework.stereotype.Component;

@Component("uuu")
public class User {

}

则后续调用getBean()方法时,就必须使用"uuu"作为参数来获取对象!

在Spring框架的作用范围内,除了@Component以外,另外还有3个注解,可以起到完全等效的效果:

  • @Controller:通常添加在控制器类的声明之前;
  • @Service:通常添加在业务类的声明之前;
  • @Repository:通常添加在持久层的类(负责数据的持久化管理)的声明之前。

也就是说,这4种注解作用、用法完全相同,只是语义不同。

目前,已经介绍了2种使得Spring框架管理类的对象的做法:

  • 自定义方法返回某个对象,并在方法的声明之前添加@Bean注解;
  • 将类放在组件扫描的包或其子孙包中,并在类的声明之前添加@Component/@Controller/@Service/@Repository注解。

以上的第1种做法是万能的,适用于任何条件,但是,在设计代码时相对麻烦,管理起来相对不便利;而第2种做法就更加简单、直观,却只适用于自定义的类。

所以,只要是自行编写的类,都应该采取第2种做法,如果需要Spring管理其它类(JDK中的,或某框架中的)的对象,只能使用第1种做法!

11. 使用Spring读取.properties文件

假设在项目的src/main/resources下存在jdbc.properties文件,其内容是:

url=jdbc:mysql://localhost:3306/db_name
driver=com.mysql.jdbc.Driver

然后,在项目中,自定义某个类,在这个类中,声明对应数量的属性,这些属性的值将会是以上配置信息的值!

public class JdbcProperties {
    private String url;
    private String driver;
    // 生成以上2个属性的Getters & Setters
}

当需要读取以上jdbc.properties配置文件时,需要在以上类的声明之前添加@PropertySource注解,并配置需要读取的文件的位置:

// 以下注解的参数是配置文件的名称
@PropertySource("jdbc.properties")
public class JdbcProperties {
    private String url;
    private String driver;
    // 生成以上2个属性的Getters & Setters
}

接下来,就可以把读取到的值赋值给类中的2个属性,可以通过@Value注解来实现:

// 以下注解的参数是配置文件的名称
@PropertySource("jdbc.properties")
public class JdbcProperties {
    @Value("${url}") // 在注解参数的大括号的值,是jdbc.properties配置中等于号左侧的名称
    private String url;
    @Value("${driver}")
    private String driver;
    // 生成以上2个属性的Getters & Setters
}

最后,整个的读取过程是由Spring框架来完成的,所以,以上JdbcProperties类还应该被Spring框架所管理,可以采取组件扫描的做法,则创建SpringConfig类,用于指定组件扫描的包:

// 以下注解参数配置的就是组件扫描的包,同时,请保证JdbcProperties类是在这个包或其子孙包中的
@ComponentScan("cn.tedu.spring")
public class SpringConfig {
}

然后,在JdbcProperties类的声明之前,补充添加@Component注解,使得Spring框架扫描到这个类时,能明确的知道“这个类是组件类”,从而创建该类的对象:

@Component
// 以下注解的参数是配置文件的名称
@PropertySource("jdbc.properties")
public class JdbcProperties {
    @Value("${url}") // 在注解参数的大括号的值,是jdbc.properties配置中等于号左侧的名称
    private String url;
    @Value("${driver}")
    private String driver;
    // 生成以上2个属性的Getters & Setters
}

全部完成后,可以自定义某个类,用于测试运行:

package cn.tedu.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringTests {
	
	public static void main(String[] args) {
		// 1. 加载配置类,得到Spring容器
		AnnotationConfigApplicationContext ac 
			= new AnnotationConfigApplicationContext(SpringConfig.class);

		// 2. 从Spring容器中获取对象
		JdbcProperties jdbcProperties
			= (JdbcProperties) ac.getBean("jdbcProperties");
		
		// 3. 测试
		System.out.println(jdbcProperties.getUrl());
		System.out.println(jdbcProperties.getDriver());

		// 4. 关闭
		ac.close();
	}

}

注意:在类似于jdbc.properties这样的配置文件中,如果某个属性的名称是username,且最终项目是在Windows操作系统的平台上运行时,读取到的值将是“当前登录Windows系统的系统用户名称”,而不是jdbc.properties文件中配置的属性值!所以,一般推荐在编写jdbc.properties这类配置文件时,各属性之前最好都添加一些特有的前缀,使得属性名一定不与某些关键名称发生冲突,例如:

project.jdbc.url=jdbc:mysql://localhost:3399/db_name
project.jdbc.driver=com.mysql.jdbc.Driver
project.jdbc.username=root
project.jdbc.password=1234

并且,在使用@Value注解时,也配置为以上各个等于号左侧的完整名称:

@Component
@PropertySource("jdbc.properties")
public class JdbcProperties {

	@Value("${project.jdbc.url}")
	private String url;
	@Value("${project.jdbc.driver}")
	private String driver;
	@Value("${project.jdbc.username}")
	private String username;
	@Value("${project.jdbc.password}")
	private String password;
    
    // Getters & Setters
    
}

最后,使用Spring框架时,如果属性的值是由Spring框架进行赋值的,Spring框架会自动的处理数据类型的转换,所以,在声明属性时,声明为所期望的类型即可,例如,在配置文件中存在:

project.jdbc.initialSize=5
project.jdbc.maxTotal=20

这2个属性分别表示“初始化连接数”和“最大连接数”,应该是数值类型的,在类中声明属性时,就可以使用intInteger类型:

@Value("${project.jdbc.initialSize}")
private int initialSize;
@Value("${project.jdbc.maxTotal}")
private int maxTotal;

当然,必须保证类型的转换是可以成功的,例如数字5既可以转换为String,又可以是intInteger,所以,声明以上initialSize时,这几个数据类型都是可用的,根据使用需求进行选取即可!

另外,还有另一种做法读取**.properties**类型的文件,就是使用@Autowired注解为Environment类型的属性自动赋值:

@Component
@PropertySource("jdbc.properties")
public class JdbcProperties {
	
	@Autowired
	private Environment environment;

	public Environment getEnvironment() {
		return environment;
	}

	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

}

最终,测试运行:

package cn.tedu.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringTests {
	
	public static void main(String[] args) {
		// 1. 加载配置类,得到Spring容器
		AnnotationConfigApplicationContext ac 
			= new AnnotationConfigApplicationContext(SpringConfig.class);

		// 2. 从Spring容器中获取对象
		JdbcProperties jdbcProperties
			= (JdbcProperties) ac.getBean("jdbcProperties");
		
		// 3. 测试
		System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.url"));
		System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.driver"));
		System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.username"));
		System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.password"));
		System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.initialSize"));
		System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.maxTotal"));

		// 4. 关闭
		ac.close();
	}

}

可以看到,使用这种做法时,Spring框架会把读取到的所有配置信息都封装到了Environment类型的对象中,当需要获取某个配置值时,调用Environment对象的getProperty()方法再获取,同时,getProperty()方法返回的是String类型的数据,如果希望的数据类型不是String,则需要开发人员自行转换类型!

一般,还是推荐使用@Value注解逐一读取各配置值,使用起来更加灵活一些!

抽象类与接口的区别

1. 共同点

都可以包含抽象方法;

2. 区别

  • 抽象类是一种“类”,是使用class作为关键字来声明的;而接口是另一种数据,是使用interface作为关键字来声明的;
  • 抽象类中可以有各种权限不同、修饰符不同的属性,也可以包含普通方法、抽象方法,或者完全没有普通方法,或者完全没有抽象方法;而接口中的所有成员都是public的,所有属性都是staticfinal的,在JDK 1.8之前,所有的方法都是抽象的;
  • 普通的类与抽象类的关系是“继承”的关系,当普通的类继承了抽象类后,就有义务重写抽象类中的抽象方法,在Java语句中,类之间的继承是1对1的关系;普通的类与接口的关系是”实现“的关系,当普通的类实现了接口后,也有义务重写接口中的所有抽象方法,类与接口的实现关系是1对多的,即1个类可以同时实现若干个接口;接口与接口之间也可以存在继承关系,且是1对多的关系,即某1个接口可以同时继承若干个接口;

3. 使用心得 / 装

类,是描述”类别“的;接口,是描述形为模式、行为特征、规范、标准的!

类与类之间是is a的关系;类与接口之间是has a的关系。

public class Person { public String name; }
public class Student extends Person {}
public class Teacher extends Person {}

public class Animal { }
public class Cat extends Animal {}

public interface 学习 { void 学习(某参数); }
public interface 授课 {}
public interface 驾驶 { void 驾驶(某参数); }
public class Person implements 学习, 授课, 驾驶 {}

Person 张三 = new Person();
Person 李四 = new Person();

附1:Eclipse常用快捷键

Ctrl + Shift + F 格式化代码(代码排版)
Ctrl + Shift + O 整理import语句(增加所必须的,删除不必要的)
Alt + 方向上/方向下 移动单行代码,操作之前需要将光标定位在那一行;移动若干行代码,操作之前需要先选中
Ctrl + Alt + 方向上/方向下 向上/向下复制若干行代码,操作模式与移动整行代码相同
Alt + Shift + R 在当前源文件中,对某个变量、方法重命名,操作之前需先选中整个名称
Ctrl + D 删除整行或若干行代码,操作模式与移动整行代码相同

🌊 行业资料:精品PPT模板几千套,简历模板一千多套
🌊 面试题库:Java核心知识点大全和面试真题资料
🌊 学习资料:2300套PHP建站源码,微信小程序入门资料,Python全集(400集)
🌊 学习交流群:点击此处进入

公众号【海拥】内回复【资源】获取以上所有资料

我已经写了很长一段时间的技术博客,这是我的一篇Spring框架基础知识(02)教程。我乐于通过文章分享技术与快乐。您可以访问我的博客主页: 华为云-海拥、我的个人博客:haiyong.site 以了解更多信息。希望你们会喜欢!

💌 欢迎大家在评论区提出意见和建议!💌

如果你真的从这篇文章中学到了一些新东西,喜欢它,收藏它并与你的小伙伴分享。🤗最后,不要忘了❤或📑支持一下哦。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。