一、入门案例
1.导入依赖:
org.springframework
spring-context
5.1.5.RELEASE
2.编写beans.xml
3.使用
public class UserController {
public static void main(String[] args) {
//获取核心容器
//ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");//更常用
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\config\\beans.xml");
//根据id获取Bean对象
//方式一
UserService userService = (UserService) applicationContext.getBean("userService");
//方式二
UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
//com.hcx.service.impl.UserSerivceImpl@3e6fa38a
System.out.println(userService);
//com.hcx.dao.impl.UserDaoImpl@66a3ffec
System.out.println(userDao);
// UserService userService = (UserService) BeanFactory.getBean("userService");
// userService.saveUser();
}
}
二、重要类详解
1.BeanFactory
在构建核心容器时,创建对象采取的策略是延迟加载。即根据id获取对象时才创建对象。
适用场景:多例对象
public class UserController {
public static void main(String[] args) {
Resource resource = new ClassPathResource("beans.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
UserService userService = (UserService) beanFactory.getBean("userService");
System.out.println(userService);
}
}
2.ApplicationContext
在构建核心容器时,创建对象采取的是立即加载的方式。即一读取完配置文件就马上创建对象。
适用场景:单例对象
public class UserController {
public static void main(String[] args) {
//获取核心容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// ApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\config\\beans.xml");
//根据id获取Bean对象
//方式一
UserService userService = (UserService) applicationContext.getBean("userService");
//方式二
UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
//com.hcx.service.impl.UserSerivceImpl@3e6fa38a
System.out.println(userService);
//com.hcx.dao.impl.UserDaoImpl@66a3ffec
System.out.println(userDao);
}
}
ApplicationContext的三个常用实现类:
①ClassPathXmlApplicationContext
可以加载类路径下的配置文件,要求配置文件必须在类路径下,否则无法加载。
②FileSystemXmlApplicationContext
可以加载磁盘任意路径下的配置文件(须有访问权限)
③AnnotationConfigApplicationContext
用于读取注解创建的容器
三、Bean详解
1.创建Bean对象的三种方式
①方式一:采用默认构造函数
spring配置文件中使用bean标签,仅配置id和class属性(没有其他属性和标签),此时采用默认构造函数创建对象,如果类中没有默认构造函数,则对象无法创建。
②方式二:使用普通工厂中的方法创建对象
即使用某个类中的方法创建对象,并存入spring容器
当使用一些外部类时,如引入的jar中的类,都是字节码文件。
InstanceFactory:
/**
* 模拟工厂类(该类可能存在于jar包中,无法修改代码提供默认构造函数)
*/
public class InstanceFactory {
public UserService getUserService(){
return new UserSerivceImpl();
}
}
此时要获取的是InstanceFactory 类中的getUserService返回值
③方式三:使用工厂中的静态方法创建对象
使用某个类中的静态方法,并存入spring容器
StaticFactory:
public class StaticFactory {
public static UserService getUserService(){
return new UserSerivceImpl();
}
}
xml配置:
④注解方式:
作用和xml配置的
标签功能一样
@Component
:把当前类对象存入spring容器中
属性value用于指定bean的id,不写时默认为类名首字母小写
@Controller
:一般用于表现层
@Service
:一般用于业务层
@Repository
:一般用于持久层
使用注解的方式,需要加入context命名空间,并且要配置告诉spring使用注解的方式,需要spring去扫描对应的包,配置如下:
2.bean对象的作用范围
①xml配置:
默认情况spring创建的bean都是单例的。
bean的作用范围通过bean标签的scope属性可以指定:
取值:
- singleton:单例(默认值)
- prototype:多例
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,等同于session
②注解配置:
@Scope:
指定bean的作用范围
属性value:指定范围的取值,常用取值:singleton
、prototype
3.bean对象的生命周期
①单例对象
生命周期跟容器的相同,让其创建时对象创建,只要容器还在,对象一直存活,容器销毁,对象死亡。
通过init-method
和destory-method
属性指定
UserSerivceImpl:
public class UserSerivceImpl implements UserService {
public UserSerivceImpl() {
System.out.println("对象创建了");
}
public void saveUser() {
System.out.println("service实现类被调用了");
// userDao.saveUser();
}
public void init() {
System.out.println("对象初始化");
}
public void destory() {
System.out.println("对象销毁");
}
}
xml:
调用:
public class UserController {
public static void main(String[] args) {
//获取核心容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据id获取Bean对象
//方式一
UserService userService = (UserService) applicationContext.getBean("userService");
//方式二
// UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
userService.saveUser();
//手动关闭容器
applicationContext.close();
}
}
结果:
对象创建了
对象初始化
service实现类被调用了
对象销毁
②多例对象
出生:使用对象时spring框架创建
存活:对象只要在使用则一直存活
死亡:当对象长时间不用且没有其他对象引用时,由垃圾回收器回收
即使手动关闭容器,对象也不会销毁
打印结果:
对象创建了
对象初始化
service实现类被调用了
③使用注解指定生命周期
作用与bean标签中使用init-method
和destroy-method
作用相同
PostConstruct:
用于指定初始化方法
PreDestroy:
用于指定销毁方法
UserSerivceImpl :
@Service("userService")
public class UserSerivceImpl implements UserService {
@Resource(name = "userDao1")
private UserDao userDao;
@PostConstruct
public void init(){
System.out.println("初始化方法");
}
@PreDestroy
public void destroy(){
System.out.println("销毁方法");
}
public void saveUser() {
userDao.saveUser();
}
}
调用:
public class UserController {
@Autowired
private UserService userService;
public static void main(String[] args) {
//获取核心容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据id获取Bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
userService.saveUser();
//手动关闭才会调用销毁方法
applicationContext.close();
}
}
打印结果:
初始化方法
保存用户1
销毁方法
四、Spring的依赖注入
依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现
程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。
ioc 解耦只是降低他们的依赖关系,但不会消除。
例如:我们的业务层仍会调用持久层的方法。
这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。
简单的说,就是框架把持久层对象传入业务层,而不用我们自己去获取。
依赖关系的管理:交由spring来维护
当前类需要用到其他类对象时,由spring为我们提供,我们只需要在配置文件中说明。
能注入的数据类型:
- 基本类型和String
- 其他bean类型(在配置文件或者注解配置过的bean)
- 复杂类型(集合类型)
注入的方式:
- 使用构造器
- 使用set方法
- 使用注解
注意:经常变化的一些数据不适用于注入的方式
1.构造函数注入
在bean标签中使用标签constructor-arg
,属性:
- type:指定要注入的数据的数据类型,该数据类型是构造函数中某个或某些参数的数据类型
- index:指定给构造函数中指定索引位置的参数赋值。(索引从0开始)
- name:指定给构造函数中指定名称的参数赋值
- value:赋的值是基本数据类型和 String 类型
- ref:赋的值是其他 bean 类型(必须是在配置文件中配置过的 bean)
注意:
type
、index
、name
用于指定给构造函数中的哪个参数赋值
UserSerivceImpl:
public class UserSerivceImpl implements UserService {
private String name;
private Integer age;
private Date birthday;
public UserSerivceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void saveUser() {
System.out.println("service实现类被调用了:"+name+","+age+","+birthday);
}
}
beans.xml
使用:
public class UserController {
public static void main(String[] args) {
//获取核心容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据id获取Bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
userService.saveUser();
}
}
结果:
service实现类被调用了:极多人小红,24,Wed Dec 11 20:09:09 CST 2019
此种方式的优缺点:
优点:在获取bean对象时,注入数据是必须的操作,否则无法创建对象(如果同时提供无参构造器则没问题)
缺点:改变了bean对象的实例化方式,使得在创建对象时,即使不使用这些数据也必须提供。
2.set 方法注入
通过配置文件给 bean 中的属性传值:使用 set 方法的方式,
property标签:
- name:找的是类中 set 方法后面的部分(并不是属性的名称)
- ref:给属性赋值是其他 bean 类型的
- value:给属性赋值是基本数据类型和 string 类型的
UserSerivceImpl:
public class UserSerivceImpl implements UserService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void saveUser() {
System.out.println("service实现类被调用了:"+name+","+age+","+birthday);
}
}
beans.xml:
优点:创建对象时没有明确的限制,可以直接使用默认构造函数
缺点:如果某个成员必须有值,则获取对象时有可能set方法没有执行。
复杂类型注入
用于给List结构注入的标签包括:list
、array
、set
用于给Map结构集合注入的标签包括:map
、properties
即结构相同的数据类型,标签都通用。
UserSerivceImpl:
public class UserSerivceImpl implements UserService {
private String[] strArrs;
private List strList;
private Set strSet;
private Map strMap;
private Properties properties;
public void setStrArrs(String[] strArrs) {
this.strArrs = strArrs;
}
public void setStrList(List strList) {
this.strList = strList;
}
public void setStrSet(Set strSet) {
this.strSet = strSet;
}
public void setStrMap(Map strMap) {
this.strMap = strMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void saveUser() {
System.out.println("service实现类被调用了");
System.out.println(Arrays.toString(strArrs));
System.out.println(strList);
System.out.println(strSet);
System.out.println(strMap);
System.out.println(properties);
}
}
beans.xml:
arr1
arr2
arr3
list1
list2
list3
set1
set2
set3
properties1
properties2
调用:
public class UserController {
public static void main(String[] args) {
//获取核心容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据id获取Bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
userService.saveUser();
}
}
打印结果:
3.注解注入
@Autowired
:按照类型注入
只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就能注入成功。
如果容器中没有bean的类型和要注入的变量匹配,报错。
如果容器中有多个类型匹配时,报错。
注意:当有多个类型都匹配上时,会再次按照名称匹配,如果相同了,也能匹配上。
即注入时的类的名称private UserDao userDao2;
userDao2与声明时的注解@Repository("userDao2")
匹配上了。
@Qualifier
:按照类型注入的基础之上再按照名称注入。
(给方法参数注入时可以单独使用,给类成员注入时则不能单独使用,必须配合@Autowired注解)
value属性:用于指定注入bean的id
@Service("userService")
public class UserSerivceImpl implements UserService {
@Autowired
@Qualifier("userDao1")
private UserDao userDao;
public void saveUser() {
userDao.saveUser();
}
}
@Resource
:按照bean的id注入,可以单独使用
属性name:用于指定bean的id
@Service("userService")
public class UserSerivceImpl implements UserService {
// @Autowired
// @Qualifier("userDao1")
@Resource(name = "userDao1")
private UserDao userDao;
public void saveUser() {
userDao.saveUser();
}
}
总结:以上三个注解只能注入bean类型的数据。
@Value
:用于注入基本类型和String类型数据
属性value:指定数据的值,可以使用SpEL(spring的el表达式)
SpEL写法:${表达式}
位置:变量、方法
五、使用纯注解的方式配置(没有beans.xml配置文件)
beans.xml:
@Configuration
:指定当前类是一个配置类
当配置类作为AnnotationConfigApplicationContext对象创建的参数传递时,可以省略该注解。
@ComponentScan(basePackage="com.hcx")
:指定spring在创建容器时需要扫描的包
属性value和basePackages:作用相同,都是用于指定创建容器时需要扫描的包。
使用此注解就相当于在xml中配置了:
@Bean
:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性name:指定bean的id,默认值为当前方法名
@import
:导入其他配置类
属性value:用于指定其他配置类的字节码(使用了该注解的类就是主配置类)
@PropertySource
:用于指定配置文件的路径
属性value:指定文件名称和路径(classpath:类路径)
SpringConfiguration:
@Configuration
@ComponentScan("com.hcx")
public class SpringConfiguration {
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springdemo");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("root");
return comboPooledDataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
测试:
public class UserServiceTest {
@Test
public void testSelectAll(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
List userList = userService.findAll();
for(User user : userList){
System.out.println(user);
}
}
}
注意:使用注解配置,方法如果有参数,spring会寻找有没有对应的bean对象,查找方式与Autowired一样。
@import注解
当有多个配置类时,可以通过@import注解导入其他的配置类
主配置类:
@ComponentScan("com.hcx")
@Import(JdbcConfig.class)
public class SpringConfiguration {
}
Jdbc配置类:
public class JdbcConfig {
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springdemo");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("root");
return comboPooledDataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@PropertySource注解
使用读取配置文件的方式读取数据库配置信息:
将数据库的配置抽取到单独的properties文件中:
jdbcConfig.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdemo
jdbc.useranme=root
jdbc.password=root
JdbcConfig:
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(username);
comboPooledDataSource.setPassword(password);
return comboPooledDataSource;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
在配置类中指定读取该配置:
SpringConfiguration:
@ComponentScan("com.hcx")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
在方法参数上使用@Qualifier注解
当有多个数据源时,可以在创建对象时通过@Qualifier注解参数指定具体使用哪一个: