一、Spring 组成
二、Spring Bean 作用域
默认情况下Sping只为每个在IOC容器里面声明的bean创建唯一一个实例,整个IOC容器范围内只能共享该实例,该作用域被称为singleton
- singleton 在SpingIOC容器中仅存在一个bean实例,Bean以单实例的方式存在。
- prototype 每次调用getBean()时都会返回一个新的实例。
- request 每次Http请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境。
- session 同一个HTTP Session共享一个bean,不同的HTTP Session适用不同的Bean。该作用域仅适用于WebApplicationContext环境。
三、Spring Bean 是否线程安全
Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 线程安全 https://www.yuque.com/wangchao-volk4/fdw9ek/qky2rb#oQA9L
1、分析
原型Bean
对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean
对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
2、解决bean的线程安全问题
1、加注解@Scope(value = “prototype”)
public class MyConfig {
@Bean
@Scope(value = "prototype")
public User user(){
return new User();
}
}
3、总结
1、在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
2、尽量不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。
3、默认注入的Bean对象,在不设置scope的时候他也是线程不安全的。
4、一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的。
四、IOC
控制反转,把创建对象的过程和对象之间的调用过程,交给Spring进行管理。 目的:耦合度降低。 IOC的简单实现如下:
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 获取配置创建的对象
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
1、IOC原理
2、IOC接口(BeanFactory)
1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
2、Spring提供IOC容器实现方式:(两个接口)
(1)BeanFactory:IOC容器基本实现,是Sping内部的使用接口,不提供开发人员使用。加载配置文件的时候不会去创建对象,在获取(使用)对象的时候才去创建。
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多强大的功能,一般由开发人员使用。在加载配置文件的时候就会创建对象。
3、ApplicationContext接口有实现类就是我们常用的一些创建bean的类
3、Bean 管理
Spring有两种bean,一种是普通bean,另外一种是工厂bean (FactoryBean)
3.1 普通bean
如下里面的context.getBean,就是返回一个普通bean
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 获取配置创建的对象
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
3.2 工厂bean (FactoryBean)
在配置文件定义bean类型可以和返回类型不一样,代码如下可见,Dog对象的获取是靠getObject,这里明显返回的是User。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.supking.service.impl.UserImpl"></bean>
<bean id="dog" class="com.supking.factorybean.Dog"></bean>
</beans>
public class Dog implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new UserImpl();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
执行如下代码则报错:
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
//报错信息如下,期望的是Dog,实际是UserImpl
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'dog' is expected to be of type 'com.supking.factorybean.Dog' but was actually of type 'com.supking.service.impl.UserImpl'
需要修改返回值,代码如下,指定返回值为User,则不会报错:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 注意配置文件中dog对应的类是com.supking.factorybean.Dog
// <bean id="dog" class="com.supking.factorybean.Dog"></bean>
User user = context.getBean("dog", User.class);
System.out.println(user);
}
输出:
com.supking.service.impl.UserImpl@6500df86
3.3 装配方式
3.3.1 手动装配
<bean id="orders" class="com.supking.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="phone"></property>
</bean>
<bean id="emp" class="com.supking.autowire.Emp">
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.supking.autowire.Dept"></bean>
3.3.2 自动装配
<!-- 实现自动装配
autowire,配置自动装配
autowire属性常用两个值 byName:根据名字 type:根据类型(缺点,多个相同类型的bean会失败)
-->
<bean id="emp" class="com.supking.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.supking.autowire.Dept"></bean>
4、注解管理 bean
注解是什么?
11-注解 注解是代码标记,格式,@注解名称(属性名称=属性值,………),可以作用在类、方法、属性上面; 使用注解的目的:简化xml的配置
4.1 依赖和配置
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!--告知spring在创建容器时,要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束中
use-default-filters:false,不再设置默认filter(扫描所有东西),知己配置filter
-->
<context:component-scan base-package="com.supkingx" use-default-filters="false">
<!-- 指定扫描哪些内容-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:component-scan base-package="com.supkingx">
<!-- 指定不扫描哪些内容-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
4.2 创建Bean的注解
@Component、@Service、@Controller、@Repository,这四个注解功能一样,都是用来创建 bean 实例的。
4.3 基于注解实现注入
@Autowired:根据属性类型进行自动装配
@Qualifier(value=”userDaoImpl“):根据属性名称进行注入,需要和@Autowired一起使用
@Resource(name=”userDaoImpl“):可以根据类型注入,可以根据名称注入
@Value:注入普通类型
4.4 完全注解开发
无 xml
@Configuration
@ComponentScan(basePackages = {"com.supkingx"})
public class SpringConfig {
}
// 使用AnnotationConfigApplicationContext
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean(UserService.class);
userService.add();
}
五、AOP
Apsect Oriented Programming面向切面:不修改源代码进行功能的增强。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合性降低,提高程序的可用性,同时提高了开发效率。 通俗描述:不通过修改源代码,在主干方法里面添加新功能。
1、优先级
- 前置通知 (@Before) 。
- 返回通知 (@AfterReturning) 。
- 异常通知 (@AfterThrowing) 。
- 后置通知 (@After)。
环绕通知 (@Around) :(优先级最高)
@Aspect
@Component
public class SysTimeAspect {
/**
* 切入点
*/
@Pointcut("bean(sysMenuServiceImpl)")
public void doTime(){}
@Before("doTime()")
public void doBefore(JoinPoint jp){
System.out.println("time doBefore()");
}
@After("doTime()")
public void doAfter(){//类似于finally{}代码块
System.out.println("time doAfter()");
}
/**核心业务正常结束时执行
* 说明:假如有after,先执行after,再执行returning*/
@AfterReturning("doTime()")
public void doAfterReturning(){
System.out.println("time doAfterReturning");
}
/**核心业务出现异常时执行
* 说明:假如有after,先执行after,再执行Throwing*/
@AfterThrowing("doTime()")
public void doAfterThrowing(){
System.out.println("time doAfterThrowing");
}
@Around("doTime()")
public Object doAround(ProceedingJoinPoint jp)
throws Throwable{
System.out.println("doAround.before");
try {
Object obj=jp.proceed();
return obj;
}catch(Throwable e) {
System.out.println("doAround.error-->"+e.getMessage());
throw e;
}finally {
System.out.println("doAround.after");
}
}
}
2、springboot1和2 对 AOP的影响
boot1对应spring4,boot2对应spring5
spring4和spring5的AOP顺序对比结果
综上图中可以看到,spring5之后,@After的优先级被放到(@afterReturn和@AfterThrowing)之后了,@Around的环绕后通知被置到最后
3、AOP 术语
- 连接点:类里面那些方法可以被增强,这些方法称为连接点
- 切入点:实际上真正被增强的方法
- 通知(增强):实际增加的那一部分逻辑,即在原方法的前面和后面增加的那一部分逻辑
- 前置、后置、环绕、异常、最终
- 切面:是一个过程,即把通知应用到切入点的过程。
表达式:权限修饰符 返回来下 全类名路径 方法名称 (参数列表)
列如
@After(value = "execution(* com.supkingx.UserService.add(..))")
// 提取出表达式
@Pointcut(value = "execution(* com.supkingx.UserService.add(..))")
public void pointdemo(){
}
@After(value="pointdemo()")
public void xxx(){
.............
}