CHY的Spring学习笔记---师从动力节点王鹤老师(B站白嫖)

Spring学习笔记

核心技术:ioc和aop

ioc:使用di(依赖注入)实现控制反转,底层使用的是反射机制


spring可以创建自己写的类的对象,也可以创建非自定义对象,只要知道类的全限定名即可。

spring创建对象:默认调用的是无参构造方法。


使用spring框架的步骤
  1. 加入spring-context依赖

  2. 创建类:接口,实现类、没有接口的类

  3. 创建spring的配置文件applicationContext.xml,使用声明对象

  4. 使用容器中的对象:

    ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml);
    
            Student myStudent = (Student) context.getBean("myStudent");
    

IOC

依赖注入方式

  1. set注入(设值注入)

    该方式下,spring先调用无参构造,再调用set方法。

    • 简单类型的set注入:
    <bean id="myStudent" class="xxx">
            <property name="name" value="张三"/>
            <property name="age" value="20"/>
    </bean>
    

    注:只要有set方法,就可以给任意类对象设置属性值。

    • 对引用类型的set注入:
        <bean id="myStudent" class="com.springstudy.DiSet02.Student">
            <property name="name" value="陈汉源"/>
            <property name="age" value="22"/>
            <!--引用类型的赋值-->
            <property name="school" ref="mySchool"/>
        </bean>
        
        <bean id="mySchool" class="com.springstudy.DiSet02.School">
            <property name="name" value="上海应用技术大学"/>
            <property name="address" value="上海奉贤区"/>
        </bean>
    
  2. 构造注入

使用标签

标签属性:

name:表示构造方法的形参名

index:表示构造方法的参数的位置,从左到右依次是0、1、2…

value:构造方法的形参类型是简单类型的,使用value

ref:构造方法的形参类型是引用类型的,使用ref

    <bean id="myStudent" class="com.springstudy.DiSet03.Student">
        <constructor-arg name="name" value="zs"/>
        <constructor-arg name="age" value="21"/>
        <constructor-arg name="school" ref="mySchool"/>
    </bean>

    <bean id="mySchool" class="com.springstudy.DiSet03.School">
        <property name="name" value="上海应用技术大学"/>
        <property name="address" value="上海奉贤区"/>
    </bean>

什么样的对象需要放入容器中?

dao类、service类、controller类、工具类等。

不需要放入到spring容器中的对象?

  1. 实体类对象,实体类数据是来自数据库的
  2. servlet、listener、filter等
引用类型的自动注入
  1. byName(按名称注入)

    java类中引用类型的属性名和bean的id一样,且数据类型是一致的,这样spring就能够赋值给引用类型。

    语法:

    <bean id="xx" class="yyy" autowire="byName">
        简单类型属性赋值
    </bean>
    
  2. byType(按类型注入)

    java类中引用类型的数据类型和bean的class的类型是同源关系时,这样spring就能够赋值给引用类型。

    何为同源关系:

    • java类中引用类型的数据类型和bean的class值是一样的
    • java类中引用类型的数据类型和bean的class值是父子类的关系
    • java类中引用类型的数据类型和bean的class值是接口和实现类的关系

    语法:autowire=“byType”

    注:在byType中,在xml配置文件中声明的bean只能有一个符合条件,否则会报错。


多配置文件的导入

使用标签来导入其他的spring配置文件到主配置文件中。


注解方式DI

使用注解的步骤:

  1. 加入maven的依赖spring-context,使用注解必须使用spring-aop依赖;

  2. 在类中使用spring的注解(@xxx)

  3. 在spring配置文件中,加入一个组件扫描器的标签(component-scan)

    component-scan的工作方式:spring会扫描遍历base-package指定的包,把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。

主要注解
  1. @Component:相当于spring配置文件中bean的声明

  2. @Repository:用在持久层(DAO)类上面,创建dao对象,dao对象是能访问数据库的

  3. @Service:用在业务层上面的,创建service对象,service对象是做业务处理的,可以有事务等功能

  4. @Controller:用在控制器上面,创建控制器对象的,控制器对象能够接受用户提交的参数,显示请求的处理结果

    以上四个注解的功能是一样的,语法也一样,只不过使用的地方不一样,有额外的功能。

  5. @Value:简单类型的属性赋值

    • 写在属性定义上面,无需set方法(推荐)
    • 写在set方法的上面
  6. @Autowired:引用类型的属性赋值

    byType和byName都可以,默认是ByType

    • 写在属性定义上面,无需set方法(推荐)
    • 写在set方法的上面

    若要使用byName方式,需@Autowired和@Qualifier搭配使用:

    • 在属性上面加@Autowired
    • 在属性上面加入@Qualifier(value=“bean的id”)
  7. @Resource:引用类型的属性赋值

    默认byName赋值,若赋值失败,则byType

到底用配置文件赋值好还是注解赋值好?

经常改动的话使用xml配置文件,不常改的话用注解。注解为主,配置文件为辅。


AOP

面向切面编程,底层就是采用动态代理实现的。

在不改变代码的情况下,增加功能,解耦合。

动态代理

可以在程序的执行过程中,创建代理对象,通过代理对象执行方法,给目标类的方法增加额外的功能(功能增强)。

常用方式有两种:

  • JDK动态代理
  • CGLIB动态代理(了解)
JDK动态代理

实现步骤:

  1. 创建目标类
  2. 创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能
  3. 使用JDK中类Proxy,创建代理对象。
//2.创建InvocationHandler接口的实现类
public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        Tool.log();
        res = method.invoke(target, args);
        Tool.commitTrans();
        return res;
    }
}
        //3.创建代理对象
        SomeService target = new SomeServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(target);
        SomeService proxy = (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler);
        proxy.doSome();
        System.out.println("======================");
        proxy.doOther();

动态代理的作用:

1)在目标类源代码不改变的情况下,增加功能

2)减少代码的重复

3)专注业务逻辑代码

4)解耦合,让你的业务功能和日志、事务等非业务功能分离

aop总结

就是动态代理的规范化,它把动态代理的实现步骤、方式都定义好了。

aop相关术语

Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能。非业务方法,常见的切面功能有日志、事务、统计信息、参数检查、权限验证

JoinPoint:连接点,连接业务方法和切面的位置。即某类中的业务方法

Pointcut:切入点,带有通知的连接点

Advice:通知,在特定切入点上执行的增强处理,有before、after、afterReturning、afterThrowing,around。

Aop实现

  1. spring内部支持aop,但比较笨重。
  2. aspectJ:一个开源的专门做aop的框架spring框架中已集成aspectJ框架,有两种使用方法,xml配置文件和注解。

execution表达式:

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

AspectJ框架实现AOP步骤

@Before案例

  1. 加入maven依赖,spring-context和spring-aspects

  2. 创建一个接口和它的实现类,即目标类

  3. 创建切面类,里面有需要增强的功能代码,功能的执行时间,功能执行的位置

    //切面类
    @Aspect
    public class MyAspect {
        //增强功能的执行位置
        @Before(value = "execution(public void com.springstudy.ba01.impl.SomeServiceImpl.doSome(String,Integer))")
        public void myBefore(){
            //增强的功能
            System.out.println("前置通知,功能增强,记录方法执行时的时间:"+new Date());
        }
    }
    
  4. 在spring的配置文件applicationContext.xml中利用bean创建以上类的对象,并加入自动代理生成器的标签aop:aspectj-autoproxy

  5. 编写测试类测试代码

    public class MyTest01 {    
       @Test    
       public void test01(){        
          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        
          SomeService proxy = (SomeService) 
          context.getBean("someService");       
          proxy.doSome("chy",22);    
          }
    

}




注意:增强的功能方法myBefore()中可以有一个参数JoinPoint,它可以获取方法的完整定义、方法的实参等信息

```java
//切面类
@Aspectpublic class MyAspect {    
//增强功能的执行时间    
@Before(value = "execution(public void com.springstudy.ba01.impl.SomeServiceImpl.doSome(String,Integer))")    public void myBefore(JoinPoint jp){        
//获取方法的完整定义        
System.out.println("方法定义:"+jp.getSignature());        
//获取方法的名称        
System.out.println("方法名称:"+jp.getSignature().getName());        
//获取方法的实参        
Object[] args = jp.getArgs();       
for (Object arg : args) {            
System.out.println("参数:"+arg);        }        
//增强的功能        
System.out.println("前置通知,功能增强,记录方法执行时的时间:"+new Date());    
}
}

@AfterReturning

后置通知,在目标方法之后执行,能获取目标方法的返回值,可以修改这个返回值。属性:

  • value:切入点表达式(execution)
  • returning:自定义的变量,表示目标方法的返回值,自定义变量名必须和通知方法的形参名一样

@Around

环绕通知,经常用于做事务方面功能。特点:

在目标方法的前后都能增强功能

控制目标方法是否被调用执行

修改原来的目标方法的执行结果,影响最后的调用结果

该方法的定义格式:

  • public的
  • 必须有一个返回值,推荐使用Object
  • 方法有固定参数,为ProceedingJoinPoint

@ AfterThrowing,异常通知

@After,最终通知,类似finally语句

辅助功能的注解@Pointcut,给切入点表达式起别名用的,在某方法上加该注解,则该方法名就是切入点表达式的别名。

@Pointcut(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public void myPointCut(){        //无需写代码    }

现在,通知注解上就只需要写成

@Around(value = "myPointCut()")

即可。

CGLIB动态代理

有接口时,框架默认使用的是JDK动态代理

CGLIB动态代理是在没有接口时使用的,要想在有接口的情况下使用,则需要在spring配置文件中写

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

Spring的事务处理

什么时候想到要使用事务?

当我的操作,涉及到多个表,或者是多个sql语句的insert、update、delete。需要保证这些语句都是成功的才能完成我的功能,或者都失败,保证操作是符合要求的。

在java代码中控制事务,此时事务应该放在哪里?

写在service类的业务方法上,业务业务方法会调用多个dao方法,执行多条sql语句。

有jdbc、mybatis、hibernate等方式访问数据库,他们的不足之处?

多种数据库的访问技术,有不同的事务处理的机制、对象、方法,对开发人员要求较高。

如何解决以上不足?

Spring提供一种处理事务的统一模型,能使用统一的步骤或方式完成多种不同数据库访问技术的事务处理。

spring是声明式事务:把事务相关的资源和内容都提供给spring,spring就能处理事务提交、回滚。几乎不用写代码。

spring如何处理事务?

有固定的步骤,只需把事务需要的信息提供给spring即可。

事务管理器

事务内部提交、回滚事务,使用事务管理器对象,代替你完成commit、rollback操作。

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

接口:PlatformTransactionManager,定义事务的重要方法commit、rollback

实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。

​ mybatis访问数据库:spring创建好的是DataSourceTransactionManager

​ hibernate访问数据库:spring创建好的是HibernateTransactionManager

怎么使用:在spring的配置文件中声明bean,例如你要使用mybatis访问数据库,bean为:

<bean id="xxx" class="DataSourceTransactionManager">
事务的4个隔离级别(isolation)

MySql默认REPEATABLE_READ;Oracle默认READ_COMMITTED

  • READ_UNCOMMITTED:读未提交。未解决任何并发问题
  • READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读
  • REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
  • SERIALIZABLE:串行化。不存在并发问题
事务的超时时间(不用管,默认即可)

表示一个方法最长的执行时间,如果方法执行超过了时间,事务就回滚。单位是秒,整数值,默认-1。

事务的传播行为(propagation)

控制业务方法是不是有事务的,怎样的事务

7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的。

PROPAGATION_REQUIRED:支持当前事务,假设当前没有事务,就新建一个事务

PROPAGATION_REQUIRES_NEW:新建事务,假设当前存在事务,把当前事务挂起

PROPAGATION_SUPPORTS:支持当前事务,假设当前没有事务,就以非事务方式运行

以上三个需要掌握

PROPAGATION_MANDATORY

PROPAGATION_NESTED

PROPAGATION_NEVER

PROPAGATION_NOT_SUPPORTED

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

rollback-for:指定的异常类名,发生异常时一定回滚

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

2)当你的业务方法抛出运行时异常或ERROR。spring执行回滚操作,调用事务管理器的rollback

3)当你的业务方法抛出非运行时异常,主要是受查异常时,默认提交事务

​ 受查异常:在你写代码中,必须处理的异常。例如IOException,SQLException。

总结:你要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。

spring框架中提供的事务处理方案
  1. 注解方案:适合中小型项目使用

    spring框架使用AOP实现给业务方法增加事务功能,使用@Transactional注解增加事务

    @Transactional注解是属于spring框架,放在public方法上面,表示当前方法具有事务

    可以给注解的属性赋值,表示具体的隔离级别、传播行为、异常信息等等

    @Transactional使用步骤:

    1)声明事务管理器对象,指定数据源

        <!--声明事务管理器-->    
        <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--连接的数据库,指定数据源-->        
        <property name="dataSource" ref="myDataSource"/>    
        </bean>
    

    2)开启事务注解驱动,告诉spring我要使用注解的方式管理事务

        <!--transaction-manager:事务管理器对象的id-->    
        <tx:annotation-driven transaction-manager="transactionManager"/>
    

    spring使用AOP机制,创建@Transactional所在的类代理对象,给方法加入事务功能:在你的业务方法执行之前,先开启事务,在业务方法执行之后提交或回滚事务,使用的是AOP的环绕通知

    3)在业务方法上加入@Transactional

    @Transactional(            
    propagation = Propagation.REQUIRED,           
     isolation = Isolation.DEFAULT,           
      readOnly = false,            
      rollbackFor = {                    
      NullPointerException.class,NotEnoughException.class            
      }    )    
      @Override    
      public void buy(Integer goodsId, Integer nums) {   
      }
    

    或者只加上@Transactional即可

  2. 使用AspectJ框架方案:大型项目使用

    在大型项目中,有很多的类、方法,需要大量的配置事务,使用AspectJ框架功能,在spring配置文件中声明类、方法需要的事务。这种方式的业务方法和事务配置完全分离。

    实现步骤:(都是在xml配置文件中实现的)

    1)加入spring-aspects依赖

    2)声明事务管理器对象

    3)声明方法需要的事务类型(配置方法的事务属性【隔离级别、传播行为、超时等】)

    <tx:advice id="myAdvice" transaction-manager="transactionManager">        <tx:attributes>            
    <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"                       rollback-for="java.lang.NullPointerException,com.chenhanyuan.excep.NotEnoughException"/>        </tx:attributes>    
    </tx:advice>
    

    若有多个方法,可以使用通配符*指定

    4)配置AOP:指定哪些类要创建代理

        <!--AOP的配置-->    
        <aop:config>        
        <!--配置切入点表达式:指定哪些类要使用事务,aspectj会创建代理对象-->        
        <aop:pointcut id="servicePC" expression="execution(* *..service..*.*(..))"/>        
        <!--配置增强器,关联advice和pointcut        
        advice-ref:通知,上面tx:advice的配置        
        pointcut-ref:切入点表达式的id-->        
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePC"/>    
        </aop:config>
    

aspectj方式是固定格式的,可当作模板使用,不用记。

Spring Web项目

配置监听器:目的是创建spring容器对象,就能把spring配置文件中的所有对象都创建好,用户发起请求就可以直接使用对象了。不用每次都读取配置文件,重新创建容器的对象,减少了内存的开销,提高程序运行速度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值