文章目录
篇1
Spring学习小结_1
https://blog.csdn.net/m0_58730471/article/details/130075657?spm=1001.2014.3001.5501
12 Bean的生命周期
Bean的初始化阶段
Bean实例属性填充
Spring在进行属性注入时,会分为如下几种情况:
注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
<bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
<property name="name" value="hyl"/>
</bean>
注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
<bean class="com.hyl.mapper.impl.UserDaoImpl" id="userDao"/>
<bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
<property name="userDao" ref="userDao"/>
</bean>
注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题
<bean class="com.hyl.mapper.impl.UserDaoImpl" id="userDao">
<property name="userService" ref="userService"/>
</bean>
<bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
<property name="userDao" ref="userDao"/>
</bean>
循环引用
多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用"
<bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
<property name="userDao" ref="userDao"/>
</bean>
<bean class="com.hyl.mapper.impl.UserDaoImpl" id="userDao">
<property name="userService" ref="userService"/>
</bean>
执行创建过程:
Spring提供了三级缓存
存储 完整Bean实例
和 半成品Bean实例
,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
图解:
Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。
Aware接口 | 回调方法 | 作用 |
---|---|---|
ServletContextAware | setServletContext(ServletContext context) | Spring框架回调方法注入ServletContext对象,web环境下才生效 |
BeanFactoryAware | setBeanFactory(BeanFactory factory) | Spring框架回调方法注入beanFactory对象 |
BeanNameAware | setBeanName(String beanName) | Spring框架回调方法注入当前Bean在容器中的beanName |
ApplicationContextAware | setApplicationContext(ApplicationContext applicationContext) | Spring框架回调方法注入applicationContext对象 |
Spring IoC 整体流程总结
13 Spring整合MyBatis剖析
xml整合形式
导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
applicationConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--读取外部数据库配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--创建userService对应的bean-->
<bean class="com.hyl.service.impl.UserServiceImpl" id="userService" >
<property name="userMapper" ref="userMapper"/>
</bean>
<!--配置数据源,使用el表达式,从db.properties中加载-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<!--创建SqlSessionFactory存入容器中-->
<bean id="sessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描指定路径下的mapper接口,自动创建对象,还是通过
this.getSqlSession().getMapper(this.mapperInterface)-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hyl.mapper"/>
</bean>
</beans>
配置SqlSessionFactoryBean作用是向容器中提供SqlSessionFactory
SqlSessionFactoryBean实现了FactoryBean和InitializingBean两个接口,所以会自动执行getObject() 和afterPropertiesSet()方法
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
...
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
....
}
配置MapperScannerConfigurer作用是扫描Mapper,向容器中注册Mapper对应的MapperFactoryBean
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor和InitializingBean两个接口,会在postProcessBeanDefinitionRegistry方法中向容器中注册MapperFactoryBean
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.set....
...
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
}
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
...
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
...
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
....
//设置Mapper的beanClass是org.mybatis.spring.mapper.MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
.....
//设置MapperBeanFactory 进行自动注入
//autowireMode取值:1是根据名称自动装配,2是根据类型自动装配
definition.setAutowireMode(2);
}
}
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
public int scan(String... basePackages) {
...
this.doScan(basePackages);
...
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
...
//将扫描到的类注册到beanDefinitionMap中,此时beanClass是当前类全限定名
this.registerBeanDefinition(definitionHolder, this.registry);
...
return beanDefinitions;
}
}
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionTemplate = this.createSqlSessionTemplate(sqlSessionFactory);
}
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
}
14 Spring常用注解
<bean> 标签及其标签属性的配置
<bean id="" name="" class="" scope="" lazy-init="" init-method="" destroy-method="" abstract="" autowire="" factory-bean="" factory-method=""></bean>
xml配置 | 注解 | 描述 |
---|---|---|
<bean id=“” class=“”> | @Component | 被该注解标识的类,会在指定扫描范围内被Spring加载并实例化 |
<bean scope=“”> | @Scope | 在类上或使用了@Bean标注的方法上,标注Bean的作用范围,取值为singleton或prototype |
<bean lazy-init=“”> | @Lazy | 在类上或使用了@Bean标注的方法上,标注Bean是否延迟加载,取值为true和false |
<bean init-method=“”> | @PostConstruct | 在方法上使用,标注Bean的实例化后执行的方法 |
<bean destroy-method=“”> | @PreDestroy | 在方法上使用,标注Bean的销毁前执行方法 |
Bean依赖注入的注解
属性注入注解 | 描述 |
---|---|
@Value | 使用在字段或方法上,用于注入普通数据 |
@Autowired | 使用在字段或方法上,用于根据类型(byType)注入引用数据 |
@Qualifier | 使用在字段或方法上,结合@Autowired,根据名称注入 |
@Resource | 使用在字段或方法上,根据类型或名称进行注入 |
不同点:
@Qualifier配合@Autowired可以完成根据名称注入Bean实例,使用@Qualifier指定名称
@Autowired
@Qualifier("userDao2")
private UserDao userDao;
@Autowired
@Qualifier("userDao2")
public void setUserDao(UserDao userDao){
System.out.println(userDao);
}
@Component衍生注解
为了每层Bean标识的注解语义化更加明确,@Component又衍生出如下三个注解
@Component衍生注解 | 描述 |
---|---|
@Repository | 在Dao层类上使用 |
@Service | 在Service层类上使用 |
@Controller | 在Web层类上使用 |
非自定义Bean注解
//将方法返回值Bean实例以@Bean注解指定的名称存储到Spring容器中
@Bean("dataSource")
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
/****************************************************/
@Bean
@Autowired //根据类型匹配参数
public Object objectDemo01(UserDao userDao){
System.out.println(userDao);
return new Object();
}
@Bean
public Object objectDemo02(@Qualifier("userDao") UserDao userDao,
@Value("${jdbc.username}") String username){
System.out.println(userDao);
System.out.println(username);
return new Object();
}
Bean配置类的注解
@Configuration
public class ApplicationContextConfig {}
@Configuration
@ComponentScan({"com.xxx.service","com.xxx.dao"})
public class ApplicationContextConfig {}
@Configuration
@ComponentScan
@PropertySource({"classpath:jdbc.properties","classpath:xxx.properties"})
public class ApplicationContextConfig {}
如下一个基础的配置类(代替applicationContext.xml)
@Configuration
@ComponentScan({"com.xxx.service","com.xxx.dao"})
@PropertySource("classpath:jdbc.properties")
@Import(MybatisConfig.class)
public class ApplicationContextConfig {
}
其他注解
@Primary
@Primary注解用于标注相同类型的Bean优先被使用权,@Primary 是Spring3.0引入的,与@Component和@Bean一起使用,标注该Bean的优先级更高,则在通过类型获取Bean或通过@Autowired根据类型进行注入时,会选用优先级更高的。
@Repository("userDao")
public class UserDaoImpl implements UserDao{}
@Repository("userDao2")
@Primary
public class UserDaoImpl2 implements UserDao{}
@Profile
@Profile 注解的作用同于xml配置时学习profile属性<beans profile=“test”>,是进行环境切换使用的。
注解 @Profile 标注在类或方法上,标注当前产生的Bean从属于哪个环境,只有激活了当前环境,被标注的Bean才能被注册到Spring容器里,不指定环境的Bean,任何环境下都能注册到Spring容器里。
@Repository("userDao")
@Profile("test")
public class UserDaoImpl implements UserDao{}
@Repository("userDao2")
public class UserDaoImpl2 implements UserDao{}
注解整合mybatis
详细过程看这篇
https://blog.csdn.net/m0_58730471/article/details/128595925?spm=1001.2014.3001.5501
最主要的就是将下述的xml中的内容通过注解实现
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--配置SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置Mapper包扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"></property>
</bean>
使用@Bean将DataSource和SqlSessionFactoryBean存储到Spring容器中
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
//dataSource.setXxx(); Xxx->driverClassName ,url ,username,password
....
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
使用注解@MapperScan进行指明需要扫描的Mapper在哪个包下(代替MapperScannerConfigurer)
@Configuration
@ComponentScan("com.xxx")
@MapperScan("com.xxx.mapper")
public class ApplicationContextConfig {
}
注解方式,Spring整合MyBatis的原理,关键在于@MapperScan,@MapperScan不是Spring提供的注解,是MyBatis为了整合Spring,在整合包org.mybatis.spring.annotation中提供的注解。
源码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends Annotation> annotationClass() default Annotation.class;
// ... ...
}
14 AOP
概念 | 单词 | 解释 |
---|---|---|
目标对象 | Target | 被增强的方法所在的对象 |
代理对象 | Proxy | 对目标对象进行增强后的对象,客户端实际调用的对象 |
连接点 | Joinpoint | 目标对象中可以被增强的方法 |
切入点 | Pointcut | 目标对象中实际被增强的方法 |
通知\增强 | Advice | 增强部分的代码逻辑 |
切面 | Aspect | 增强和切入点的组合 |
织入 | Weaving | 将通知和切入点组合动态组合的过程 |
例子:
//表示访问修饰符为public、无返回值、在com.itheima.aop包下的TargetImpl类的无参方法show
execution(public void com.itheima.aop.TargetImpl.show())
//表述com.itheima.aop包下的TargetImpl类的任意方法
execution(* com.itheima.aop.TargetImpl.*(..))
//表示com.itheima.aop包下的任意类的任意方法
execution(* com.itheima.aop.*.*(..))
//表示com.itheima.aop包及其子包下的任意类的任意方法
execution(* com.itheima.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..))
通知名称 | 配置方式 | 执行时机 |
---|---|---|
前置通知 | < aop:before > | 目标方法执行之前执行 |
后置通知 | < aop:after-returning > | 目标方法执行之后执行,目标方法异常时,不在执行 |
环绕通知 | < aop:around > | 目标方法执行前后执行,目标方法异常时,环绕后方法不在执行 |
异常通知 | < aop:after-throwing > | 目标方法抛出异常时执行 |
最终通知 | < aop:after > | 不管目标方法是否有异常,最终都会执行 |