Spring

BeanFactory和ApplicationContext

  • ApplicationContext间接继承了BeanFactory,ApplicationContext接口是Spring容器的核心接口,也是spring用的自多的接口
  • ApplicationContext初始化时bean立即加载,而BeanFactory创建完毕后,所有的bean均为延迟加载(懒加载)
  • BeanFactory才是spring的核心容器,ApplicationContext是实现和组合了BeanFactory(组合的意思是也继承或实现了其他的类),ApplicationContext比BeanFacotry增强的功能:
    • MessageSource()国际化功能
    • getResources()获取多个配置资源
    • getEnvironment()获取环境变量或参数配置信息
    • publishEvent()发布事件,事件都是继承ApplicationEvent,事件发送demo:
// step1、定义事件,继承ApplicationEvent
public class SpeederEvent extends ApplicationContext{
	public SpeederEvent(Object source){ // source指事件调用的来源,指谁发的事件
		super(source);
	}
}

// step2、定义监听器,即收事件的方法
public class SpeederListener{

	@EventListener // 一定加这个注解
	public void listener(SpeederEvent event){
		sysout(event);// 收到监听后打印日志
	}
}

// step3、发送事件
ApplicationEventEventPublisher context;
context.publishEvent(new SpeederEvent(context));
  • 创建容器的两种方式:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
  • singletonObjects是ioc容器中单例bean的集合,
// 打印ioc容器中以component开头的单例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的销毁,两种方法:
// 1
ConfigurableApplicationContext.close()

// 2
ConfigurableApplicationContext.registerShutdownHook()
  • 三种生命周期注册方法:
// 方法一:使用配置类的创建销毁方法
// 1、定义类
public class XXX {
	public void myInit(){
		sysout("inited");
	}
	public void myDestory(){
		sysout("destoried");
	}
}
// 2、配置
<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" />
// classpath*代表可以加载第三方jar包中的properties
<context:property-placeholder location="classpath*:*.properites">
<property name="username" value="${jdbc.username}">

注解

  • 配置注解的扫描范围:
// 方法1使用配置定义、配置文件中定义上下文扫描范围
<context:component-scan base-package="xxx"/>

// 方法2、定义配置类,从配置类中配置扫描的范围,此时配置文件就可以删掉了
@Configuration // 设定当前类为配置类,此注解为命名空间,上下文等的声明集合
@ComponentScan({"xxx1","xxx2"}) // 定义注解扫描范围
@PropertySource({"xxx1.properties","xxx2.properties"}) // 定义参数配置文件的位置,这个地方不支持通配符,即*.properties会报错
public class SpringConfig{
}
// 方法2的初始化容器读取配置时,要指定读取的是配置类
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class)
  • 管理第三方Bean
// 1、定义第三方bean的引入类
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;
	}
}

// 2、在配置类中引入上述config类
@Configuration
@Import({jdbcConfig.class, xxx.class})
public class SpringConfig{
}

AOP

  • 核心概念
    • 通知:在切入点要执行的操作,例如记录日志,记录性能之类
    • 通知类:定义通知的类
    • 连接点:程序在执行过程中的任意位置,粒度可为执行方法、抛出异常、设置变量等
    • 切入点:匹配连接点的表达式
    • 切面:描述通知与切入点的对应关系
  • 开发一个AOP的大致步骤
    1. 制作连接点,即目标业务逻辑代码
    2. 制作要切入的额外操作,即通知类与通知
    3. 定义切入点,即定义哪些连接点需要添加额外操作
    4. 绑定切入点和通知的关系,即切面
  • 开发一个示例AOP,为了实现在user对象保存之前,打印系统当前时间
// step1、导入依赖坐标,aop和aspectj,aop会随着springcontext包一起导入,需要额外引入aspectj坐标
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver<artifactId/>
	<version>1.9.4</version>
</dependency>

// step2、定义切入点class和方法
package com.speeder
public class user{
	public void save(){
		sysout("saved");
	}
}

// step3、定义通知类和通知
@Component // 这里保证spring可以扫描到本通知类
@Aspect // Aspect为了声明本类是个aop类
public class printTime{

	// 定义好切入点pointcut
	@Pointcut("execution(void com.speeder.user.save())")
	public void entry(){}

	@Before("entry()")// 绑定通知和切入点的关系,并且在切入点之前之前执行
	public void print(){// 定义通知
		sysout(System.currentTimeMillis());
	}
}

// step4、在主配置文件中开启切面aspectj
@Configuration
@ComponentScan("com.speeder")
@EnableAspectJAutoProxy // 开启spring对AOP注解的驱动支持
public class SpringConfig{
}
  • AOP工作流程(本质是代理模式)
    1. Spring容器启动
    2. 读取所有切面配置中的切入点
    3. 初始化bean,判定bean对应的类和方法有没有匹配到定义好的切入点
      • 如果匹配失败,就不做任何处理,并且创建bean对应的对象
      • 如果匹配成功,就bean原始对象的代理对象
    4. 获取bean执行方法
      • 如果并不是切入点,获取bean原始对象,调用方法并执行,完成操作
      • 如果是切入点,获取bean的代理对象,根据代理对象的运行模式运行原始方法和定义的增强内容,完成操作
  • 切入点表达式
    • 标准格式:动作关键词(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
    • 访问修饰符和异常名可以省略
    • 通配符
      • *星号:代表单个独立的任意符号
      • …:多个连续的任意符号
      • +:专用于匹配子类类型
    • 书写技巧
      • 切入点描述接口类和接口方法,而不描述实现类
      • 包名书写尽量不用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配
      • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService写成 *Service
  • AOP的通知类型
    • 前置通知
    • 后置通知
    • 环绕通知(最常用):
      • 一定要返回Object类型
      • 一定要执行原始方法ProceedingJoinPoint.proceed()
      • 一定要抛出异常Throwable
      • Signature signature = pjp.getSignature(); // 获取切入点签名信息
      • signature.getDeclaringTypeName();// 切入点的类名
      • signature.getName();// 连接点的方法名
    • 返回后通知(了解)
    • 抛出异常后通知(了解)
  • AOP通知获取数据
    • 数据的类型:参数、返回值、抛出的异常
    • 参数获取:通过JoinPoint和ProceedingJoinPoint获取参数,其中ProceedingJoinPoint implements JoinPoint
    • 返回值
// step1、定义一个类和连接点
package com.speeder;
public class User{
	public void save(){
		sysout("saved");
	}
	public String select(){
		sysout("select user");
		return "user";
	}
}
// step2、定义通知类
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");
	}
	// 环绕通知
	// 环绕通知获取异常可以try catch
	@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");
	}
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值