文章目录
一、Spring
Spring是一个分层的Java SE/EE应用一站式的轻量级开源框架
。Spring核心是IOC(Inversion of Control)
和AOP(Aspect Oriented Programming)
。
Spring主要优点包括:
- 方便解耦,简化开发,通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码造成的程序耦合度高。
- AOP编程的支持,通过Spring提供的AOP功能,方便进行面向切面编程。
- 声明式事务的支持,在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
- 方便程序的测试,可以用非容器依赖的编程方式进行几乎所有的测试工作。
- 方便集成各种优秀框架,Spring提供了对各种优秀框架的直接支持。
Spring框架结构组成图如下所示:
0、 配置
- 在
pom.xml
添加spring依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
- 在resources文件夹内新建
applicationContext.xml
文件
//在beans标签内,新建bean标签
<!--配置bean-->
<!--bean标签表示配置bean-->
<!--id属性为bean的标识名称-->
<!--name属性给bean起名-->
<!--class属性表示给bean定义类型-->
<bean id="bookDao" name="bookDao2,dao" class="com.minqo.dao.impl.BookDaoImpl"></bean>
- 管理bean
程序主要是通过Spring容器来访问容器中的Bean,ApplicationContext是Spring容器最常用的接口,该接口有如下两个实现类:
- ClassPathXmlApplicationContext: 从类加载路径下搜索配置文件,并根据配置文件来创建Spring容器。
- FileSystemXmlApplicationContext: 从文件系统的相对路径或绝对路径下去搜索配置文件,并根据配置文件来创建Spring容器
//获取Ioc容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
BookDao bookDao = ctx.getBean(BookDao.class); //按类查找,这种方法中,beans中只能有一个BookDao
1、Ioc
Inversion of Control
控制反转:将对象的创造控制权移交到Core Container容器,降低类与类之间的耦合性。
BeanFactory
为Ioc容器顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
Application
为Spring容器的核心接口,初始化时bean立即加载
调用方法时,
从Ioc容器中通过id获取bean
,根据bean中的关系
来关联其他类
若没有property关系,则当前类中的其他类为Null。当前类中的set方法由Ioc容器调用
示例代码如下:
public class BookDaoImpl implements BookDao {
public void save(){
System.out.println("book dao save ...");
}
}
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void save(){
System.out.println("book sesrvice save ...");
bookDao.save();
}
//Ioc容器调用
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
public class App2 {
public static void main(String[] args) {
//获取Ioc容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
<bean id="bookDao" class="com.minqo.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.minqo.service.impl.BookServiceImpl">
<!--配置关系-->
<!--
property标签表示当前bean的属性
name属性表示配置哪个具体属性 //类的属性
ref属性表示参照哪个bean //bean的id
-->
<property name="bookDao" ref="bookDao"/>
</bean>
Bean
生命周期:
- 初始化容器
-
- 创建对象(内存分配)
-
- 执行构造方法
-
- 执行属性注入(set操作)
-
执行bean初始化方法
- 使用bean
-
- 执行业务操作
- 关闭/销毁容器
-
- 执行bean销毁方法
name
属性可以用来取别名,名字间可用, ; (空格)
相隔,ref属性也可以通过其他bean的name属性
获取其他beanscope
属性可以设置bean为单例singleton
(默认)/非单例prototype
,即获取bean为同一个对象或多个对象- 含有
成员属性值
的不适合作为bean交给容器init-method || destroy-method
属性设置bean初始化||结束的方法,在daoImpl
建立init()与destroy()方法赋值给属性值。
- 调用容器的
close()
方法,会调用destroy方法强制关闭容器。在容器创建后任何地方
调用容器的registerShutdownHook()
方法,在虚拟机运行结束前会将容器关闭。
lazy-init
属性控制bean的延迟加载(获取bean时是否调用构造方法)
依赖注入
bean之间的相互依赖关系,通过注入的方式使数据在bean与bean之间传输
setter注入(通过set方法)
-
- 简单类型:在bean中定义引用数据类型属性,并定义其set方法,在配置文件中使用
property
标签中的value
属性注入简单数据类型,name属性值为属性名
- 简单类型:在bean中定义引用数据类型属性,并定义其set方法,在配置文件中使用
-
- 引用类型:在bean中定义引用数据类型属性,并定义其set方法,在配置文件中使用
property
标签中的ref
属性,找到引用数据的bean的id
,name属性值为属性名
- 引用类型:在bean中定义引用数据类型属性,并定义其set方法,在配置文件中使用
private int num;
private String name;
public void setNum(int num) {
this.num = num;
}
public void setName(String name) {
this.name = name;
}
<bean id="bookDao" class="com.minqo.dao.impl.BookDaoImpl" >
<property name="num" value="10"/>
<property name="name" value="name"/>
</bean>
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
<bean id="bookService" class="com.minqo.service.impl.BookServiceImpl" >
<property ref="bookDao" name="bookDao"/>
</bean>
构造器注入(通过构造方法)
-
- 简单类型:在bean中定义引用数据类型属性,在构造方法中通过参数实例化属性,在配置文件中使用
constructor-arg
标签中的value
属性注入简单数据类型,name属性值为构造方法中的参数名
- 简单类型:在bean中定义引用数据类型属性,在构造方法中通过参数实例化属性,在配置文件中使用
-
- 引用类型:在bean中定义引用数据类型属性,在构造方法中通过参数实例化属性,在配置文件中使用
constructor-arg
标签中的ref
属性,找到引用数据的bean的id
,name属性值为构造方法中的参数名
- 引用类型:在bean中定义引用数据类型属性,在构造方法中通过参数实例化属性,在配置文件中使用
- 构造器注入中,注入需要联系构造方法中的参数名
可以通过index属性,通过参数的位置注入
,耦合度过高,使用setter更好
private int num;
private String name;
public BookDaoImpl(int num, String name) {
this.num = num;
this.name = name;
}
<bean id="bookDao" class="com.minqo.dao.impl.BookDaoImpl" >
<constructor-arg index="1" value="name"/>
<constructor-arg index="0" value="10"/>
</bean>
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
<bean id="bookService" class="com.minqo.service.impl.BookServiceImpl" >
<constructor-arg ref="bookDao" name="bookDao"/>
</bean>
自动装配
通过bean标签的autowire属性,设置自动装配的类型自动装配用于引用类型依赖注入,不能对简单类型操作
- byType,通过类型匹配,一个属性变量
只能有一个bean标签
即相同bean标签唯一 - byName,通过名字匹配,即bean的
id与set xxx的xxx匹配
,耦合度较高
private BookDao bookDao;
<bean id="bookDao" class="com.minqo.dao.impl.BookDaoImpl" >
<bean id="bookService" class="com.minqo.service.impl.BookServiceImpl" autowire="byType">
集合注入
书写格式如下(setter注入):
private int[] array;
private List list;
private Map map;
<property name="array">
<array>
<value>10</value>
<value>102</value>
<value>103</value>
</array>
</property>
<property name="list">
<list>
<value>10</value>
<value>102</value>
</list>
</property>
<property name="map">
<map>
<entry key="10" value="min"></entry>
</map>
</property>
加载properties文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
//在xml新开辟命名空间:context
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
http://www.springframework.org/schema/context/spring-context.xsd">
//context属性占位符,location表示文件位置,多个文件用逗号隔开,classpath表示类路径,
//classpath*包含jar包中的文件,*.properties表示引用全部文件,system-properties-mode为系统变量,当文件变量名与系统变量(优先级高)名相同时,通过设置属性值“NEVER”来限制系统变量属性值。
<context:property-placeholder location="test.properties"/>
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
<bean id="bookDao" class="com.minqo.dao.impl.BookDaoImpl" >
<constructor-arg value="${test}"/> //利用${}占位符读取文件中的数据
</bean>
2、注解开发
在类的上方用注解标注
来标识该类将作为Ioc容器中的bean
在xml文件中删除对应的bean
@Component("bookDao") //声明bean并设置id
public class BookDaoImpl implements BookDao
@Component //不设置id可以通过按类查找
public class BookServiceImpl implements BookService
//在xml中扫描component注解
<context:component-scan base-package="com.minqo"/>
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
BookService bookService = ctx.getBean(BookService.class);
bookService.save();
@Component注解的衍生注解
- @Controller:用于表现层bean的定义
- @Service:用于业务层bean的定义
- @Repository:用于数据层bean的定义
纯注解开发
Spring3.0开启纯注解开发,使用java类
代替配置文件
//新建java类,@Configuration注解定义当前类为配置类
//@ComponentScan注解用于设定扫描路径,若有多个数据,用字符串数组格式表示
@Configuration
@ComponentScan("com.minqo")
public class SpringConfig {
}
//加载配置文件初始化容器->加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
@Scope("singleton" / "prototype") //@Scope注解设置单多例
@PostConstruct //生命周期,方法名自拟
public void init(){
System.out.println("init...");
}
@PreDestroy
public void destroy(){
System.out.println("desrtoy..");
}
依赖注入
使用@Autowired注解
开启自动装配(按类型)
自动装配基于反射设计创建对象并暴力反射对应属性私有属性初始化数据,无需提供setter方法
自动装配建议使用无参构造方法
创建对象(默认)
使用@Qualifier注解
开启指定名称装配bean(当一个类含有多个其他类)
@Autowired
@Qualifier("bookDao")
private BookDao bookDao;
@Autowired
@Qualifier("bookDao2")
private BookDao bookDao;
@Repository("bookDao")
public class BookDaoImpl implements BookDao
@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao
使用@Value注解
对基本数据类型进行赋值,从配置文件中读取数据,耦合度低。
@Configuration
@ComponentScan("com.minqo")
@PropertySource("test.properties")
public class SpringConfig {
}
@Value("minqo")
String name;
@Value("${test}")
String name2;
第三方bean的依赖注入
通过将@Bean注释
添加到自拟方法上,方法的返回值作为Bean。
通过@Import注释
导入含有第三方Bean的类。
引用类型依赖注入
,只需要为Bean定义形参即可,容器会根据类型自动装配对象
。
简单类型依赖注入
,通过@Value注释
赋值。
@Configuration
@ComponentScan("com.minqo")
@PropertySource("test.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}
public class JdbcConfig {
@Value("minqo")
String name;
@Bean
public DataSource dataSource(BookDao bookDao){
DruidDataSource ds = new DruidDataSource();
ds.setName(name);
System.out.println(ds);
bookDao.save();
return ds;
}
}
3、AOP
0.简介
OOP(Object Orient Programming)面向
对象
编程
AOP(Aspect OrientProgramming)面向切面
编程,AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点
的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。
作用:在不改变原始设计
的基础上进行功能增强
面向编程:
- 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。
- 切入点(Pointcut): 匹配连接点的式子,可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。在SpringAOP中,一个切入点可以只描述一个方法,也可以匹配多个方法。
- 通知(Advice): AOP框架在特定的切入点执行的增强处理。处理有“around”、“before”、“after”等类型。共性功能。
- 切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义。
1.配置
- 在
pom.xml
导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 新建通知类
用@Component注解
标识该类作为bean,用@Aspect标识
该类作为AOP
处理,作为切面类
用@Pointcut注解
标识方法作为切入点
增强处理,参数为匹配的方法
,该注解放在无返回值、无参数的类上,方法体无具体实意
用@Before注解
标识方法作为增强处理内容,在参数方法执行前
执行增强的内容。绑定切入点与通知的关系
用@EnableAspectJAutoProxt注释
标识该Spring框架开启AOP注解驱动
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.minqo.dao.Impl.BookDaoImpl.update())")
public void fd(){
}
@Before("fd()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
@Configuration
@ComponentScan("com.minqo")
@EnableAspectJAutoProxy
public class SpringConfig {
}
2.流程
- 启动Spring容器
- 获取切面中所有切入点
- 获取bean,判断bean中是否有方法与切入点匹配
- 匹配失败,创建bean
- 匹配成功,创建原始对象(
目标对象
)的代理
对象
- 获取bean的执行方法
- 获取bean,调用方法并执行,完成操作
- 获取的是bean的代理对象时,根据
代理对象
运行操作以及增强操作
目标对象(Target):原始功能去掉共性功能(增强处理,上方的method())
对应的类产生的对象,无法直接完成最终操作
代理(Proxy):原始对象无法直接完成最终任务,通过对其进行增强处理,将其原始对象扩展为代理对象
3.AOP切入点表达式
切入点表达式格式:动作关键词(访问修饰符 返回类型 包.类.方法(参数) 异常抛出)
- 动作关键词:描述切入点的动作,例如execution描述执行到制定切入点
- 访问修饰符:public、private等,可以省略
- 异常抛出:方法定义中抛出的指定方法,可省略
可以使用通配符
描述切入点,快速描述
- *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution (public * com.minqo.*.UserService.find* (*))
匹配com.minqo包下的任意包
中的UserService类或接口中所有find开头
的带有一个参数
的方法 - . . : 多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution (public User com. .UserService.findById(. .))
匹配com包下的任意包
中的UserService类或接口中所有名称为findByld的方法
- +:专用于匹配子类类型
execution(* *. .Service+.(. .))
4.通知(Advice)类型
@Pointcut("execution(int com.minqo.dao.Impl.BookDaoImpl.select(..))")
public void fd2(){
}
public int select(){
System.out.println("select ...");
return 100;
}
- 前置通知:
@Before注解
,设置当前方法(before())
与切入点(fd2())
间的绑定关系,当前通知方法在切入点之前
执行
@Before("fd2()")
public void before(){
System.out.println("before advice.");
}
- 后置通知:
@After注解
,设置当前方法(after())
与切入点(fd2())
间的绑定关系,当前通知方法在切入点之后
执行
@After("fd2()")
public void after(){
System.out.println("after advice..");
}
- 环绕通知:
@Around注解
,设置当前方法(around())
与切入点(fd2())
间的绑定关系,当前通知方法可以在切入点前后
执行。原始方法中的内容必须通过形参ProceedingJoinPoint
执行,若没有该形参,原始方法中的操作将被覆盖
。接收返回值,代理方法需要将返回类型设置为Object
。因为无法预测原始方法是否出错,需要抛出异常
@Around("fd2()")
public Object around(ProceedingJoinPoint pjt) throws Throwable { //抛出异常,防止原始方法还有异常AOP切面无法处理
System.out.println("around before.");
Object obj = pjt.proceed(); //执行原始方法中的操作(以上作为切入点的方法还有返回值)
System.out.println("around after.");
return obj;
}
- 返回后通知:
@AfterReturning注解
,设置当前方法(afterReturn())
与切入点(fd2())
间的绑定关系,当前通知方法在切入点返回之后
执行,若切入点未返回
,不执行
该操作
@AfterReturning("fd2()")
public void afterReturn(){
System.out.println("afterReturning.");
}
- 执行异常后通知:
@AfterThrowing注解
,设置当前方法(afterThrow())
与切入点(fd2())
间的绑定关系,当前通知方法在切入点抛出异常之后
执行,若切入点未抛出异常
,不执行
该操作
@AfterThrowing("fd2()")
public void afterThrow(){
System.out.println("afterThrow");
}
5.AOP通知获取数据
- 获取参数
-
JoinPoint:适用于前置、后置、返回后、抛出异常通知
-
ProceedJointPoint:适用于环绕通知
-
@Before("fd2()")
public void before(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice.");
}
@Around("fd2()")
public Object around(ProceedingJoinPoint pjt) throws Throwable { //抛出异常,防止原始方法还有异常AOP切面无法处理
System.out.println("around before.");
Object[] args = pjt.getArgs();
args[0] = 50; //可以修改原参数
pjt.proceed(args); //执行原始方法中的操作(以上作为切入点的方法还有返回值)
System.out.println("around after.");
return 200;
}
- 获取返回值
- 返回后通知
- 环绕通知
@AfterReturning(value = "fd2()",returning = "ret")//参数returning的值与Object参数名称对应
public void afterReturn(JoinPoint jp,Object ret){
System.out.println("afterReturning."+ret);
}
- 获取异常
- 抛出异常后通知
- 环绕通知
@AfterThrowing(value = "fd2()",throwing = "t")//参数名称对应
public void afterThrow(Throwable t ){
System.out.println("afterThrow");
}
@Around("fd2()")
public Object around(ProceedingJoinPoint pjt){
System.out.println("around before.");
try {
pjt.proceed();
} catch (Throwable e) { //捕捉异常
System.out.println(e);
throw new RuntimeException(e);
}
System.out.println("around after.");
return 200;
}