目录

注解开发简介

注解开发的好处:使用注解的形式替代 xml 配置,将繁杂的 Spring 配置文件从工程中彻底消除掉,简化书写。

注解驱动的弊端:

  • 为了达成注解驱动的目的,可能会将原先很简单的书写,变得更加复杂。

  • XML 中配置第三方开发的资源是很方便的,但使用注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量。

常用注解

Spring 原始注解:主要是替代 <Bean> 的配置。

注解 说明
@Component 使用在类上用于实例化 Bean
@Controller 使用在 web 层类上用于实例化 Bean
@Service 使用在 service 层类上用于实例化 Bean
@Repository 使用在 dao 层类上用于实例化 Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合 @Autowired 一起使用,用于根据名称进行依赖注入引用类型
@Resource 相当于 @Autowired + @Qualifier,按照名称进行注入引用类型
@Value 注入普通类型的属性
@Scope 标注 Bean 的作用范围
@PostConstruct 使用在方法上标注该方法是 Bean 的初始化方法
@PreDestroy 使用在方法上标注该方法是 Bean 的销毁方法

Spring 新注解:

使用上面的注解还不能全部替代 xml 配置文件,还需要使用注解替代的配置如下:

  • 非自定义的Bean的配置:<bean>
  • 加载properties文件的配置:<context:property-placeholder>
  • 组件扫描的配置:<context:component-scan>
  • 引入其他文件:<import>
注解 说明
@Configuration 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解。用于指定 Spring 在初始化容器时要扫描的包。
@ComponentScan 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package="com.itheima"/> 一样。
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中。
@PropertySource 用于加载 .properties 文件中的配置。
@Import 用于导入其他配置类。

启用注解功能

  • 启动注解扫描,加载类中配置的注解项:
<context:component-scan base-package="packageName"/>
  • 说明:

    • 在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描。

    • 扫描过程是以文件夹递归迭代的形式进行的。

    • 扫描过程仅读取合法的 java 文件。

    • 扫描时仅读取 spring 可识别的注解。

    • 扫描结束后会将可识别的有效注解转化为 spring 对应的资源加入 IoC 容器。

  • 注意:

    • 无论是注解格式还是 XML 配置格式,最终都是将资源加载到 IoC 容器中,差别仅仅是数据读取方式不同。

    • 从加载效率上来说,注解优于 XML 配置文件。

bean 定义:@Component、@Controller、@Service、@Repository

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置该类为 spring 管理的 bean 。

  • 示例:

@Component
public class ClassName{}
  • 说明:@Controller、@Service 、@Repository 是 @Component 的衍生注解,功能同 @Component 。

  • 相关属性:

    • value(默认) :定义 bean 的访问 id 。

bean 的引用类型属性注入:@Autowired、@Qualifier

  • 类型:属性注解、方法注解

  • 位置:属性定义上方,方法定义上方。

  • 作用:设置对应属性的对象或对方法进行引用类型传参。

  • 说明:@Autowired 默认按类型装配,指定 @Qualifier 后则可以指定装配的 bean 的 id 。

  • 相关属性:

    • required:定义该属性是否允许为 null 。

bean 的引用类型属性注入:@Inject、@Named、@Resource

  • 说明:

    • @Inject 与 @Named 是 JSR330 规范中的注解,功能与 @Autowired 和 @Qualifier 完全相同,适用于不同架构场景。
    • @Resource 是 JSR250 规范中的注解,可以简化书写格式。
  • @Resource 相关属性:

    • name:设置注入的 bean 的 id 。

    • type:设置注入的 bean 的类型,接收的参数为 Class 类型。

bean 的引用类型属性注入:@Primary

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置类对应的bean按类型装配时优先装配。

  • 说明:@Autowired 默认按类型装配,当出现相同类型的 bean,使用 @Primary 会提高按类型自动装配的优先级,但多个 @Primary 会导致优先级设置无效。

bean 的非引用类型属性注入:@Value

  • 类型:属性注解、方法注解

  • 位置:属性定义上方,方法定义上方。

  • 作用:设置对应属性的值或对方法进行传参。

  • 说明:

    • value 值仅支持非引用类型数据,赋值时对方法的所有参数全部赋值。

    • value 值支持读取 properties 文件中的属性值,通过类属性将 properties 中数据传入类中。

    • value 值支持 SpEL 。

    • @value 注解如果添加在属性上方,可以省略 set 方法(set 方法的目的是为属性赋值)。

bean 的作用域:@Scope

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置该类作为 bean 对应的 scope 属性。

  • 相关属性

    • value(默认):定义 bean 的作用域,默认为 singleton 。

bean 的生命周期:@PostConstruct、@PreDestroy

  • 类型:方法注解

  • 位置:方法定义上方。

  • 作用:设置该类作为 bean 对应的生命周期方法。

加载第三方资源:@Bean

  • 类型:方法注解

  • 位置:方法定义上方。

  • 作用:设置该方法的返回值作为 spring 管理的 bean 。

  • 范例:

@Bean("dataSource")
public DruidDataSource createDataSource() { return ……; }
  • 说明:

    • 因为第三方 bean 无法在其源码上进行修改,因此可以使用 @Bean 解决第三方 bean 的引入问题。

    • 该注解用于替代 XML 配置中的静态工厂与实例工厂创建 bean,不区分方法是否为静态或非静态。

    • @Bean 所在的类必须被 spring 扫描加载,否则该注解无法生效。

  • 相关属性

    • value(默认):定义 bean 的访问 id 。

加载 properties 文件:@PropertySource

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:加载 properties 文件中的属性值。

  • 范例:

@PropertySource(value="classpath:jdbc.properties")
public class ClassName {
@Value("${propertiesAttributeName}")
private String attributeName;
}
  • 说明:不支持*通配格式,一旦加载,所有 spring 控制的 bean 中均可使用对应属性值

  • 相关属性

    • value(默认):设置加载的 properties 文件名。

    • ignoreResourceNotFound:如果资源未找到,是否忽略,默认为 false 。

纯注解开发:@Configuration、@ComponentScan

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置当前类为 spring 核心配置加载类(不再需要 spring 配置文件)。

  • 范例:

@Configuration

@ComponentScan("scanPackageName")

public class SpringConfigClassName{

}


- 说明: - 核心配合类用于替换 spring 核心配置文件,此类可以设置空的,不设置变量与属性。 - bean 扫描工作使用注解 @ComponentScan 替代。 - 加载纯注解格式上下文对象,需要使用 AnnotationConfigApplicationContext: ```java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

导入第三方配置:@Import

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:导入第三方 bean 作为 spring 控制的资源。

  • 范例:

@Configuration
@Import(OtherClassName.class)
public class ClassName {
}
  • 说明:

    • @Import 注解在同一个类上,仅允许添加一次,如果需要导入多个,使用数组的形式进行设定。

    • 在被导入的类中可以继续使用 @Import 导入其他资源(了解)。

    • @Bean 所在的类可以使用导入的形式进入 spring 容器,无需声明为 bean 。

综合案例

maven 依赖:

    <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>

spring 配置类

DataSourceConfig.java

package com.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource; // 数据源配置类
// 相当于 <context:property-placeholder location="classpath:jdbc.properties"/>,且不能用通配符*
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig { @Value("${jdbc.driver}")
private static String driver;
@Value("${jdbc.url}")
private static String url;
@Value("${jdbc.username}")
private static String username;
@Value("${jdbc.password}")
private static String password; @Bean("dataSource") // 将方法的返回值放置Spring容器中
public static DruidDataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

SpringConfig.java

package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; // Spring核心配置类
@Configuration
@ComponentScan("com") // 相当于 <context:component-scan base-package="com"/>
@Import({DataSourceConfig.class}) // 相当于 <import resource=""/>
public class SpringConfig { }

dao 层

UserDaoImpl.java

package com.dao.impl;

import com.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository; // 相当于 <bean id="UserDao" ref="com.dao.impl.UserDaoImpl"/>
// @Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("UserDao save...");
}
}

service 层

UserServiceImpl.java

package com.service.impl;

import com.dao.UserDao;
import com.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.sql.DataSource; // 相当于 <bean id="UserService" ref="com.service.impl.UserServiceImpl"/>
// @Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService { // <property name="userDao" ref="userDao"></property>
// @Autowired // 可单独使用,按照数据类型从spring容器中进行匹配的(有多个相同数据类型的bean时则会有匹配问题)
// @Qualifier("userDao") // 指定bean的id从spring容器中匹配,且要结合@Autowired一起用
@Resource(name="userDao") // 相当于 @Autowired+@Autowired
UserDao userDao; @Resource(name="dataSource")
DataSource dataSource; @Value("${jdbc.driver}") // 读取配置文件中的值
private String driver; /* 使用注解开发可以省略set方法,使用配置文件则不能省略
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
*/ @Override
public void save() {
System.out.println("driver: "+driver);
System.out.println("dataSource: "+dataSource);
userDao.save();
} @PostConstruct
public void init() {
System.out.println("service对象的初始化方法");
} @PreDestroy
public void destroy() {
System.out.println("service对象的销毁方法");
}
}

controller 层

App.java

package com.controller;

import com.config.SpringConfig;
import com.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class App { public static void main(String[] args) {
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = (UserService)context.getBean("userService");
userService.save();
context.close();
}
}

运行结果

service对象的初始化方法
dataSource: {
CreateTime:"2021-12-03 01:05:00",
ActiveCount:0,
PoolingCount:0,
CreateCount:0,
DestroyCount:0,
CloseCount:0,
ConnectCount:0,
Connections:[
]
}
UserDao save...
service对象的销毁方法

整合第三方技术

注解整合 Mybatis

注解整合 MyBatis 的开发步骤

  1. 修改 mybatis 外部配置文件格式为注解格式;
  2. 业务类使用 @Component 声明 bean,使用 @Autowired 注入对象;
  3. 建立配置文件 JDBCConfig 与 MyBatisConfig 类,并将其导入到核心配置类 SpringConfig;
  4. 开启注解扫描;
  5. 使用 AnnotationConfigApplicationContext 对象加载配置项。

项目工程地址

核心内容如下:

  • Maven 依赖:
        <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
  • MybatisConfig.java(Mybatis 配置类):
package com.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import javax.sql.DataSource; public class MybatisConfig { /*
<!-- spring整合mybatis后,创建连接用的对象 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.domain"/>
</bean> <!-- 扫描mybatis映射配置,将其作为spring的bean进行管理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dao"/>
</bean>
*/ // 以下注解代替以上配置文件内容:返回值会作为Spring容器的bean
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.domain");
return sqlSessionFactoryBean;
} @Bean
public MapperScannerConfigurer getMapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.dao");
return mapperScannerConfigurer;
} }
  • SpringConfig.java(Spring 核心配置类):
package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; @Configuration
@ComponentScan("com") // 相当于 <context:component-scan base-package="com"/>
@Import({DataSourceConfig.class, MybatisConfig.class}) // 相当于 <import resource=""/>
public class SpringConfig { }

注解整合 Junit

注解整合 Junit 的开发步骤

  1. Spring 接管 Junit 的运行权,使用 Spring 专用的 Junit 类加载器;

2.为 Junit 测试用例设定对应的 Spring 容器:

  • 从 Spring 5.0 以后,要求 Junit 的版本必须是 4.12 或以上。

  • Junit 仅用于单元测试,不能将 Junit 的测试类配置成 Spring 的 bean,否则该配置将会被打包进入工程中。

示例:整合 Junit5

  • Maven 依赖:
        <dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
  • 测试类:
package com.service;

import com.config.SpringConfig;
import com.domain.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; // 设定spring专用的类加载器
@ExtendWith(SpringExtension.class)
// 设定加载的spring上下文对应的配置
@ContextConfiguration(classes=SpringConfig.class)
public class UserServiceTest { @Autowired
UserService userService; @Test
public void testFindById() {
User user = userService.findById(1);
// System.out.println(user);
Assertions.assertEquals("Mick", user.getName());
} @Test
public void testFindAll() {
List<User> users = userService.findAll();
Assertions.assertEquals(3, users.size());
} }

IoC 底层核心原理

IoC 核心接口

组件扫描器:@ComponentScan

组件扫描器:开发过程中,需要根据需求加载必要的 bean 或排除指定 bean。

应用场景:

  • 数据层接口测试
  • 业务层接口测试
  • 各种运行环境设置

配置扫描器

  • 名称:@ComponentScan

  • 类型:类注解

  • 位置:类定义上方

  • 作用:设置 spring 配置加载类扫描规则

  • 范例:

@Configuration
@ComponentScan(
value="com", // 设置基础扫描路径
excludeFilters = // 设置过滤规则,当前为排除过滤
@ComponentScan.Filter( // 设置过滤器
type= FilterType.ANNOTATION, // 设置过滤方式为按照注解进行过滤
classes=Repository.class) // 设置具体的过滤项。如不加载所有@Repository修饰的bean
)
public class SpringConfig { }
  • includeFilters:设置包含性过滤器

  • excludeFilters:设置排除性过滤器

  • type:设置过滤器类型(过滤策略)

    • ANNOTATION
    • ASSIGNABLE_TYPE
    • ASPECTJ
    • REGEX
    • CUSTOM

自定义扫描器

  • 名称:TypeFilter

  • 类型:接口

  • 作用:自定义类型过滤器

示例:

  • 自定义扫描器
public class MyTypeFilter implements TypeFilter {
public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException {
ClassMetadata cm = metadataReader.getClassMetadata();
tring className = cm.getClassName();
if(className.equals("com.itheima.dao.impl.BookDaoImpl")){
return true; // 进行过滤(拦截)
}
return false; // 不过滤(放行)
}
}
  • 配置类:
@Configuration
@ComponentScan(
value = "com",
excludeFilters = @ComponentScan.Filter(
type= FilterType.CUSTOM,
classes = MyTypeFilter.class
)
)
public class SpringConfig { }

自定义导入器:ImportSelector

bean 只有通过配置才可以进入 spring 容器,被 spring 加载并控制。配置 bean 的方式如下:

  • XML 文件中使用 <bean/> 标签配置

  • 使用 @Component 及衍生注解配置

企业开发过程中,通常需要配置大量的 bean,因此需要一种快速高效配置大量 bean 的方式。

ImportSelector 注解:

  • 类型:接口

  • 作用:自定义 bean 导入器(导入未加 @Component 注解的 bean)

示例:

  • 自定义导入器:
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata icm) {
// 返回需要导入的bean数组。该bean即使没加@Component注解也能被扫描识别
return new String[]{"com.dao.impl.AccountDaoImpl"};
}
}
  • 配置类:
@Configuration
@ComponentScan("com")
@Import(MyImportSelector.class) // 导入自定义导入器
public class SpringConfig {
}

自定义导入器的封装工具类:

import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.TypeFilter; import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set; public class CustomerImportSelector implements ImportSelector { private String expression; public CustomerImportSelector(){
try {
//初始化时指定加载的properties文件名
Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
//设定加载的属性名
expression = loadAllProperties.getProperty("path");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} @Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//1.定义扫描包的名称
String[] basePackages = null;
//2.判断有@Import注解的类上是否有@ComponentScan注解
if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
//3.取出@ComponentScan注解的属性
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
//4.取出属性名称为basePackages属性的值
basePackages = (String[]) annotationAttributes.get("basePackages");
}
//5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
if (basePackages == null || basePackages.length == 0) {
String basePackage = null;
try {
//6.取出包含@Import注解类的包名
basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//7.存入数组中
basePackages = new String[] {basePackage};
}
//8.创建类路径扫描器
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
//9.创建类型过滤器(此处使用切入点表达式类型过滤器)
TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());
//10.给扫描器加入类型过滤器
scanner.addIncludeFilter(typeFilter);
//11.创建存放全限定类名的集合
Set<String> classes = new HashSet<>();
//12.填充集合数据
for (String basePackage : basePackages) {
scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
}
//13.按照规则返回
return classes.toArray(new String[classes.size()]);
}
}

自定义注册器:ImportBeanDefinitionRegistrar

  • 类型:接口

  • 作用:自定义 bean 定义注册器(识别标记了 @Component 的 bean)

示例:

  • 自定义注册器:
// 表示com目录下的bean全部注册
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry r) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(r, false);
TypeFilter tf = new TypeFilter() {
public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException {
return true;
}
};
scanner.addIncludeFilter(tf); // 包含
// scanner.addExcludeFilter(tf); // 排除
scanner.scan("com");
}
}
  • 配置类:
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class) // 作用等同于 @ComponentScan("com")
public class SpringConfig {
}

封装工具类:

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.TypeFilter; import java.io.IOException;
import java.util.Map;
import java.util.Properties; public class CustomeImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { private String expression; public CustomeImportBeanDefinitionRegistrar(){
try {
//初始化时指定加载的properties文件名
Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
//设定加载的属性名
expression = loadAllProperties.getProperty("path");
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//1.定义扫描包的名称
String[] basePackages = null;
//2.判断有@Import注解的类上是否有@ComponentScan注解
if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
//3.取出@ComponentScan注解的属性
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
//4.取出属性名称为basePackages属性的值
basePackages = (String[]) annotationAttributes.get("basePackages");
}
//5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
if (basePackages == null || basePackages.length == 0) {
String basePackage = null;
try {
//6.取出包含@Import注解类的包名
basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//7.存入数组中
basePackages = new String[] {basePackage};
}
//8.创建类路径扫描器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
//9.创建类型过滤器(此处使用切入点表达式类型过滤器)
TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());
//10.给扫描器加入类型过滤器
scanner.addIncludeFilter(typeFilter);
//11.扫描指定包
scanner.scan(basePackages);
}
}

bean 初始化过程解析

bean 统一初始化

  • BeanFactoryPostProcessor

    • 作用:定义了在 bean 工厂对象创建后,bean 对象创建前执行的动作,用于对工厂进行创建后业务处理。

    • 运行时机:当前操作用于对工厂进行处理,仅运行一次。

  • BeanPostProcessor

    • 作用:定义了所有 bean 初始化前后进行的统一动作,用于对 bean 进行创建前业务处理与创建后业务处理。

    • 运行时机:当前操作伴随着每个 bean 的创建过程,每次创建 bean 均运行该操作。

  • InitializingBean

    • 作用:定义了每个 bean 的初始化前进行的动作,属于非统一性动作,用于对 bean 进行创建前业务处理。

    • 运行时机:当前操作伴随着任意一个 bean 的创建过程,保障其个性化业务处理。

  • 注意:上述操作均需要被 spring 容器加载方可运行。

示例:

  • BeanFactoryPostProcessor:
package com.post;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; public class MyBeanFactory implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("Bean工厂制作好了");
}
}
  • BeanPostProcessor:
package com.post;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBean implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean之前巴拉巴拉");
System.out.println(beanName);
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean之后巴拉巴拉");
return bean;
}
}
  • InitializingBean:
package com.service.impl;

import com.dao.UserDao;
import com.domain.User;
import com.service.UserService;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import javax.annotation.Resource;
import java.util.List; @Service("userService")
public class UserServiceImpl implements InitializingBean { // 定义当前bean初始化操作,功效等同于init-method属性配置
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("UserServiceImpl......bean ...init......");
}
}
  • 运行结果:
Bean工厂制作好了
bean之前巴拉巴拉
springConfig
bean之后巴拉巴拉
bean之前巴拉巴拉
com.config.DataSourceConfig
bean之后巴拉巴拉
bean之前巴拉巴拉
dataSource
bean之后巴拉巴拉
bean之前巴拉巴拉
getSqlSessionFactoryBean
bean之后巴拉巴拉
bean之后巴拉巴拉
bean之前巴拉巴拉
userDao
bean之后巴拉巴拉
bean之后巴拉巴拉
bean之前巴拉巴拉
userService
UserServiceImpl......bean ...init......
bean之后巴拉巴拉
bean之前巴拉巴拉
com.service.UserServiceTest.ORIGINAL
bean之后巴拉巴拉

单个 bean 初始化

  • FactoryBean:对单一的 bean 的初始化过程进行封装,达到简化配置的目的。

FactoryBean 与 BeanFactory 区别

  • FactoryBean:封装单个 bean 的创建过程。通常是为了创建另一个 bean 而做的准备工作。

  • BeanFactory:Spring 容器顶层接口,统一定义了 bean 相关的获取操作。

示例:

import org.springframework.beans.factory.FactoryBean;

public class UserServiceImplFactoryBean implements FactoryBean {

    // 重点:返回数据
@Override
public Object getObject() throws Exception {
return new UserServiceImpl();
} @Override
public Class<?> getObjectType() {
return null;
} @Override
public boolean isSingleton() {
return false;
}
}

Spring 注解开发的更多相关文章

  1. Annotation(三)——Spring注解开发

    Spring框架的核心功能IoC(Inversion of Control),也就是通过Spring容器进行对象的管理,以及对象之间组合关系的映射.通常情况下我们会在xml配置文件中进行action, ...

  2. spring注解开发中常用注解以及简单配置

    一.spring注解开发中常用注解以及简单配置 1.为什么要用注解开发:spring的核心是Ioc容器和Aop,对于传统的Ioc编程来说我们需要在spring的配置文件中邪大量的bean来向sprin ...

  3. JAVAEE——SSH项目实战06:统计信息管理、Spring注解开发和EasyUI

    作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7190925.html 一.统计信息管理   二.Spring注解开发 1.service ...

  4. Spring注解开发-全面解析常用注解使用方法之生命周期

    本文github位置:https://github.com/WillVi/Spring-Annotation/ 往期文章:Spring注解开发-全面解析常用注解使用方法之组件注册 bean生命周期 ​ ...

  5. spring注解开发:容器中注册组件方式

    1.包扫描+组件标注注解 使用到的注解如下,主要针对自己写的类 @Controller @Service @Repository @Component @ComponentScan 参考 spring ...

  6. Spring注解开发系列Ⅵ --- AOP&事务

    注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ...

  7. 浅尝Spring注解开发_自定义注册组件、属性赋值、自动装配

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含自定义扫描组件.自定义导入组件.手动注册组件.自动注入方法和参数.使用Spring容器底层组件等 配置 @Confi ...

  8. 浅尝Spring注解开发_Bean生命周期及执行过程

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含Bean生命周期.自定义初始化方法.Debug BeanPostProcessor执行过程及在Spring底层中的应 ...

  9. 浅尝Spring注解开发_AOP原理及完整过程分析(源码)

    浅尝Spring注解开发_AOP原理及完整过程分析(源码) 浅尝Spring注解开发,基于Spring 4.3.12 分析AOP执行过程及源码,包含AOP注解使用.AOP原理.分析Annotation ...

  10. Spring注解开发_Spring容器创建概述

    浅尝Spring注解开发_Spring容器创建概述 浅尝Spring注解开发,基于Spring 4.3.12 概述Spring容器创建的过程,包括12个方法的执行 浅尝Spring注解开发_自定义注册 ...

随机推荐

  1. Noip模拟31 2021.8.5

    T1 Game 当时先胡了一发$\textit{Next Permutation}$... 然后想正解,只想到贪心能求最大得分,然后就不会了.. 然后就甩个二十分的走了... 正解的最大得分(叫它$k ...

  2. Linux C语言多线程编程实例解析

    Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...

  3. Nginx(二):Nginx的四层(L4)和七层(L7)负载均衡

    OSI七层模型 和 TCP/IP四层模型 四层负载均衡( L4 Load Balancing ) 四层负载均衡,主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内 ...

  4. Linux上Qt旋转显示

    对于嵌入式设备来说用于显示的LCD总是千奇百怪,比如说明明是一个竖屏,但是客户却要当横屏使用,也就是意味着我们需要将整个屏幕上显示的内容旋转90度或者270度. 这个操作对于Android系统来说相当 ...

  5. Apache Solr应用服务器存在远程代码执行漏洞👻

    Apache Solr应用服务器存在远程代码执行漏洞 1.描述 Apache Solr是一个开源的搜索服务,使用Java语言开发,主要基于HTTP和Apache Lucene实现的. Solr是一个高 ...

  6. 解决虚拟机安装linux系统无法全屏问题 & vmtools安装

    修改设置 1) 如下图右单击虚拟机名,选择[settings-],调出虚拟机设置界面. 2) 在设置界面选择[hardware]->[CD/DVD2(IDE)]->[Connection] ...

  7. 学好Python不加班系列之SCRAPY爬虫框架的使用

    scrapy是一个爬虫中封装好的一个明星框架.具有高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式. 对于初学者来说还是需要有一定的基础作为铺垫的学习.我将从下方的思维导图中进行逐步的解析 ...

  8. Sqlserver中判断表是否存在

    在sqlserver(应该说在目前所有数据库产品)中创建一个资源如表,视图,存储过程中都要判断与创建的资源是否已经存在  在sqlserver中一般可通过查询sys.objects系统表来得知结果,不 ...

  9. 快速搭建 kvm web 管理工具 WebVirtMgr

    作者:SRE运维博客 博客地址: https://www.cnsre.cn/ 文章地址:https://www.cnsre.cn/posts/211117937177/ 相关话题:https://ww ...

  10. SCTL 涅槃重生:投入 RAL 的怀抱

    在<DistSQL:像数据库一样使用 Apache ShardingSphere>一文中,PMC 孟浩然为大家介绍了 DistSQL 的设计初衷和语法体系,并通过实战操作展示了一条 SQL ...