[TOC]
前言 ​
学习目标 ​
- 掌握IOC和DI的概念
- 熟悉注册组件的几种方式和相关注解
- 熟悉取出组件的几种方式和相关注解
- 理解Spring组件的生命周期的含义
- 熟悉BeanPostProcessor的执行时机
前置知识准备 ​
- 项目一开发的常用风格
- 注解的相关知识@Target、@Retention、属性
- 理解容器的思想
介绍Spring ​
SpringFramework的起源 ​
Spring Framework通常人们称之为Spring。
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。
它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。
Spring是一个分层的Java SE/EE full-stack(一站式) 轻量级开源框架。
​
Inverse of Control
控制反转这个词要拆开来看
控制:实例的生成权
反转:由应用程序反转给Spring容器
容器:生成并管理实例的抽象空间
获取依赖对象被反转了,它是被动获取;
正转就是自己去new一个对象,自己获取对象
​
Dependency Injection
Q1谁依赖谁?Q2为什么需要依赖?Q3谁注入谁?Q4注入了什么?
Answer1 应用程序依赖于IoC容器;
Answer2 应用程序需要IoC容器来提供对象需要的外部资源;
Answer3 IoC容器注入应用程序某个对象,应用程序依赖的对象;
Answer4 注入某个对象所需要的外部资源(包括对象、资源、常量数据);
Spring的优点 ​
- 方便解耦,简化开发(高内聚低耦合)
- AOP编程的支持
- 声明式 事务的支持
- 方便程序的测试
- 方便集成各种优秀框架
- 降低JavaEE API的使用难度
核心技术 ​
依赖注入、事件、资源、i18n国际化、校验、数据绑定、类型转换、Spring Expression Language、AOP
思考 ​
Spring和我们之前开发的项目的关联
入门案例 ​
入门案例1 ​
引入依赖 ​
引入依赖 beans、context、aop、expression、core、jcl 5+1
<dependencies>
<!--引入依赖 beans、context、aop、expression、core、jcl 5+1-->
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
提供接口和实现类 ​
public class UserDaoImpl implements UserDao{
@Override
public void sayHello() {
System.out.println("hello xiaowu");
}
}原先使用实例的时候,是通过构造方法生成的;后续要变更为来源Spring容器
Spring配置文件 ​
配置文件为Xml格式的文件,需要引入对应的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean definitions here -->
</beans>在配置文件中注册组件
<!--spring配置文件的名称通常叫application(-xxx).xml-->
<!-- bean definitions here -->
<!--控制反转-->
<!--id属性 👉 组件在容器中的唯一标识-->
<!--name属性 👉 名称 👉 通常省略不写-->
<!--class 全类名 👉 实现类的全类名-->
<!--组件 注册 👉 将实例交给spring管理的过程我们称之为注册-->
<bean id="userDao" class="com.cskaoyan.dao.UserDaoImpl"/>从容器中取出组件,执行方法 ​
注意:取出组件的3种方式!
@Test
public void mytest(){
//容器的具体的存在ApplicationContext
//加载的是classpath下的spring配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
//Spring容器想象为婚姻介绍所 → 介绍对象
//applicationContext.getBean(String) → 按照组件的名称(id)
UserDao userDao1 = (UserDao) applicationContext.getBean("userDao");
//applicationContext.getBean(Class) → 按照组件的类型(class)
//可以写接口的类型,也可以写实现类的类型 → 容器当中该组件的类型只有一个的时候,可以使用这种方式
UserDao userDao2 = applicationContext.getBean(UserDao.class);
//applicationContext.getBean(String,Class) → 按照组件的名称和类型共同作用(id和class)
UserDao userDao3 = applicationContext.getBean("userDao", UserDao.class);
userDao1.sayHello();
userDao2.sayHello();
userDao3.sayHello();
}入门案例2 ​
维护组件之间的依赖关系,在容器中注册dao层和service层组件,并且service层的组件依赖于dao层组件(谁需要谁就是谁依赖谁)
service类和dao类 ​
通过在service类中增加dao类成员变量维护依赖关系
public class UserServiceImpl implements UserService{
//主动 → 控制
//UserDao userDao = new UserDaoImpl();
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void serviceSayHello() {
System.out.println("service层的sayHello");
userDao.sayHello();
}
}public class UserDaoImpl implements UserDao{
@Override
public void sayHello() {
System.out.println("hello xiaowu");
}
}维护组件之间依赖关系 ​
通过property标签的ref属性维护组件之间的依赖关系
<!-- bean definitions here -->
<bean id="userDao" class="com.cskaoyan.dao.UserDaoImpl"/>
<bean id="userService" class="com.cskaoyan.service.UserServiceImpl">
<!--name属性对应的是set方法-->
<!--ref属性对应的是容器中的组件id-->
<property name="userDao" ref="userDao"/>
</bean>单元测试 ​
直接从容器中取出service组件,执行对应的方法
@Test
public void mytest1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
UserService userService = applicationContext.getBean(UserService.class);
userService.serviceSayHello();
UserDao userDao = applicationContext.getBean(UserDao.class);
}思考:右侧应用程序中的userDaoImpl的实例是否是同一个

​
在使用Spring注解的时候,我们按照功能来进行划分
配置类 ​
配置类,承担做通用配置的功能,同时在配置类中可以组件注册
- 我们在类定义上增加一些功能性的注解,增加一些通用性的配置
- 我们在类中的方法里注册组件
比如我们定义一个配置类,需要在类上增加一个@Configuration注解
// 这里增加功能性的注解
@Configuration
public class SpringConfiguration {
// 类中的方法做组件的注册
}组件注册功能(IOC) ​
类直接注册 ​
组件注册功能首先要打开扫描开关
// 这里增加功能性的注解
@Configuration
@ComponentScan("com.cskaoyan.demo1")
public class SpringConfiguration {
// 类中的方法做组件的注册
}组件注册功能的注解@Component
除了@Component注解,还有什么类似的注解
@Target(ElementType.TYPE)// 该注解写在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}- @Service → 通常是Service层的组件使用的注解,Service层组件也能使用@Component
- @Repository → 通常是Dao层的组件使用@Repository注解,dao层组件也能使用@Component
- @Configuration → 配置类组件
- @Controller(SpringMVC阶段)
- 等
@Service、@Repository、@Controller、@Configuration,这些注解的ElementType都是TYPE,也就是这些注解都是要写在类定义上。
组件id默认为类名的首字母小写,另外也可以使用注解的value属性来指定组件id
//@Component
//@Repository("userDao") //组件id为userDao
@Repository //组件id为userDaoImpl
public class UserDaoImpl implements UserDao{
}
//@Component
@Service
public class UserServiceImpl implements UserService{
}配置类注册(JavaConfig) ​
在配置类中完成对应的组件注册以及相关配置,配置类的核心就是提供对应的信息
JavaConfig的目标是干掉配置文件,JavaConfig也是SpringBoot推荐使用的配置方式,SpringBoot中不再使用Spring的xml配置文件
在配置类中,使用注册功能的注解@Bean 以及方法来完成组件注册
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {}该注解增加在方法上,并且可以和其他注解共存
这个方法的返回值注册为容器中的组件
/**
* 要通过这个方法注册一个DruidDataSource组件
* @return 应该返回的是一个DruidDataSource的实例,这个返回值会注册为容器中的组件
* 返回值的定义:可以写实现类,也可以写接口;提供组件的类型信息给到容器中;建议写接口
* SE的代码风格来提供属性值就可以
* 通过这种方式注册的组件id:1、默认值是方法名;2、@Bean注解的value属性可以指定组件id
*/
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/cskaoyan_db?useUnicode=true&characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}想要通过@Bean注册Component1这个组件
public class Component1 {
DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}/**
* Component1这个类中有一个成员变量 叫dataSource需要的是一个DataSource类型的组件
* 设置的这个DataSource想要从容器中来获得
* 形参:默认是按照类型从容器中取出组件的 ac.getBean(Class)
* @return
*/
@Bean
public Component1 component1(DataSource dataSource) {
Component1 component1 = new Component1();
component1.setDataSource(dataSource);
return component1;
}额外增加一个DataSource组件的话,容器中DataSource类型的组件不止一个,通过形参从容器中取出组件需要指定组件id
@Bean
public DataSource dataSource2(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/cskaoyan_db2?useUnicode=true&characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
/**
* Component1这个类中有一个成员变量 叫dataSource需要的是一个DataSource类型的组件
* 设置的这个DataSource想要从容器中来获得
* 形参:默认是按照类型从容器中取出组件的 ac.getBean(Class)
* 如果形参所需的组件在容器中不止一个,需要额外指定组件id的信息 → @Qualifier 的value属性指定组件id
* 形参的名称也可以作为组件的id,但是我们更建议使用@Qualifier ,更直观一些
* @return
*/
@Bean
public Component1 component1(@Qualifier("dataSource2") DataSource dataSource) {
Component1 component1 = new Component1();
component1.setDataSource(dataSource);
return component1;
}@Bean
返回值类型、方法名、形参、返回值、@Bean注解的value属性 各自的含义大家需要关注
组件注入功能(DI) ​
JavaConfig的@Bean ​
方法的形参,就是从容器中取出的组件
构造器注入 ​
如果类中没有无参构造方法的话,如果这个类上有组件注册功能的注解,它会使用有参构造方法来完成实例化。
有参构造方法的形参会从容器中取出组件
@Component
public class Component2 {
DataSource dataSource;
public Component2(DataSource dataSource) {
this.dataSource = dataSource;
}
}Component2中没有无参构造方法,就会使用有参构造方法来完成实例化
默认是按照类型从容器中取出组件
如果这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件id(
形参名称也可以但不建议)NoUniqueBeanDefinitionException:
使用@Qualifier指定组件id
- java
@Component public class Component2 { DataSource dataSource; public Component2(@Qualifier("dataSource1") DataSource dataSource) { this.dataSource = dataSource; } }
其中的常量值可以通过@Value注解来提供
- java
public UserServiceImpl(UserDao userDao,@Value("zhangsan") String name) { this.userDao = userDao; this.name = name; }
如果有多个有参构造方法,需要指定构造方法
- java
@Service public class UserServiceImpl implements UserService{ UserDao userDao; String name; public UserServiceImpl(UserDao userDao,@Value("zhangsan") String name) { this.userDao = userDao; this.name = name; } @Autowired public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } }
方法注入 ​
可以是组件中的任意方法,但是通常这样的方法我们用的是set方法,在方法上增加@Autowired注解
@Component
public class Component3 {
DataSource dataSource;
// 默认这个方法并不会自动执行
// 如果我们在上面增加了@Autowired 注解的话,在生命周期设置属性值的过程中会自动执行
// 形参默认按照类型从容器中取出;如果要指定组件id,还是@Qualifier
@Autowired
public void setDataSource(@Qualifier("dataSource2") DataSource dataSource) {
this.dataSource = dataSource;
}
}(set)方法这种形式是Spring框架建议使用的方式,但其实绝大部分程序员用的都不是这种方式
成员变量注入 ​
注入功能的注解使用这三组:
- @Autowired
- @Autowired + @Qualifier
- @Resource
容器中注册了这些类型的组件,OrderDao类型的组件(orderDaoImpl)、UserDao类型的组件userDaoImpl1和userDaoImpl2
@Repository
public class OrderDaoImpl implements OrderDao{
}
@Repository
public class UserDaoImpl1 implements UserDao{
}
@Repository
public class UserDaoImpl2 implements UserDao{
}从容器中取出的对应的组件,执行注入,要注意,要求是在容器中的组件里注入
/**
* 要使用注入功能注解,一定要保证当前类是容器中的组件
*/
@Service
public class UserServiceImpl implements UserService{
@Autowired //容器中该类型的组件只有一个
OrderDao orderDao;
@Autowired //使用@Qualifier注解指定组件id
@Qualifier("userDaoImpl1")
UserDao userDao1;
@Resource(name = "userDaoImpl2") //默认是按照组件的类型去注入,使用name属性指定组件id
UserDao userDao2;
}注意事项 ​
- 开发业务代码过程中,最常用的方式只使用一个@Autowired :绝大部分组件在容器中这个类型的组件只有一个
- 要在容器中的组件中使用这些注解,使用注解的话,所处的类上要有组件注册功能的注解,且处于扫描包目录
- 维护的是容器中的组件之间的依赖关系
Spring的单元测试 ​
引入spring-test依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
<scope>test</scope>
</dependency>使用@Runwith和@ContextConfiguration注解,在单元测试类中可以使用注入功能的注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class MyTest2 {
@Autowired
UserService userService;
@Autowired
OrderDao orderDao;
@Autowired
@Qualifier("userDaoImpl1")
UserDao userDao1;
/**
* 从容器中取出UserService组件
* 查看userService组件中的成员变量是否取出容器中的组件
*/
@Test
public void mytest1(){
}
}Bean的准备 ​
Bean的实例化 ​
无参构造方法 ​
这也是最常用的一种方式
/**
* 无参构造方法
*/
@Data //提供了getter和setter方法,以及重写了toString方法
@NoArgsConstructor //增加无参构造方法,默认存在无参构造,该注解也可以不写
public class Bean1 {
String parameter1;
String parameter2;
}对应的配置文件
<!--构造方法完成实例化-->
<!--无参构造方法 👉 默认使用的无参构造方法-->
<bean id="bean1" class="com.cskaoyan.bean.Bean1">
<property name="parameter1" value="xiaowu"/>
<property name="parameter2" value="male"/>
</bean>其中的参数,可以通过property标签使用到set方法来提供
有参构造方法 ​
/**
* 有参构造方法
*/
@Data
@AllArgsConstructor //增加有参构造方法
public class Bean2 {
String parameter1;
String parameter2;
}对应的配置文件
<!--有参构造方法-->
<bean id="bean2" class="com.cskaoyan.bean.Bean2">
<!--name属性对应的是有参构造方法的参数名-->
<constructor-arg name="parameter1" value="xiaofang"/>
<constructor-arg name="parameter2" value="female"/>
</bean>工厂 ​
工厂提供实例,而实例交给Spring容器来进行管理
工厂(略) ​
通过配置类中的方法,方法上增加@Bean注解,该方法的返回值注册为容器中的组件
FactoryBean ​
实现FactoryBean接口,组件类型和FactoryBean接口中的getObject方法的类型相同
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}比如要注册一个User类型的组件,可以通过User对应的FactoryBean来注册组件。
/**
* 直接注册为容器中的组件
* FactoryBean 👉 XXXFactoryBean
* 👉 组件类型和FactoryBean接口中的getObject方法相关
* BeanFactory和factoryBean:
* BeanFactory:生产的是容器中的所有的组件
* FactoryBean:生产的是特定的组件
*/
@Component
public class UserFactoryBean implements FactoryBean<User> {
/**
* 完成组件的实例化
* @return 组件类型和返回值相关
* @throws Exception
*/
@Override
public User getObject() throws Exception {
User user = new User();
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}思考1 ​
我们 在什么情况下使用的工厂的方式?
难道我们不是直接用构造方法更方便么,为啥还要使用工厂?
确实,使用构造方法是很方便,但是有些情况用不了构造方法。比如动态代理 ProxyFactoryBean
使用构造器要提供大量的参数,而提供大量的参数过程又比较繁琐,也就是通常在使用一些框架的时候,会给你提供一些对应的工厂
一些框架已经写好了一些代码,要在此基础上增加对Spring框架的支持,要将框架中的一些核心的对象交给Spring容器来管理,通常就会提供一个框架的拓展包,拓展包中提供对应的工厂,使用工厂可以直接将这个框架需要的核心对象注册为容器中的组件 SqlSessionFactoryBean
工厂:主要就是对已有的代码做些拓展
思考2 ​
BeanFactory是ApplicationContext的父接口,也就是容器接口 → 所有的组件的生成和管理都是通过它的
BeanFactory和FactoryBean之间的联系和区别
联系:通过他两都可以向容器中注册组件
区别:BeanFactory是容器,所有的组件注册都是通过BeanFactory;而FactoryBean注册的特定的单个组件
作用域 Scope ​
Singleton:单例,每一次取出组件都是
Prototype:原型,每一次取出组件都是
默认值是,我们通常省略不写
@Scope 使用value属性指定作用域
@Component
@Scope("singleton")
public class SingletonBean {
}
@Component
@Scope("prototype")
public class PrototypeBean {
}
@Component
public class DefaultBean {
}注册3个组件,分别给到不同的scope,然后从容器中取出组件多次,查看是否是同一个组件(查看内存地址)
@Test
public void mytest1(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
SingletonBean bean1 = applicationContext.getBean(SingletonBean.class);
SingletonBean bean2 = applicationContext.getBean(SingletonBean.class);
SingletonBean bean3 = applicationContext.getBean(SingletonBean.class);
}
@Test
public void mytest2(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
PrototypeBean bean1 = applicationContext.getBean(PrototypeBean.class);
PrototypeBean bean2 = applicationContext.getBean(PrototypeBean.class);
PrototypeBean bean3 = applicationContext.getBean(PrototypeBean.class);
}
@Test
public void mytest3(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
DefaultBean bean1 = applicationContext.getBean(DefaultBean.class);
DefaultBean bean2 = applicationContext.getBean(DefaultBean.class);
DefaultBean bean3 = applicationContext.getBean(DefaultBean.class);
}生命周期 ​
概念 ​
生命周期指组件在容器中要完成实例化,组件从实例化开始直至可用状态会执行到哪些过程。
学Servlet的时候学过生命周期,Servlet的生命周期:init、service、destroy
- 准备阶段
- 服务阶段
- 销毁阶段
对于Bean(容器中的组件),在容器中也会经历这样的一些阶段
- 容器初始化的时候,组件做准备性工作
- 组件可以从容器中取出,提供服务,比如从容器中取出userService实例,调用其sayHello方法
- 容器关闭,组件做销毁工作
在特定的时间执行一系列的方法
初始化阶段的方法 ​
Bean的实例化
设置参数方法
BeanNameAware、BeanFactoryAware、ApplicationContextAware
BeanPostProcessor的postProcessBeforeInitialization
InitializingBean的afterPropertiesSet方法
自定义的init方法
BeanPostProcessor的postProcessAfterInitialization

容器关闭阶段的方法 ​
单例的组件才会执行到对应的方法
DisposableBean的destroy方法
自定义的destroy方法
作用域对生命周期的影响 ​
scope=singleton(默认值) → 在容器初始化的时候就开始组件的初始化(立即加载),并且会执行销毁阶段
scope=prototype → 在获取组件的时候才执行生命周期(懒加载),每获取一次执行一次生命周期的初始化方法,不会执行销毁方法
代码 ​
示例生命周期的组件
/**
* 组件可以使用之前要经过这样的一些生命周期
* 组件可以使用之前:
* singleton:容器初始化的时候
* prototype:从容器中取出组件的时候,getBean
*/
@Component
public class LifeCycleBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware,
InitializingBean, DisposableBean {
//实例化
public LifeCycleBean(){
System.out.println("1、bean的实例化");
}
//参数设置
String parameter;
@Autowired
public void setParameter(String parameter) {
System.out.println("2、设置参数");
this.parameter = parameter;
}
//Aware:为组件中的成员变量提供参数 → BeanName、BeanFactory、ApplicationContext
String beanName;
BeanFactory beanFactory;
ApplicationContext applicationContext;
@Override
public void setBeanName(String beanName) {
System.out.println("3、BeanNameAware");
this.beanName = beanName;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("3、BeanFactoryAware");
this.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("3、ApplicationContextAware");
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("5、InitializingBean的afterPropertiesSet方法");
}
@PostConstruct
public void init(){
System.out.println("6、自定义的init方法");
}
@Override
public void destroy() throws Exception {
System.out.println("8、DisposableBean的destroy");
}
@PreDestroy
public void customDestroy(){
System.out.println("9、自定义的destroy");
}
}BeanPostProcessor
//该组件的before和after方法的作用范围:除了该组件本身之外,其他的所有组件
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("4、BeanPostProcessor的before方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("7、BeanPostProcessor的after方法");
return bean;
}
}组件注册到容器中才会生效
注意 ​
笔记 ​
- SpringBoot,boot是引导,引导使用Spring
- 表示层,业务逻辑层,数据访问层
- 容器就是放东西的地方,可以存和取
- 注解两个作用:标记、信息的载体
- AOP就是通过动态代理扩展功能,对标面向对象通过继承扩展功能
- Spring最核心的功能是解耦,怎么解耦呢,通过IOC和DI,IOC生成实例存入容器,DI取出实例
- SpringMVC解决的核心问题是,想办法把表示层的Servlet对象也管理到容器里
- 把对象的依赖关系放在容器里处理,应用程序直接拿处理好的实例,这就是解耦的便利之处。类比把东西都拿到手里,还是放到书包里。并且拿到的都是同一个实例,保证单例。
- jdk动态代理:委托类和代理类之间的关系,同一个接口的不同实现类;cglib:父子继承关系
- Servlet不注册为组件,因为Servlet的生命周期是在第一次访问路由的时候开始的
- @Bean注解要掌握,因为很多类都不是自己写的,不能再上面加@Component注解
- 单元测试,单元测试类也注册为容器中的组件
- FactoryBean用来注册组件的,用到了工厂方法模式。解决有些类无法加注解,比如代理类,第三方类,即已经编译好的类。提供一个打补丁的机制
- 为什么还要使用FactoryBean,因为第三方可以自己打补丁,把FactoryBean写在自己的包里
- sqlSession线程不安全
- 面试题:BeanFactory和FactoryBean有什么区别?BeanFactory就是容器,FactoryBean是用来注册单个组件的接口
- scope注解Singleton是立即加载,即容器初始化的时候。Prototype的懒加载,即获取它的时候
- 生命周期:实例化的四种方法:无参构造、有参构造、工厂设计模式(homework2)、FactoryBean。
- 组件注册的四种方式:配置文件xml,配置类@Configuration @Bean,配置类@ComponentScan @Component,FactoryBean
- 取出组件方法:构造器,方法,成员变量,形参
- BeanPostProcessor作用域是出自己之外所有注册到容器中的组件
- 声明周期做的都是善后工作,初始化资源和释放资源。围绕容器中的组件
- 非侵入式编程,没有改之前的代码,但是执行的东西变了
- AOP解决的是怎么对哪个组件增强,以及增强什么的自由组合问题
作业1 ​
增加几个(至少3个)不同的Service实现类,并且注册为容器中的组件,比如UserServiceImpl,CategoryServiceImpl,GoodsServiceImpl
然后通过AspectJ来进行增强,增加自定义的注解来指定增强的方法,如果调用其中的指定的方法,能够将方法的调用记录记录到数据库中
记录表可以参考这个表(ip可以不记录)
CREATE TABLE `cskaoyan_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`operation` varchar(255) DEFAULT NULL,
`ip` varchar(255) DEFAULT NULL,
`add_time` datetime DEFAULT NULL,
`success` tinyint(1) DEFAULT NULL,
`err_msg` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4;比如执行UserServiceImpl的sayHello方法可以记录这样的一条记录
INSERT INTO `cskaoyan_db`.`cskaoyan_log`(`id`, `operation`, `ip`, `add_time`, `success`, `err_msg`) VALUES (1, 'UserServiceImpl.sayHello', NULL, '2022-11-14 21:11:34', 1, NULL);