目录
- Springboot
- spring对Dao的支持
- AOP
- spring容器事件、监听器
- 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
- 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
- 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</include>
</includes>
</resource>
<!--resources下所有文件也编译到classes下面去-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<!-- 打包时将jsp文件拷贝到classes/META-INF目录下 -->
<resource>
<!-- 指定resources插件处理哪个目录下的资源文件 -->
<directory>src/main/webapp</directory>
<!--注意此次必须要放在此目录下才能被访问到 -->
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
- 在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("bgood@sina.com")
.build();
userInfo = userInfo.toBuilder()
.name("OK")
.email("zgood@sina.com")
.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<SqlSessionFactory>
@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创建代理对象
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<? extends ApplicationEvent> 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作用
- @Configuration的@Target(ElementType.TYPE)注解只能作用在类上,表示该类是一个配置类
- @Component在@Configuration上头,spring会实例化该bean
- @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();
给容器注册组件方式
- componentScan + @component、@controller…等,一般用于自己的写的类
- @Bean,一般用于第三方包里的组件
- @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所在类上的所有注解
- 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");
- spring.factories
对象的初始化和销毁
- 方式一:
@Bean(initMethod = "initMethodName", destroyMethod = "destroyMethodName")
单实例bean的销毁在application.close()的时候进行,多实例bean的销毁容器不会去管理
- 方式二:让Bean实现InitializingBean和disposableBean接口
- 方式三:@PostConstruct和@PreDestroy注解放在bean的方法上
- 方式四: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,怎么办
- 首先按类型选择,若有多个,再按属性名选择,可以使用primary注解优先选择
- Autowire(required = false),否则报错
@Resource和@Autowire区别
Resource和Inject是java规范的注解;
Resource也可以和Autowire一样实现自动装配功能,只能按照名称装配,没有支持primary注解的功能
inject支持primary
@Autowire标注在set方法上的注入原理是什么,标注在有参构造器上呢
- 从IOC容器中寻找参数类型的bean,然后赋值
- 标注在有参构造器上原理同上,spring创建对象调用的有参构造器了,且可以省略Autowire注解
- 在@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<String, Object> 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<String, Object>
// 例如: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源码逻辑
- 读取resources下的xml配置文件,生成一个dom.Document对象
- 通过document得到dom.Element对象,element对象就是beans标签包裹的内容
- 通过element.getChildnodes得到一个NodeList对象,这个接口有两个方法:
Node item(int index);
int getLength();
这个NodeList包含了beans下的所有bean对象,每个bean对象也是一个element
- 解析每个element,将解析的结果封装为一个beandefinition,有属性id、class、scope
- 将beanname作为key,beandefinition作为value放入beandefinitionMap中
- 遍历这个beandefinitionMap,获取每个beandefinition
- 初始化bean:分为单例和多例
- 若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
- 若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调用了哪些方法
- Resource获得其InputStream
- 创建一个org.xml.sax.InputSource,InputStream作为构造参数
- 由DocumentLoader将InputSource加载为Document
- 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能力有什么不同
- FileSystemXmlApplicationContext其基类AbstractApplicationContext继承了DefaultResourceLoader,让自己成为了一个ResourceLoader
- BeanDefinitionReader持有了一个ResourceLoader属性,它正是AbstractXmlApplicationContext,即FileSystemXmlApplicationContext或ClassPathXmlApplicationContext
- BeanDefinitionReader在loadBeanDefinitions时,参数可能是Resource或是string,若是Resource,那么不需要用到ResourceLoader的能力,只有遇到string时
为什么ApplicationContext实现了ResourcePatternResolver接口,还要AbstractApplicationContext继承了DefaultResourceLoader
- ApplicationContext是AbstractApplicationContext的基类,AbstractApplicationContext又是FileSystemXmlApplicationContext的基类
- ResourcePatternResolver只有一个方法getResources,返回的是Resource数组
- AbstractBeanDefinitionReader持有一个属性ResourceLoader,当它instanceof是ResourcePatternResolver时和不是ResourcePatternResolver时做不同处理
- 所以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处理到哪里去了
- 创建了一个BeanDefinitionDocumentReader,XmlBeanDefinitionReader对自己进行了一下包装,成为了XmlReaderContext作为了BeanDefinitionDocumentReader的属性。
- 将Resource改装成document,使用documentReader对document进行处理,document可以getDocumentElement
- BeanDefinitionDocumentReader还有一个重要的属性BeanDefinitionParserDelegate,封装了XmlReaderContext、document的根Element,BeanDefinitionParserDelegate有一个重要的方法parseBeanDefinitionElement,可以将一个Element转化为BeanDefinitionHolder,而这个holder持有了BeanDefinition
- 最后调用BeanDefinitionRegistry的registerBeanDefinition方法将beanname->beandefinition放进DefaultListableBeanFactory的beanDefinitionMap中,别忘了DefaultListableBeanFactory是BeanDefinitionRegistry接口的实现
DefaultListableBeanFactory除了继承了基类BeanFactory具有了例如getbean这样的基本能力外,还继承了哪些类或接口得到了强大功能
new ClassPathXmlApplicationContext(“beans.xml”)流程
- 保存"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,例如子标签:
<props>
<prop key="/welcome.html">welcomeController</prop>
<prop key="/helloWorld.html">helloController</prop>
</props>
而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配置为拦截所有请求时,会拦截静态资源吗
会
放行静态资源有两种方式:
方式一:
<mvc:default-servlet-handler />
检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
注释掉DispatcherServlet,发现请求可以直达webAPP下的静态资源
方式二:
由Spring MVC框架自己处理静态资源,并添加一些有用的附加值功能
<mvc:resources mapping=",/resources/**/" location="/resources/"/>
http://localhost:8080/webhello/resources/c.html
在webAPP下建了一个resources目录,c.html在resources目录下
mapping指的是访问路径,location指的是静态资源的位置,还可以location="/,/resources/"指定多个位置,推荐:
<mvc:resources location="/img/" mapping="/img/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
怎么读取MultipartFile里的Excel
new XSSFWorkbook(MultipartFile.getInputStream())
从swagger角度来看,为什么要限制请求接收方式get或post
如果不限制,则一个请求会有多种接收方式出现在swagger