目录
9. @Autowired和@Resource的区别是什么?
11. Spring中常见的ApplicationContext实现类有哪些?
12. BeanFactory和ApplicationContext有什么区别?
一、spring对于bean实例化的管理
======================springIOC/DI=========================
spring==bean容器===bean的管理
配置略
1.默认情况下调用类的无参构造方法进行
beans.xml
<!-- =========================bean的实例化方式1============================== --> <bean id="student" class="com.zkt.pojo.Student"></bean>
pojo Student
public class Student { private String stuName; private String stuHobby; private int stuAge; @Override public String toString() { return "Student{" + "stuName='" + stuName + '\'' + ", stuHobby='" + stuHobby + '\'' + ", stuAge=" + stuAge + '}'; } public Student() { System.out.println("===》执行studnet的无参构造方法"); } }
测试
public class Test01 { public static void main(String[] args) { //1.加载spring主配置文件,获取spring核心对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student student1 = (Student) applicationContext.getBean("student"); Student student2 = (Student) applicationContext.getBean("student"); System.out.println(student1 == student2); } }
2.通过普通工厂进行对象的实例化
beans.xml
<!-- =========================bean的实例化方式2============================== --> <bean id="student" class="com.zkt.pojo.Student" factory-bean="factory" factory-method="createStu"></bean> <bean id="factory" class="com.zkt.factory.BeansFactory"></bean>
factory BeansFactory
public class BeansFactory { public Student createStu(){ System.out.println("===》执行工厂方法"); return new Student(); } }
测试
public class Test01 { public static void main(String[] args) { //1.加载spring主配置文件,获取spring核心对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student student1 = (Student) applicationContext.getBean("student"); Student student2 = (Student) applicationContext.getBean("student"); System.out.println(student1 == student2); } }
3.通过静态工厂进行对象的实例化
beans.xml
<!-- =========================bean的实例化方式3============================== --> <bean id="student" class="com.zkt.factory.StaticBeansFactory" factory-method="createStu"></bean>
factory StaticBeansFactory
public class StaticBeansFactory { public static Student createStu(){ System.out.println("===》执行static工厂方法"); return new Student(); } }
测试
public class Test01 { public static void main(String[] args) { //1.加载spring主配置文件,获取spring核心对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student student1 = (Student) applicationContext.getBean("student"); Student student2 = (Student) applicationContext.getBean("student"); System.out.println(student1 == student2); } }
二、spring对于bean的作用域
含义:bean在spring容器中创建的策略以及访问的范围
位置:bean标签属性位置
语法:scope=""
属性值:singleton========>单例模式====全局唯一====性能好====安全性低(默认方式) prototype========>多例模式====全局不唯一====性能不好====安全性高 ============================以下作用域数据web模块=============================== request========>一次请求创建一个对象 session========>一个会话创建一个对象
beans.xml
<!-- =========================bean的作用域============================== --> <bean id="student" class="com.zkt.pojo.Student" scope="prototype"></bean> <bean id="student" class="com.zkt.pojo.Student" scope="singleton"></bean>
测试
public class Test01 { public static void main(String[] args) { //1.加载spring主配置文件,获取spring核心对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student student1 = (Student) applicationContext.getBean("student"); Student student2 = (Student) applicationContext.getBean("student"); System.out.println(student1 == student2); } }
作用域
prototype
singleton
三、spring对于bean的生命周期管理
单例模式:
实例化=====>容器创建的时候实例化
属性赋值
初始化=====>实例化完成,则自动初始化
接口初始化:InitializingBean中的afterPropertiesSet
属性初始化:init-method属性绑定方法
使用操作
销毁======>容器关闭,单例的bean则自动销毁
接口销毁:DisposableBean
属性销毁:destroy-method="",完成资源回收beans.xml
<!-- =========================bean的生命周期============================== --> <bean id="teacher" class="com.zkt.pojo.Teacher" init-method="doinit" destroy-method="dodestory"></bean>
pojo Teacher
public class Teacher implements InitializingBean, DisposableBean { private String tname; public void setTname(String tname) { System.out.println("生命周期===属性赋值"); this.tname = tname; } public Teacher() { System.out.println("生命周期===实例化"); } public void afterPropertiesSet() throws Exception { System.out.println("生命周期===初始化(接口)"); } public void doinit() { System.out.println("生命周期===初始化(属性)"); } public void destroy() throws Exception { System.out.println("生命周期===销毁(接口)"); } public void dodestory() { System.out.println("生命周期===销毁(属性)"); } }
测试
public class Test01 { public static void main(String[] args) { //1.加载spring主配置文件,获取spring核心对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Teacher teacher = (Teacher) applicationContext.getBean("teacher"); System.out.println(teacher); applicationContext.close(); } }
四、annotation注解
spring的配置
- 1.spring2.5前==xml
- 2.spring2.5后==xml+annotation
- 3.spring3.0后==JavaConfig+annotation配置类
spring2.5后=xml+annotation
目的优化一下代码:<bean id="" class="" init-method="" destroy-method="" scope="" autowire=""> <property></property> <constructor-arg></constructor-arg> </bean>
1. 注入类
替换:<bean id="" class=""></bean>
位置:类
语法:@Component(value="注入容器中的id,如果省略id为类名且首字母小写,value属性名称可以省略") @Component eg:Class User{} <bean id="user" class="com.apesource.包.User"></bean> ||等价于|| @Component Class User{}
<context:component-scan base-package=""></context:component-scan>功能一致
含义:扫描所有被@Component注解所修饰的类,注入容器
- @Repository=====>注入数据访问层
- @Service========>注入业务层
- @Controller=====>注入控制层
以上三个注解与@Component功能语法一致
2. 注入基本数据,javaBean
@Value
含义:注入基本数据
替换:<property></property>
修饰:成员变量或对应的set方法
语法:@Value("数据内容") @Value("${动态获取}")
加载指定资源文件:
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
@Autowired
替换:DI实现javaBean注入
语法:@Autowired(required = "true-默认、false、是否必须进行装配")
修饰:成员变量或对应的构造方法
含义:按照通过构造方法进行“类型装配”,构造方法可以省略
注意:
- 1.默认是按照类型装配且同构造方法
- 2.若容器中有一个类型可以与之匹配则装配成功,若没有一个类型可以匹配则报错NoSuchBeanDefinitionException
- 3.若容器中有多个类型可以与之匹配,则自动切换为按照名称装配,若名称没有对应,则报错 NoUniqueBeanDefinitionException
3.其他注解
@Primary
- 含义:首选项,当类型冲突的情况下,此注解修饰的类被列为首选(备胎扶正)
- 修饰:类
- 注意:不能单独使用,必须与@Component....联合使用
@Qualifier(value="名称")
- 含义:按照名称装配
- 修饰:成员变量
- 注意:不能单独使用,必须与@Autowired联合使用
@Resource(name="名称")
- 含义:按照名称装配
- 修饰:成员变量
- 注意:单独使用
@Scope
- 含义:配置类的作用域
- 修饰:类
- 注意:不能单独使用,必须与@Component....联合使用
- @Scope("prototype")
- @Scope("singleton")
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@PostConstruct:初始化,修饰方法 替换:init-method
@PreDestroy:销毁,修饰方法 替换:destory-method
实现
applicationContext.xml
<!-- 扫描包--> <context:component-scan base-package="com.zkt"/> <!-- 加载指定资源文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
controller
public interface IUserController { public void insert(); } @Controller public class UserControllerImp implements IUserController{ @Autowired private IUserService service; @Override public void insert() { System.out.println("====controller的新增===="); service.insert(); } }
dao
public interface IUserDao { public void insert(); } @Repository //@Primary public class UserDaoImp implements IUserDao{ // @Value("驱动") // private String dirver; // @Value("路径") // private String url; // @Value("用户") // private String username; // @Value("密码") // private String password; @Value("${msg1}") private String dirver; @Value("${msg2}") private String url; @Value("${msg3}") private String username; @Value("${msg4}") private String password; public void insert() { System.out.println("====dao的新增===="); } } @Repository(value = "dao2") public class UserDaoImpCopy implements IUserDao{ public void insert() { System.out.println("====dao2的新增===="); } }
service
public interface IUserService { public void insert(); } @Service public class UserServiceImp implements IUserService { @Autowired // @Qualifier(value = "dao2") // @Resource(name = "dao2") private IUserDao dao; public void insert() { System.out.println("====service的新增===="); dao.insert(); } }
pojo
@Component(value = "stu") //@Scope("prototype") //@Scope("singleton") //@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class Student { private String stuName; private String stuHobby; private int stuAge; public Student() { System.out.println("===》执行studnet的无参构造方法"); } }
测试
public class Test01 { public static void main(String[] args) { //1.加载容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.获取对象 // Student student = (Student) applicationContext.getBean("stu"); // System.out.println(student); // // IUserController controller = (IUserController) applicationContext.getBean("userControllerImp"); // controller.insert(); // IUserDao dao = (IUserDao) applicationContext.getBean("userDaoImp"); // System.out.println(dao); //scope 测试 Student student1 = (Student)applicationContext.getBean("stu"); Student student2 = (Student)applicationContext.getBean("stu"); System.out.println(student1==student2); } }
@Component(value="注入容器中的id,如果省略id为类名且首字母小写,value属性名称可以省略")
@Value
@Primary @Qualifier(value="名称")@Resource(name="名称")@Scope("prototype")@Scope("singleton")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
五、javaconfig
spring的配置
- 1.spring2.5前==xml
- 2.spring2.5后==xml+annotation
- 3.spring3.0后==JavaConfig+annotation配置类
spring中的新注解
@Configuration
- 作用:指定当前类是一个配置类,替代applicationContext.xml文件
- 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
@ComponentScan
- 作用:用于通过注解指定spring在创建容器时要扫描的包
- 替换:
<context:component-scan base-package="com.apesource"></context:component-scan>
@PropertySource
- 作用:用于指定properties文件的位置
- 属性:value:指定文件的名称和路径。
- 替换:
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
@Bean
- 作用:用于把当前方法的返回值作为bean对象存入spring的容器中
- 属性:name:用于指定bean的id。当不写时,默认值是当前方法的名称
@Import
- 作用:用于导入其他的配置类
- 属性:value:用于指定其他配置类的字节码。
- 例子:@Import(SystemSpringConfig.class)
实现
config
@Configuration @ComponentScan(basePackages = "com.zkt") @PropertySource(value = "classpath:jdbc.properties") @Import(DataSourceConfig.class) public class ApplicationConfig { @Bean public Student stu1() { return new Student(); } @Bean(name = "abc") public Student stu2() { return new Student(); } @Bean("time") public Date date() { return new Date(); } // @Bean // public IUserDao dao() { // return new UserDaoImp(); // } // // @Bean // public IUserService service() { // return new UserServiceImp(dao()); // } // // @Bean // public IUserController controller() { // return new UserControllerImp(service()); // } }
@Configuration public class DataSourceConfig { @Bean public IUserDao dao() { return new UserDaoImp(); } @Bean //@Autowired默认有 可写 可不写 public IUserService service(@Autowired IUserDao dao) { return new UserServiceImp(dao); } @Bean public IUserController controller(/*@Autowired*/IUserService service) { return new UserControllerImp(service); } }
dao
public interface IUserDao { public void insert(); } public class UserDaoImp implements IUserDao { @Value("${msg1}") private String dirver; @Value("${msg2}") private String url; @Value("${msg3}") private String username; @Value("${msg4}") private String password; public void insert() { System.out.println("====dao的新增===="); System.out.println(dirver); System.out.println(url); System.out.println(username); System.out.println(password); } }
service
public interface IUserService { public void insert(); } public class UserServiceImp implements IUserService { // @Autowired private IUserDao dao; public UserServiceImp(IUserDao dao) { this.dao = dao; } public void insert() { System.out.println("====service的新增===="); dao.insert(); } }
controller
public interface IUserController { public void insert(); } public class UserControllerImp implements IUserController{ // @Autowired private IUserService service; public UserControllerImp(IUserService service) { this.service = service; } @Override public void insert() { System.out.println("====controller的新增===="); service.insert(); } }
pojo
public class Student { }
test
public class Test01 { public static void main(String[] args) { //1.加载容器 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class); //2.获取对象 Student stu1 = (Student) applicationContext.getBean("stu1"); Student stu2 = (Student) applicationContext.getBean("abc"); System.out.println(stu1); System.out.println(stu2); IUserController controller = (IUserController) applicationContext.getBean("controller"); controller.insert(); // IUserDao dao = (IUserDao) applicationContext.getBean("userDaoImp"); // System.out.println(dao); } }
两者等价
六、实现单表增删改查 (spring)
1. xml实现
初始准备
poi.xml
<dependencies> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- dbutil --> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- 数据源 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> </dependencies>
applicationContext.xml
<!-- 加载文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 注入数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${msg1}"/> <property name="jdbcUrl" value="${msg2}"/> <property name="user" value="${msg3}"/> <property name="password" value="${msg4}"/> </bean> <!-- 注入queryRunner --> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> <constructor-arg name="ds" ref="dataSource"/> </bean> <!-- 注入dao --> <bean id="dao" class="com.apesource.dao.AccountDaoImp"> <property name="queryRunner" ref="queryRunner"/> </bean> <!-- 注入service --> <bean id="service" class="com.apesource.service.AccountServiceImp"> <property name="dao" ref="dao"/> </bean> <!-- 注入controller --> <bean id="controller" class="com.apesource.controller.AccountControllerImp"> <property name="service" ref="service"/> </bean>
jdbc.properties
msg1=com.mysql.cj.jdbc.Driver msg2=jdbc:mysql://localhost:3306/javaee2404?serverTimezone=GMT msg3=root msg4=123456
dao
public interface IAccountDao { public void save(Account account); public void deleteById(int id); public void updateById(Account account); public List<Account> findAll(); public Account findByName(String name); } public class AccountDaoImp implements IAccountDao{ //装配QueryRunner QueryRunner queryRunner; public void setQueryRunner(QueryRunner queryRunner) { this.queryRunner = queryRunner; } @Override public void save(Account account) { try { queryRunner.update("insert into account(aname,amoney) value(?,?)",account.getAname(),account.getAmoney()); } catch (SQLException throwables) { throwables.printStackTrace(); } } @Override public void deleteById(int id) { try { queryRunner.update("delete from account where aid = ?",id); } catch (SQLException throwables) { throwables.printStackTrace(); } } @Override public void updateById(Account account) { try { queryRunner.update("update account set aname=?,amoney=? where aid=?",account.getAname(),account.getAmoney(),account.getAid()); } catch (SQLException throwables) { throwables.printStackTrace(); } } @Override public List<Account> findAll() { try { return queryRunner.query("select * from account",new BeanListHandler<Account>(Account.class)); } catch (SQLException throwables) { throwables.printStackTrace(); } return null; } @Override public Account findByName(String name) { try { return queryRunner.query("select * from account where aname=?",new BeanHandler<Account>(Account.class),name); } catch (SQLException throwables) { throwables.printStackTrace(); } return null; } }
service
public interface IAccountService { public void save(Account account); public void deleteById(int id); public void updateById(Account account); public List<Account> findAll(); public Account findByName(String name); } public class AccountServiceImp implements IAccountService { //装配dao IAccountDao dao; public void setDao(IAccountDao dao) { this.dao = dao; } @Override public void save(Account account) { dao.save(account); } @Override public void deleteById(int id) { dao.deleteById(id); } @Override public void updateById(Account account) { dao.updateById(account); } @Override public List<Account> findAll() { return dao.findAll(); } @Override public Account findByName(String name) { return dao.findByName(name); } }
controller
public interface IAccountController { public void save(Account account); public void deleteById(int id); public void updateById(Account account); public List<Account> findAll(); public Account findByName(String name); } public class AccountControllerImp implements IAccountController { //装配SERVICE IAccountService service; public void setService(IAccountService service) { this.service = service; } @Override public void save(Account account) { service.save(account); } @Override public void deleteById(int id) { service.deleteById(id); } @Override public void updateById(Account account) { service.updateById(account); } @Override public List<Account> findAll() { return service.findAll(); } @Override public Account findByName(String name) { return service.findByName(name); } }
pojo
public class Account implements Serializable { private int aid; private String aname; private int amoney; get set 构造 tostring .. }
test 使用junit
IAccountController controller = null; ClassPathXmlApplicationContext applicationContext = null; @Before public void beforeMethod(){ applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); controller = (IAccountController) applicationContext.getBean("controller"); } @After public void afterMethod(){ applicationContext.close(); } @Test public void show1(){ controller.save(new Account("李世伟",2000)); controller.save(new Account("马宇航",2000)); } @Test public void show2(){ List<Account> all = controller.findAll(); for (int i = 0; i < all.size(); i++) { Account account = all.get(i); System.out.println(account); } } }
@RunWith(SpringJUnit4ClassRunner.class)//运行环境 @ContextConfiguration(locations = "classpath:applicationContext.xml") public class Test02 { @Autowired IAccountController controller; @Test public void show1(){ controller.save(new Account("李世伟",2000)); controller.save(new Account("马宇航",2000)); } @Test public void show2(){ List<Account> all = controller.findAll(); for (int i = 0; i < all.size(); i++) { Account account = all.get(i); System.out.println(account); } } }
2. annotation注解
只需要加上注解就行 其余不变
dao
service
controller
applicationContext.xml
<!-- 扫描包--> <context:component-scan base-package="com.zkt"/> <!-- 加载指定资源文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 注入数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${msg1}"/> <property name="jdbcUrl" value="${msg2}"/> <property name="user" value="${msg3}"/> <property name="password" value="${msg4}"/> </bean> <!-- 注入queryRunner --> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> <constructor-arg name="ds" ref="dataSource"/> </bean>
测试 同上
3. javaconfig
没有xml 文件了
dao service controller 不变 只需要将xml文件 通过配置类实现就行
config
@Configuration @ComponentScan(basePackages = "com.zkt") @Import(DataSourceConfig.class) public class ApplicationConfig { }
@Configuration @PropertySource(value = "classpath:jdbc.properties") public class DataSourceConfig { @Value("${msg1}") private String driver; @Value("${msg2}") private String url; @Value("${msg3}") private String uname; @Value("${msg4}") private String upwd; // <!-- 注入数据源 --> // <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> // <property name="driverClass" value="${msg1}"/> // <property name="jdbcUrl" value="${msg2}"/> // <property name="user" value="${msg3}"/> // <property name="password" value="${msg4}"/> // </bean> public DataSource dataSource() { try { ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); comboPooledDataSource.setDriverClass(driver); comboPooledDataSource.setJdbcUrl(url); comboPooledDataSource.setUser(uname); comboPooledDataSource.setPassword(upwd); return comboPooledDataSource; } catch (PropertyVetoException e) { e.printStackTrace(); } return null; } // <!--注入QueryRunner--> // <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> // <constructor-arg name="ds" ref="dataSource"></constructor-arg> // </bean> @Bean public QueryRunner queryRunner() { return new QueryRunner(dataSource()); } }
其余同上
七、面试题
6. 什么是Spring 的bean
- ●简单来说,bean 代表被IOC容器管理的对象
- ●我们通过配置文件或注解,告诉IOC容器帮助我们管理哪些对象
7. 将一个类声明为Bean的注解有哪些?
- ●@Component:定义通用bean的注解,可标注任意类为 bean。如果一个 bean 不知道属层,可以使用 @component 注解标注
- ●@Repository:定义数据访问层bean的注解
- ●@Service:定义业务层bean的注解
- ●@Controller:定义控制器bean的注解
8. @Component和@Bean的区别是什么?
- ●@Component 注解作用于类,而 @bean 注解作用于方法
- ●@Component 通常是通过类路径扫描来实现自动扫描并完成装配 bean 到 Spring IOC 容器
- ●@Bean 注解通常用于注解某个方法,告诉了 spring ioc 容器,该方法的返回实例是一个bean
9. @Autowired和@Resource的区别是什么?
- ●@Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解
- ●@Autowired 默认的注入方式为 bytype (按类型自动注入),@Resource 默认注入方式为 byname (按名称自动注入)
10. Spring框架中的常见注入方式有几种?
●关键字: 整体介绍三种注入方式、分别介绍每个注入方式的细节
●Spring IOC 有三种注入方式:构造注入、setter注入、属性注入
○构造注入:使用构造方法注入 bean
○setter注入:使用setter方法注入 bean
○属性注入:使用成员属性注入bean,不推荐。原因:使用私有的成员属性变量,依靠反射实现,破坏封装,只能依靠IOC 容器实现注入,不严谨
11. Spring中常见的ApplicationContext实现类有哪些?
- ●关键字:分别介绍每种实现类或子接
- ●ClassPathXmlApplicationContext:根据项目类路径classpath下的配置文件加载 bean(通过相对路径加载主配置文件)
- ●FileSystemXmlApplicationContext:根据当前磁盘的一个绝对系统文件路径下的配置文件加载bean (通过绝对路径加载主配置文件通过绝对路径加载主配置文件)
- ●AnnotationConfigApplicationContext:根据读取到的注解加载 bean
- ●WebApplicationContext: web容器下按照配置文件加载 bean
12. BeanFactory和ApplicationContext有什么区别?
- ●关键字:两者之间的关系、区别与不同、bean的创建加载方式
- ●两者之间的关系: BeanFactory 和ApplicationContext 是 Spring 的两大核心接口,都可当做 Spring 的容器
两者区别∶
- ●Beanfactory是 Spring 里面最底层的接口,是IOC 的核心,定义了IOC 的基本功能,包含了各种 Bean 的定义、加载、实例化,依赖注入和生命周期管理等行为
- ●ApplicationContext 接口作为 BeanFactory 接口的子接口,包含 BeanFactory 所具备的功能外,还提供了其它框架功能:继承 MessageSource(支持国际化),资源文件访问、可同时加载多个配置文件、可以通过监听器管理 Bean 的生命周期
●Bean的创建加载方式:
- ○BeanFactroy 采用的是延迟加载形式来注入 bean,只有在使用到某个 bean 时,才对该 bean 进行加载实例化。这样不能提前发现一些存在的 spring 的配置问题。如果 bean 的某一个属性没有注入,beanfactory 加载后,直至第一次使用调用 getbean()方法才会抛出异常
- ○ApplicationContext是在容器启动时,一次性创建了所有的 bean。这样,在容器启动时, 我们就可以发现 spring 中存在的配置错误,这样有利于检查所依赖属性是否注入。 运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当用程序配置 bean 较多时,程序启动较慢
13. Spring 框架启动流程(了解)
●关键字:初始化spring容器、注册BeanDefinition、刷新容器
●首先,初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器
- ○实例化 BeanFactory【 Defaultlistablebeanfactory 】工厂,用于生成 bean 对象
- ○实例化 beandefinitionreader,用于读取 spring 配置文件或注解,将配置文件中 spring 需要管理的组件类,转化成 beandefinition 对象,( beandefinition 是 spring 中极其重要的一个概念,它存储了 bean 对象的所有特征信息,例如: bean的id,是否 单例,是否懒加载等)
- ○实例化classpathbeandefinitionscanner路径扫描器,用于对指定的包目录进行扫描查找bean 对象
●将配置类的beandefinition注册到容器中
●调用refresh()方法刷新容器
- ○preparerefresh)刷新前的预处理
- ○obtainffreshbeanfactory(): 获取在容器初始化时创建的BeanFactory
- ○preparebeanfactory(beanfactory):BeanFactory的预处理工作,向容器中添加一些组件
- ○postprocessbeanfactory(beanfactory):子类重写该方法,可以实现在beanfactory创建并预处理完成以后做进一步的设置
- ○invokebeanffactorypostprocessors(beanfactory):在beanfactory标准初始化之后执行 beanfactorypostprocessor的方法,即pbeanffactory的后置处理器
- ○registerbeanpostprocessors(beanfactory):向容器中注册bean的后置处理器 beanpostprocessor,它的主要作用是干预spring初始化bean的流程,从而完成代理、自动注入、循环依赖等功能
- ○initmessagesource():初始化messagesource组件,主要用于做国际化功能,消息绑定与消解析
- ○initapplicationeventmulticaster():初始化事件派发器,在注册监听器时会用到
- ○onrefresh():留给子容器、子类重写这个方法,在容器刷新的时候可以自定义逻辑
- ○registerlisteners():注册监听器:将容器中所有的applicationlistener注册到事件派发器中,并派发之前步骤产生的事件
- ○finishbeanfactorylnitialization( beanfactory ):初始化所有剩下的单实例bean,核心方法prelnstantiatesingletons(),会调用getBean()方法创建对象
- ○finishRefresh():发布BeanFactory容器刷新完成事件
14. Spring 框架中的Bean的作用域
- ●singleton:Spring为该bean对象只会创建唯一实例,Spring中的bean默认都是单例
- ●prototype:每次获取bean,Spring会创建一个新的bean实例
- ●request:每一次http请求,Spring会创建一个新的bean实例
- ●session:不同的http会话,Spring会创建不同的bean实例
15. Spring 框架中的Bean的线程安全
- ●对于prototype作用域的bean,每次都创建一个新对象,也就是线程之间不存在bean共享,因此不会有线程安全问题
- ●对于singleton作用域的bean ,所有的线程都共享一个单例状态的bean,存在资源竞争,因此是存在线程安全问题的
- ●解决办法:对于singleton作用域的单例bean,它的线程安全问题,常见有两种解决办法
- ○在bean中尽量避免定义可变的成员变量(用于保存数据的成员变量)
- ○在类中定义一个ThreadLocal成员变量,将需要可变的成员变量保存在 ThreadLocal