写点什么

Spring 框架基础知识 (02)

  • 2021 年 12 月 29 日
  • 本文字数:5437 字

    阅读完需:约 18 分钟

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

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


  • 应该使用public权限;

  • 使用void表示返回值类型;

  • 方法名称可以自定义;

  • 参数列表为空。


例如:


package cn.haiyong.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.haiyong.spring;
@Configurationpublic class BeanFactory {
@Bean(initMethod = "init", destroyMethod = "destroy") public User user() { return new User(); } }
复制代码


最终,可以看到:


  • 初始化方法会在构造方法之后执行,且只执行 1 次;

  • 销毁方法会在 Spring 容器被销毁之前执行,且只执行 1 次。

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

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


package cn.haiyong.spring;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("cn.haiyong.spring")public class SpringConfig {
}
复制代码


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


package cn.haiyong.spring;
import org.springframework.stereotype.Component;
@Componentpublic class User {
}
复制代码


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


使用@ComponentScan时,配置的是需要扫描的“根包”,假设需要扫描的是cn.haiyong.spring,在配置时,配置为cn.haiyong甚至配置为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.haiyong.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_namedriver=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.haiyong.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.haiyong.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_nameproject.jdbc.driver=com.mysql.jdbc.Driverproject.jdbc.username=rootproject.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=5project.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.haiyong.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 常用快捷键


发布于: 刚刚
用户头像

公众号:海拥 2021.11.29 加入

【个人网站】haiyong.site 【软件技能】Java,Python,JS 【兴趣爱好】学习使我快乐,编程令我永生 【个人称号】HDZ核心组成员,CSDN原力作者

评论

发布
暂无评论
Spring框架基础知识(02)