springboot、spring、springMVC、filter、AOP

目录

    • Springboot
      • 项目结构
      • springboot的核心配置文件
      • 编码配置
      • springboot集成Jsp
      • Swagger
      • 接口返回值统一标准格式
      • 全局异常处理器
      • 全局异常处理集成进接口返回值统一标准格式
      • 参数校验器validator
      • Assert参数校验器
      • @springbootApplication
        • @SpringBootConfiguration
        • @ComponentScan
        • @EnableAutoConfiguration 与 spring.factories
      • springboot异步框架
      • springboot开启事务
      • springboot与lombok
      • springboot与日志
      • PathVariable与RestFul
      • 随机端口
      • 热部署
      • springboot内嵌tomcat原理
      • springboot与Redis
        • Springboot整合Redis
        • RedisTemplate设置序列化器
      • springboot整合mybatis原理
      • springboot使用Interceptor
    • spring对Dao的支持
      • spring整合jdbcTemplate
      • spring事务源码
    • AOP
      • 基本概念
      • JDK动态代理与CGLib动态代理
      • AOP-demo
      • AOP原理
        • 注册组件:AnnotationAwareAspectJAutoProxyCreator
        • AnnotationAwareAspectJAutoProxyCreator创建代理对象
        • InstantiationAwareBeanPostProcessor # postProcessBeforeInstantiation
        • BeanPostProcessor # postProcessAfterInitialization
    • spring容器事件、监听器
      • 实例
      • 事件相关概念
      • SmartApplicationListener
      • 怎么修改SimpleApplicationEventMulticaster默认为自定义EventMulticaster
    • spring注解驱动 new AnnotationConfigApplicationContext(MainConfig.class)
      • @Configuration、@Bean作用
      • @controller、@service、@repository和@component生效范围
      • ComponentScan注解
      • 懒加载@lazy
      • 两个同类型的bean在容器中
      • 根据条件判断是否将bean加入容器
      • 判断容器是否有某个bean定义
      • 获得Environment的方式:
      • 给容器注册组件方式
      • 对象的初始化和销毁
      • 上面方式三@PostConstruct初始化与销毁原理是什么
      • 遇到过的BeanPostProcessor
      • @Value(“张三”)、@Value(“#{xxx}”)、@Value(“${xxx}”)区别
      • @Value可以用在哪些地方
      • 若容器中有多个相同类型的bean,Autowire怎么选择装配的,若容器没有Autowire需要的bean,怎么办
      • @Resource和@Autowire区别
      • @Autowire标注在set方法上的注入原理是什么,标注在有参构造器上呢
      • 将spring底层组件注入到自定义的bean中,使用过这些aware吗:ApplicationContextAware、EmbeddedValueResolverAware、BeanNameAware、EnvironmentAware、ResourceLoaderAware、原理是什么
      • 设置profile的方式
      • 没有标注环境标识的bean,会加载吗
      • PropertySource加载的文件怎么加密解密文件的敏感内容???
    • spring底层基础
      • BeanDefinitionRegistry # registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      • AnnotatedElementUtils和AnnotationAttributes
      • ResourceLoader与DefaultResourceLoader加载Resource实例和源码
      • ClassPathScanningCandidateComponentProvider # findCandidateComponents(String basePackage)加载包下所有的组件beandefinition
      • PathMatchingResourcePatternResolver源码
      • PathMatchingResourcePatternResolver和Resource使用
      • PropertySource
      • 属性编辑器/PropertyEditor
      • BeanWrapperImpl
        • PropertyAccessor、PropertyValue、AttributeAccessor
        • PropertyValues与MutablePropertyValues
      • MetadataReader 、ClassMetadata 、AnnotationMetadata 实例和源码
    • IOC
      • IOC源码AnnotationConfigApplicationContext之refresh() # invokeBeanFactoryPostProcessors(beanFactory)
      • IOC源码AnnotationConfigApplicationContext # refresh() # finishBeanFactoryInitialization(beanFactory)
      • IOC源码AnnotationConfigApplicationContext之register(annotatedClasses)
      • IOC源码(ClassPathXmlApplicationContext)
      • ioc源码逻辑
      • IOC循环引用问题,A里有B属性对象,B里有属性对象,会不会出现死循环
      • 运行时增强,编译时增强
      • BeanFactory接口有哪些方法,getBean、()、()
      • Resource是spring用来做什么的
      • 分析Resource到Document到Element调用了哪些方法
      • Resource是怎么获得InputStream的
      • ClassPathXmlApplicationContext与FileSystemXmlApplicationContext区别
      • FileSystemXmlApplicationContext是如何具有getResource的能力的,与BeanDefinitionReader具有的getResource能力有什么不同
      • 为什么ApplicationContext实现了ResourcePatternResolver接口,还要AbstractApplicationContext继承了DefaultResourceLoader
      • ApplicationContext继承了ResourcePatternResolver接口,其实现由其子类AbstractApplicationContext提供了,那ResourceLoader接口的实现由谁提供的
      • DefaultListableBeanFactory与ClassPathXmlApplicationContext有什么不同
      • ResourceLoader有哪两个方法()()
      • AbstractRefreshableApplicationContext的refreshBeanFactory方法做了些什么
      • FileSystemXmlApplicationContext的beanDefinitionReader在哪里定义的
      • 从上到下梳理一下这个继承体系下的模板方法DefaultResourceLoader、~~AbstractApplicationContext~~ 、AbstractRefreshableApplicationContext、~~AbstractRefreshableConfigApplicationContext~~ 、AbstractXmlApplicationContext、(FileSystemXmlApplicationContext、ClassPathXmlApplicationContext)
      • string类型的spring-config.xml最后怎么包装成Resource对象的
      • 在创建DefaultListableBeanFactory之前,要判断是否已经存在BeanFactory,若存在要销毁所有的bean和关闭BeanFactory,具体怎么实现
      • new XmlBeanDefinitionReader(beanFactory)的beanfactory到哪去了
      • XmlBeanDefinitionReader怎么loadBeanDefinitions的,将beandefinition处理到哪里去了
      • DefaultListableBeanFactory除了继承了基类BeanFactory具有了例如getbean这样的基本能力外,还继承了哪些类或接口得到了强大功能
      • new ClassPathXmlApplicationContext("beans.xml")流程
    • javaweb三大组件之过滤器filter
      • 过滤器的执行顺序是什么
      • 两个以上过滤器怎么执行
      • 为什么使用javax.servlet.http.HttpServletRequest时要引入javaee-api的依赖,且scope为provided
    • SpringMVC
      • springMVC有哪些组件:前端控制器,controller,视图解析器,(),(),modelandview
      • web + maven 的项目目录结构
      • web + maven 的项目web.xml属于类路径下吗
      • springMVC之helloworld
      • 当controller的返回值是string时,一般()
      • 使用controller返回静态页面
      • 返回静态页和重定向区别
      • ControllerClassNameHandlerMapping这个handlermapping是怎么处理映射的,哪些请求会被映射到HelloController
      • 视图解析器与handlermapping区别
      • SimpleUrlHandlerMapping与ControllerClassNameHandlerMapping区别
      • RequestMapping中的produces与consumes区别
      • 怎么使用postman上传文件
      • springMVC接收上传文件
      • 浏览器是通过mime类型还是文件名后缀识别文件的
      • mime类型的语法解释:type/subtype
      • 有哪些常见独立类型,意思是什么,text、image、audio、video,(),动态图是image还是video
      • text有哪些subtype,image呢
      • application有哪些subtype,若没有特定subtype默认是(),text的默认subtype是()
      • response.setContentType(MIME)的作用及参数,导出下载Excel的mime类型是()
      • 导出/下载Excel时候,文件名需要怎么传过去
      • springMVC下载文件
      • DispatcherServlet本质是一个servlet,其servletmapping该怎么配
      • 监听器,过滤器,servlet在web.xml中顺序是
      • DispatcherServlet的servletmapping配置为拦截所有请求时,会拦截静态资源吗
      • 怎么读取MultipartFile里的Excel
      • 从swagger角度来看,为什么要限制请求接收方式get或post

Springboot

项目结构

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

springboot的核心配置文件

  • spring.profile.active
  • @Value(${boot.name})读取配置文件值
@Value(${boot.xxx})
private String name;
@ConfigurationProperties(prefix = "boot")
@Component
public class ConfigClass {

    private String xxx;

    public String getXxx() {
        return xxx;
    }

    public void setXxx(String xxx) {
        this.xxx = xxx;
    }
}


    @Autowired
    private ConfigClass configClass;
    configClass.getXxx()

编码配置

spring.http.encoding.charset=utf-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true

springboot集成Jsp

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--内嵌tomcat对jsp的解析包-->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>


<!--servlet依赖的jar包start-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
</dependency>

<!--jsp依赖jar包-->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
</dependency>

<!-- jstl标签依赖的jar包-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

       <resources>
            <!--java下的xml也编译到classes下面去,默认是不能做到的-->
            <!--使用mybatis时也会用到-->
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml
                
            
            
            
                src/main/resources
                
                    **/*.*</include>
                </includes>
            </resource>
            <!-- 打包时将jsp文件拷贝到classes/META-INF目录下 -->
            <resource>
                <!-- 指定resources插件处理哪个目录下的资源文件 -->
                <directory>src/main/webapp</directory>
                <!--注意此次必须要放在此目录下才能被访问到 -->
                <targetPath>META-INF/resources</targetPath>
                <includes>
                    <include>**/*.*
                
            
        
  
  • 在main下创建一个webapp目录,目录下创建一个myjsp文件夹
spring.mvc.view.prefix=/myjsp/
spring.mvc.view.suffix=.jsp
@RequestMapping("/hello")
public String hello(){
    return "index";
}

Swagger

解决人力编写接口文档问题
交互式API,测试工具

加入两个依赖:
springfox-swagger2
springfox-swagger-ui
spring.swagger2.enabled=true //不过线上要关闭
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Value(value = "${spring.swagger2.enabled}")
    private Boolean swaggerEnabled;

    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(swaggerEnabled)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.xxx"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder().title("接口文档")
                .description("阿甘讲解springboot")
                .termsOfServiceUrl("http:xxx")
                .version("1.0")
                .build();
    }
}
@Api(description = "用户接口")
class MyController{

	@ApiOperation"方法干嘛的"public String myGetMethod(){
	}
}
@ApiParam :描述类方法参数的作用
@ApiModel:描述对象的作用
@ApiModelProperty:对象字段作用

接口返回值统一标准格式

status:状态码
desc;本次状态描述
data:本次返回的数据

初级做法

return Result.suc("xxxx");
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Result<T> {
	private Integer status;
	private String desc;
	private T data;

	public static Result suc(){
		Result result = new Result();
		result.setSuccessCode(ResultCode.SUCCESS);
		return result;
	}
	
	public static Result suc(Object data){
		Result result = new Result();
		result.setSuccessCode(ResultCode.SUCCESS);
		result.setData(data);
		return result;
	}
	
	public static Result fail(ResultCode resultCode){
		Result result = new Result();
		result.setResultCode(resultCode);
		return result;
	}

}
public enum ResultCode{
	SUCCESS(0,"成功");
	SYSTEM_ERRO(10000,"系统异常,稍后再试");

	ResultCode(Integer code,String message){
		this.code = code;
		this.message = message;
	}
}

弊端:每写一个接口都需要手动指定返回值Result.suc
高级做法如下:
springboot提供的ResponseBodyAdvice实现response统一格式,拦截controller方法的返回值

public interface ResponseBodyAdvice<T> {

	boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

	@Nullable
	T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType,
			ServerHttpRequest request, ServerHttpResponse response);

}
@ControllerAdvice(basePackages = "com.xxx")
public class ResponseHandler implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof String) {
            return JsonUtil.object2Json(Result.suc(body));
        }

        return Result.suc(body);
    }
}
controller中方法可以返回
void、string、object、异常、
最后结果返回:
{
	"status""desc":
	"data":
}
是由Result.suc(body)处理的

全局异常处理器

@builder
@AllArgsConstructor
@NoArgsContructor
@Data
public class ErrorResult {
	private Integer status;
	private String messsage;
	private String exception; // java.lang.xxxException

	public static ErrorResult fail(ResultCode resultCode,Throwable e,String message) {
		this.xxx = xxx;
		return result;
	}

	public static ErrorResult fail(ResultCode resultCode,Throwable e) {
		this.xxx = xxx;
		return result;
	}
}
@RestControllerAdvice
@slf4j
public class GlobalExceptionHandler {
	@ResponseStatus(HTTPStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(Throwrable.class)
	public ErrorResult handlerThrowable(Throwable e, HttpServletRequest request){
		ErrorResult error = ErrorResult.fail(ResultCode.SYSTEM_ERROR, e);
		log.error(xxx);
		return error;
	}
}

好处:1.不用强制写try-catch
2.直接controller抛出的运行时异常对客户端很不友好
3.参数校验器不通过会抛异常,但是无法使用try-catch语句直接捕获,只能使用全局异常处理器

  • 将自定义异常集成进全局异常处理器
@Data
public class BusinessException extends RuntimeException{
	protected Integer code;
	protected String message;

	public BusinessException(ResultCode resultCode){
		this.xxx = xxx
	}
}
@ExceptionHandler(Throwrable.class)改为
@ExceptionHandler(BusinessException.class)

全局异常处理集成进接口返回值统一标准格式

{
	"status:":2001
	"message":"用户名已存在"
	"exception":xxx.myException
}
接口格式:
{
	"status":2001
	"desc":"用户名已存在"
	"data":null
}
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    if (body instanceof ErrorResult){
		...// 提取ErrorResult信息到Result并返回
    }
	if (body instanceof String) {
        return JsonUtil.object2Json(Result.suc(body));
    }

    return Result.suc(body);
}

参数校验器validator

spring的validator校验框架遵循了JSR-303验证规范,java specification request的缩写
默认情况下,springboot会引入hibernate validator机制来支持,对其再次封装
1.jsr303是一个标准,只提供规范,不提供实现,规定一些注解:@Null、notnull、Pattern,位于javax.validation.constrains包下。
2.hibernate validation:对jsr303规范的实现,并增加了一些其他校验注解,如@Email、length、range
3.spring-validation:对hibernate-validation进行二次封装,在springMVC 模块添加了自动校验

spring-starter-web // web中自动集成了,springboot天然支持数据校验
class MyVo{
	@NotEmpty(message = "用户名不能为空")
	@Length(min = 6,max = 12,message = "用户名长度必须在6到12之间")
	String username;
	
	@Email(message="请输入正确的邮箱")
	String email;

	@Pattern(regexp = "^(一个正则)$",message = "身份格式验证错误")
	String IDCard;
}
class MyController{

	public xxx method(@Validated MyVo myvo){
	}
}
常用注解:
@null、notnull、max、min...
  • 自定义注解
...
@Constraint(validatedBy = PhoneValidator,class)
@interface MyPhone{
	String message() default "请输入正确手机号"
	...
}
public class PhoneValidator implements ConstraintValidator<Phone,String> {
    @Override
    public void initialize(Phone constraintAnnotation) {

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 实现校验逻辑
        return false;
    }
}
  • 将validator异常加入全局异常处理器
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
method(e,request){
	// 获得e中的异常信息,封装进ErrorResult中
	// 400参数错误
	// 业务异常log.warn就够了
}

Assert参数校验器

上面的jsr校验只能校验参数,而不能校验其他业务代码

User user = findMyUser();
Assert.notNull(user,"用户不存在!");

会抛异常:IllegalArgumentException("用户不存在")
  • 常用Assert
1.逻辑断言
isTrue() //IllegalArgumentsException
state() //IllegalStateException

2.对象和断言
notNull()
isNull()
isInstanceOf()
isAssignable()

3.文本断言
hasLength()
hasText()
doesNotContain()

4.Collection和map断言
notEmpty

5.数组断言
notEmpty
  • 加入全局异常处理器
捕获非法参数异常

@springbootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented //默认情况下生成javadoc是不包括注解的,指定之后doc工具会生成注解的文档
@Inherited // 子类要继承父类的注解,那么父类要加此注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@SpringBootConfiguration

@Configuration
public @interface SpringBootConfiguration {}

相当于将SpringBootApplication 标记位Configuration,里面可以添加@bean

@ComponentScan

前面有说
此处ComponentScan并没有指定任何扫描包名,所以扫描所在类下所有包

@EnableAutoConfiguration 与 spring.factories

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import之前有将
class AutoConfigurationImportSelector {
	selectImports() {
		从META-INF/spring.factories中读取内容去加载
	}
}

spring.factories是springboot的解耦扩展机制,仿照了java的SPI扩展机制实现
spring.factories文件:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...
  • 创建自己的spring.factories文件
在src/main/resources下创建META-INF/spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.AganConfig
AganConfig 可以不被此项目扫描到,因为在spring.factories中配置了

@Configuration
@EnableAsync
public class AganConfig {
    @Bean
    public Agan agan(){
        return new Agan();
    }
}

springboot异步框架

例子一:注册用户,送100积分
1.如果送积分出现异常,不能因为送积分而导致用户注册失败
2.提升性能,用户注册20毫秒,送积分80毫秒,若使用异步,则用户响应只需要20毫秒
例子二:修改用户信息接口,刷新Redis缓存
例子三:下订单,发送APPpush信息

1. @EnableAsync
2. @Async加在异步方法上
两个线程池:
一个是tomcat的用户线程
一个是异步方法所在线程
  • 为Async自定义线程池
为什么要自定义线程池
因为默认使用的是simpleAsyncTaskExecutor,这不是真正的线程池,每次任务都会创建一个新的线程,不能重用线程
推荐使用ThreadPoolTaskExecutor
	@Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        poolTaskExecutor.setCorePoolSize(10);
        // 线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        poolTaskExecutor.setMaxPoolSize(100);
        // 缓存队列
        poolTaskExecutor.setQueueCapacity(50);
        // 核心线程之外的线程在空闲时间到达之后会被销毁
        poolTaskExecutor.setKeepAliveSeconds(200);
        // 异步方法内部线程名称
        poolTaskExecutor.setThreadNamePrefix("myThread-");

        // n当然不是只有CallerRunsPolicy一种可选
        poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        poolTaskExecutor.initialize();
        return poolTaskExecutor;
    }
@Async("threadPoolTaskExecutor") // 指定线程池的名字

springboot开启事务

@EnableTransactionManagement

springboot与lombok

lombok是idea一个插件,也是一个jar包

@Data :放在实体类上,省略get/set方法
@slf4j:省略以下代码:
static final Logger log = LoggerFactory.getLogger(Test.class
@Builder(toBuilder = true)
@Getter
public class UserInfo {
  private String name;
  private String email;
  @MinMoney(message = "金额不能小于0.")
  @MaxMoney(value = 10, message = "金额不能大于10.")
  private Money price;
}
@Builder注解赋值新对象

UserInfo userInfo = UserInfo.builder()
        .name("zzl")
        .email("[email protected]")
        .build();
userInfo = userInfo.toBuilder()
        .name("OK")
        .email("[email protected]")
        .build();

springboot与日志

目前市面流行日志框架:
logback、log4j
slf4j:门面API
logback、log4j都依赖了slf4j

而spring-boot-starter-logging都包含了上面的依赖,不需要再引其他依赖了
spring默认使用logback+slf4j
static final Logger logger = LoggerFactory.getLogger(Test.class)

logger.trace("");
logger.debug("");
logger.info("");
logger.warn("");
logger.error("");
springboot默认只输出info级别后日志

logging.level.com.xxx=trace //trace级别日志也会记录
logging.path=日志存放文件夹 // 根目录是当前项目
// 自定义目录与文件名
logging.file=/xxx/myfile.log
设置控制台日志格式:时间、Thread、level、msg
logging.pattern.console=
logging.pattern.file=

PathVariable与RestFul

@RequestMapping("/boot/user/{id}")
public String hello(@PathVariable("id") Integer id) {
    return "";
}

随机端口

1.避免端口冲突
2.开发人员不用记住ip和端口

application.properties
server.port=${random.int[1024,9999]}

热部署

idea需要手动build一下项目

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

springboot内嵌tomcat原理

一、传统web.xml

web.xml
	(1)classpath:/applicationContext.xml // 扫描业务类,service、dao
	(2)ContextLoaderListener
	(3)DispatcherServlet
			load-on-startup = 1 //web容器启动时执行dispatcherservlet的init方法
			servletMapping = ...
			xxx-servlet.xml

xxx-servlet.xml
	(1)component-scan base-package = "com.xxx"
	(2)InternalResourceViewResolver
			
最后外置tomcat根据web.xml启动

二、

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}
@ComponentScan("")
@Configuration
class AppConfig {

}

tomcat-embed-core
SpringApplication.run():

Tomcat tomca = new Tomcat();
tomcat.setPort(8080);
tomcat.addContext(contextPath:"/",tomcat工作目录:System.getProperty("java.io.temdir"));

tomcat.start();
tomcat.getServer().await();

虽然tomcat启动,但是并没有执行WebApplicationInitializer # onStartup方法

特定文件夹(resources/META-INF/services/containerinitializer)下指定MySpringServletContainerInitializer,让tomcat找到

@HandlesTypes(WebApplicationInitializer.class) // javax.servlet.annotation.HandlesTypes
public class SpringServletContainerInitializer implements 
				javax.servlet.ServletContainerInitializer {
	// tomcat会根据HandlesTypes指定的类型找出所有这个类型的class放到webAppInitializerClasses里面
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) {
		if(WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
			waiClass.newInstance();
			initializer.onStartup(servletContext)
		}
	}
}

还有个问题,toncat要求项目是web项目

修改tomcat.addContext为 tomcat.addWebapp("","")
tomcat.addContext(contextPath:"/",tomcat工作目录:System.getProperty("java.io.temdir"));

整个流程可以执行了,但是还报错找不到jspservlet的错误,因为addWebapp让tomcat一定要加jsp
最后还是要回到addContext
最终这样就行了:

Wrapper w = tomcat.addServlet(contextPath:"",servletName:"",new DispatcherServlet(applicationContext))
w.setLoadOnStartUp(1);
w.addMapping("/");

springboot与Redis

Springboot整合Redis

<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
  redis:
    database: 0
    host: 192.168.1.6
    port: 6379
    password: 123456
    pool:
      max-active: 8
      max-wait: -1
      max-idle: 8
      min-idle: 0
    timeout: 0

spring自动帮我们配置好RedisTemplate

/**
 * 选择redis作为默认缓存工具
 * @param redisTemplate
 * @return
 */
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
    return rcm;
}
@Autowired
private RedisTemplate<String, Object> redisTemplate;
List<Object> mylist = new ArrayList();
// opsForHash()、opsForList
redisTemplate.opsForValue().get("myKey");
//opsForValue是操作String类型的,mylist里面的数据会转为json字符串
// 需要存进去的对象需要实现序列化接口
redisTemplate.opsForValue().set("myKey",mylist);

RedisTemplate设置序列化器

没有序列化器Redis数据库里面的数据乱七八糟,可读性不好

@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {

    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    // 配置连接工厂
    template.setConnectionFactory(factory);

    //使用Jackson2JsonRediSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
    Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

    ObjectMapper om = new ObjectMapper();
    // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jacksonSeial.setObjectMapper(om);

    // 值采用json序列化
    template.setValueSerializer(jacksonSeial);
    //使用StringRedisSerializer来序列化和反序列化redis的key值
    template.setKeySerializer(new StringRedisSerializer());

    // 设置hash key 和value序列化模式
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(jacksonSeial);
    template.afterPropertiesSet();

    return template;
}

springboot整合mybatis原理

  • mybatis独立使用
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
<configuration>
	  <environments default="development">
	    <environment id="development">
	      <transactionManager type="JDBC"/>
	      <dataSource type="POOLED">
	        <property name="driver" value="${driver}"/>
	        <property name="url" value="${url}"/>
	        <property name="username" value="${username}"/>
	        <property name="password" value="${password}"/>
	      </dataSource>
	    </environment>
	  </environments>
	  <mappers>
	    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
	  </mappers>
</configuration>
<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>
public interface BlogMapper {
  	Blog selectBlog(int id);
}
SqlSession session = sqlSessionFactory.openSession()
BlogMapper mapper = session.getMapper(BlogMapper.class)
  • spring注解方式集成mybatis
@Configuration
@PropertySource(value="classpath:/db.properties")
@MapperScan(basePackages="xxx.mapper")
@EnableTransaction
public class MybatisConfiguration {

	@Autowired
	private Environment env;
    /*获取sqlSession工厂*/
    // SqlSessionFactoryBean implements FactoryBean
    @Bean
    @Autowired
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) throws Exception{
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置 MyBatis 配置文件路径
        // 还可以通过new Properties().setProperty,然后setConfigurationProperties
        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        
        // 设置 SQL 映射文件路径
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        // 设置 JavaBean 类型别名,在 SQL 映射文件中就不用写全类名
        //sqlSessionFactoryBean.setTypeAliasesPackage("ssm.pojo");
        return sqlSessionFactoryBean;
    }

    /* Druid 数据源*/
    @Bean
    public DataSource getDataSource() {
        DruidDataSource ds= new DruidDataSource();
        // env.getProperty相当于@Value(${"jdbc.driver"})
        ds.setDiverClassName(env.getProperty("jdbc.driver"));
        ds.setUrl(env.getProperty("jdbc.url"));
        ds.setUsername("root");
        ds.setPassword("root");
        return dataSource;
    }


    /*开启事务管理*/
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 配置一个可以进行批量执行的 sqlSession
     */
    @Bean
    public SqlSessionTemplate getSqlSessionTemplate(SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
        /**
         * Simple Executor -- SIMPLE 普通的执行器,默认
         * Reuse Executor -执行器会重用预处理语句(prepared statements)
         * Batch Executor --批量执行器
         */
        SqlSessionTemplate sessionTemplate = new SqlSessionTemplate(sqlSessionFactoryBean.getObject(), ExecutorType.BATCH);
        return sessionTemplate;
    }
}
mybatis-config.xml:

<configuration>
    <settings>
        <!-- 本地(一级)缓存作用域,默认 SESSION,会缓存一个会话(SqlSession)中执行的所有查询。 设置为 STATEMENT,会话仅作用在语句执行上,对 SqlSession 的调用将不会共享数据,可认为是禁用一级缓存 -->
        <setting name="localCacheScope" value="SESSION"/>
        <!-- 控制台打印SQL -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
        <setting name="useColumnLabel" value="true"/>
        <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>
        <!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <!-- 使用驼峰命名法转换字段。 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->
        <setting name="jdbcTypeForNull" value="NULL"/>
        <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
        <setting name="multipleResultSetsEnabled" value="true"/>
        <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
        <setting name="useGeneratedKeys" value="false"/>
    </settings>
</configuration>
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MybatisConfiguration.class);
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");

最终:this.singletonObjects.get(beanName);
寻找:this.singletonObjects.put
put的是一个MapperFactoryBean:产生UserDao的
FactoryBean的getObject方法是:getSqlSession.getMapper(this.MapperInterface)

寻找哪里创建的FactoryBean
createBean(beanName,mbd,args),发现根据beanDefinition创建的FactoryBean
寻找在哪里完成MapperFactoryBean的beanDefinition注册
发现从getMergeLocalBeandefinition(beanName)取到,也就是从
this.beandefinitionMap.get(beanName)
寻找哪里beandefinitionMap.put了MapperFactoryBean的beandefinition

ClassPathMapperScanner.doScan扫描然后进行注册
class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
	  @Override
	  // 扫描指定Mapper包
	  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
	    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
	    ...
	  }
}
又是谁调用的ClassPathMapperScanner.doScan
class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
	void registerBeanDefinitions(...){
		AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        	.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
       	ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
       	scanner.doScan(StringUtils.toStringArray(basePackages));
	}
}
最后,是在哪里导入的MapperScannerRegistrar 

@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
  • springboot使用mybatis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/weibo?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
@MapperScan(basePackages = "com.example.springmtbatis.mapper")
ConfigurableApplicationContext run = SpringApplication.run(SpringmtbatisApplication.class, args);
UserMapper bean = run.getBean(UserMapper.class);

springboot使用Interceptor

@Configuration
public class WebConfigurer implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }

    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns("/**") 表示拦截所有的请求,
        // excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login", "/register");
    }
}
@Component
public class LoginInterceptor implements HandlerInterceptor {

    //这个方法是在访问接口之前执行的,我们只需要在这里写验证登陆状态的业务逻辑,就可以在用户调用指定接口之前验证登陆状态了
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //每一个项目对于登陆的实现逻辑都有所区别,我这里使用最简单的Session提取User来验证登陆。
        HttpSession session = request.getSession();
        //这里的User是登陆时放入session的
        User user = (User) session.getAttribute("user");
        //如果session中没有user,表示没登陆
        if (user == null){
            //这个方法返回false表示忽略当前请求,如果一个用户调用了需要登陆才能使用的接口,如果他没有登陆这里会直接忽略掉
            //当然你可以利用response给用户返回一些提示信息,告诉他没登陆
            return false;
        }else {
            return true;    //如果session里有user,表示该用户已经登陆,放行,用户即可继续调用自己需要的接口
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

spring对Dao的支持

spring整合jdbcTemplate

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>5.1.5.RELEASE</version>
</dependency>
        
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.14</version>
</dependency>

// IOC
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>

// AOP
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>
@Configuration
public class MainConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/weibo");
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        // dataSource()并不会另外再创建一个数据源,spring会特殊处理
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
        return jdbcTemplate;
    }
}
@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert() {
        String sql = "insert into t_user(username) values (?)";

        jdbcTemplate.update(sql,"wangwu");
    }
}


UserDao userDao = annotationContext.getBean(UserDao.class);
userDao.insert();
  • 加入事务
// 增加下面这个注解
@EnableTransactionManagement
public class MainConfig {
	// 添加事务管理器
    @Bean
    public PlatformTransactionManager transactionManager() {
        // 注意这里要将数据源传入进去
        return new DataSourceTransactionManager(dataSource());
    }
}


@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Transactional
    public void insert() {
        userDao.insert();
        int i = 1 / 0;
    }
}

spring事务源码

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {}
class TransactionManagementConfigurationSelector ... implements ImportSelector {
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				...
			default:
				...
		}
	}
}
class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 其实是注册一个 InfrastructureAdvisorAutoProxyCreator
		AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
	}
	
}
@Configuration
public class ProxyTransactionManagementConfiguration {

	// name = "internalTransactionAdvisor"
	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		advisor.setAdvice(transactionInterceptor());
		return advisor;
	}
	
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}
	
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor() {
		// TransactionInterceptor implements MethodInterceptor
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}
}
class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}
	
	Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
		final TransactionAttribute txAttr = ...;
		final PlatformTransactionManager tm = ...;
		
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
			// getTransactionManager().rollback(txInfo.getTransactionStatus())
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			cleanupTransactionInfo(txInfo);
		}
		// getTransactionManager().commit(txInfo.getTransactionStatus())
		commitTransactionAfterReturning(txInfo);
		return retVal;
	
	}
}
class AnnotationTransactionAttributeSource {
	public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
		this.annotationParsers.add(new SpringTransactionAnnotationParser());
		if (jta12Present) {
			this.annotationParsers.add(new JtaTransactionAnnotationParser());
		}
		if (ejb3Present) {
			this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
		}
	}
}
class SpringTransactionAnnotationParser {
	public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
		AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
				element, Transactional.class, false, false);
		if (attributes != null) {
			return parseTransactionAnnotation(attributes);
		}
	}
	
	TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
		RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

		Propagation propagation = attributes.getEnum("propagation");
		rbta.setPropagationBehavior(propagation.value());
		Isolation isolation = attributes.getEnum("isolation");
		rbta.setIsolationLevel(isolation.value());
		rbta.setTimeout(attributes.getNumber("timeout").intValue());
		rbta.setReadOnly(attributes.getBoolean("readOnly"));
		rbta.setQualifier(attributes.getString("value"));

		List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
		for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
			rollbackRules.add(new RollbackRuleAttribute(rbRule));
		}
		for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
			rollbackRules.add(new RollbackRuleAttribute(rbRule));
		}
		for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
			rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
		}
		for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
			rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
		}
		rbta.setRollbackRules(rollbackRules);

		return rbta;
	}
}

AOP

基本概念

  • 连接点(joinPoint)
  • 切点(PointCut)
  • 增强(Advice)
  • 目标对象(Target)
  • 引介(Introduction)
  • 织入(weaving)
  • 代理(Proxy)
  • 切面(Aspect)
  • advice 和 advisor区别
BeforeAdvice advice;

ProxyFactory pf = new ProxyFactory();
pf.setTarget(targetObj);
pf.addAdvice();
Object proxyObj = pf.getProxy();
ProxyFactory proxyFactory = new ProxyFactory();
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
return proxyFactory.getProxy(getProxyClassLoader());
interface Advisor {
	Advice getAdvice();
	// Return whether this advice is associated with a particular instance
	boolean isPerInstance();
}

advisor包含了advice和目标对象
advisor叫做切面,advice叫做增强;1增强 + 1切点 = 一个切面
我们说的advisor一般指PointCutAdvisor

JDK动态代理与CGLib动态代理

  • CGLib动态代理采用底层字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截父类的方法并顺势织入横切逻辑
  • 由于采用动态创建子类的方式生成代理对象,所以不能对目标类中的final或private方法进行代理
  • jdk动态代理必须要确保拦截的目标方法在接口中有定义
  • 同一个类方法间调用不会被代理
@Component
public class MathCalculator {
    public int div(int i,int j){
        f();
        return i/j;
    }

    public void f(){
        System.out.println("");
    }
}
@Pointcut("execution(public void com.aop.MathCalculator.f())")
    public void pointCut1() {
    }

    @Before("pointCut1()")
    public void logStart(JoinPoint joinPoint) {
    }
MathCalculator mathCalculator = annotationConfigApplicationContext.getBean(MathCalculator.class);
// mathCalculator.div(1,1);不会触发logStart
mathCalculator.f(); // 这个可以触发

AOP-demo

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>
@Component
public class MathCalculator {
    public int div(int i,int j){
        return i/j;
    }
}
@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(public int com.aop.MathCalculator.div(int,int))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println("除法运行"+joinPoint.getSignature().getName()+ Arrays.asList(args));
    }

    @After("pointCut()")
    public void logEnd() {
        System.out.println("除法结束");
    }

    @AfterReturning(value = "pointCut()",returning = "result")
    public void logReturn(Object result) {
        System.out.println("除法正常返回"+result);
    }

    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void logException(Exception ex) {
        System.out.println("除法出现异常"+ex);
    }
    
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
    	System.out.println("执行之前");
        try {
        	// 执行目标方法
            proceed = proceedingJoinPoint.proceed();
            System.out.println("执行之后:" + proceed);
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("执行方法发生了错误:"+e.getMessage());         
        }
        return proceed;
	}
}
@Configuration
@EnableAspectJAutoProxy
public class MainConfig {}


AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
MathCalculator mathCalculator = context.getBean(MathCalculator.class);
mathCalculator.div(1,1);

AOP原理

注册组件:AnnotationAwareAspectJAutoProxyCreator

@EnableAspectJAutoProxy
public class MainConfig {}

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		...
	}
}

// 过度
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);

// 最终
// AUTO_PROXY_CREATOR_BEAN_NAME =
// "org.springframework.aop.config.internalAutoProxyCreator"; 
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);

AnnotationAwareAspectJAutoProxyCreator创建代理对象

springboot、spring、springMVC、filter、AOP_第1张图片

registerBeanPostProcessors(beanFactory)
BeanPostProcessor pp = beanFactory.getBean("internalAutoProxyCreator", BeanPostProcessor.class)
beanFactory.addBeanPostProcessor(postProcessor)

getBean:
	invokeAwareMethods # BeanFactoryAware
		this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory)
		if (this.aspectJAdvisorFactory == null) {
			this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
		}
		this.aspectJAdvisorsBuilder =
				new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);

InstantiationAwareBeanPostProcessor # postProcessBeforeInstantiation

class AbstractAutowireCapableBeanFactory {
	Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
		if (bean != null) {
			bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
		}
	}
}
class AbstractAutoProxyCreator {
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		// advisedBeans是所有需要增强的业务bean,例如MathCalculator 
		// 判断当前bean是否在里面,第一次debug进去并不包含
		if (this.advisedBeans.containsKey(cacheKey)) {
			return null;
		}
		// Advice、Pointcut、Advisor、AopInfrastructureBean是Infrastructure基础类型
		// 子类重写isInfrastructureClass:super.isInfrastructureClass(beanClass) || isAspect(beanClass)
		// 所以切面类型也是基础类型
		// shouldSkip判断isOriginalInstance,永远返回false,所以下面返回false
		if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return null;
		}
	}
}

BeanPostProcessor # postProcessAfterInitialization

class AbstractAutoProxyCreator {
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		return wrapIfNecessary(bean, beanName, cacheKey);
	}
	
	Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			// 创建了一个cglib的增强了的代理对象
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			return proxy;
		}
	}
	
	Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
		ProxyFactory proxyFactory = new ProxyFactory();
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		return proxyFactory.getProxy(getProxyClassLoader());
	}
	
	Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		// 定义的合格的、可用的增强方法:logException、logReturn、logEnd、logStart
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	}
}
class ProxyFactory {

	public Object getProxy(@Nullable ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
	}
	
	// AopProxy有三个子类:CglibAopProxy、JdkDynamicAopProxy、ObjenesisCglibAopProxy
	// 我们一般返回的是CglibAopProxy
	final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		// getAopProxyFactory() = DefaultAopProxyFactory
		// 有两种代理对象JdkDynamicAopProxy(config)或ObjenesisCglibAopProxy(config)
		return getAopProxyFactory().createAopProxy(this);
	}
}
// 执行代理对象会走这个
class CglibAopProxy {

	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
		// this.advised是proxyFactory
		// 获取目标方法将要执行的拦截器链
		// 顺序:ExposeInvocationInterceptor、AfterThrowingAdviceInterceptor、AfterReturningAdviceInterceptor、
		// AfterAdviceInterceptor、AspectAroundAdvice、BeforeAdviceInterceptor
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		if (chain.isEmpty()...){
			// We can skip creating a MethodInvocation: just invoke the target directly
			retVal = methodProxy.invoke(target, argsToUse);
		}else {
			// We need to create a method invocation...
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		}
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
}
// CglibMethodInvocation.proceed
class ReflectiveMethodInvocation implements ProxyMethodInvocation{
	/**
	 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
	 * that need dynamic checks.
	 */
	protected final List<?> interceptorsAndDynamicMethodMatchers;

	Object proceed() throws Throwable {
		//	We start with an index of -1 and increment early.
		// 当前拦截器索引是最后一个拦截器,执行到最后一个拦截器了
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			// 执行目标方法
			return invokeJoinpoint();
		}
		// 从拦截器数组中获取拦截器
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// 没见到这里面来过
		}else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
}
class ExposeInvocationInterceptor {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}
}

class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}
	
	Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
		final TransactionAttribute txAttr = ...;
		final PlatformTransactionManager tm = ...;
		
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
			// getTransactionManager().rollback(txInfo.getTransactionStatus())
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			cleanupTransactionInfo(txInfo);
		}
		// getTransactionManager().commit(txInfo.getTransactionStatus())
		commitTransactionAfterReturning(txInfo);
		return retVal;
	
	}
}


class AspectJAfterThrowingAdvice {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
	}
}


class AfterReturningAdviceInterceptor {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
}


class AspectJAfterAdvice {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
}

// 这个还没弄明白
class AspectJAroundAdvice {
	public Object invoke(MethodInvocation mi) throws Throwable {
		if (!(mi instanceof ProxyMethodInvocation)) {
			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
		}
		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
		ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
		JoinPointMatch jpm = getJoinPointMatch(pmi);
		return invokeAdviceMethod(pjp, jpm, null, null);
	}
}


class MethodBeforeAdviceInterceptor {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		// 调用前置增强
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		// 会走到 return invokeJoinpoint();调用目标方法
		return mi.proceed();
	}
}
// this.advised是proxyFactory
class AdvisedSupport {

	List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
		return cached;
	}

}


class DefaultAdvisorChainFactory implements AdvisorChainFactory {

	List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {
		// advisors:targetClass的所有方法的Advisor
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.add(interceptors);
			}else if (advisor instanceof IntroductionAdvisor) {
				...
			}else {
				...
			}
		}
		
		return interceptorList;
	}

}

registry # MethodInterceptor[] getInterceptors(Advisor advisor){
	Advice advice = advisor.getAdvice();
	if (advice instanceof MethodInterceptor) {
		// AspectJAfterThrowingAdvice、AspectJAfterAdvice
		interceptors.add((MethodInterceptor) advice);
	}
	// adapters:MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter
	for (AdvisorAdapter adapter : this.adapters) {
		if (adapter.supportsAdvice(advice)) {
			interceptors.add(adapter.getInterceptor(advisor));
		}
	}
}

spring容器事件、监听器

实例

public class MailSendEvent extends ApplicationContextEvent {
    // 邮件发送给 “to”
    private String to;

    // source是事件源
    public MailSendEvent(ApplicationContext source,String to) {
        super(source);
        this.to = to;
    }

    public String getTo() {
        return this.to;
    }
}
@Component
public class MailSendListener implements ApplicationListener<MailSendEvent> {
    @Override
    public void onApplicationEvent(MailSendEvent event) {
        System.out.println("MailSendListener:向"+ event.getTo() + "发送一封邮件");
    }
}
@Component
public class MailSender implements ApplicationContextAware {
    // 事件源和容器发布事件都需要 applicationContext
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void sendMail(String to) {
        System.out.println("sendMail 发送邮件...");
        MailSendEvent event = new MailSendEvent(this.applicationContext,to);
        applicationContext.publishEvent(event);
    }
}
MailSender mailSender =(MailSender) annotationConfigApplicationContext.getBean("mailSender");
mailSender.sendMail("中情局");

事件相关概念

  • 事件源:事件产生者,任何EventObject都必须拥有一个事件源
abstract class ApplicationContextEvent extends ApplicationEvent {
	public ApplicationContextEvent(ApplicationContext source) {
		super(source);
	}
}

abstract class ApplicationEvent extends EventObject {
	public ApplicationEvent(Object source) {
		super(source);
	}
}

class EventObject {
	transient Object  source;
	
	public EventObject(Object source) {
        this.source = source;
    }
}
  • 事件监听器注册表:保存事件监听器的地方
abstract class AbstractApplicationContext {
	void addApplicationListener(ApplicationListener<?> listener) {
		if (this.applicationEventMulticaster != null) {
			this.applicationEventMulticaster.addApplicationListener(listener);
		}
		this.applicationListeners.add(listener);
	}
}
  • 事件广播器:负责把事件通知给事件监听器

SmartApplicationListener

public class MyApplicationEvent extends ApplicationContextEvent {
    public MyApplicationEvent(ApplicationContext source) {
        super(source);
    }
}
@Component
public class MySmartListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        if (eventType.isAssignableFrom(MyApplicationEvent.class)) {
            return true;
        }
        return false;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("我监听到了");
    }
}
@Component
public class EventProduce implements ApplicationContextAware {
    ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void sendEvent(){
        MyApplicationEvent myApplicationEvent = new MyApplicationEvent(this.applicationContext);
        applicationContext.publishEvent(myApplicationEvent);
        System.out.println("我发布了事件");
    }
}
  • 源码分析
abstract class AbstractApplicationContext {
	void publishEvent(Object event, @Nullable ResolvableType eventType) {
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}
}
class SimpleApplicationEventMulticaster {
	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		// resolveDefaultEventType(event) = ResolvableType.forInstance(event)
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			// 调用listener.onApplicationEvent(event);
			invokeListener(listener, event);
		}
	}
	
	// 找出符合条件的listener
	Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {
		// Class sourceType = event.getSource().getClass()
		Collection<ApplicationListener<?>> listeners =
					retrieveApplicationListeners(eventType, sourceType, retriever);
	}
	
	
	Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
		// 获得所有的listener
		listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
		// 筛选出监听该event的listener
		for (ApplicationListener<?> listener : listeners) {
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					retriever.applicationListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}
	}
	
	boolean supportsEvent(
			ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

		GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
				(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
		return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
	}
}
class GenericApplicationListenerAdapter implements GenericApplicationListener , SmartApplicationListener{
	final ApplicationListener<ApplicationEvent> delegate;
	
	boolean supportsEventType(ResolvableType eventType) {
		if (this.delegate instanceof SmartApplicationListener) {
			Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
			// 最后调用MySmartListener.supportsEventType方法
			return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
		}
	}
}
  • GenericApplicationListener
@Component
public class MyGenericListener implements GenericApplicationListener {
	// 与smartListener # supportsEventType(Class eventType)只支持ApplicationEvent相比
	// ResolvableType支持任意类型event
    @Override
    public boolean supportsEventType(ResolvableType eventType) {
        boolean equals = eventType.getType().equals(MyApplicationEvent.class);
        if (equals == true) {
            return true;
        }
        return false;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("监听到了");
    }
}

怎么修改SimpleApplicationEventMulticaster默认为自定义EventMulticaster

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Initialize event multicaster for this context.
initApplicationEventMulticaster();
void initApplicationEventMulticaster() {
	// 只要让beanfactory包含applicationEventMulticaster,那么就不会创建SimpleApplicationEventMulticaster
	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
		this.applicationEventMulticaster =
		beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
	}else {
		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);	
	}
}

invokeBeanFactoryPostProcessors会调用下面的PostProcessor

@Component
public class MyBeanfactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerSingleton("applicationEventMulticaster",MyApplicationeventMuticaster.class);
    }
}

spring注解驱动 new AnnotationConfigApplicationContext(MainConfig.class)

@Configuration、@Bean作用

  1. @Configuration的@Target(ElementType.TYPE)注解只能作用在类上,表示该类是一个配置类
  2. @Component在@Configuration上头,spring会实例化该bean
  3. @Bean会将方法返回的对象交给容器管理
@Configuration
public class MainConfig {

    @Bean
    public Person person(){
        return new Person("lisi",20);
    }
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

Person person = applicationContext.getBean(Person.class);
MainConfig mainConfig = applicationContext.getBean(MainConfig.class);

System.out.println(person);
System.out.println(mainConfig);

@controller、@service、@repository和@component生效范围

@Configuration
@ComponentScan(value = {"com.annotationdevelop"})
public class MainConfig {
}

@ComponentScan扫描到的

ComponentScan注解

  • componentScan可以放到任何位置
@Configuration
@ComponentScan(value = {"com.annotationdevelop.service"},excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
})
public class MainConfig {}

主配置类只扫描到BookService
@Service
@ComponentScan("com.annotationdevelop")
public class BookService {}

而BookService去扫描所有的组件
  • 排除controller
@ComponentScan(value = {"com.annotationdevelop"},excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
public @interface ComponentScan {

	@AliasFor("basePackages")
	String[] value() default {};
	
	Filter[] includeFilters() default {};

	Filter[] excludeFilters() default {};
	
	boolean useDefaultFilters() default true;
@interface Filter {

		FilterType type() default FilterType.ANNOTATION;

		@AliasFor("classes")
		Class<?>[] value() default {};
		
		@AliasFor("value")
		Class<?>[] classes() default {};
  • ComponentScan是一个Repeatable注解,什么是@Repeatable
ComponentScan可多次使用在同一个类上,Repeatable是java8的新注解

@Repeatable(ComponentScans.class)
public @interface ComponentScan {
  • ComponentScan的excludeFilters自定义过滤规则
@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
public class MyTypeFilter implements TypeFilter {

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        Resource resource = metadataReader.getResource();

        System.out.println("----------(1)--------------");
        System.out.println(classMetadata.getClassName());
        Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
        for (String annotationType : annotationTypes) {
        	System.out.println("---------(2)---------------");
            System.out.println(annotationType);
        }
        
        System.out.println("-----------(3)-------------");
        if (classMetadata.getClassName().contains("xxx")) {
            return true;
        }
        return false;
    }
}
  • 1处输出的类名是指定包下扫描到的所有组件,但是不包括配置类MainConfig和配置类下的@bean return Person
  • 2处输出的是扫描到的类上的所有注解
  • 3处是判断该类是否要排除出容器的条件,若返回true,则excludeFilter会将该类排除,不会交给容器管理

懒加载@lazy

  • 只有单实例才有懒加载
  • 一般单例创建对象都是跟着容器创建完成同时完成的,而懒加载会等到context.getBean时再创建对象

两个同类型的bean在容器中

public class MainConfig {

    @Bean
    public Person personZS(){
        return new Person("zhangsan",28);
    }

    @Bean
    public Person personLS(){
        return new Person("lisi",20);
    }
}

毫无疑问,容器中会有两个Person 的bean对象,但是下面获取bean会报错:

Person person1 = applicationContext.getBean(Person.class);

这样不会报错:

Person person =(Person) applicationContext.getBean("personZS");

输出所有的BeanDefinitionNames:

String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

注意:personZS、personLS
得到:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookedController
bookDao
bookService
personZS
personLS

虽然不能使用getBean方法根据类类型获得bean 了,但是可以获得该类型的所有beanName:

String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String s : beanNamesForType) {
    System.out.println(s);
}

虽然不能使用getBean方法根据类类型获得bean 了,但是可以使用getBeansOfType根据类型获得所有bean,以下得到张三、李四两个bean的实例

Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
for (Map.Entry<String, Person> personEntry : beansOfType.entrySet()) {
    System.out.println(personEntry);
}

根据条件判断是否将bean加入容器

public class MainConfig {

    @Conditional(value = {WindowsConditional.class})
    @Bean
    public Person personZS(){
        return new Person("zhangsan",28);
    }

    @Conditional(value = {LinuxConditional.class})
    @Bean
    public Person personLS(){
        return new Person("lisi",20);
    }
}
public class WindowsConditional implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Environment environment = context.getEnvironment();
        BeanDefinitionRegistry registry = context.getRegistry();
        ResourceLoader resourceLoader = context.getResourceLoader();

        String osName = environment.getProperty("os.name");
        if (osName.contains("windows")) {
            return true;
        }
        return false;
    }
}

@Conditional(value = {WindowsConditional.class})不只是可以放在方法上,还可以放在@Configuration类上,这样连配置类都不会注入到容器了,更别说配置类添加的组件

判断容器是否有某个bean定义

registry.containsBeanDefinition(“beanName”)

获得Environment的方式:

方式一:applicationContext

Environment environment = applicationContext.getEnvironment();
String osName = environment.getProperty("os.name");
System.out.println(osName);

方式二:ConditionContext

ConditionContext context
context.getEnvironment();

给容器注册组件方式

  1. componentScan + @component、@controller…等,一般用于自己的写的类
  2. @Bean,一般用于第三方包里的组件
  3. @Import,快速给容器导入一个组件,用在配置类上
@Import({Color.class,MyImportSelector.class, MyImportBeanDefinitionRegister.class})
public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.annotationdevelop.domain.Blue",
        					"com.annotationdevelop.domain.Yellow"};
    }
}
public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);

        if (registry.containsBeanDefinition("com.annotationdevelop.domain.Blue")
                && registry.containsBeanDefinition("com.annotationdevelop.domain.Yellow")) {
            registry.registerBeanDefinition("rainBow",beanDefinition);
        }
    }
}

AnnotationMetadata封装了Import所在类上的所有注解

  1. factoryBean
@Bean
public ColorFactoryBean colorFactoryBean(){
    return new ColorFactoryBean();
}
public class ColorFactoryBean implements FactoryBean<Color> {
    public Color getObject() throws Exception {
        return new Color();
    }

    public Class<?> getObjectType() {
        return Color.class;
    }

    public boolean isSingleton() {
        return true;
    }
}
Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
System.out.println(colorFactoryBean.getClass());

输出的是Color.class,要想得到FactoryBean的class,则:

getBean("&colorFactoryBean");
  1. spring.factories

对象的初始化和销毁

  1. 方式一:
@Bean(initMethod = "initMethodName", destroyMethod = "destroyMethodName")

单实例bean的销毁在application.close()的时候进行,多实例bean的销毁容器不会去管理

  1. 方式二:让Bean实现InitializingBean和disposableBean接口
  2. 方式三:@PostConstruct和@PreDestroy注解放在bean的方法上
  3. 方式四:BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization-------------");
        System.out.println("bean" + ":" + bean);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization++++++++++++++++++++");
        System.out.println("bean" + ":" + bean);
        return bean;
    }
}

postProcessBeforeInitialization在构造方法执行后立即执行,在所有初始化执行之前执行,例如前三种方式
postProcessAfterInitialization在所有初始化完成之后立即调用,例如前三种方式的初始化方法

class AbstractAutowireCapableBeanFactory:Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)

applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
	// bdp = AutowiredAnnotationBeanPostProcessor
	MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
	// 准备需要注入的属性对象,beanName -> InjectionMetadata(有injectedElements)
	bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
	
populateBean(beanName, mbd, instanceWrapper);
	// ibp = AutowiredAnnotationBeanPostProcessor
	InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) AutowiredAnnotationBeanPostProcessor;
	PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
		return InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), PropertyValues);
			InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		metadata.inject(bean, beanName, pvs);

exposedObject = initializeBean(beanName, exposedObject, mbd);
	invokeAwareMethods(beanName, bean);
	wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	invokeInitMethods(beanName, wrappedBean, mbd);
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

上面方式三@PostConstruct初始化与销毁原理是什么

由一个叫做InitDestroyAnnotationBeanPostProcessor抽象类实现的,但是自定义的BeanPostProcessor执行更靠前,实现类:CommonAnnotationBeanPostProcessor

遇到过的BeanPostProcessor

ApplicationContextAwareProcessor、ConfigurationClassPostProcessor、PostProcessorRegistrationDelegate、MyBeanPostProcessor、CommonAnnotationBeanPostProcessor、AutowireAnnotationBeanPostProcessor、ApplicationListenerDetector,
按顺序放在一个copyOnWriteArrayList里

@Value(“张三”)、@Value(“#{xxx}”)、@Value(“${xxx}”)区别

  • 第二种是接SpEl表达式,#{beanName.PropName}方式引用bean的属性值
  • 第三种是取出配置文件的值,也就是运行环境中的值,因为配置文件的值都会载入到environment中,需要先使用PropertySource注解载入配置文件

@Value可以用在哪些地方

成员变量上、方法参数上

若容器中有多个相同类型的bean,Autowire怎么选择装配的,若容器没有Autowire需要的bean,怎么办

  1. 首先按类型选择,若有多个,再按属性名选择,可以使用primary注解优先选择
  2. Autowire(required = false),否则报错

@Resource和@Autowire区别

Resource和Inject是java规范的注解;
Resource也可以和Autowire一样实现自动装配功能,只能按照名称装配,没有支持primary注解的功能
inject支持primary

@Autowire标注在set方法上的注入原理是什么,标注在有参构造器上呢

  1. 从IOC容器中寻找参数类型的bean,然后赋值
  2. 标注在有参构造器上原理同上,spring创建对象调用的有参构造器了,且可以省略Autowire注解
  3. 在@bean的方法参数上,Autowire可以省略
@Bean
public RainBow rainBow(@Autowired Color color) {
    RainBow rainBow = new RainBow();
    rainBow.setColor(color);
    return rainBow;
}

将spring底层组件注入到自定义的bean中,使用过这些aware吗:ApplicationContextAware、EmbeddedValueResolverAware、BeanNameAware、EnvironmentAware、ResourceLoaderAware、原理是什么

EmbeddedValueResolverAware

public void setEmbeddedValueResolver(StringValueResolver resolver) {
    String s = resolver.resolveStringValue("你好${os.name}");
}

原理:回调,

if (bean instanceof EmbeddedValueResolverAware) {
	((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}

设置profile的方式

  • VM argument:-Dspring.profiles.active=dev
  • annotationConfigApplicationContext.getEnvironment().setActiveProfiles(“dev”,“test”);

没有标注环境标识的bean,会加载吗

在任何环境都会加载

PropertySource加载的文件怎么加密解密文件的敏感内容???

spring底层基础

BeanDefinitionRegistry # registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

AnnotationConfigApplicationContext annotationContext= new AnnotationConfigApplicationContext(MainConfig.class);

ApplicationContext classpathContext= new ClassPathXmlApplicationContext(configLocations);

annotationContext.register(MyBean.class);

ClassPathXmlApplicationContext classpathContext= new ClassPathXmlApplicationContext(configLocations[0]);
ConfigurableListableBeanFactory beanFactory = classpathContext.getBeanFactory();
beanFactory.registerSingleton("myBean",new MyBean());
MyBean myBean = classpathContext.getBean("myBean", MyBean.class);

AnnotatedElementUtils和AnnotationAttributes

AnnotationAttributes mergedAnnotationAttributes = AnnotatedElementUtils.getMergedAnnotationAttributes(BookDao.class, Scope.class);

class AnnotationAttributes extends LinkedHashMap<String, Object> {
}

public static AnnotationAttributes getMergedAnnotationAttributes(
			AnnotatedElement element, Class<? extends Annotation> annotationType) {

ResourceLoader与DefaultResourceLoader加载Resource实例和源码

  • 实例
DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();
Resource classpathResource = defaultResourceLoader.getResource("classpath:/spring-config.xml");
Resource urlResource = defaultResourceLoader.getResource("https://www.baidu.com/index.php");

// Resource # getFile与获得Inputstream,建议使用流,因为项目打成jar包后不能获得file了
  • 源码
class DefaultResourceLoader implements ResourceLoader {
	Resource getResource(String location) {
		if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}else {
			URL url = new URL(location);
			return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
		}
	}
}

ClassPathScanningCandidateComponentProvider # findCandidateComponents(String basePackage)加载包下所有的组件beandefinition

  • 使用
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);

Set<BeanDefinition> candidateComponents = provider.findCandidateComponents("com.annotationdevelop.service");
for (BeanDefinition candidateComponent : candidateComponents) {
	// Generic bean: class [com.annotationdevelop.service.BookService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\ideaprojects\springIOC-simple\ioc\spring-ioc-test\target\classes\com\annotationdevelop\service\BookService.class]
	// Generic bean: class [com.annotationdevelop.service.service2.BookService2]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\ideaprojects\springIOC-simple\ioc\spring-ioc-test\target\classes\com\annotationdevelop\service\service2\BookService2.class]
    System.out.println(candidateComponent);
}
  • 源码
class ClassPathScanningCandidateComponentProvider {
	// new PathMatchingResourcePatternResolver();
	ResourcePatternResolver resourcePatternResolver;
	
	Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			// 这里是关键
			return scanCandidateComponents(basePackage);
		}
	}
	
	Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		// packageSearchPath = classpath*:com/annotationdevelop/**/*.class
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		// FileSystemResource
		// file [D:\ideaprojects\springIOC-simple\ioc\spring-ioc-test\target\classes\com\annotationdevelop\service\BookService.class]
		Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
		for (Resource resource : resources) {
			// metadataReaderFactory = CachingMetadataReaderFactory
			MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
		}
	}
}

PathMatchingResourcePatternResolver源码

class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
	@Override
	public Resource[] getResources(String locationPattern) {...}
	@Override
	public Resource getResource(String location) {
		return getResourceLoader().getResource(location);
	}
}
interface ResourcePatternResolver extends ResourceLoader {
	Resource[] getResources(String locationPattern)
}
interface ResourceLoader {
	Resource getResource(String location);
	@Nullable
	ClassLoader getClassLoader();
}

PathMatchingResourcePatternResolver和Resource使用

ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

Resource urlResource = resourcePatternResolver.getResource("https://www.baidu.com/index.php");
InputStream inputStream = urlResource.getURL().openStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
Resource classpathResource = resourcePatternResolver.getResource("classpath:/spring-config.xml");
// 项目打成jar包后会抛出fileNotFoundException
File file = classpathResource.getFile();
BufferedReader reader = new BufferedReader(new FileReader(file));
// 不要使用上面的getFile,因为项目打成jar包后会抛出fileNotFoundException
// 但是可以使用inputstream
InputStream inputStream = classpathResource.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(inputStreamReader);

String line = reader.readLine();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
// Ant风格资源地址:** 匹配多层路径,*匹配一层路径,?匹配一个字符
// classPath* 与 不加*的区别:加*匹配类型路径下所有jar包,不加则只匹配成功第一个
// FileSystemResource
Resource[] fileSystemResources = resourcePatternResolver.getResources("classpath*:com/annotationdevelop/service/**/*.class");
for (Resource resource : fileSystemResources) {
	//file [D:\ideaprojects\springIOC-simple\ioc\spring-ioc-test\target\classes\com\annotationdevelop\service\BookService.class]
	//file [D:\ideaprojects\springIOC-simple\ioc\spring-ioc-test\target\classes\com\annotationdevelop\service\service2\BookService2.class]
    System.out.println(resource);
}
UrlResource urlResource = new UrlResource("file:D:\\a.txt");
UrlResource urlResource = new UrlResource("http://www.beans.xml");
UrlResource urlResource = new UrlResource("ftp://www.beans.xml");

PropertySource

class PropertySourceTests {
	@Test
	@SuppressWarnings("serial")
	void equals() {
		Map<String, Object> map1 = new HashMap<String, Object>() {{
			put("a", "b");
		}};
		Map<String, Object> map2 = new HashMap<String, Object>() {{
			put("c", "d");
		}};
		Properties props1 = new Properties() {{
			setProperty("a", "b");
		}};
		Properties props2 = new Properties() {{
			setProperty("c", "d");
		}};

		MapPropertySource mps = new MapPropertySource("mps", map1);
		assertThat(mps).isEqualTo(mps);

		assertThat(new MapPropertySource("x", map1).equals(new MapPropertySource("x", map1))).isTrue();
		assertThat(new MapPropertySource("x", map1).equals(new MapPropertySource("x", map2))).isTrue();
		assertThat(new MapPropertySource("x", map1).equals(new PropertiesPropertySource("x", props1))).isTrue();
		assertThat(new MapPropertySource("x", map1).equals(new PropertiesPropertySource("x", props2))).isTrue();

		assertThat(new MapPropertySource("x", map1).equals(new Object())).isFalse();
		assertThat(new MapPropertySource("x", map1).equals("x")).isFalse();
		assertThat(new MapPropertySource("x", map1).equals(new MapPropertySource("y", map1))).isFalse();
		assertThat(new MapPropertySource("x", map1).equals(new MapPropertySource("y", map2))).isFalse();
		assertThat(new MapPropertySource("x", map1).equals(new PropertiesPropertySource("y", props1))).isFalse();
		assertThat(new MapPropertySource("x", map1).equals(new PropertiesPropertySource("y", props2))).isFalse();
	}
	
		@Test
	@SuppressWarnings("serial")
	void collectionsOperations() {
		Map<String, Object> map1 = new HashMap<String, Object>() {{
			put("a", "b");
		}};
		Map<String, Object> map2 = new HashMap<String, Object>() {{
			put("c", "d");
		}};

		PropertySource<?> ps1 = new MapPropertySource("ps1", map1);
		ps1.getSource();
		List<PropertySource<?>> propertySources = new ArrayList<>();
		assertThat(propertySources.add(ps1)).isEqualTo(true);
		assertThat(propertySources.contains(ps1)).isTrue();
		assertThat(propertySources.contains(PropertySource.named("ps1"))).isTrue();

		PropertySource<?> ps1replacement = new MapPropertySource("ps1", map2); // notice - different map
		assertThat(propertySources.add(ps1replacement)).isTrue(); // true because linkedlist allows duplicates
		assertThat(propertySources).hasSize(2);
		assertThat(propertySources.remove(PropertySource.named("ps1"))).isTrue();
		assertThat(propertySources).hasSize(1);
		assertThat(propertySources.remove(PropertySource.named("ps1"))).isTrue();
		assertThat(propertySources).hasSize(0);

		PropertySource<?> ps2 = new MapPropertySource("ps2", map2);
		propertySources.add(ps1);
		propertySources.add(ps2);
		assertThat(propertySources.indexOf(PropertySource.named("ps1"))).isEqualTo(0);
		assertThat(propertySources.indexOf(PropertySource.named("ps2"))).isEqualTo(1);
		propertySources.clear();
	}
}

属性编辑器/PropertyEditor

属性编辑器主要功能就是将外部设置的值转换为JVM对应的类型,例如字符串类型的时间转换为Date、字面值为10.9转换为double

public class DatePropertyEditor extends PropertyEditorSupport {
    private static final Logger logger = LoggerFactory.getLogger(DatePropertyEditor.class);
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        logger.info("类型转换  传入数据:"+text);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");  
        try {
            setValue(df.parse(text));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <!-- key:将字符串类型转为时间类型或其他类型;value:属性编辑器-->
            <entry key="java.util.Date"
                    value="net.deniro.spring4.editor.DatePropertyEditor" />
        </map>
    </property>
</bean>

CustomEditorConfigurer # customEditors.add(DatePropertyEditor)
最后配置DatePropertyEditor为customEditors这个map的元素

  • BeanWrapperImpl类扩展了PropertyEditorRegistrySupport
  • PropertyEditorRegistrySupport有两个用于保存属性编辑器的Map,defaultEditors和customEditors
  • 但是这两个map都为空

BeanWrapperImpl

PropertyAccessor、PropertyValue、AttributeAccessor

AbstractPropertyAccessor createAccessor(Object target) {
    return new BeanWrapperImpl(target);
}

Person target = createPerson("John", "Paris", "FR");
AbstractPropertyAccessor accessor = createAccessor(target);
accessor.setPropertyValue("address.city", "London");
Object propertyValue = accessor.getPropertyValue("address.city");

accessor.setPropertyValue(new PropertyValue("spouse.age", 35));

// PropertyValue implements ... AttributeAccessor先忘掉这件事,PropertyValue似乎和AttributeAccessor毫无关系
// AttributeAccessor的实现:Map attributes = new LinkedHashMap<>()
interface AttributeAccessor {
	void setAttribute(String name, @Nullable Object value);
	Object getAttribute(String name);
	...
}

interface PropertyAccessor {
	Class<?> getPropertyType(String propertyName);
	Object getPropertyValue(String propertyName);
	void setPropertyValue(String propertyName, @Nullable Object value);
	void setPropertyValue(PropertyValue pv)
	void setPropertyValues(Map<?, ?> map)
}

PropertyValues与MutablePropertyValues

// PropertyValues 类似一个Map,每个Entry相当于PropertyValue 
interface PropertyValues extends Iterable<PropertyValue> {
	default Iterator<PropertyValue> iterator() {
		return Arrays.asList(getPropertyValues()).iterator();
	}

	PropertyValue[] getPropertyValues();

	PropertyValue getPropertyValue(String propertyName);
}

class MutablePropertyValues implements PropertyValues, Serializable {
	private final List<PropertyValue> propertyValueList;

	public MutablePropertyValues addPropertyValue(PropertyValue pv) {}
	public MutablePropertyValues addPropertyValues(@Nullable PropertyValues other) {}
	public MutablePropertyValues addPropertyValues(@Nullable Map<?, ?> other) {}
	public MutablePropertyValues add(String propertyName, @Nullable Object propertyValue) {}

}

MetadataReader 、ClassMetadata 、AnnotationMetadata 实例和源码

  • 实例
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource classpathResource = resourcePatternResolver.getResource("classpath:com/annotationdevelop/service/service2/BookService2.class");
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(classpathResource);

AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
String serviceAnnotationName = Service.class.getName();
String scopeAnnotationName = Scope.class.getName();
Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(serviceAnnotationName);
// 暂时没发现annotatedMethods 的作用
Set<MethodMetadata> annotatedMethods = annotationMetadata.getAnnotatedMethods(serviceAnnotationName);
// 注解所标注的类
String className = annotationMetadata.getClassName();

ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
String[] interfaceNames = classMetadata.getInterfaceNames();
String superClassName = classMetadata.getSuperClassName();
  • 源码
interface MetadataReader {
	Resource getResource();

	ClassMetadata getClassMetadata();

	AnnotationMetadata getAnnotationMetadata();
}
final class SimpleMetadataReader implements MetadataReader {

	private final Resource resource;

	private final ClassMetadata classMetadata;

	private final AnnotationMetadata annotationMetadata;
	
	// 构造需要的resource 是.class文件的resource
	SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) {...}

}
interface ClassMetadata {
	String getClassName();
	boolean isInterface();
	boolean isAnnotation();
	String getSuperClassName();
	String[] getInterfaceNames();
}
interface AnnotationMetadata extends ClassMetadata{
	Map<String, Object> getAnnotationAttributes(String annotationName);
	Set<MethodMetadata> getAnnotatedMethods(String annotationName);
	Set<String> getAnnotationTypes();
}

IOC

IOC源码AnnotationConfigApplicationContext之refresh() # invokeBeanFactoryPostProcessors(beanFactory)

class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
	final AnnotatedBeanDefinitionReader reader;
	final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
	final DefaultListableBeanFactory beanFactory;
	
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this.beanFactory = new DefaultListableBeanFactory();
		this.reader = new AnnotatedBeanDefinitionReader(this);
		register(annotatedClasses);
		refresh();
	}
	void refresh(){
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// Invoke factory processors registered as beans in the context.
		// 注册beandefinitions
		invokeBeanFactoryPostProcessors(beanFactory);
	}
	
	ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}
	
	final void refreshBeanFactory() {
		this.beanFactory.setSerializationId(getId());
	}
	// 注册beandefinitions
	void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	}
}
final class PostProcessorRegistrationDelegate {

	static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			// currentRegistryProcessors集合里就一个ConfigurationClassPostProcessor
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
	}
	
	static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			// 就一个ConfigurationClassPostProcessor
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}
}
class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
		
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		processConfigBeanDefinitions(registry);
	}
	
	void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			// 若是自己标的配置类,例如@Configuration:MainConfig.java,则添加进去
			if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
		// 此时candidates只有一个元素,MainConfig
		// 这里只注入componentscan扫描包下的beandefinition
		parser.parse(candidates);
		
		Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
		// 这里注入@Import标签和@Bean的beandefinition
		this.reader.loadBeanDefinitions(configClasses);
	}
}
class ConfigurationClassParser {
	final ComponentScanAnnotationParser componentScanParser;

	void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			if (bd instanceof AnnotatedBeanDefinition) {
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
		}
	}
	
	final void parse(AnnotationMetadata metadata, String beanName){
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}
	
	void processConfigurationClass(ConfigurationClass configClass) {
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
	}
	
	final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass){
		// AnnotationAttributes extends LinkedHashMap
		// 例如:value = com.annotationdevelop,
		//excludeFilters = AnnotationAttributes(type = Custom,value = com.MyTypeFilter)
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		
		// AnnotationAttributes componentScan是MainConfig的ComponentScan注解的所有属性
		for (AnnotationAttributes componentScan : componentScans) {
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
		}
	}
}
class ComponentScanAnnotationParser {
	Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
		// 在这里,basePackages只有一个元素com.annotationdevelop
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}
}
class ClassPathBeanDefinitionScanner {
	final BeanDefinitionRegistry registry;
	
	Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		
		for (String basePackage : basePackages) {
			Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
		
		return beanDefinitions;
	}
	
	void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
	}
	
}

IOC源码AnnotationConfigApplicationContext # refresh() # finishBeanFactoryInitialization(beanFactory)

abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	void refresh() {
		// 上面那个方法
		// Invoke factory processors registered as beans in the context.
		invokeBeanFactoryPostProcessors(beanFactory);
		
		// 初始化单例
		// Instantiate all remaining (non-lazy-init) singletons.
		finishBeanFactoryInitialization(beanFactory);
	}
	
	void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		...
		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}
}
class DefaultListableBeanFactory extends ... DefaultSingletonBeanRegistry{

	final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
	
	void preInstantiateSingletons() {
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
		for (String beanName : beanNames) {
			if (isFactoryBean(beanName)) {
				// 因为加了前缀,只是把factory的单例放进单例缓存中
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				final FactoryBean<?> factory = (FactoryBean<?>) bean;
			}else {
				getBean(beanName);
			}
		}
	}
	
	Object getBean(String name) {
		return doGetBean(name, null, null, false);
	}
	
	<T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) {
		final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
		if(factoryBean走这个分支) {
			// 其实最后就是:object = factory.getObject();
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		
		if (mbd.isSingleton()) {// 普通bean走这个分支
			// (1)
			sharedInstance = getSingleton(beanName, () -> {
				try {
					// (2)
					return createBean(beanName, mbd, args);
				}
				catch (BeansException ex) {
					// 处理异常的经典
					// Explicitly remove instance from singleton cache: It might have been put there
					// eagerly by the creation process, to allow for circular reference resolution.
					// Also remove any beans that received a temporary reference to the bean.
					destroySingleton(beanName);
					throw ex;
				}
			});
			// (3)
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
	}
	// (1.)
	Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			singletonObject = singletonFactory.getObject();
			addSingleton(beanName, singletonObject);
		}
		return singletonObject;
	}
	// (3.)
	Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
			if (!(beanInstance instanceof FactoryBean)){
				return beanInstance;
			}
	}
	// (2.)
	Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		RootBeanDefinition mbdToUse = mbd;
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		return beanInstance;
	}
	
	Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
		// 准备好创建该对象需要的属性
		applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		
		BeanWrapper instanceWrapper = null;
		// 创建出一个还没注入属性的对象instanceWrapper 
		instanceWrapper = createBeanInstance(beanName, mbd, args);
		final Object bean = instanceWrapper.getWrappedInstance();
		Object exposedObject = bean;
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
	
	void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof MergedBeanDefinitionPostProcessor) {
				// AutowiredAnnotationBeanPostProcessor
				MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
				bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
			}
		}
	}
	
	void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				// AutowiredAnnotationBeanPostProcessor才能注入属性
				PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
			}
		}
	}	
}
class AutowiredAnnotationBeanPostProcessor {
	void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

	PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		metadata.inject(bean, beanName, pvs);
		return pvs;	
	}
	
	InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			metadata = buildAutowiringMetadata(clazz);
			this.injectionMetadataCache.put(cacheKey, metadata);
		}
		return metadata;
	}
	
	InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		Class<?> targetClass = clazz;
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			AnnotationAttributes ann = findAutowiredAnnotation(field);
			if (ann != null) {
				...
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});
	}
	
	AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
		if (ao.getAnnotations().length > 0) {  // autowiring annotations have to be local
			// type是Autowire或者Value...
			for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
				// 找出Field上的所有注解,若注解匹配type,获得该注解的所有属性AnnotationAttributes
				AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
				if (attributes != null) {
					return attributes;
				}
			}
		}
		return null;
	}
}
  • ApplicationListenerDetector # postProcessAfterInitialization
class ApplicationListenerDetector {
	final transient AbstractApplicationContext applicationContext;
	
	public ApplicationListenerDetector(AbstractApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
	
	Object postProcessAfterInitialization(Object bean, String beanName) {
		if (bean instanceof ApplicationListener) {
			this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			return bean;
		}		
	}	
}

abstract class AbstractApplicationContext {
	void addApplicationListener(ApplicationListener<?> listener) {
		if (this.applicationEventMulticaster != null) {
			this.applicationEventMulticaster.addApplicationListener(listener);
		}
		this.applicationListeners.add(listener);
	}
}
class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
	void addApplicationListener(ApplicationListener<?> listener) {
		this.defaultRetriever.applicationListeners.add(listener);
	}
}

IOC源码AnnotationConfigApplicationContext之register(annotatedClasses)

class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
	final AnnotatedBeanDefinitionReader reader;
	
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this.beanFactory = new DefaultListableBeanFactory();
		this.reader = new AnnotatedBeanDefinitionReader(this);
		register(annotatedClasses);
		refresh();
	}
	
	void register(Class<?>... annotatedClasses) {
		this.reader.register(annotatedClasses);
	}
	
}
class AnnotatedBeanDefinitionReader {
	// AnnotationConfigApplicationContext 
	final BeanDefinitionRegistry registry;

	void register(Class<?>... annotatedClasses) {
		for (Class<?> annotatedClass : annotatedClasses) {
			registerBean(annotatedClass);
		}
	}
	
	void registerBean(Class<?> annotatedClass) {
		doRegisterBean(annotatedClass, null, null, null);
	}
	
	void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
}
class BeanDefinitionReaderUtils {
	static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
	}
}

// AnnotationConfigApplicationContext extends GenericApplicationContext
class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		// this.beanFactory = new DefaultListableBeanFactory();
		this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
	}
}

IOC源码(ClassPathXmlApplicationContext)

abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	DefaultListableBeanFactory beanFactory;
		
	public void refresh(){
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
	}

	ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}
	
	final void refreshBeanFactory() {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
	}
	
	void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}
	
	void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
	}
}
abstract class AbstractBeanDefinitionReader {
	final BeanDefinitionRegistry registry;

	loadBeanDefinitions(Resource... resources) {
		for (Resource resource : resources) {
			count += loadBeanDefinitions(resource);
		}
	}
	
	loadBeanDefinitions(Resource resource) {
		InputStream inputStream = resource.getInputStream();
		InputSource inputSource = new InputSource(inputStream);
		doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
	
	doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
		Document doc = doLoadDocument(inputSource, resource);
		registerBeanDefinitions(doc, resource);
	}
	
	registerBeanDefinitions(Document doc, Resource resource) {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	}
	
	XmlReaderContext createReaderContext(Resource resource) {
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, getNamespaceHandlerResolver());
	}
	public XmlReaderContext(
			Resource resource, ProblemReporter problemReporter,
			ReaderEventListener eventListener, SourceExtractor sourceExtractor,
			XmlBeanDefinitionReader reader, 
			NamespaceHandlerResolver namespaceHandlerResolver)
	
}
class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

	XmlReaderContext readerContext;

	void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
	
	void doRegisterBeanDefinitions(Element root) {
		parseBeanDefinitions(root, this.delegate);
	}
	
	void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
					parseDefaultElement(ele, delegate);
				}
				else {
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	
	void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
	}
	
	void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
	}


}

ioc源码逻辑

  1. 读取resources下的xml配置文件,生成一个dom.Document对象
  2. 通过document得到dom.Element对象,element对象就是beans标签包裹的内容
  3. 通过element.getChildnodes得到一个NodeList对象,这个接口有两个方法:
Node item(int index);
int getLength();

这个NodeList包含了beans下的所有bean对象,每个bean对象也是一个element

  1. 解析每个element,将解析的结果封装为一个beandefinition,有属性id、class、scope
  2. 将beanname作为key,beandefinition作为value放入beandefinitionMap中
  3. 遍历这个beandefinitionMap,获取每个beandefinition
  4. 初始化bean:分为单例和多例
  5. 若scope为singleton,使用Class.forName.newInstance创建bean对象,然后将beanName=bean对象放入singletonObjectsMap中,然后再为这个bean对象属性赋值,属性值又分为普通string、int这种和其他bean,遇到普通string属性时,直接通过属性name拼接出setAttributeName方法,再通过class.getDeclaredMethod获得Method对象,然后Method.invoke;遇到其他bean作为属性时,那么又从beandefinitionMap中根据beanName取出beandefinition,创建对象,这里有个递归,最后将属性bean对象赋值给外层bean。其实在创建单例对象时,会先从singletonObjectsMap检查这个对象是否已经存在,若存在直接返回已存在的bean
  6. 若scope为prototype,获取bean的时候则不会从缓存中获取,创建bean的时候也不会将bean放入缓存

IOC循环引用问题,A里有B属性对象,B里有属性对象,会不会出现死循环

A注入B的时候,创建B,B又需要注入A,那么又创建A,解决这个问题很简单,A创建出一个还没有注入属性的空对象时就将其放入map中 ,当B需要注入A时,先从map中检索是否有A,有A则无需再创建A了

运行时增强,编译时增强

BeanFactory接口有哪些方法,getBean、()、()

containsBean、isSingleton、isPrototype、
Class getType(string name)

Resource是spring用来做什么的

spring用来封装I/O操作的类,例如我们的beandefinition信息放在类路径的xml文件中,new ClassPathResource(String path)来定位,若放在文件系统中,则使用FileSystemResource

分析Resource到Document到Element调用了哪些方法

  1. Resource获得其InputStream
  2. 创建一个org.xml.sax.InputSource,InputStream作为构造参数
  3. 由DocumentLoader将InputSource加载为Document
  4. Document有getDocumentElement获得一个element对象

Resource是怎么获得InputStream的

例如ClassPathResource里有个ClassLoader属性和path

InputStream = this.classLoader.getResourceAsStream(this.path)
InputStream  = this.clazz.getResourceAsStream(this.path)

ClassPathXmlApplicationContext与FileSystemXmlApplicationContext区别

加载的Resource不同,例如:FileSystemXmlApplicationContext的getResourceByPath方法返回的是FileSystemResource,ClassPathXmlApplicationContext的getConfigResources返回的是Resource数组;getResourceByPath是DefaultResourceLoader的方法,默认实现是返回ClassPathContextResource,getResourceByPath主要是被getResource方法调用,而getResource主要是被AbstractBeanDefinitionReader调用

FileSystemXmlApplicationContext是如何具有getResource的能力的,与BeanDefinitionReader具有的getResource能力有什么不同

  1. FileSystemXmlApplicationContext其基类AbstractApplicationContext继承了DefaultResourceLoader,让自己成为了一个ResourceLoader
  2. BeanDefinitionReader持有了一个ResourceLoader属性,它正是AbstractXmlApplicationContext,即FileSystemXmlApplicationContext或ClassPathXmlApplicationContext
  3. BeanDefinitionReader在loadBeanDefinitions时,参数可能是Resource或是string,若是Resource,那么不需要用到ResourceLoader的能力,只有遇到string时

为什么ApplicationContext实现了ResourcePatternResolver接口,还要AbstractApplicationContext继承了DefaultResourceLoader

  1. ApplicationContext是AbstractApplicationContext的基类,AbstractApplicationContext又是FileSystemXmlApplicationContext的基类
  2. ResourcePatternResolver只有一个方法getResources,返回的是Resource数组
  3. AbstractBeanDefinitionReader持有一个属性ResourceLoader,当它instanceof是ResourcePatternResolver时和不是ResourcePatternResolver时做不同处理
  4. 所以ApplicationContext的子类在AbstractBeanDefinitionReader里一定走的是ResourcePatternResolver这个逻辑

ApplicationContext继承了ResourcePatternResolver接口,其实现由其子类AbstractApplicationContext提供了,那ResourceLoader接口的实现由谁提供的

也是AbstractApplicationContext提供,因为AbstractApplicationContext继承了DefaultResourceLoader,而DefaultResourceLoader提供了实现

DefaultListableBeanFactory与ClassPathXmlApplicationContext有什么不同

DefaultListableBeanFactory是一个纯粹的IOC容器,需要配置特定的读取器和resource,例如xmlBeandefinitionReader、classPathResource,而applicationcontext看名字就知道全都指定好了

ResourceLoader有哪两个方法()()

getResource、getClassLoader

AbstractRefreshableApplicationContext的refreshBeanFactory方法做了些什么

new了DefaultListableBeanFactory和XmlBeanDefinitionReader
new reader(factory).loadbeanDefinitions(resource)

FileSystemXmlApplicationContext的beanDefinitionReader在哪里定义的

其父类AbstractXmlApplicationContext的loadBeanDefinitions(DefaultListableBeanFactory)方法中new了一个XmlBeanDefinitionReader

从上到下梳理一下这个继承体系下的模板方法DefaultResourceLoader、AbstractApplicationContext 、AbstractRefreshableApplicationContext、AbstractRefreshableConfigApplicationContext 、AbstractXmlApplicationContext、(FileSystemXmlApplicationContext、ClassPathXmlApplicationContext)

DefaultResourceLoader

有getResource方法实现,所以其子类都具有getResource的能力,它有一个步骤getResourceByPath,并且已经有了一个默认实现 return ClassPathContextResource,而FileSystemXmlApplicationContext覆盖了对此步骤的实现,返回了FileSystemResource。

AbstractRefreshableApplicationContext

有refreshBeanFactory实现,创建了DefaultListableBeanFactory,启动了抽象的loadBeanDefinitions(DefaultListableBeanFactory),具体实现由子类AbstractXmlApplicationContext提供

AbstractXmlApplicationContext

有loadBeanDefinitions(DefaultListableBeanFactory)的实现,创建了XmlBeanDefinitionReader,并且reader.loadBeanDefinitions(resource)

FileSystemXmlApplicationContext

重写了DefaultResourceLoader的getResourceByPath步骤,返回的是FileSystemResource

ClassPathXmlApplicationContext

父类的getConfigResources返回值是null,ClassPathXmlApplicationContext重写了getConfigResources,返回了Resource类型的属性,当构造传入Resource时,调用这个方法,返回值就不会是null,所以BeanDefinitionReader其loadBeanDefinitions方法参数则是Resource,而不是string

string类型的spring-config.xml最后怎么包装成Resource对象的

ClassPathXmlApplicationContext的基类AbstractRefreshableConfigApplicationContext有一个string数组类型的属性configLocations,保存了“spring-config.xml”,其实AbstractBeanDefinitionReader的参数不一定是要Resource,String也行,string会被转化成Resource,那么谁有将string转化为Resource的能力呢?ResourceLoader的子接口ResourcePatternResolver对ResourceLoader做了增强,其getResources方法传入string,返回Resource

在创建DefaultListableBeanFactory之前,要判断是否已经存在BeanFactory,若存在要销毁所有的bean和关闭BeanFactory,具体怎么实现

AbstractRefreshableApplicationContext持有一个DefaultListableBeanFactory,若BeanFactory不为空,那么调用ConfigurableBeanFactory接口的destroySingletons方法,关闭
BeanFactory只需将BeanFactory置为空就行。那什么时候将创建的BeanFactory赋值为ApplicationContext的属性呢?loadBeanDefinitions(beanFactory),XmlBeanDefinitionReader.loadBeanDefinitions(resource)之后

new XmlBeanDefinitionReader(beanFactory)的beanfactory到哪去了

XmlBeanDefinitionReader的构造其实需要的参数是BeanDefinitionRegistry,而DefaultListableBeanFactory也实现了BeanDefinitionRegistry接口,不用想也知道这个BeanDefinitionRegistry是XmlBeanDefinitionReader的一个成员变量

XmlBeanDefinitionReader怎么loadBeanDefinitions的,将beandefinition处理到哪里去了

  1. 创建了一个BeanDefinitionDocumentReader,XmlBeanDefinitionReader对自己进行了一下包装,成为了XmlReaderContext作为了BeanDefinitionDocumentReader的属性。
  2. 将Resource改装成document,使用documentReader对document进行处理,document可以getDocumentElement
  3. BeanDefinitionDocumentReader还有一个重要的属性BeanDefinitionParserDelegate,封装了XmlReaderContext、document的根Element,BeanDefinitionParserDelegate有一个重要的方法parseBeanDefinitionElement,可以将一个Element转化为BeanDefinitionHolder,而这个holder持有了BeanDefinition
  4. 最后调用BeanDefinitionRegistry的registerBeanDefinition方法将beanname->beandefinition放进DefaultListableBeanFactory的beanDefinitionMap中,别忘了DefaultListableBeanFactory是BeanDefinitionRegistry接口的实现

DefaultListableBeanFactory除了继承了基类BeanFactory具有了例如getbean这样的基本能力外,还继承了哪些类或接口得到了强大功能

new ClassPathXmlApplicationContext(“beans.xml”)流程

  1. 保存"beans.xml"到configLocations。AbstractRefreshableConfigApplicationContext

javaweb三大组件之过滤器filter

过滤器的执行顺序是什么

web.xml中配置的顺序

两个以上过滤器怎么执行

执行A过滤器的dofilter方法
执行A的dofilter方法中的内容,若遇到filterChain.doFilter,则执行下一个过滤器
过滤器执行完后再执行目标资源
执行完目标资源后,最后执行filterChain.doFilter之后的内容,注意这次执行是从后到前

为什么使用javax.servlet.http.HttpServletRequest时要引入javaee-api的依赖,且scope为provided

SpringMVC

springMVC有哪些组件:前端控制器,controller,视图解析器,(),(),modelandview

handlerMapping、适配器

web + maven 的项目目录结构

跟maven项目目录结构相比,在main目录下多了一个webapp与java和resources平级,webAPP下会有一个WEB-INF目录和一个index.jsp,WEB-INF一定存在,index.jsp一般会有,web-INF目录下有一个web.xml文件

web + maven 的项目web.xml属于类路径下吗

不属于,

springMVC之helloworld

web-INF下两个文件:web.xml以及HelloWeb-servlet.xml
web.xml有两个子元素,servlet和servletmapping,servlet和servletmapping都需配置servlet-name子元素,不同的是servlet主要配置servlet-class这个前端控制器org…DispatcherServlet,而servlet-mapping主要是配置url-pattern,一般为/,表示所有请求。
HelloWeb-servlet.xml文件的名字不能随便取,必须和servlet-name配置的名字一致,不是完全相同,HelloWeb部分相同就行了,否则会报错。HelloWeb-servlet.xml配置了一个InternalResourceViewResolver视图解析器的bean,这个bean有两个property子标签,一个是prefix,一个suffix,确定了这两个值那么controller返回逻辑视图时就不需要写页面的全路径了

当controller的返回值是string时,一般()

返回是页面的逻辑视图,而不是json数据,要返回json数据,可以使用restcontroller或responsebody

使用controller返回静态页面

跟返回视图一样,return “redirect:/jsp/final.html”,多了一个redirect

返回静态页和重定向区别

都是 return "redirect: ,但是静态页末尾是.html,重定是一个requestMapping的路径

ControllerClassNameHandlerMapping这个handlermapping是怎么处理映射的,哪些请求会被映射到HelloController

/hello*

视图解析器与handlermapping区别

都是一个bean,需要配置
视图解析器要配prefix和suffix,用来找到返回视图的
handlermapping例如ControllerClassNameHandlerMapping,用来找到controller的

SimpleUrlHandlerMapping与ControllerClassNameHandlerMapping区别

SimpleUrlHandlerMapping将指定好的路径匹配到指定好的controller,例如子标签:

		
            welcomeController           
            helloController
         

而ControllerClassNameHandlerMapping则不需要配置路径,意思就是将/hello*请求映射到HelloController

RequestMapping中的produces与consumes区别

consumes限制请求数据类型,例如consumes=“application/json” 限制只接收json数据
produces:将返回值转指定类型,其实有responsebody,produces = { “application/json;charset=UTF-8” }这个作用重复了

怎么使用postman上传文件

选择post请求方式
从body的form-data、x-www-form-urlencoded、raw、binary中选择form-data
添加headers:Content-Type = multipart/form-data
最后选择需要上传的文件,选择文件上传时,要注意key旁边还可以选择file或text,记住选择file,因为上传文件的表单不一定只能上传文件

springMVC接收上传文件

配置CommonsMultipartResolver这个bean,还可以限制文件的大小
使用MultipartFile 类型的参数接收文件
MultipartFile有transferTo方法可以将自己内容传给一个File,也可以获得MultipartFile的inputstream,如果你上传的是一个Excel,可以使用如下方式保存

new XSSFWorkbook(file.getInputStream())

要获取文件名,在这个请求头中

Content-Disposition:form-data; name="file"; filename="test.txt"

浏览器是通过mime类型还是文件名后缀识别文件的

mime类型
因此Web服务器在响应头中添加正确的MIME类型非常重要。如果配置不正确,浏览器可能会曲解文件内容,网站将无法正常工作,并且下载的文件也会被错误处理。

mime类型的语法解释:type/subtype

类型/子类型
type 表示可以被分多个子类的独立类别。subtype 表示细分后的每个类型

有哪些常见独立类型,意思是什么,text、image、audio、video,(),动态图是image还是video

application:表明是某种二进制数据
text:普通文本,理论上人类可读
动态图是image

text有哪些subtype,image呢

text/plain, text/html, text/css, text/javascript
image/gif, image/png, image/jpeg

application有哪些subtype,若没有特定subtype默认是(),text的默认subtype是()

application/octet-stream、/json、/pdf、/xml

response.setContentType(MIME)的作用及参数,导出下载Excel的mime类型是()

告诉浏览器我传过去的数据类型
response.setContentType(“text/html; charset=utf-8”); html
(“text/plain; charset=utf-8”); 文本
还有json的:application/json
response.setContentType(“application/vnd.ms-excel”)

导出/下载Excel时候,文件名需要怎么传过去

excelfileName = new String(excelfileName.getBytes(“utf-8”), “ISO-8859-1”);
response.setHeader(“Content-Disposition”, “attachment;” + “filename=” + excelfileName);

springMVC下载文件

准备好被下载的文件,可能是一个HSSFWorkbook对象,可能是一个路径filepath,

DispatcherServlet本质是一个servlet,其servletmapping该怎么配

一般是 / ,匹配所有请求
也可以是 *.do 或 *.html

监听器,过滤器,servlet在web.xml中顺序是

先是监听器,再是过滤器,最后servlet

DispatcherServlet的servletmapping配置为拦截所有请求时,会拦截静态资源吗


放行静态资源有两种方式:
方式一:


检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
注释掉DispatcherServlet,发现请求可以直达webAPP下的静态资源
方式二:
由Spring MVC框架自己处理静态资源,并添加一些有用的附加值功能


http://localhost:8080/webhello/resources/c.html

在webAPP下建了一个resources目录,c.html在resources目录下
mapping指的是访问路径,location指的是静态资源的位置,还可以location="/,/resources/"指定多个位置,推荐:

    
     
  

怎么读取MultipartFile里的Excel

new XSSFWorkbook(MultipartFile.getInputStream())

从swagger角度来看,为什么要限制请求接收方式get或post

如果不限制,则一个请求会有多种接收方式出现在swagger

你可能感兴趣的:(技术)