Spring学习(一)
简介&使用及学习
Spring现在包含着Spring boot、Spring Cloud等一系列技术,但是作为Spring的初学者,我们需要学习Spring Framework,这是Spring最初的设计框架,所以以下先用Spring代替Spring Framework。
Spring Framework主要包括几个模块:
- 支持IoC和AOP的容器;
- 支持JDBC和ORM的数据访问模块;
- 支持声明式事务的模块;
- 支持基于Servlet的MVC开发;
- 支持基于Reactive的Web开发;
- 以及集成JMS、JavaMail、JMX、缓存等其他模块。
Spring(Spring Framework)主要就包括IoC和AOP两个方面。
Spring最初是以写配置的方式来开发,然后出现了注解功能,我们在日常开发中通常是使用写注解的方式。但是注解实际上是写配置的方式的变形,所以我们想要了解原理,仍然要学习如何写配置。
系统架构
- 最底层是一个核心容器
- 然后是AOP和Aspects
- 然后是数据访问(包含一个高效的事务控制)
- 然后是Web开发
核心容器
思想:
核心概念:IoC、DI
解决的问题:
- 代码耦合度偏高(当修改一个功能时需要重新使用一个对象,于是跟此对象关联的所有地方都要进行修改)
解决方案:
- 不主动产生对象,转为由外部提供对象
技术方案:
- IoC(控制反转)
IoC就是一个产生对象的容器,IoC负责管理大量的对象的创建和初始化,被创建或被管理的对象在容器中统称为Bean
若IoC中的对象存在依赖关系,可以直接在容器中进行绑定,这就是DI(依赖注入)技术
如何实现:
IoC的实现
- 首先确定自己需要管理的类
- 在resources中创建Spring配置文件
- 编写配置
<bean id="" class="" ...></bean>
- 获取IoC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(创建的配置文件)
- 获取Bean
ctx.getBean
方法
DI的实现
创建其他对象不再使用new方法
创建一个set方法
在配置文件中添加依赖配置
<property name="属性中的名称" ref="其他bean的名称"/>
Bean的配置
bean的基础属性有id、class,也可以使用name为bean添加别名;
默认创建的bean是单例,可以在配置中添加scope=”prototype”设置为非单例,可以复用的对象都可以以单例形式交给IoC管理。
bean是如何创建的
本质上bean也是用类的构造方法来创建bean的,但是是使用java的反射机制实行的(private构造方法也可以创建),默认是使用无参构造方法实现的,需要提供无参构造方法。
bean也有其他方式实例化,比如使用静态工厂的方式。在使用静态工厂的方式创建对象时,需要在配置中将类改为工厂类名,然后通用再加一个factory-method=""
来实现。
还有使用实例工厂的方式,这种还需要再实例化这个工厂,比较复杂。配置时要先把工厂的bean配置出来,然后配置要使用的bean的factory-method和factory-bean。
后来Spring改进了实例工厂的方式,需要接口FactoryBean,这种方式在配置时就不用配置完工厂bean后再配置要使用的bean,只需要配置工厂bean。这种方法可以在工厂类中使用isSingleto()方法修改是否是单例。
bean的生命周期
在配置中使用init-method=”” destoy-method=”” 配置bean的生命周期,在类中写init和destroy方法。(或者使用接口的方式)
关闭容器的方式(方法是ClassPathXmlApplication类中的方法,需要创建此类型的IoC容器)
close
registerShutdownHook()
依赖注入的实现
使用set方法,然后配置
<porperty name="" ref=""/>
如果是基本数据类型或者String则property中的ref换成valuie即可
构造器注入。将构造方法改成带参数的构造方法,配置中使用
<constructor name="形参名" ref/value=""/>
配置,name=""
可以换成index和type索引属性。依赖的自动装配。 在bean的配置中加上
autowire=""
可完成自动装配,需要注意的是类中的set方法不可省去,但是省去了写property
这一行。关于参数byType指按类型装配,一个类只能有一个对象/bean,这种情况需要使用参数byName,Name是对象名,一般是使用byType。自动装配只能应用于引用类型,不能应用于基本类型。自动装配的优先级低于手动装配。集合注入
数组:
1
2
3
4
5
6
7
8
9<property name="">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
<ref bean=""/>
...
</array>
</property>list:
1
2
3
4
5
6
7
8<property name="">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
...
</list>
</property>set:
1
2
3
4
5
6
7
8
9<property name="">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
<!--set中重复的部分会过滤掉-->
...
</set>
</property>map
1
2
3
4
5
6
7<property name="">
<map>
<entry key="one" value="1"/>
<entry key="two" value="2"/>
...
</map>
</property>props(properties)
1
2
3
4
5
6
7
8<property name="">
<props>
<prop key="one">1</prop>
<prop key="two">2</prop>
<prop key="three">3</prop>
...
</props>
</property>
加载其他的properties文件
- 开启context命名空间
- 使用context命名空间加载指定properties文件
- 使用${ }读取加载的属性值
容器补充
从文件系统加载配置文件
1
ApplicationContext ctx = new FileSystemXmlApplicationContext("path/to/.xml")
加载多个配置文件
1
ApplicationContext ctx = new ClassPathXmlApplicationContext("name1.xml","name2.xml")
获取bean的方式补充
1
2
3BookDao bookDao = (BookDao) ctx.getBean("bookDao")//根据名称
BookDao bookDao = ctx.getBean("bookDao",BookDao.class)//根据名称并指定类
BookDao bookDao = ctx.getBean(BookDao.class)//根据类
半注解开发
半注解开发是Spring2的时候推出的开发模式,在Spring3后出现了纯注解开发的模式。具体实现如下:
- 在要配置的bean前加上
@Component
注解, - 在配置文件中通过组件扫描加载bean
@Service
用于业务层的bean@Repository
用于数据层的bean@Controller
用于表现层的bean
纯注解开发
创建IoC容器和bean
新建一个配置类,使用此配置类替代xml配置文件,加上注解
@Configuration
(代替注解的说明)和@ComponentScan(Bean模块)
(代替组件扫描)初始化容器的语句不再是
1
ApplicationContext ctx = new ClassPathXmlApplicationContext("name.xml")
而是
1
ApplicationContext ctx = new AnnotationConfigApplicationContext(配置类的名称.class)
配置bean
- 修改非单例:加注解
@Scope("prototype")
- 设置初始化和销毁方法:
@PostConstruct
@PreDestroy
- 自动装配实现依赖注入
@Autowired
,并且此时不用再创建set方法实现注入了。 - 想要按名称注入就需要在
@Autowired
后加上@Qualifier("name")
- 注入基本类型和String类型 加注解
@Value("值")
加载外部配置文件
- 在配置类中加上标签
@PropertySource("name.properties")
- 注入时使用
${外部配置文件中的变量名}
第三方bean管理
- 在配置类中新建一个方法获得想要的bean
- 加上@Bean注解
- 一般的要把这个方法写在新的配置类中,然后在主配置类中加上注解
@import()
或者@ComponentScan()
想要在第三方bean中进行依赖注入,如果是简单类型使用@Value注解,如果是引用类型的bean直接在获取bean的方法加上形参即可。
AOP面向切面编程
在不修改原始设计的基础上进行功能增强,符合Spring无侵入式设计理念。
常用名词:
- 连接点:程序执行过程中的任意位置
- 切入点:匹配连接点的式子
- 通知:在切入点进行的操作
- 通知类:定义通知的类
- 切面:描述通知和切入点的对应关系
实现思路:
导入坐标(pom.xml)
制作连接点方法
制作共性功能
定义切入点
加上@Pointcut(“切入点表达式”)注解
绑定切入点和通知的关系
加上在何处执行共性方法的注解,如@Before(“切入点”)
在通知类前加上@Component和@Aspect注解,然后在配置类前加上@EnableAspectJAutoProxy注解,即启动切面编程。
切入点表达式
标准格式:
动作关键字 (访问修饰符 返回值 包名.类/接口.方法名(参数) 异常名)
在AOP切入点表达式中可以使用通配符
- *: 单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀出现
- ..: 多个连续的任意符号,可以独立出现,常用于简化包名和参数的书写
- +: 专用于匹配子类类型
AOP通知类型
前置通知:
@Before("pt()")
后置通知:
@After("pt()")
环绕通知:
@Around("pt()")
,通知上要有pjp.proceed()
;表示对原方法的调用,并且通知方法需要有返回值。标准写法如下:
1
2
3
4
5
6
7@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwables{
原始方法前想要执行的代码
Object ret = pjp.proceed();//表示调用了原始方法
原始方法后想要执行的代码
return ret;
}无异常运行完成后:
@AfterReturning("pt()")
抛出异常后:
@AfterThrowing("pt()")
AOP通知获取数据
- 获取切入点方法的参数
- JointPoint:适用于前置、后置、返回后、抛出异常后通知
- ProceedJointPoint:适用于环绕通知
- 获取切入点方法返回值
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息
- 抛出异常后通知
- 环绕通知