Spring框架_学习笔记

Spring框架

Spring是一个大框架:包括Spring,SpringMVC,SpringBoot,SpringCloud
Spring功能:类和类之间的管理,帮助开发人员创建和管理对象
Spring核心技术:Ioc 和 AOP,能实现模块之间和类之间的解耦合

Spring优点

  1. 轻量,Jar 包一般都较小
  2. 针对接口编程,解耦合
  3. AOP 编程的支持
  4. 可以集成其他各种框架

Spring体系结构

  • 数据库模块
  • Web模块
  • AOP模块
  • 集成功能模块
  • 核心容器模块
  • 测试模块

 IOC (Inversion of Control)控制反转

本质是一种理论,一种思想,即把对象的创建,赋值,管理工作都交给代码之外的容器实现
容器本质可以是一种服务器,或者一种框架

服务器:如 Tomcat 帮助我们自动创建 Servlet,Listener,Filter 对象
框架:如 Spring 框架

控制:创建对象,对象赋值,对象关系管理
正转:由开发人员用 new 构造方法来创建对象
反转:创建对象,对象赋值,对象关系管理交给代码之外的容器实现

使用 IOC 的优点

  • 减少对代码的改动,也能实现不同功能的修改

  • 易于维护,解耦合

IOC 技术实现

DI 是 IOC 的技术实现,意为依赖注入(Dependency Injection)
只需要提供要使用的对象名称即可,创建赋值查找等过程由容器封装实现

Spring 使用 DI 实现了 IOC 功能,底层创建对象使用的是反射机制

Spring的配置文件

<?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="student" class="service.impl.StudentImpl"/>
</beans>

beans:是根标签,spring把java对象成为bean。
spring-beans.xsd 是约束文件,和mybatis指定dtd是一样的。
id: 对象的自定义名称,唯一值。spring通过这个名称找到对象
class: 类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
Spring 完成 Someservice someservice = new SomeserviceImpl();
被Spring生成的对象会放置Spring容器中,是一个Map集合的方式

Spring的使用方法

public static void main(String[] args){
        String springConfig = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(springConfig);
        Student student = (Student) ac.getBean("student");
        int num = ac.getBeanDefinitionCount();
        student.doService();
        System.out.println("对象数量:" + num);
        String[] objects = ac.getBeanDefinitionNames();
        for(String name:objects){
            System.out.println(name);
        }
}

/*
    通过 getBean() 方法来获取生成好的对象
    ac.getBeanDefinitionCount() 获取Map集合中对象数量
    ac.getBeanDefinitionNames() 获取对象名称数组
*/

ApplicationContext 就是表示Spring容器的对象
当ApplicationContext被创建时,Spring中Map中的所有对象都被构造方法创建

通过Spring 给 Java 对象赋值

基于 XML文件的 DI 给对象赋值

1. 通过 set 方法
本质是在调用完构造方法创建对象后去调用对象中的set方法
PS:有set方法,但是对象没有对应的属性并不影响其执行
① 简单类型

<bean id="student" class="service.impl.StudentImpl">
    <property name="id" value="20"/>
    <property name="age" value="21"/>
</bean>

② 引用类型

<bean id="mySchool" class="service.impl.School">
    <property name="name" ref="北京大学"/>
</bean>
<bean id="student" class="service.impl.StudentImpl">
    <property name="school" ref="mySchool"/>
</bean>

ref中填写某个bean的id

2. 通过构造方法

<bean id="student" class="service.impl.StudentImpl">
        <constructor-arg name="name" value="李华"/>
        <constructor-arg Index="1" ref="lihuaSchool"/>
</bean>

name为形参名称,value为简单类型的值
Index为参数位置(从0开始计算),ref为引用类型的值

引用类型自动注入

1. byName
java对象中的属性和Spring中 <bean> 的id是名称一样的,且数据类型相同

<bean id="student" class="class" autowire="byName">
    简单类型属性赋值
</bean>

这种方法本质通过set 方法为引用类型赋值,而构造方法中只能包含简单类型的属性

2. byType

  • java 类中的数据类型和Spring中的数据类型是同源的

同源的定义:

  1. java类中引用类型的数据类型和bean的class的值是―样的。
  2. java类中引用类型的数据类型和bean的class的值父子类关系的。
  3. java类中引用类型的数据类型和bean的class的值接口和实现类关系的语法
    <bean id="xx" class="yyy" autowire="byType">
        简单类型属性赋值
    </bean>

    PS:在byType中,在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的

引入配置文件

<import resource="classpath:其他配置文件路径">

注解 DI

使用注解 DI 必须加入spring-aop依赖,而加入spring-context依赖同时会间接加入spring-aop依赖

@Component
用于创建对象,等同于<bean>的功能

@Component(value="myStudent")
public class Student{
    private String name;
}

属性:value就是对象的名称,也就是bean的id值(当只有value一个属性时,value可以省略不写)
value的值是唯一的,创建的对象在整个spring容器中就一个
当不对属性进行赋值时,默认的value为类名的首字母小写

声明组件扫描器

<context:component-scan base-package="包名,包名1"/>

包名和包名之间可以用 ; 或者 , 来分隔

Spring中和@component功能一致,创建对象的注解还有:

  1. @Repository (用在持久层类的上面):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
  2. @Service(用在业务层类的上面):放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务等功能的。
  3. @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的控制器对象,能够接受用户提交的参数,显示请求的处理结果。

以上三个注解的使用语法和@component一样的。都能创建对象,但是这三个注解还有额外的功能,他们三个可以用来给项目分层

@Value 简单属性赋值
位置:
1. 属性定义上面,推荐使用
2. 在set方法的上面

@Autowired : Spring框架提供的注解,实现引用类型自动注入
默认使用 byType
位置:
1. 属性定义上面,推荐使用
2. 在set方法的上面
byName方法
在属性上方加入@Autowired并且在属性上方加入@Qualifier(value="beanId")

@Autowired 属性 required 默认为true
意为引用类型赋值失败,报错并终止程序,可以手动赋值为false,使引用类型可以为null

@Resource
来源于JDK,而不是Spring框架
也是自动注入原理,支持byName和byType
默认为byName(如果byName赋值失败,改用byType )
位置:
1. 属性定义上面,推荐使用
2. 在set方法的上面

@Resource(name="school")指定了name之后假如赋值失败也不会使用byType方式

注解方式缺点:

  1. 对代码有较强的侵入性,建议是不常更改的代码才使用注解方式
  2. 代码结构繁杂

注解可以和配置文件联用,可维护性更高
在Spring主配置文件中引入 property 配置文件

<context:property-placeholder location="classpath:----"/>
<!--
    引用配置信息
    @Value("${key}");
-->

AOP 面向切面编程

Jdk动态代理要求目标对象必须实现接口

动态代理可以给目标类方法增加额外功能(功能增强)而不改变原目标类的代码,如添加日志

如果不使用动态代理而改变目标类方法很有可能使 业务方法 与 非业务方法(增加日志功能) 冗杂在一起,不符合开闭原则,从而使代码耦合度提高,并且不利于维护

AOP实现就是基于动态代理(规范化的动态代理)
当目标类有接口时使用的是JDK动态代理,而当目标类没有接口时,使用的是cgLib的动态代理
如果有接口时也想要用cglib动态代理,在主配置文件下加入如下代码:

<aop:aspectj-autoproxy proxy-target-class="true"/>

Aspect:切面,给目标类增加功能就是切面。
切面特点:一般都是非业务功能,可以独立使用

怎么理解面向切面编程?

需要在分析项目功能时,找出切面
合理的安排切面的执行时间(在目标方法前,还是目标方法后)
合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

术语:

  1. 切面   Aspect:给目标类增加功能就是切面。
  2. 连接点   JionPoint,连接业务方法和切面的位置。即某类中的业务方法
  3. 切入点   Pointcut,多个连接点方法的集合
  4. 目标对象  哪个类增加功能哪个类就是目标对象
  5. 通知   Advice,通知切面功能执行的时间

项目开发中很少使用Spring 的 aop 实现,因为Spring的aop比较笨重。

aspectJ

  • 一个开源的aop框架

aspectJ 框架实现 aop 有两种方式

  1. 使用xml配置文件
  2. 使用注解,aspectJ 有 5个注解

AspectJ 的切入点表达式

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

aspectJ依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

@Aspect

public class MyAspect{
    //定义切面方法
    @Before(value="execution(public void classpath.class(...))")
    public void myBefore(){}
}

@Aspect表示当前类是切面类
切面方法规范:

  • 修饰符 public
  • 返回值 void
  • 如果有参数,参数不是自定义的

@Before:前置通知注解
在目标方法之前执行,不会改变方法的执行结果

声明切面类对象

<bean id="myAspect" class="classpath"/>

声明自动代理生成器(会把Spring中所有目标对象都生成代理对象)

<aop:aspectj-auto proxy/>

实现步骤:

创建业务功能接口

public interface SomeService {
    void doThis();

    void doThat();
}

创建业务接口实现类

public class DoSomeService implements SomeService {
    public void doThis(){
        System.out.println("I am doThis");
    }

    public void doThat(){
        System.out.println("I am doThat");
    }
}

创建AspectJ切面类

@Aspect
public class MyAspectJ {

    @Before("execution(public void service.impl.DoSomeService.*())")
    public void doAspectJ(){
        System.out.println("AspectJ Time is" + new Date());
    }
}

将对象交给Spring容器管理,并声明切面类对象和自动代理生成器

<bean id="doSomeService" class="service.impl.DoSomeService"/>

<bean id="MyAspectJ" class="util.MyAspectJ"/>

<aop:aspectj-autoproxy/>

测试AspectJ代码

@Test
public void testAspectJ(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        SomeService someService = (SomeService) ac.getBean("doSomeService");
        someService.doThat();
}

代理中获得的必须是SomeService类型的接口,而不能是DoSomeService的接口实现类

JoinPoint 参数
如果要有JoinPoint参数,它必须在参数列表的第一个位置

@Before("execution(public void service.impl.DoSomeService.*(..))")
public void doAspectJ(JoinPoint jp){
        System.out.println(jp.getSignature());    //获取完整的jp信息
        System.out.println(jp.getSignature().getName());//获取触发的函数名
        Object[] objects = jp.getArgs();        //获取实参列表
        for(Object arg:objects){
            System.out.println(arg);
        }
}

@AfterReturning:后置通知
属性:

  1. value切入点表达式
  2. returning自定义的变量,表示目标方法返回值。自定义变量名必须和通知方法的形参名一样。

特点:

  1. 在目标方法之后执行的。
  2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
  3. 可以修改这个返回值
@AfterReturning(value = "execution(public int service.impl.DoSomeService.*(..))",returning = "result")
public void doAfterJ(Object result){
        System.out.println(result);
        System.out.println((int)result*2);
}

可以读取和修改result的值,但是因为他是在目标方法执行完毕后该进行的,
因此目标方法已经将返回值赋值给了某个变量,而修改的result并不能改变原目标方法的返回值

@Around:环绕通知

特点:

  1. 在目标方法前后都增强功能
  2. 控制目标方法是否被调用
  3. 修改原目标方法执行结果

环绕通知可以理解为JDK动态代理

规范:

  1. public
  2. 必须有一个返回值,推荐使用Object
  3. 方法名称自定义
  4. 方法有参数,固定的参数ProceedingJoinpoint
@Around("execution(* *..impl.DoSomeService.doAround(..))")
public Object doAroundJ(ProceedingJoinPoint jp) throws Throwable{
        Object[] objs = jp.getArgs();
        int num = 0;
        Object result = 1;
        if(objs != null && objs.length >=1){
            num = (int) objs[0];
        }
        System.out.println("环绕通知,前通知");
        if(num == 2){
            result = jp.proceed();        //相当于动态代理的method.invoke()
        }
        System.out.println("环绕通知,前通知");
        return result;
}

返回类型为Object,通过if判断实参来控制是否执行环绕通知
最后修改result的值会影响目标方法的返回值

@AfterThrowing:异常通知

规范:

  1. public
  2. 没有返回值
  3. 方法名称自定义
  4. 方法可以没有参数,如果有是Joinpoint

属性:

  1. value切入点表达式
  2. throwinng自定义的变量,表示目标方法抛出的异常对象。变量名必须和方法的参数名一样

特点:

  1. 在目标方法抛出异常时执行的
  2. 可以做异常的监控程序,监控目标方法执行时是不是有异常。 (如果有异常,可以发送邮件,短信进行通知 )

@After:最终通知
一般用于做资源清除

规范:
1.public
2.没有返回值
3.方法名称自定义
4.方法可以没有参数,如果有是Joinpoint

属性:value切入点表达式
位置:在方法的上面
特点:
1.总是会执行
2.在目标方法之后执行的

辅助功能

@Pointcut 
用于给切入点表达式(execution())取别名

@Pointcut(value = "execution(* *..impl.*(..))")
private void myPointcut(){
    //内部不需要代码
    //其他切入点可以直接将myPointcut()当中切入点表达式使用
}

Spring 集成 MyBatis

详见此处

Spring 事务处理

Spring 事务管理器

事务管理器是一个接口和众多实现类

接口:PlatfromTransactionManager,定义了 commit 和 rollback 方法
实现类: 创建了每一种数据库访问技术对应的事务处理类

1) 事务的隔离级别:有4个值。

  • READ_UNCOMMITTED:   读未提交。未解决任何并发问题。
  • READ_COMMITTED:   读已提交。解决脏读,存在不可重复读与幻读。
  • REPEATABLE READ:   可重复读。解决脏读、不可重复读,存在幻读。
  • SERIALIZABLE:   串行化。不存在并发问题。

2) 事务超时时间
表示一个方法执行的最长执行时间,如果时间超出则事务回滚
单位是秒,整数值,默认是 -1(项目中影响因素过多,一般保持默认)

3) 事务传播行为
控制业务方法是不是事务的,是什么样的事务的。

PROPAGATION_REQUIRED若当前存在事务,则该方法加入当前事务之中,否则新建一个事务
PROPAGATION_REQUIRES_NEW总是新建一个事务
PROPAGATION_SUPPORTS不论有没有事务都可以执行事务

提交事务,回滚事务的时机

  1. 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器的 comnit

  2. 当你的业务方法抛出运行时异常或ERROR,spring执行回滚,调用事务管理器的 rollback 运行时异常的定义: RuntimeException和他的子类都是运行时异常,例如NullPointException ,NumberFormatExcept.

  3. 当你的业务方法抛出非运行时异常,主要是受查异常时,提交事务( 受查异常:在你写代码中,必须处理的异常。例如IOException,SQLException )

事务管理实现流程

方法1:使用@Transactional的步骤,适合中小型项目
1.声明事物管理器对象

<bean id="transactionManager" class="DataSourceTransactionManager">
    <property name="dataSource" ref="myDataSource"><!--指定对于的数据源-->
</bean>

2.开启事物注解驱动
Spring使用aop机制,创建@Transactional所在的类代理对象,给其方法加入事物功能
本质是:利用环绕通知,在方法执行前后分别开启和提交(回滚)事物

<tx:annotation-driven transaction-manager="transactionManager">

PS:有多个不同包下的annotation-driven,应该使用结尾为tx的,否则会报错

3.在相应方法上添加@Transactional

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.DEFAULT,
    readOnly = false,
    rollbackFor = {异常类.class}
)

/*
    propagation 指定传播方式
    isolation 使用默认即可
    readOnly 当事物只用查询功能时为true,可以提高数据库查询效率
    rollbackFor 指定当抛出指定异常时,事务发生回滚
    (一般继承与RuntimeException运行时异常,
    当异常在rollbackFor列表中,不论是什么类型的异常都会回滚,
    当不在表中时,会判断是否继承于RuntimeException异常,是则回滚)
    PS:当属性全都不写时,全都使用默认值
*/

方法2:配置 xml 文件实现,适合大型项目

1.需要引入 aspectj 依赖
2.声明事物管理器对象
3.声明业务方法的事务属性(隔离级别,传播行为)

<tx:advice id="xx" transaction-manager="transactionManager">
    <!--transaction-manager要写声明的事物管理器的 id-->
    <tx:attributes>
        <!--
            name:方法名,不带用包名,可以使用通配符
            rollback-for: 必须是全限定名称
        -->
        <tx:method name="" propagation="" rollback-for=""/>
    </tx:attributes>
</tx:advice>

4.配置aop(用于指定哪些包中需要使用到事务)

<aop:config>
    <!--配置事务方法切入点-->
    <aop:pointcut id="" expression="execution(* *..service..*  .*(..))"/>
    <!--
        配置增强器:
            advice-ref 使用 <tx:advice> 标签的id
            pointcut-ref 使用 <aop:pointcut> 标签的id
    -->
    <aop:advisor advice-ref="" pointcut-ref=""/>
</aop:config>

Spring 和 Web

1.创建监听器
将 applicationContext 对象交给全局变量管理,可以使用框架中提供的,也可以自己创建

<listener>
    <listener-class>ContextLoaderListener</listener-class>
</listener>

2.修改默认文件位置
因为在创建监听器时,会默认到WEB-INF/目录下寻找applicationContext.xml文件

<context-param>
    <param-name>contextConfigLocation<param-name>
    <param-value>classpath:spring.xml</param-value>
</context-param>

3.获取 applicationContext 对象

WebApplicationContext ctx = null;
ServletContext sc = getServlertContext();
ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);

扩展:

单元测试

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

测试方法

@Test
public void test01(){
    System.out.println("Test is running");
}


必须有@Test注释,方法修饰符必须是public,返回类型必须是void

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值