Spring框架基础知识(02)
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个属性分别表示“初始化连接数”和“最大连接数”,应该是数值类型的,在类中声明属性时,就可以使用int
或Integer
类型:
@Value("${project.jdbc.initialSize}")
private int initialSize;
@Value("${project.jdbc.maxTotal}")
private int maxTotal;
- 1
- 2
- 3
- 4
当然,必须保证类型的转换是可以成功的,例如数字5
既可以转换为String
,又可以是int
或Integer
,所以,声明以上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
的,所有属性都是static
、final
的,在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
- 点赞
- 收藏
- 关注作者
评论(0)