01spring入门

#Spring概述

Spring是分层的、JavaSE/EE一站式(full-stack)、轻量级开源框架。以IoC(Inverse Of Control:反转控制)AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了表现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。

Spring的发展历史:

1997年IBM提出了EJB的思想

1998年,SUN制定开发标准规范EJB1.0

1999年,EJB1.1发布

2001年,EJB2.0发布

2003年,EJB2.1发布

2006年,EJB3.0发布

**Rod Johnson**(****spring****之父)

    Expert One-to-One J2EE Design andDevelopment(2002)

    阐述了J2EE使用EJB开发设计的优点及解决方案

    Expert One-to-One J2EE Developmentwithout EJB(2004)

    阐述了J2EE开发不使用EJB的解决方式(Spring雏形)

Spring的优势

spring是为了解决javaEE实际问题

1.方便解耦,简化开发

通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

2.AOP编程的支持

通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP(面向对象)实现的功能可以通过AOP轻松应付。

3. 声明式事务的支持

可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

4. 方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

5. 方便集成各种优秀框架

Spring可以降低各种框架的使用难度,提供了对各种优秀框架Struts、Hibernate、Hessian(远程通讯,类似于webService)、Quartz(定时任务)等的直接支持。

6. 降低JavaEE API的使用难度

Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。

Spring的体系结构

核心容器(Core Container) 包括Core、Beans、Context、EL模块。

1:Core和Beans模块提供了Spring最基础的功能,提供IoC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。

2:Context模块基于Core和Beans来构建,它提供了用一种框架风格的方式来访问对象,有些像JNDI注册表。Context封装包继承了beans包的功能,还增加了国际化(I18N),事件传播,资源装载,以及透明创建上下文,例如通过servlet容器,以及对大量JavaEE特性的支持,如EJB、JMX。核心接口是ApplicationContext。

3:Expression Language,表达式语言模块,提供了在运行期间查询和操作对象图的强大能力。支持访问和修改属性值,方法调用,支持访问及修改数组、容器和索引器,命名变量,支持算数和逻辑运算,支持从Spring 容器获取Bean,它也支持列表投影、选择和一般的列表聚合等。

数据访问/集成部分(Data Access/Integration)

1:JDBC模块,提供对JDBC的抽象,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。

2:ORM模块,提供了常用的”对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行”对象/关系”映射,如简单声明性 事务管理 。

3:OXM模块,提供一个支持Object和XML进行映射的抽象层,其中包括JAXB、Castor、XMLBeans、JiBX和XStream。

4:JMS模块,提供一套”消息生产者、消费者”模板用于更加简单的使用JMS,JMS用于用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

5:Transaction模块,支持程序通过简单声明性 事务管理,只要是Spring管理对象都能得到Spring管理事务的好处,即使是POJO,也可以为他们提供事务。

Web

1:Web模块,提供了基础的web功能。例如多文件上传、集成IoC容器、远程过程访问、以及Web Service支持,并提供一个RestTemplate类来提供方便的Restful services访问

2:Web-Servlet模块,提供了Web应用的Model-View-Controller(MVC)实现。Spring MVC框架提供了基于注解的请求资源注入、更简单的数据绑定、数据验证等及一套非常易用的JSP标签,完全无缝与Spring其他技术协作。

3:Web-Struts模块, 提供了对Struts集成的支持,这个功能在Spring3.0里面已经不推荐了,建议你迁移应用到使用Struts2.0或Spring的MVC。

4:Web-Portlet模块,提供了在Portlet环境下的MVC实现

AOP

1:AOP模块,提供了符合AOP 联盟规范的面向方面的编程实现,让你可以定义如方法拦截器和切入点,从逻辑上讲,可以减弱代码的功能耦合,清晰的被分离开。而且,利用源码级的元数据功能,还可以将各种行为信息合并到你的代码中 。

2:Aspects模块,提供了对AspectJ的集成。

3:Instrumentation模块, 提供一些类级的工具支持和ClassLoader级的实现,可以在一些特定的应用服务器中使用。

Test

1:Test模块,提供对使用JUnit和TestNG来测试Spring组件的支持,它提供一致的ApplicationContexts并缓存这些上下文,它还能提供一些mock对象,使得你可以独立的测试代码。

Spring核心

IoC(Inverse ofControl 控制反转): 将对象创建权利交给Spring工厂进行管理。

IoC底层实现:工厂(设计模式)+反射(机制) + 配置文件

AOP(Aspect OrientedProgramming 面向切面编程),基于动态代理的功能增强方式。

高内聚、低耦合

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。

耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:高内聚,低耦合。

内聚与耦合:

内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。

耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。

内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合


解释:

程序的耦合

  • 耦合:程序间的依赖关系

  • 包括:

  • 类之间的依赖

  • 方法间的依赖

  • 解耦: 降低程序间的依赖关系

  • 实际开发中:

  • 应该做到:编译期不依赖,运行时才依赖。

  • 解耦的思路:

  • 第一步:使用反射来创建对象,而避免使用 new 关键字。

  • 第二步:通过读取配置文件来获取要创建的对象全限定类名

Spring中工厂的类结构

BeanFactory和 ApplicationContext 的区别


BeanFactory 才是 Spring 容器中的顶层接口。

ApplicationContext 是它的子接口。

BeanFactory 和 ApplicationContext 的区别: 创建对象的时间点不一样。

ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。

BeanFactory:什么使用什么时候创建对象。

Beanfactory的用法:

1
BeanFactory ac = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

ApplicationContext 接口的实现类


ClassPathXmlApplicationContext:

它是从类的根路径下加载配置文件 推荐使用这种

FileSystemXmlApplicationContext:

它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

AnnotationConfigApplicationContext:

当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

#Bean的相关的配置

用于配置对象,让spring创建管理。默认使用无参的构造方法,如果没有无参构造方法则会创建失败。

1.Bean标签的属性:

id:指定对象的唯一标识名称,里面不能出现特殊字符

name:没有使用约束中的唯一约束(理论上可以出现重复,但是实际开发中不能出现)里面可以出现特殊字符。

class:指定类的全限定名称(包名+类名)

2.Bean的作用范围的配置

scope:指定bean的作用范围

singleton:单例(默认值 Spring会采用单例模式创建这个对象)

prototype:多列 (Struts2和Spring整合一定会用到)

单例对象:scope="singleton"

         一个应用只有一个对象的实例。它的作用范围就是整个应用。

         生命周期:

                 对象出生:当应用加载,创建容器时,对象就被创建了。

                 对象活着:只要容器在,对象一直活着。

                 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

多例对象:scope="prototype"

         每次访问对象时,都会重新创建对象实例。

         生命周期:

                 对象出生:当使用对象时,创建新的对象实例(getBean)。

                 对象活着:只要对象在使用中,就一直活着。

                   对象死亡:当对象长时间不用时,被java的垃圾回收器回收了。

生命周期方法:

init-method:指定类中的初始化方法名称(生命周期相关)。

destroy-method:指定类中销毁方法名称(生命周期相关)。

request:web项目中,spring将创建的对象,存入request域中session

globalsession:web项目中,如果是服务器集群环境,应用在整个集群中;如果没有服务器集群,相当于session

3.Bean生命周期

init-method:指定类中的初始化方法名称,一般用于执行资源的初始化

destory-method:指定类的初始化方法名称,一般用于执行资源的释放

4.Bean的实例化方式

1.无参构造方式

2.静态工厂实例化的方式

3.实例工厂

#spring的属性注入

1.构造方法的属性注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 将service 实现类装配到容器中 -->
<bean id="accountDao" class="com.qiezicy.dao.impl.AccountDaoImpl"/>
<bean id="accountService" class="com.qiezicy.service.impl.AccountServiceImpl"/>

<!--
使用构造函数注入属性值:
涉及的标签:constructor-arg
属性:index:指定参数在构造函数参数列表的索引位置
name: 指定参数在构造函数中的名称,指定给谁赋值
=================
上面三个属性是指给谁赋值,下面属性是指赋什么值

value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得在配置文件中配置过的bean
-->
<bean id="accountServiceImpl2" class="com.qiezicy.service.impl.AccountServiceImpl2">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="12"/>
<constructor-arg name="accountDao" ref="accountDao"/>
</bean>

2.set方法的属性注入

1
2
3
4
5
6
7
8
9
10
11
12
        <bean id="bean1" class="com.qiezi.xmlpeizhi.Bean1">
<property name="name" value="BMW"/>
<property name="price" value="80"/>
</bean>
// 设置对象类型的属性
// name 属性:是标识要注入属性的setName 方法后面的 Name 单词名称首字母小写

<bean id="bean2" class="com.qiezi.xmlpeizhi.Bean2">
<property name="name" value="BMW"/>
// 这里的 第一个name = "bean1" 是 类中的名字 ref是连接 上一个属性的id
<property name="bean1" ref="bean1"/>
</bean>

3.p名称空间的属性注入(spring2.5以后)

首先引入p名称空间完成属性的注入

1
xmlns:p = "http://www.springframework.org/schema/p"

1.普通属性 p:属性名 = “值”

2.对象属性 p:属性名-ref = “值”

1
2
<bean id="bean1" class="com.qiezi.xmlpeizhi.Bean1" p:name="凯迪拉克" p:prince="20"></bean>
<bean id="bean1" class="com.qiezi.xmlpeizhi.Bean2" p:name="张三" p:bean1-ref="bean1"></bean>

4.SplEL的属性注入(spring3.0以后)

Spring Expression Language,Spring的表达式语言。

{SpEl}

1
2
3
4
5
6
7
8
9
10
<bean id="carInfo" class="com.qiezi.xmlpeizhi.CarInfo"/>
<bean id="car2" class="com.qiezi.xmlpeizhi.car2">
<property name="name" value="#{carInfo.name}"></property>
<property name="price" value="#{carInfo.calculatorPrice()}"></property>
</bean>

<bean id="employee" class="com.qiezi.xmlpeizhi.Employee">
<property name="name" value="#{'赵六'}"></property>
<property name="car2" value="#{car2}"></property>
</bean>

5.集合类型属性注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<bean id="collection" class="com.qiezi.collectionBean.demo01">

// 数组类型
<property name="arrs">
<list>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</list>
</property>

// 注入list 集合
<property name="list">
<list>
<value>赵三</value>
<value>李六</value>
<value>老七</value>
</list>
</property>

// 注入 set 集合
<property name="set">
<set>
<value>aaa</value>
<value>bb</value>
<value>cccc</value>
</set>
</property>


// 注入 map 集合
<property name="map">
<map>
<entry key="aaa" value="111" />
<entry key-ref="bean1" value="211" />
<entry key="cc" value-ref="bean2" />
</map>
</property>
</bean>

#分模块开发的配置

1.在加载配置文件的时候,加载多个

1
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml1","applicationContext.xml2");

2.在一个配置文件中引入多个配置文件

1
<import resource="applicationContext2.xml"/>

#spring的IOC注解

1
2
3
4
5
<!-- 配置资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

<!--配置注解扫描的包:声明到指定的包下去进行扫描,如果发现类上有对应的注解 将其装配到容器中-->
<context:component-scan base-package="com.qiezicy"></context:component-scan>

1.spring4还需要引入aop的包

2.导入包logging-1.1.1jar, log4j-1.2.15.jar, aop-4.2.4, beans, context, core, expression

3.创建配置文件并引入约束

4.配置组件扫描(那些包下的类使用IOC注解)

1.在类上添加注解

@Component(“”) 组件

作用:

在类上使用该注解,把资源让spring来管理。相当于在xml中配置一个bean。

属性:

value:指定bean的id。如果不指定value属性,默认bean的id是当前类的类名。首字母小写。

@Component 注解:相当于配置了标签

value= "accountService":相当于配置了bean标签的id属性,单独配置value时,可以省略value属性名称。

1.修饰一个类,将我们这个类交给spring管理

2.这个注解有三个衍生注解(功能类似) 修饰类

@Controller web层

@Service service层

@Repository dao层

2.设置属性的值

注解方式:可以没有set方法

​ 1.有set方法需要将属性注入的注解添加到set方法

​ 2.属性如果没有set方法,需要将属性注入到注解添加到属性上

设置普通属性的 值

@Value(“”) :设置普通属性的 值

设置对象属性的 值

@Autowired 注解的方式按照类型的方式设置 , xml是按照名称的方式进行设置

可以用@Autowired和@Qualified(value=”userDao”) 一起使用 强制按照名称的方式进行配置

上面两种的注解 可以使用一个替代类型是一套spring实现的一套接口规范

自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象的变量名称作为bean的id,在spring容器查找,找到了也可以注入成功。找不到就报错。

自动安装类型注入:比如说注入IAccount 自动去spring容器中寻找其子类比如说IAccountImpl在spring中,则将其注入到IAccount中 ,当有多个类型匹配时,使用要注入对象的变量名称作为bean的id在spring中找到并匹配,还可以配合@Qualifier(value = “xxx”)指定某一个id的bean进行注入。当都不喜欢用这两个的时候,可以直接使用@Resource(name = “xxxx”) 指定某一id的bean进行**注入**。

它只能注入其他bean类型:是受spring管理的bean

@Qualifier

作用:

在自动按照类型注入的基础之上,再按照Bean的id注入。它在给字段注入时不能独立使用,必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。

属性:

value:指定bean的id。

@Resource 按照名称注入 来对 对象设置属性

1
2
3
4
作用: 
直接按照Bean的id注入。它也只能注入其他bean类型。
属性:
name:指定bean的id。

@Value

作用:

注入基本数据类型和String类型数据的

属性:

value:用于指定值

1
2
3
// value 可以读取资源文件中的数据,但是必须现在applicationContext.xml中声明
@Value("${jdbc.url}")
private String url;

3.Bean的其他注解

1.声明周期相关的注解

@PostConstruct:初始化方法

@PreDestroy :销毁方法

2.Bean的作用范围的注解

@Scope :作用范围

1
2
3
4
5
6
位于类上
作用:
指定bean的作用范围。
属性:
value:指定范围的值。
取值:singleton prototype request session globalsession

使用:@Scope(“prototype”)

singleton 默认单例
prototype 多例
request
session
globalsession

3.xml和注解的比较

​ xml可以使用任何场景 结构清晰,维护方便

​ 注解:有些地方不能用 比如说类不是自己写的,但是开发方便

4.可以使用xml管理Bean,注解完成属性注入

5.注意事项:

​ 扫描是为了扫描类上的注解

​ 在没有扫描的情况下,使用属性注入的注解 @Resource @Value @Autowired @Qulifier

@Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
作用: 
用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。
获取容器时需要使用AnnotationApplicationContext(有@Configuration注解的类.class)。
示例代码:
/**
* spring的配置类,相当于applicationContext.xml文件
*/
@Configuration
public class SpringConfiguration {

}
注意:
我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?
请看下一个注解。

@ComponentScan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
作用: 
用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的:
<context:component-scan base-package="cn.itcast"></context:component-scan>
属性:
Value(单独使用可省略):用于指定要扫描的包。和标签中的basePackages属性作用一样。
示例代码:
/**
* spring的配置类,相当于bean.xml文件
*
*/
@Configuration
@ComponentScan("com.qiezicy")
public class SpringConfiguration {

}
注意:
我们已经配置好了要扫描的包,但是数据源和QueryRuner对象如何从配置文件中移除呢?
请看下一个注解。

@Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
作用: 
该注解只能写在方法上,将方法的返回值作为一个bean,并且放入spring容器。
属性:
name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。
示例代码:
@Bean(name="dataSource")
public DataSource createDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
dataSource.setUsername("root");
dataSource.setPassword("111");
return dataSource;
}

这里添加一个小贴士:其中的id可以选择不写是 因为看是否有人用到它,如果不写则这个id则默认是方法名称
注意:
我们已经把数据源和QueryRunner从配置文件中移除了,此时可以删除bean.xml了。
但是由于没有了配置文件,创建数据源的配置又都写死在类中了。如何把它们配置出来呢?
请看下一个注解。

@PropertySource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
作用: 
用于加载.properties文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties配置文件中,就可以使用此注解指定properties配置文件的位置。
属性:
value[]:用于指定properties文件位置。如果是在类路径下,需要写上classpath:

示例代码:

//加载外部资源文件
@PropertySource(value = {"classpath:jdbc.properties"})
public class JdbcConfig {

@Value("${jdbc.driverClass}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;


@Bean(name="jdbcTemplate")
public JdbcTemplate createJdbcTempalte(@Qualifier("dataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}

@Bean(name="dataSource")
public DataSource createDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

@Import

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
作用: 
用于导入其他配置类,在引入其他配置类时,其他类上可以不用再写@Configuration注解。当然,写上也没问题。
属性:
value[]:用于指定其他配置类的字节码。
示例代码:
@Configuration
@ComponentScan("cn.itcast")
@Import(value = { JdbcConfig.class })
public class SpringConfiguration {

}


@Configuration//写不写都行
@PropertySource(value = { "classpath:jdbc.properties" })
public class JdbcConfig {
}
注意:
我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
go on----》

通过注解获取容器

1
2

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

4.spring整合Junit

1.导入坐标

1
2
3
4
5
6
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
此处需要注意的是,spring5及以上版本要求junit的版本必须是4.12及以上,否则用不了。

2.在测试类上添加如下注解

@RunWith(SpringJUnit4ClassRunner.class)

1
2
3
4
5
@RunWith(SpringJUnit4ClassRunner.class)
作用:
替换掉junit的运行器,换成一个可以初始化spring容器的运行器。
属性:
value:单独配置时,value属性名称可以省略,配置SpringJUnit4ClassRunner.class来代替原来junit的运行器

@ContextConfiguration(locations = {“classpath:applicationContext.xml”})

这是在有配置文件的情况下

如果没有配置文件,纯注解的情况下则使用

@ContextConfiguration(classes = {SpringConfiguration.class})

作用:
    加载配置类或者xml配置文件
属性:
    value[]:用来指定xml配置文件的路径
    class[]: 用来指定配置类

5.注解总结

5.1用于装配Bean的注解

@Component(value = “xxx”)

​ 一般用于将三层中的bean装配到容器中,value可以省略,value的属性值作为bean的id

@Controller(value = “xxx”)

​ 一般用于将web层装配到容器中,使用方法和@Component(value = “xxx”)一模一样

@Service(value = “xxx”)

​ 一般用于将service层装配到容器中,使用方法和@Component(value = “xxx”)一模一样

@Repository(value = “xxx”)

​ 一般用于将dao层装配到容器中,使用方法和@Component(value = “xxx”)一模一样

5.2用于属性注入的注解

@Autowired

​ 只能安装bean类型注入,如果有多个类型匹配,默认将属性名称作为id去容器中查找

@Qualifier

​ 一般和@Autowired配合使用,用来注入指定id的bean,做方法的参数中可以独立使用

@Resource

​ 用来注入指定id的bean类型,相当于@Autowired + @Qualifier

@Value

​ 只能注入基本类型等数据,不能注入bean类型,可以使用#{}在资源文件中获取数据,前提是,外部资源文件被加载

5.3作用域的注解

@Scope

​ 用于指定bean的作用域,一般是singleton和prototype

5.4与生命周期相关的注解

@PostConstruct

​ 用于指定某一个方法为初始化方法

@PreDestory

​ 用于指定某一方法为销毁方法

5.5其他配置类相关的

@Configuration

​ 声明一个类为配置类,用于代替applicationContext.xml

@ComponentScan

​ 用于开启注解扫描的包

@Import

​ 用于导入其他类的

@Bean

​ 用于将方法返回的bean类型的对象装配到容器中

5.6与Junit相关的

@RunWith

​ 用于替换底层的运行器,初始化spring容器的

@ContextConfiguration

​ 用于指定配置文件或者配置类的

#AOP

在软件行业中,AOP为Aspect Oriented Programming 面向切面编程,通过预编译的方式和运行时期动态代理实现程序功能的统一维护的一种技术。AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑之间的耦合度降低,提供程序的可用性,同时提高了开发效率

AOP实现原理

动态代理

​ 特点:

​ 什么时候开始功能增加了,什么时间就创建这个代理对象,然后对目标对象进行功能的增强,不可见,一开始并不存在,在使用目标对象的方法的过程中才 出现。

​ 1.JDK动态代理(基于接口的动态代理) 只能对实现了接口的类产生代理。要求被代理类最少实现一个接口

​ 2.Cglib动态代理(基于子类的动态代理) (类似于Javassist第三方代理技术):可以对没有实现接口的类产生代理对象。要求被代理对象不能用final修饰的类(最终类)

spring底层可以自动进行切换使用这两种

AOP相关术语

1
2
3
4
5
6
class demo{
public void save(){};
public void find(){};
public void update(){};
public void delete(){};
}

Joinpoint:连接点,可以被拦截到的点,增删改差都可以被增强,这些方法被称为连接点

Pointcut:切入点,真正被拦截到的点,,如果只对save方法的增强,save称为切入点

Advice:通知、增强 方法层面的增强

现在对save方法进行权限校验,权限校验的方法称为通知。

Introduction:引介 是类层面的增强

Target:被增强的对象 如果demo 被增强 则demo就是Target

Weaving:植入,通知应用(Advice)到目标(Target)过程

将权限校验的方法的代码应用到UserDao的save方法上的过程被称为植入

Proxy:代理对象

Aspect:切面,多个通知和多个切入点组合!!!

#springAOP入门

AspectJ的XMl方式

spring的AOP简介

AOP思想最早由AOP联盟组织提出的,spring使用这种思想的最好的框架

1.引入jar包

2.导入配置文件

3.编写类

4.

1
2
3
4
5
// 引入spring和Junit4整合的jar 
@RunWith(SpringJUnit4ClassRunner.class)
// 加载配置文件
直接可以调用 工厂类 不用每次都创建了
@ContextConfiguration("classpath:applicationContext.xml")

AspectJ切入点表达式说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
execution:匹配方法的执行(常用)
execution(表达式)
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明:
全匹配方式:
public void com.qiezycy.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void com.qiezycy.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用*号,表示任意返回值
* com.qiezycy.service.impl.AccountServiceImpl.saveAccount()
包名可以使用*号,表示任意包,但是有几级包,需要写几个*
* *.*.*.*.AccountServiceImpl.saveAccount()
使用..来表示当前包,及其子包
* com..AccountServiceImpl.saveAccount()
类名可以使用*号,表示任意类
* com..*.saveAccount()
方法名可以使用*号,表示任意方法
* com..*.*()
参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数
* com..*.*(*)
参数列表可以使用..表示有无参数均可,有参数可以是任意类型
* com..*.*(..)
全通配方式:
* *..*.*(..)
注:
通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution(* com.qiezycy.service.impl.*.*(..))

常用标签

  1. aop:config

作用:用于声明开始aop配置

2.aop:aspect

作用:用于配置切面

属性:id给切面提供一个唯一标识

ref:引用配置好的通知类bean的id

3.aop:pointcut

作用:用于配置切入点表达式

属性:expression:用于定义切入点表达式

​ id:用于给切入点表达式提供一个唯一标识

4.aop:before

作用:用于配置前置通知

属性:

​ method:指定通知中方法的名称

​ pointcut:定义切入点表达式

​ pointcut-ref :指定切入点表达式的引用

5.aop:after-returning

作用:用于配置后置通知

属性:

​ method:指定通知中放的名称

​ pointcut:定义切入点表达式

​ pointcut-ref:指定切入点表达式的引用

6.aop:after-throwing

作用:

​ 用于配置异常通知

属性:

​ method:指定通知中方法的名称

​ pointcut:定义切入点表达式

​ pointcut-ref:指定切入点表达式的引用

7.aop:after

作用:

​ 用于配置最终通知

属性:

​ method:指定通知中方法的名称

​ pointcut:定义切入点表达式

​ pointcut-ref:指定切入点表达式的引用

8.aop:around

作用:

​ 用于配置环绕通知

属性:

​ method:指定通知中方法的名

​ pointcut:定义切入点表达式

​ pointcut-ref:指定切入点表达式的引用

说明:

​ 它是spring框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。

注意:

    通常情况下,环绕通知都是独立使用的

注解配置AOP

1.在配置文件中开启注解扫描和注解AOP

1
2
3
4
5
<!--开启注解扫描-->
<context:component-scan base-package="com.qiezicy"></context:component-scan>

<!--开启注解AOP-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2.注解装配service实现类

3.通知上使用注解配置并声明该类是一个切面类

1
2
3
4
@Component("logger")
@Aspect//表示当前类是一个切面类(也可以称之为通知类)
public class Logger {
}

4.使用注解配置前置通知类型

1
2
3
4
5
6
7
8
9
10
11
12
@Before
作用:
把当前方法看成是前置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

@Before("execution(* com.qiezicy.service.impl.*.*(..))")
//前置通知
public void beforePrintLog() {
System.out.println("前置通知执行了");
}
其他方法类似

后置通知

1
2
3
4
5
@AfterReturning
作用:
把当前方法看成是后置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用

异常通知

1
2
3
4
5
@AfterThrowing
作用:
把当前方法看成是异常通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用

最终通知

1
2
3
4
5
@After
作用:
把当前方法看成是最终通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用

环绕通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Around
作用:
把当前方法看成是环绕通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。

/**
* 环绕通知
* spring框架为我们提供了一个接口,该接口可以作为环绕通知的方法参数来使用
* ProceedingJoinPoint。当环绕通知执行时,spring框架会为我们注入该接口的实现类。
* 它有一个方法proceed(),就相当于invoke,明确的业务层方法调用
*
* spring的环绕通知:
* 它是spring为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*/
@Around("pt1()")
public Object around(ProceedingJoinPoint point) {
Object object = null;
try {
System.out.println("环绕通知");
point.proceed();
} catch (Throwable throwable) {
System.out.println("异常通知");
throwable.printStackTrace();
}finally {
System.out.println("最终通知");
}
return object;
}

配置切入点

1
2
3
4
5
6
7
8
9
10
@Pointcut
作用:
指定切入点表达式
属性:
value:指定表达式的内容
// 配置切入点
@Pointcut("execution(* com.qiezicy.service.impl.*.*(..))")
public void pt1() {

}

ps:如果开启全注解配置

需要创建一个配置类,与之前类似,只是多加了个AOP注解扫描

1
2
3
4
5
@Configuration//声明这是个配置类
@ComponentScan("cn.itcast")//配置包扫描
@EnableAspectJAutoProxy//开启AOP的注解扫描
public class SpringConfiguration {
}

#事务控制

事务控制API介绍

1.PlatformTransactionManager

​ 平台事务管理器,是spring真正管理事务的对象,是一个接口

实现类:

DataSourceTransactionManager:针对JDBC和Mybatis事务管理

​ HibernateTransactionManager:针对Hibernate事务管理

涉及到的两个接口:

​ TransactionDefinition:事务定义的对象

​ 事务的隔离级别

事务的传播行为

​ 事务是否只读

​ 事务超时信息

​ TransactionStatus:事务状态信息的对象

​ 是否有保存点

​ 是否已经完成

​ 等。。。

spring框架进行事务的管理,首先使用TransactionDefinition对事务进行定义,通过PlatformTransactionManager根据TransactionDefinition的定义信息进行事务的管理。在事务的管理过程中产生一系列的状态:保存到TransactionStatus中。

2.TransactionDefinition 事务的定义信息对象,里面有如下方法

获取事务对象名称 -String getName()

获取事务隔离级别 -int getlsolationLevel()
获取事务的超时时间 -int getTimeout()

获取事务是否只读 -boolean isReadOnly() 读写型事务:增删该开启事务;只读型事务:执行查询时,也会开启

事务的隔离级别

事务隔离反映事务提交并发访问时的处理态度

  • ISOLATION_DEFAULT 默认级别,归属于下列某一种
  • ISOLATION_READ_UNCOMMITTED 可以读取未提交数据
  • ISOLATION_REPEATABLE_READ 是否读取其他事务提交修改后的数据,解决不可重复读问题(MySQL默认级别)
  • ISOLATION_SERIALIZABLE 是否读取其他事务提交添加后的数据,解决幻读问题

事务的传播行为

传播行为解决的问题:一个业务层事务调用另一个业务层事务时,事务之间关系如何处理

事务传播行为PROPAGATION的取值:

REQUIRED支持当前事务,如果不存在,就新建一个(默认的传播行为)

​ 应用:删除客户 删除订单 处于同一个事务,如果 删除订单失败,删除客户也要回滚

​ SUPPORTS 支持当前事务,如果不存在,就不使用事务

​ MANDATORY支持当前事务,如果不存在,抛出异常

REQUIRES_NEW:如果有事务存在,挂起当前事务,创建一个新的事务

​ 应用:生成订单,发送通知邮件,通知会创建一个新的事务,如果邮件失败,不影响订单生成

​ NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务

​ NEVER:以非事务方式运行,如果有事务存在,抛出异常

NESTED:如果当前事务存在,则嵌套事务执行

​ -依赖于JDBC3.0提供SavePoint技术

​ -删除客户 删除订单,在删除客户后,设置SavePOint执行删除订单,删除订单和删除客户在同一个事务,删除部分订单失败,事务回滚SavePoint,由用户控制是事务提交,还是回滚

小结:

​ REQUIRED:一个事务,要都成功,要么都失败

​ REQUIRES_NEW:两个不同事务,彼此直接没有关系,一个事务失败了不影响另一个事务

​ NESTED:一个事务,在A事务调用B过程中,B失败了,回滚事务到之前SavePoint,用户可以选择提交事务或者回滚事务

超时时间

默认值是-1,没有超时限制,如果有,以秒为单位进行设置。

-建议查询时设置为只读事务

-TransactionStatus接口描述了某个时间点上事务对象的状态信息,包含有6个具体的操作

刷新事务

  • void flush()

获取是否存在储存点

  • boolean hashSavepoint()

获取事务是否完成

  • boolean isCompleted()

获取事务是否为新的事务

  • boolean isNewTransaction()

获取事务是否回滚

  • boolean isRollbackOnly()

设置事务回滚

  • void setRollbackOnly()

基于XML的声明式事务控制(配置方式)

1.装配事务管理器

1
2
3
4
5
 <!-- 配置一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入DataSource -->
<property name="dataSource" ref="dataSource"></property>
xxxxxxxxxx

2.应用事务管理器

首先需要配置tx和aop的名称空间和约束

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

配置事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!--配置事务策略-->
<tx:advice id="txAdvice">
<!--配置事务的属性-->
<tx:attributes>
<!--
指定对那些方法使用事务
name:方法的名称 使用通配符 * 代表对所有方法使用事务
isolation:配置事务的隔离级别,默认使用当前数据库默认的隔离级别
read-only:是否只读,一般对增删改方法使用false ,标识读写。对查询方法使用true 标识只读即可,默认读写
propagation:指定事务的传播行为,默认REQUIRED,增删改的时候使用 SUPPORTS:用于查询
no-rollback-for:指定对那种异常不会滚
rollback-for:指定那种异常进行回滚 没有默认值, 一般不用
timeout:设置事务的超时时间,单位是秒,默认是-1 永不超时

-->
<tx:method name="*"/>
<!--
表示只对query开头的方法使用只读事务和SUPPORTS的传播行为
相似度越高,匹配越高。因此query*代表了所有以query开头的方法都会使用它的事务配置
-->
<tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>

<!--配置aop-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt1" expression="execution(* com.qiezicy.service.impl.*.*(..))"/>

<!--配置事务管理器应用到切入点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>

基于注解配置事务

1.开启注解事务管理

1
2
3
4
5
6
7
<!--开启注解事务管理-->
<tx:annotation-driven/>
<!--装配事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>

2.在需要开启事务的类上添加注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Service
@Transactional
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;

public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}

@Override
public void transfer(String sourceName, String targetName, Float money) {
// 通过账户名称查询账户
Account source = accountDao.queryAccountByName(sourceName);
Account target = accountDao.queryAccountByName(targetName);

// 修改金额
source.setMoney(source.getMoney() - money);
target.setMoney(source.getMoney() - money);


// 执行修改
accountDao.updateAccount(source);
int x = 1 / 0;
accountDao.updateAccount(target);
}
}

基于配置类的方式

1。创建配置类

1
2
3
4
5
6
7
@Configuration
@ComponentScan("cn.itcast")
@EnableTransactionManagement//声明使用注解方式的事务管理器
@Import(value = {JdbcConfig.class,TxConfig.class})
public class SpringConfiguration {

}

2.JdbcConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@PropertySource({"classpath:jdbc.properties"})
public class JdbcConfig {

@Value("${jdbc.driverClassName}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;


@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}

@Bean
public DataSource getDataSource(){

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);

return dataSource;
}

}

3.TxConfig

1
2
3
4
5
6
7

public class TxConfig {
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}

Spring的监听器

Powered by Hexo and Hexo-theme-hiker

Copyright © 2016 - 2018 Francis的个人博客 All Rights Reserved.

UV : | PV :