BeanFactory和ApplicationContext
- ApplicationContext间接继承了BeanFactory,ApplicationContext接口是Spring容器的核心接口,也是spring用的自多的接口
- ApplicationContext初始化时bean立即加载,而BeanFactory创建完毕后,所有的bean均为延迟加载(懒加载)
- BeanFactory才是spring的核心容器,ApplicationContext是实现和组合了BeanFactory(组合的意思是也继承或实现了其他的类),ApplicationContext比BeanFacotry增强的功能:
- MessageSource()国际化功能
- getResources()获取多个配置资源
- getEnvironment()获取环境变量或参数配置信息
- publishEvent()发布事件,事件都是继承ApplicationEvent,事件发送demo:
public class SpeederEvent extends ApplicationContext{
public SpeederEvent(Object source){
super(source);
}
}
public class SpeederListener{
@EventListener
public void listener(SpeederEvent event){
sysout(event);
}
}
ApplicationEventEventPublisher context;
context.publishEvent(new SpeederEvent(context));
- 创建容器的两种方式:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
- singletonObjects是ioc容器中单例bean的集合,
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccesible(true);
ConfigurableListalbeBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(e-> e.getKey().startsWith("component")).forEach(e->{
sysout(e.key() +":"+e.getValue())
})
Bean
- 实例化bean的三种方式:
- 构造方法(常用)(Spring初始化bean的时候,会调用无参构造方法,如果类中没有无参构造方法,spring启动过程会报错)
- 静态工厂(了解)
- 实例工厂(了解)和factorybean(实用),多数框架选择这种方式和spring集成(实现FactoryBean接口)
- 单例和多例(singleton & prototype),spring中bean默认都是单例。点击这里参考更详细说明
- 单例优势:减少性能消耗,减少jvm垃圾回收,性能更高获取bean(从缓存中获取)
- 单例劣势:线程安全问题
- 使用注解@Scpoe指定: @Scope(“prototype”)
- 有状态bean和无状态bean
- bean的生命周期
- 伴随着ioc容器的创建和销毁
- ioc容器关闭前触发bean的销毁,两种方法:
ConfigurableApplicationContext.close()
ConfigurableApplicationContext.registerShutdownHook()
public class XXX {
public void myInit(){
sysout("inited");
}
public void myDestory(){
sysout("destoried");
}
}
<bean id="xxx" class="xxx" init-method="myInit" destory-method="myDestory">
public class xxx implements BookService, InitializingBean, DisposableBean{
public void afterPropertiesSet() throws Exception{
sysout("afterPropertiesSet");
}
public void destory() throws Exception(
sysout("destory");
)
}
public class a {
@PostConstruct
public void init(){
sysout("构造方法后执行");
}
@PreDestory
public void destory(){
sysout("彻底销毁之前执行");
}
}
spring加载properties文件
- 开启一个新的context命名空间
- 使用context空间命名,加载指定的properties文件
- 使用${}读取加载属性的值
<beans xmlns:context="http://www.springframwork.org/schema/context" />
<context:property-placeholder location="classpath*:*.properites">
<property name="username" value="${jdbc.username}">
注解
<context:component-scan base-package="xxx"/>
@Configuration
@ComponentScan({"xxx1","xxx2"})
@PropertySource({"xxx1.properties","xxx2.properties"})
public class SpringConfig{
}
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class)
public class jdbcConfig{
@Value("xx")
private String name;
@Value("${password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUsername(name);
ds.setPassword(password);
return ds;
}
}
@Configuration
@Import({jdbcConfig.class, xxx.class})
public class SpringConfig{
}
AOP
- 核心概念
- 通知:在切入点要执行的操作,例如记录日志,记录性能之类
- 通知类:定义通知的类
- 连接点:程序在执行过程中的任意位置,粒度可为执行方法、抛出异常、设置变量等
- 切入点:匹配连接点的表达式
- 切面:描述通知与切入点的对应关系
- 开发一个AOP的大致步骤
- 制作连接点,即目标业务逻辑代码
- 制作要切入的额外操作,即通知类与通知
- 定义切入点,即定义哪些连接点需要添加额外操作
- 绑定切入点和通知的关系,即切面
- 开发一个示例AOP,为了实现在user对象保存之前,打印系统当前时间
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver<artifactId/>
<version>1.9.4</version>
</dependency>
package com.speeder
public class user{
public void save(){
sysout("saved");
}
}
@Component
@Aspect
public class printTime{
@Pointcut("execution(void com.speeder.user.save())")
public void entry(){}
@Before("entry()")
public void print(){
sysout(System.currentTimeMillis());
}
}
@Configuration
@ComponentScan("com.speeder")
@EnableAspectJAutoProxy
public class SpringConfig{
}
- AOP工作流程(本质是代理模式)
- Spring容器启动
- 读取所有切面配置中的切入点
- 初始化bean,判定bean对应的类和方法有没有匹配到定义好的切入点
- 如果匹配失败,就不做任何处理,并且创建bean对应的对象
- 如果匹配成功,就bean原始对象的代理对象
- 获取bean执行方法
- 如果并不是切入点,获取bean原始对象,调用方法并执行,完成操作
- 如果是切入点,获取bean的代理对象,根据代理对象的运行模式运行原始方法和定义的增强内容,完成操作
- 切入点表达式
- 标准格式:动作关键词(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
- 访问修饰符和异常名可以省略
- 通配符
- *星号:代表单个独立的任意符号
- …:多个连续的任意符号
- +:专用于匹配子类类型
- 书写技巧
- 切入点描述接口类和接口方法,而不描述实现类
- 包名书写尽量不用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配
- 接口名/类名书写名称与模块相关的采用*匹配,例如UserService写成 *Service
- AOP的通知类型
- 前置通知
- 后置通知
- 环绕通知(最常用):
- 一定要返回Object类型
- 一定要执行原始方法ProceedingJoinPoint.proceed()
- 一定要抛出异常Throwable
- Signature signature = pjp.getSignature(); // 获取切入点签名信息
- signature.getDeclaringTypeName();// 切入点的类名
- signature.getName();// 连接点的方法名
- 返回后通知(了解)
- 抛出异常后通知(了解)
- AOP通知获取数据
- 数据的类型:参数、返回值、抛出的异常
- 参数获取:通过JoinPoint和ProceedingJoinPoint获取参数,其中ProceedingJoinPoint implements JoinPoint
- 返回值
package com.speeder;
public class User{
public void save(){
sysout("saved");
}
public String select(){
sysout("select user");
return "user";
}
}
public class myAspect {
@PointCut("execution(void com.speeder.save())")
public void mySave(){}
@PointCut("execution(String com.speeder.select())")
public int mySelect(){}
@Before("mySave()")
public void before(JoinPoint jp){
Object[] args = jp.getArgs();
sysout(Arrays.toString(args));
sysout("before save");
}
@After("mySave()")
public void after (){
sysout("after save");
}
@Around("mySelect()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
sysout(Arrays.toString(args));
Signature signature = pjp.getSignature();
sysout(signature.getDeclaringTypeName());
sysout(signature.getName());
sysout("around before");
Object ret = pjp.proceed();
sysout("around after");
return ret;
}
@AfterReturning(value = "mySelect()", returning = "ret")
public void afterReturning(Object ret){
sysout("获取返回值:"+ret);
sysout("xxx");
}
@AfterThrowing(value = "mySelect()", throwing = "t")
public void afterReturning(Throwable t){
sysout("exception:"+t);
sysout("xxx");
}
}
|