Spring框架基础知识(02)

举报
海拥 发表于 2021/08/05 01:07:23 2021/08/05
【摘要】 9. 由Spring管理的对象的生命周期 如果需要管理Bean的生命周期,可以在对应的类中自定义生命周期的初始化方法和销毁方法,关于这2个方法的声明: 应该使用public权限;使用void表示返回值类型;方法名称可以自定义;参数列表为空。 例如: package cn.tedu.spring; public class User { public User(...

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()");
	}

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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

package cn.tedu.spring;

@Configuration
public class BeanFactory {

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最终,可以看到:

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

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

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

package cn.tedu.spring;

import org.springframework.context.annotation.ComponentScan;

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

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

package cn.tedu.spring;

import org.springframework.stereotype.Component;

@Component
public class User {

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当完成以后配置后,后续,程序执行时,只要加载了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 {}; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以看出,配置的值可以是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 {

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

则后续调用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

  
 
  • 1
  • 2

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

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

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

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

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

  
 
  • 1
  • 2
  • 3
  • 4

然后,在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
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

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();
	}

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

注意:在类似于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

  
 
  • 1
  • 2
  • 3
  • 4

并且,在使用@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 }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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

  
 
  • 1
  • 2

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

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

  
 
  • 1
  • 2
  • 3
  • 4

当然,必须保证类型的转换是可以成功的,例如数字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;
	}

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

最终,测试运行:

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();
	}

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

可以看到,使用这种做法时,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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

附1:Eclipse常用快捷键

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

文章来源: haiyong.blog.csdn.net,作者:海拥✘,版权归原作者所有,如需转载,请联系作者。

原文链接:haiyong.blog.csdn.net/article/details/106269637

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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