spring复习

一、spring简介

1.Spring 中常用术语:

1.1框架:是能完成一定功能的半成品。

框架能够帮助我们完成的是:项目的整体框架、一些基础功能、规定了类和对象如何创建,如何协作等

1.2非侵入式设计:

从框架的角度可以理解为:无需继承框架提供的任何类,这样我们在更换框架时,之前写过的代码几乎可以继续使用

1.3轻量级和重量级:

轻量级是相对于重量级而言的,轻量级一般就是非入侵性的、所依赖的东西非常少、资源占用非常少、 部署简单等等,其实就是比较容易使用,而重量级正好相反

1.4容器:

在日常生活中容器就是一种盛放东西的器具,从程序设计角度看就是装对象的的对象,因为存在放入、 拿出等操作,所以容器还要管理对象的生命周期(拿来即用,方便统一管理)

1.5bean:

把Bean理解为 类的代理或代言人(实际上确实是通过反射、代理来实现的),这样它就能代表类拥有该拥有的东西了;

凡是子类及带属性、方法的类都注册Bean到Spring中,交给它管理

2、Spring 的优势

  • 低侵入 / 低耦合 (降低组件之间的耦合度,实现软件各层之间的解耦)
  • 声明式事务管理(基于切面和惯例) 方便集成其他框架(如MyBatis、Hibernate)
  • Spring 框架中包括了 J2EE 三层的每一层的解决方案(一站式)
  • 降低 Java 开发难度

3、spring能帮我们做什么

  • Spring 能帮我们根据配置文件创建及组装对象之间的依赖关系
  • Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制
  • Spring 能非常简单的帮我们管理数据库事务
  • Spring 还提供了与第三方数据访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套 JDBC访问模板来方便数据库访问。
  • Spring 还提供与第三方Web(如Struts1/2、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建
  • Spring 能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。

4、spring框架的结构

img

结构介绍点击查看

二、IOC容器

1.首先聊聊控制反转

  • 这不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框 架来管理。
  • 以往的思路:若要使用某个对象,需要自己去负责对象的创建
  • 反转的思路:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创 建过程,也就是把创建对象的控制权反转给了Spring框架

2.生活举例

过年了,需要给家里打扫个卫生,于是想请几个钟点工来擦擦玻璃。

解决方案:

1、自己主动的去,询问查找,看看谁认识钟点工,有了联系方式后,自己打电话邀约,谈价钱

2、直接打电话给家政公司,提出要求即可。

那么家政公司从哪里来?

1、自行构建 :我们可以使用配置文件,或者注解的方式定义一下咱们自己的容器里存放的东西。

2、使用别人的 :一定会有很多有钱人,成立自己的各类公司,他们的这些服务都可以集成在咱们的容器里,为我们提供很多强大的功能,比如spring自带的就很多template模板类。

3.构造器注入

编写javaBean

public class Dog {	
    private String name;
    private int age;

    public Dog(){}
    public Dog(String name, int age) {
    this.name = name;
    this.age = age;
    }
    public String getName() { return name;}
    public void setName(String name) {
    this.name = name;
    }
    public int getAge() { return age; }
    public void setAge(int age) {
    this.age = age;
    }
}

有参构造

注:id是自定义名字(一般为实体类名小写),后面.getBean(“dog”)中的名字与之对应

<!-- 推荐使用,根据名字注入 -->
<bean id="dog" class="com.xinzhi.entity.Dog">
<constructor-arg name="name" value="goldilocks"/>
<constructor-arg name="age" value="11"/>
</bean>
<!-- 直接注入也是按顺序注入,了解 -->
<bean id="dog" class="com.xinzhi.entity.Dog">
    <constructor-arg value="goldilocks"/>
    <constructor-arg value="11"/>
</bean>	
<!-- 按照参数类型注入,了解 -->
<bean id="dog" class="com.xinzhi.entity.Dog">
    <constructor-arg type="java.lang.String" value="goldilocks"/>
    <constructor-arg type="java.lang.Integer" value="11"/>
</bean>

4.set 方法注入

注:要求被注入的属性 , 必须有set方法 。

实体类

@Data
public class Address {
	private String addressInfo;
}
@Data
public class User implements Serializable {
    private String name;
    private Address address;
    //爱好
    private String[] hobbies;
    //职务
    private List<String> duties;
    //家庭关系
    private Map<String,String> familyTies;
    //购物车商品
    private Set<String> carts;
    //工作经历
    private Properties workExperience;
    //女儿
    private String daughter;
}

set注入(property 相当于调用了我们的setter方法)

<bean id="address" class="cn.itnanls.Address" >
	<property name="addressInfo" value="珠琳旺角大厦"/>
</bean>

<bean id="user" class="cn.itnanls.User">
    <!-- 基本属性注入 -->
    <property name="name" value="小白" />
    <!-- 引用类型注入(也可以使用自动装配,下面有介绍) -->
    <property name="address" ref="address"/>
    <!-- 数组注入 -->
    <property name="hobbies">
        <array>
            <value>写程序</value>
            <value>漂亮姑娘</value>
            <value>赢钱</value>
        </array>
    </property>
    <!-- list注入 -->
    <property name="duties">
        <list>
            <value>IT老师</value>
            <value>互联网从事人员</value>
        </list>
    </property>
    <!-- set注入 -->
    <property name="carts">
        <set>
            <value>纸尿裤</value>
            <value>玩具</value>
        </set>
    </property>
    <!-- map注入 -->
    <property name="familyTies">
         <map>
            <entry key="father" value="某某1" />
            <entry key="mather" value="某某2" />
        </map>
    </property>
    <!-- property注入 -->
    <property name="workExperience">
        <props>
            <prop key="first">电厂职工</prop>
            <prop key="second">java开发工程师</prop>
            <prop key="third">java讲师</prop>
        </props>
    </property>
    <!-- null注入 -->
    <property name="daughter">
        <null/>
    </property>
</bean>

测试

@Test
public void testDI(){
    ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext("applicationContext.xml");
    //User user = applicationContext.getBean(User.class);
    //或者如下这种
    User user = (User)applicationContext.getBean("user");
    System.out.println(user);
}

5.Bean的作用域

Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,作用域包括singleton(单例模式)、prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)。

点击查看详细介绍

6.自动装配

spring作为长期以来java最火的框架,其IOC做的十分的健全,以上情况都是我们手动装配,但是我们也 说了spring及其灵活的帮助我们完成了解耦工作,如果所以的类都是自己手动完成注入,他的解耦能力 就不会体现的那么强烈了,于是spring还为我们提供了自动装配的能力

6.1、按照名字自动装配

修改bean配置,增加一个属性 autowire=“byName”;

过程:

从javabena的set方法获取名字,如setAddress,就去spring容器中找名字为address的bean(通过实体类中的某个字段名和所需要的bean的id相同进行匹配),如果有,就取出注入;如果没有,就不注入。

//id="address",如果和user实体类中的字段名不一样就匹配不了
<bean id="address" class="cn.itnanls.Address" >
</bean>

<bean id="user" class="com.xinzhi.entity.User" autowire="byName">
//...中间省略,也不用手动注入address
</bean>

6.2、按照类型自动装配

修改bean配置,增加一个属性 autowire=“byType”

过程:

根据class="cn.itnanls.Address"自动装配,当然不可以同时出现既按照类型和名字都可以完成装配的

//id="address",如果和user实体类中的字段名不一样就匹配不了
<bean id="address555" class="cn.itnanls.Address" >
</bean>

<bean id="user" class="com.xinzhi.entity.User" autowire="byType">
//...中间省略,也不用手动注入address
</bean>

7.注解开发(常用)

注解只是个标识,我们需要去解析他。 所以要让注解生效,还需要进行一个配置。在运行前,扫描一下所有的文件,看看哪些类头上戴了注解。 自己写扫描文件的方法太麻烦,spring当然提供了方式。

准备工作

1、配置文件需要加头文件(context相关的配置)

<?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: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
        https://www.springframework.org/schema/context/spring-context.xsd
>	

2、在配置文件中加次注解,就是告诉spring【cn.itnanls】下的类都扫描一下看看

<context:component-scan base-package="cn.itnanls"/>

此时准备工作做完了,只需加入相应的注解就行

使用注解

1.修改文件,在user的address属性上加注解@Autowired

注:@Autowired先按类型匹配,没有匹配到则按照名字匹配

@Autowired
private Address address;

2.删除user配置文件中的address注入(这时就不需要下面这个了)

<!-- 引用类型注入 -->
<property name="address" ref="address"/>

3.测试。。。

常用的注解

第一组,自动装配

1.@Autowired

@Autowired先按类型匹配,没有匹配到则按照名字匹配

2.@Qualifier

@Qualifier不能单独使用,它和@Autowired配合可以根据名称(bean中的id)进行自动装配

例:

@Qualifier(value = "cat2")
private Cat cat;

3.@Resource

@Resource是javaee的注解

@Resource如有指定的name属性,先按该属性进行byName方式查找装配

其次再进行默认的byName方式进行装配

如果以上都不成功,则按byType的方式自动装配

都不成功,则报异常

例子:

@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
第二组,分层开发

@Controller 控制层

@Service 业务层

@Repository Dao层

其实一个注解都能搞定,@Bean能代替所有,但是应为分层的原因,使用不同的注解可读性更高。 这些注解就是在容器启动时生成bean,和配置文件的作用一样。

第三组,组件开发

@Component

@Value

例子:

@Component
public class Student {
@Value("张三")
private String name;
@Autowired
private Address address;
}
第四组,配置文件

@Configuration

@Bean

@Configuration
public class AnimalConfig {
    @Bean
    public Mouse mouse(){
        Mouse mouse = new Mouse();
        mouse.setAge(12);
        mouse.setName("jerry");
        return mouse;
    }
    //参数是你要注入的其他的bean
    @Bean
    public Cat cat(Mouse mouse){
        Cat cat = new Cat();
        cat.setAge(5);
        cat.setName("Tom");
        cat.setFood(mouse);
        return cat;
    }
}

小结:很多注解的功能都是一样的,都是讲javabean注入到容器,只是多种多样的注解使得我们的语义化更加友好

8.生命周期

点击查看详细

9.懒加载

例:

<bean id="address" class="cn.itnanls.Address" lazy-init="true"/>

用到就加载,不用就不加载,默认是及时加载

三、AOP

1.什么是AOP

  • AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统 层面上的问题,比如日志,事务,权限等待
  • 在不改变原有逻辑的基础上,增加一些额外的功能。代理也是这个功能。
  • AOP技术利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个 类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是 那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降 低模块之间的耦合度,并有利于未来的可操作性和可维护性。

长话短说:就是有这样的需求,在某一些类中,增加很多统一的代码,比如,日志,事务,权限等。

2.AOP的相关名词

1.通知、增强处理(Advice)

通知、增强处理(Advice) 就是你想要的功能,也就是上说的安全、事物、日子等。你给先定义好,然后再想用的地方用一下。包含Aspect的一段处理代码

2.连接点(JoinPoint)

连接点(JoinPoint) 这个就更好解释了,就是spring允许你是通知(Advice)的地方,那可就真多了, 基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点。只要记住,和方法有关的前前后后都是连接点。

3.切入点(Pointcut)

切入点(Pointcut) 具体要要执行的连接点(连接点有很多,但是真正的去执行的连接点就是我们的切入点)

4.切面(Aspect)

切面(Aspect) 切面是通知和切入点的结合

5.引入(introduction)

引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通 知定义的)用到目标类中吗(描述的是一种过程)

6.目标对象(TargetObject)

目标对象(TargetObject) 引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况 下,被咱们织入切面。而自己专注于业务本身的逻辑。

7.织入(weaving)

织入(weaving) 把切面应用到目标对象来创建新的代理对象的过程。

3.练习

1.自定义类和方法实现

第一步 : 写我们自己的一个切入类(MyAop.class)

public class MyAop {
    public void before(){
    	System.out.println("执行方法之前打印一条日志! -- 自定义形式");
    }
    public void after(){
   		System.out.println("执行方法之后打印一条日志! -- 自定义形式");
    }
}

第二步去spring中配置

//加入头文件
xmlns:aop="http://www.springframework.org/schema/aop"


http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd


<!--注册bean-->
<bean id="myAop" class="com.xinzhi.aop.MyAop"/>
<!--aop的配置-->
<aop:config>
<!--使用AOP的标签实现一个切面-->
    <aop:aspect ref="myAop">
        <aop:pointcut id="pointcut" expression="execution(*com.xinzhi.service.impl.*.*(..))"/>
        <aop:before pointcut-ref="pointcut" method="before"/>
        <aop:after pointcut-ref="pointcut" method="after"/>
    </aop:aspect>
</aop:config>

2.注解实现(当下主流)

第一步:编写一个注解实现的增强类

@Component
@Aspect
public class AnnotationAOP {
    //springaop自动的5种aop这里全部列出
    @Before("execution(* com.xinzhi.service.impl.*.*(..))")
    public void before(){
    	System.out.println("---------方法执行前before()---------");
    }
    @After("execution(* com.xinzhi.service.impl.*.*(..))")
    public void after(){
    	System.out.println("---------方法执行后after()---------");
    }
    @AfterReturning("execution(* com.xinzhi.service.impl.*.*(..))")
    public void afterReturning(){
    	System.out.println("---------方法返回后afterReturning()---------");
    }
    @Around("execution(* com.xinzhi.service.impl.*.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
    	System.out.println("-------环绕前-------");
    	System.out.println("签名(拿到方法名):"+jp.getSignature());
    //执行目标方法proceed
    Object proceed = jp.proceed();
    	System.out.println("-------环绕后------");
    	System.out.println(proceed);
    }
    @AfterThrowing("execution(* com.xinzhi.service.impl.*.*(..))")
    public void afterThrow() {
    	System.out.println("--------------有异常发生-----------------" + new Date());
    }
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<aop:aspectj-autoproxy/>

注:定义切入点表达式execution ( com.sample.service.impl…(…))*

execution()是最常用的切点函数, 整个表达式可以分为五个部分:

  • execution(): 表达式主体。
  • 第一个号:表示返回类型,号表示所有的类型。
  • 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包, com.sample.service.impl包、子孙包下所有类的方法。
  • 第二个号:表示类名,号表示所有的类。
  • (…):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任 何参数。

四、整合MyBatis

动态Mapper(常用)

配置文件:

<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<!-- Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 扫描 cn.wmyskxz.mapper 包下的组件 -->
    <property name="basePackage" value="com.xinzhi.dao"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--关联Mybatis-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations"
    value="classpath:com/xinzhi/dao/*.xml"/>
</bean>
	

企业的做法 一般使用动态mapper,使用动态代理完成工作,一般会使用xml和mappeer配合使用。这才是最佳实践。

五、声明式事务

1、事务特性

  1. 原子性(Atomicity):事务是数据库逻辑工作单元,事务中包含的操作要么都执行成功,要 么都执行失败。
  2. 一致性(Consistency):事务执行的结果必须是使数据库数据从一个一致性状态变到另外一 种一致性状态。当事务执行成功后就说数据库处于一致性状态。如果在执行过程中发生错误, 这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这是数据库就处于不一致状 态。
  3. 隔离性(Isolation):一个事务的执行过程中不能影响到其他事务的执行,即一个事务内部 的操作及使用的数据对其他事务是隔离的,并发执行各个事务之间无不干扰。
  4. 持续性(Durability):即一个事务执一旦提交,它对数据库数据的改变是永久性的。之后 的其它操作不应该对其执行结果有任何影响。

2.Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以 使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

2.1编程式事务管理

将事务管理代码嵌到业务方法中来控制事务的提交和回滚 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

2.2声明式事务管理 (常用)

一般情况下比编程式事务好用。 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

2.3声明式事务管理举例

2.3.1注解的形式

头文件的约束导入 : tx

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 开启事务注解,并配置一个事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
</bean>

加注解:

@Service
@Transactional
public class UserServiceImpl implements IUserService{
    @Autowired
    private UserMapper userMapper;
    public void transaction() {
        userMapper.addUser(new User(100,"ITbai","123"));
        int i = 1/0;
        userMapper.updateUser(new User(100,"ITbai","234"));
    }
}

2.3.2配置文件进行声名式事务的配置

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置aop织入事务-->
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(*com.xinzhi.service.impl.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

2.4spring事务传播特性

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这 个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 propagation_required类似的操作

注:Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。前三种常用,其他一般不使用

最后张口松耦合,闭口高内聚

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐