【Java】Spring5学习
基础环境与文档资料:
见黑马视频:
https://www.bilibili.com/video/BV1P44y1N7QG
依赖坐标:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.cloud9</groupId>
<artifactId>Spring5</artifactId>
<version>1.0-SNAPSHOT</version> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
</project>
一、ApplicationContext接口
package cn.cloud9; import cn.cloud9.bean.Component1;
import cn.cloud9.event.UserRegisteredEvent;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource; import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map; /**
* @projectName: Spring5
* @author: OnCloud9
* @date: 2023年02月07日 09:13
* @version: 1.0
*/
@Slf4j
@SpringBootApplication
public class MainApplication { @SneakyThrows
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(MainApplication.class, args);
log.info("ConfigurableApplicationContext -> {}", applicationContext); /*
* ApplicationContext的功能
* 1、I18N支持, MessageSource接口实现
* - 默认读取的配置文件:messages.properties
* - 指定文件文件名称 中文zh 英文en 日文ja
*/
String cnHello = applicationContext.getMessage("hello", null, Locale.CHINA);
String enHello = applicationContext.getMessage("hello", null, Locale.ENGLISH);
String jpHello = applicationContext.getMessage("hello", null, Locale.JAPANESE);
log.info("I18N 国际化参数读取 CN -> {}, EN -> {}, JP -> {}", cnHello, enHello, jpHello); /*
* ApplicationContext的功能
* 2、读取类路径下的资源
* - classpath: * 读取当前项目下的类路径资源
* - classpath*: * 读取项目所有依赖下的类路径
*/
Resource[] resources = applicationContext.getResources("classpath:*");
Arrays.stream(resources).forEach(resource -> log.info("classpath: -> {}", resource)); resources = applicationContext.getResources("classpath*:*");
Arrays.stream(resources).forEach(resource -> log.info("classpath*: -> {}", resource)); // 获取Spring的SPI配置文件
resources = applicationContext.getResources("classpath*:META-INF/spring.factories");
Arrays.stream(resources).forEach(resource -> log.info("META-INF/spring.factories: -> {}", resource)); /*
* ApplicationContext的功能
* 3、读取配置文件的值
*/
ConfigurableEnvironment environment = applicationContext.getEnvironment(); // 系统变量 + 配置文件
String javaHome = environment.getProperty("java_home");
String serverPort = environment.getProperty("server.port");
log.info("javaHome -> {}, serverPort -> {}", javaHome, serverPort); /*
* ApplicationContext的功能
* 4、发布事件
* - 发布事件的意义是为了解耦业务逻辑
*/ // 1、使用ApplicationContext直接发布事件
UserRegisteredEvent userRegisteredEvent = new UserRegisteredEvent(applicationContext);
applicationContext.publishEvent(userRegisteredEvent); // 2、使用Bean对象发布事件
Component1 component1 = applicationContext.getBean("component1", Component1.class);
component1.registerBusinessAction();
}
}
I18N国际化配置文件:
UserRegisteredEvent事件源类:
package cn.cloud9.event; import org.springframework.context.ApplicationEvent; /**
* 用户注册事件类
* @author OnCloud9
* @version 1.0
* @project Spring5
* @date 2023年02月08日 10:45
*/
public class UserRegisteredEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public UserRegisteredEvent(Object source) {
super(source);
}
}
在Component2类的监听方法:
package cn.cloud9.bean; import cn.cloud9.event.UserRegisteredEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; /**
* @projectName: Spring5
* @author: OnCloud9
* @date: 2023年02月07日 09:30
* @version: 1.0
*/
@Slf4j
@Component
public class Component2 { /**
* @author OnCloud9
* @date 2023/2/8 11:01
* @description
* @params [event]
* @return void
*/
@EventListener
public void receiveEvent(UserRegisteredEvent event) {
log.info("用户注册事件触发, 监听方法执行!!!!");
}
}
Component1中的发布事件方法:
package cn.cloud9.bean; import cn.cloud9.event.UserRegisteredEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component; import javax.annotation.Resource; /**
* @projectName: Spring5
* @author: OnCloud9
* @date: 2023年02月07日 09:30
* @version: 1.0
*/
@Slf4j
@Component
public class Component1 { @Resource
private ApplicationEventPublisher applicationEventPublisher; /**
* @author OnCloud9
* @date 2023/2/8 11:06
* @description 用户注册业务行为
* @params []
* @return void
*/
public void registerBusinessAction() {
log.info("用户注册业务行为开始!");
applicationEventPublisher.publishEvent(new UserRegisteredEvent(this));
}
}
启动时打印信息:
2023-02-08 14:36:50.523 INFO 6548 --- [ main] cn.cloud9.bean.Component2 : 用户注册事件触发, 监听方法执行!!!!
2023-02-08 14:36:50.524 INFO 6548 --- [ main] cn.cloud9.bean.Component1 : 用户注册业务行为开始!
2023-02-08 14:36:50.524 INFO 6548 --- [ main] cn.cloud9.bean.Component2 : 用户注册事件触发, 监听方法执行!!!!
二、BeanFactory接口
/*
* BeanFactory
* 1、是ApplicationContext的父接口
* 2、是Spring的核心容器
* 3、ApplicationContext组合了BeanFactory的功能
* 4、获取Bean对象,控制反转,依赖注入,Bean的生命周期的各种功能,由BeanFactory的实现类完成
*/
Field field = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
field.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
Map<String,Object> beanTanker = (Map<String,Object>)field.get(beanFactory);
beanTanker.entrySet().stream().filter(e -> e.getKey().startsWith("component")).forEach(e -> {
log.info("key -> {}, value -> {}", e.getKey(), e.getValue());
});
import cn.cloud9.MainApplication;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource;
import java.util.Map; /**
* @author OnCloud9
* @version 1.0
* @project Spring5
* @date 2023年02月08日 11:12
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MainApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BeanFactoryTest { /**
* @author OnCloud9
* @date 2023/2/8 13:47
* BeanFactory
* - 不会主动调用BeanFactory的后置处理器
* - 不会主动调用Bean的后置处理器
* - 不会主动初始化单例Bean对象
* - 不会解析BeanFactory, 包括${} #{} 值注入
* Bean的后处理有排序逻辑
*
*/
@Test
public void beanInitializeProcess() {
/*
* Bean的定义, 初始化, scope, 销毁,
*/
final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); /* 1、生成Bean的定义对象 */
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition(); /* 2、Bean工厂注册定义对象 */
beanFactory.registerBeanDefinition("config", beanDefinition); /* 3、获取注册的Bean定义名称集合 */
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) log.info("beanDefinitionName: {}", beanDefinitionName);
} @Configuration
static class Config { @org.springframework.context.annotation.Bean
public Bean1 bean1() {
return new Bean1();
} @org.springframework.context.annotation.Bean
public Bean2 bean2() {
return new Bean2();
}
} @Data
private static class Bean1 {
@Autowired
private Bean2 bean2;
} private static class Bean2 {} }
在注册Bean定义对象之后
beanFactory.registerBeanDefinition("config", beanDefinition);
BeanFactory对象中只有当前这个config的Bean定义对象
一些自动装配注解,事件监听需要后置处理器的加入:
/* 4、添加一些常用的后处理器 */
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
/*
* 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
*/
log.info("添加后置处理器后...");
beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) log.info("beanDefinitionName : {}", beanDefinitionName);
打印结果:
2023-02-08 14:54:26.964 INFO 33432 --- [ main] BeanFactoryTest : 添加后置处理器后...
2023-02-08 14:54:26.964 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: config
2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor
2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor
2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerFactory
但是我们发现Bean1和Bean2并没有在BeanFactory中注册
BeanFactory的后置处理器只是被添加到BeanFactory,并没有调用处理方法
所以需要把每个处理器的处理方法都调用一遍才行
/* 5、获取BeanFactory处理器的容器, 用于BeanFactory的后置处理器的添加 */
Map<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
processorMap.values().forEach(p -> {
/* 调用每个处理器的处理方法, 扩充Bean的定义 */
p.postProcessBeanFactory(beanFactory);
}); log.info("在后置处理器处理后..."); /* config下的 bean1 bean2 被注册进来 */
beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) log.info("beanDefinitionName: {}", beanDefinitionName);
再次打印之后发现,Bean1和Bean2加入进来了:
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : 在后置处理器处理后...
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: config
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerFactory
2023-02-08 14:54:26.973 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: bean1
2023-02-08 14:54:26.973 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: bean2
在Bean1中我们设置了Bean2成员属性,并且标注自动装配
然后在BeanFactory获取Bean1内的Bean2实例时,发现Bean2对象并没有注入:
/* 在没有扫描@Autowired注解驱动时不能装填Bean实例 */
log.info("打印bean2对象:{}", beanFactory.getBean(Bean1.class).getBean2());
打印结果:
2023-02-08 15:09:38.299 INFO 3852 --- [ main] BeanFactoryTest : 打印bean2对象:null
因为之前的处理只针对BeanFactory的后置处理,对于一个配置Bean来说
配置Bean中要注册的Bean并不归于BeanFactory的后置处理,而是Bean的后置处理
所以这里需要做Bean的后置处理:
/* 添加Bean的后置处理器 */
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor); /* 在Bean的处理器添加之后,Bean2的对象装配了 */
log.info("打印bean2对象:{}", beanFactory.getBean(Bean1.class).getBean2());
打印结果:
2023-02-08 15:15:54.219 INFO 23160 --- [ main] BeanFactoryTest : 打印bean2对象:BeanFactoryTest$Bean2@2c6aed22
Bean不会被BeanFactory主动初始化,在首次获取这个Bean时初始化
如果需要的情况,可以调用预先实例化方法,提前创建
/* Bean对象一般在调用getBean获取时才创建, 也可以调用预先实例化方法提前创建 (只针对scope为单例的对象) */
beanFactory.preInstantiateSingletons();
2、自动装配的执行顺序
新增一个InterFaceType接口,Bean3和Bean4同时实现这个接口
在Bean1注册这个接口,Spring如何装配这个成员属性?
@Data
private static class Bean1 {
@Autowired
private Bean2 bean2; // 1、指定声明bean名称
// @Autowired @Qualifier("bean4")
// private InterFaceType interFaceType; // 2、或者变量名称也可以匹配bean名称
// @Autowired
// private InterFaceType bean4; // 3、使用@Resource声明了名称,则优先级高于变量名称
// @Resource(name = "bean3")
// private InterFaceType bean4; // 4、又加 @Autowired 又加 @Resource, 交给后处理器的顺序决定先由哪个注解解析器处理
@Autowired @Qualifier("bean4")
@Resource(name = "bean3")
private InterFaceType interFaceType;
}
private static class Bean2 {} private interface InterFaceType {}
private static class Bean3 implements InterFaceType {}
private static class Bean4 implements InterFaceType {}
我们打印这个接口类型看看:
/*
* 可以看到默认是自动装配注解处理器先执行,所以bean4先被装填
* org.springframework.context.annotation.internalAutowiredAnnotationProcessor
* org.springframework.context.annotation.internalCommonAnnotationProcessor
*/
log.info("InterFaceType Spring实际装填对象 -> {}", beanFactory.getBean(Bean1.class).getInterFaceType());
打印结果:
2023-02-08 15:22:30.444 INFO 23516 --- [ main] BeanFactoryTest : InterFaceType Spring实际装填对象 -> BeanFactoryTest$Bean4@3c46dcbe
若要改变注解的处理顺序,就改变Bean后置处理器类的处理顺序:
/* 更改后置处理器的添加顺序 将@Resource优先被添加执行 @Autowired滞后 */
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator())
.forEach(beanFactory::addBeanPostProcessor); /*
* 默认是自动装配注解处理器先执行
* org.springframework.context.annotation.internalAutowiredAnnotationProcessor
* org.springframework.context.annotation.internalCommonAnnotationProcessor
*/
log.info("InterFaceType Spring实际装填对象 -> {}", beanFactory.getBean(Bean1.class).getInterFaceType());
打印结果:
2023-02-08 15:29:52.865 INFO 32756 --- [ main] BeanFactoryTest : InterFaceType Spring实际装填对象 -> BeanFactoryTest$Bean3@3c46dcbe
3、排序比较器对象
基于这个对象来决定处理器的先后顺序
beanFactory.getDependencyComparator()
在一开始的BeanFactory注册中就已经放置了
比较器基于处理器的顺序属性决定先后:
这里在源码可以看到两个顺序值:
我们写个测试类比较便知:
@Test
public void processorOrderCompare() {
int orderForResource = Ordered.LOWEST_PRECEDENCE - 3;
int orderForAutowired = 2147483645;
log.info("@Resource -> {}, @Autowired -> {}", orderForResource, orderForAutowired);
log.info("@Resource > @Autowired ? {}", orderForResource > orderForAutowired);
}
打印结果:
2023-02-08 15:57:31.929 INFO 17840 --- [ main] BeanFactoryTest : @Resource -> 2147483644, @Autowired -> 2147483645
2023-02-08 15:57:31.931 INFO 17840 --- [ main] BeanFactoryTest : @Resource > @Autowired ? false
三、ApplicationContextImpl的实现
4种上下文的实现方式配置:
package cn.cloud9.spring; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller; import java.net.URL; @Slf4j
public class ApplicationContextImpl { public static void main(String[] args) {
classpathXmlApplicationContext();
systemXmlApplicationContext();
annotationConfigApplicationContext();
webServletApplicationContext();
} /**
* @author OnCloud9
* @date 2023/2/8 16:30
* @description xml方式配置bean
* @params []
* @return void
*/
private static void classpathXmlApplicationContext() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("context1.xml");
for (String beanDefinitionName : classPathXmlApplicationContext.getBeanDefinitionNames()) {
log.info("xmlRegisteredBean: {}", beanDefinitionName);
}
log.info("inside bean2 is {}", classPathXmlApplicationContext.getBean(Bean1.class).getBean2());
} /**
* @author OnCloud9
* @date 2023/2/8 16:35
* @description 文件系统方式读取xml配置bean
* @params []
* @return void
*/
private static void systemXmlApplicationContext() {
URL resource = ApplicationContextImpl.class.getClassLoader().getResource("context1.xml");
String path = resource.getPath();
FileSystemXmlApplicationContext fsXmlApplicationContext = new FileSystemXmlApplicationContext(path);
for (String beanDefinitionName : fsXmlApplicationContext.getBeanDefinitionNames()) {
log.info("systemXmlRegisteredBean: {}", beanDefinitionName);
}
log.info("inside bean2 is {}", fsXmlApplicationContext.getBean(Bean1.class).getBean2());
} /**
* @author OnCloud9
* @date 2023/2/8 16:35
* @description 当前配置类方式实现Bean配置
* @params []
* @return void
*/
private static void annotationConfigApplicationContext() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfigurationClass.class);
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
log.info("javaAnnotationBean: {}", beanDefinitionName);
}
log.info("inside bean2 is {}", applicationContext.getBean(Bean1.class).getBean2());
} /**
* @author OnCloud9
* @date 2023/2/9 09:10
* @description WebServlet应用上下文对象Bean配置
* @params []
* @return void
*/
private static void webServletApplicationContext() {
AnnotationConfigServletWebServerApplicationContext applicationContext =
new AnnotationConfigServletWebServerApplicationContext(WebMvcConfig.class);
} @Data
static class Bean1 {
private Bean2 bean2;
}
static class Bean2 {} @Configuration
static class BeanConfigurationClass {
@Bean
public Bean1 bean1(Bean2 bean2) {
Bean1 bean1 = new Bean1();
bean1.setBean2(bean2);
return bean1;
} @Bean
public Bean2 bean2() {
return new Bean2();
}
} @Configuration
static class WebMvcConfig {
/* ServletWeb服务器工厂对象配置,默认按Tomcat实现 */
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
} /* 配置前置请求分发处理器 */
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
} /* 分发注册Bean */
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
} /* 直接注册一个控制器 */
@Bean("/controller1")
public Controller controller() {
return (request, response) -> {
response.getWriter().write("这是配置类的Controller!");
return null;
};
}
}
}
XML方式的配置文件(context1.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <bean id="bean1" class="cn.cloud9.spring.ApplicationContextImpl.Bean1" >
<property name="bean2" ref="bean2" />
</bean>
<bean id="bean2" class="cn.cloud9.spring.ApplicationContextImpl.Bean2" /> <!--
后处理器的注册
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
-->
<context:annotation-config />
</beans>
【Java】Spring5学习的更多相关文章
- 一位资深程序员大牛推荐的Java技术学习路线图
Web应用,最常见的研发语言是Java和PHP. 后端服务,最常见的研发语言是Java和C/C++. 大数据,最常见的研发语言是Java和Python. 可以说,Java是现阶段中国互联网公司中,覆盖 ...
- Java的学习之路
记事本 EditPlus eclipse Java的学习软件,已经系统性学习Java有一段时间了,接下来我想讲一下我在Java学习用到的软件. 1.第一个软件:记事本 记事本是Java学习中最基础的编 ...
- Java多线程学习笔记
进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...
- Java Web 学习路线
实际上,如果时间安排合理的话,大概需要六个月左右,有些基础好,自学能力强的朋友,甚至在四个月左右就开始找工作了.大三的时候,我萌生了放弃本专业的念头,断断续续学 Java Web 累计一年半左右,总算 ...
- Java基础学习-- 继承 的简单总结
代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...
- 20145213《Java程序设计学习笔记》第六周学习总结
20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...
- [原创]java WEB学习笔记95:Hibernate 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- Java多线程学习(转载)
Java多线程学习(转载) 时间:2015-03-14 13:53:14 阅读:137413 评论:4 收藏:3 [点我收藏+] 转载 :http://blog ...
- java基础学习总结——java环境变量配置
前言 学习java的第一步就要搭建java的学习环境,首先是要安装JDK,JDK安装好之后,还需要在电脑上配置"JAVA_HOME”."path”."classpath& ...
- Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问
本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...
随机推荐
- Qt-数据库操作MySql
1 简介 参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=87 说明:本文简单说明在Qt中操作数据库,用MySql数据库进行试验. Qt 提供了 ...
- c#使用webView2 访问本地静态html资源跨域Cors问题 (附带代理服务helper帮助类)
背景 在浏览器中访问本地静态资源html网页时,可能会遇到跨域问题如图. 是因为浏览器默认启用了同源策略,即只允许加载与当前网页具有相同源(协议.域名和端口)的内容. WebView2默认情况下启用了 ...
- Node安装mongodb
Node操作mongodb Mongoose介绍 网址:http://www.mongoosejs.net/docs/index.html mongoose是Node环境下异步操作mongodb数据库 ...
- Linux扩展篇-shell编程(三)-shell运算符
基本语法: 格式一 expr +. -. \*./. %(加.减.乘.除.求余) 格式二 "$((运算式))"或者"$[运算式]" 基本运算符 Shell 和其 ...
- 微信支付(付款码支付,条码支付,刷卡支付)左上角LOGO显示
微信支付(付款码支付,条码支付,刷卡支付)左上角LOGO显示 如果你上送的sub_appid 公众号(小程序),优先显示你公众号(小程序)的LOGO,如果你的公众号(小程序)未设置LOGO,会显示上游 ...
- Vue学习:4.v-model使用
第一节算是对v-model的粗略了解,仅仅是将input的输入与Vue 实例的数据之间双向绑定.这一节将更详细的了解v-model在不同表单元素中的使用. v-model实例:找对象 实现功能: 使用 ...
- SQL 如何去掉字段中千位的逗号(比如set @= '1,320.00' 想得到@= '1320.00' )
1/去掉字段里的逗号.(比如set @= '1,320.00' 想得到@= '1320.00' )UPDATE table SET fieldA = REPLACE(fieldA, ',', '') ...
- 学习ThreeJS
创建第一个应用 使用Three JS进行编程的时候,都是在调用new Three().XXX 来实现方法,让我们先根据官方文档创建一个demo https://threejs.org/docs/ind ...
- ecnuoj 5039 摇钱树
5039. 摇钱树 题目链接:5039. 摇钱树 感觉在赛中的时候,完全没有考虑分数规划这种做法.同时也没有想到怎么拆这两个交和并的式子.有点难受-- 当出现分数使其尽量大或者小,并且如果修改其中直接 ...
- WPF/C#:如何实现拖拉元素
前言 在Canvas中放置了一些元素,需要能够拖拉这些元素,在WPF Samples中的DragDropObjects项目中告诉了我们如何实现这种效果. 效果如下所示: 拖拉过程中的效果如下所示: 具 ...