Spring Node

IoC控制反转
实现解耦合 (servlet中的自动创建对象是由tomcat来实现的,同ioc形式)
DI(dependency injection)是IoC的技术实现,底层是反射机制

把对象的创建,赋值和管理工作都交给代码之外的容器实现,也就是对象的创建是由外部资源完成的
控制:创建对象,对象的属性赋值,对象之间的关系管理
反转:把原来开发人员创建和管理对象的权力交给代码之外的容器管理
正转:开发人员主动管理对象

1.加入的依赖

<!--加入依赖-->
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

2.配置文件

<!--    告诉spring创建对象
        声明bean,就是告诉spring要创建某个类对象
        id为对象的自定义名称,唯一值,通过这个名字找到该对象
        class:为类的全限定名称,不能是接口
        spring将创建好的对象放在了map中,
        一个bean声明一个对象
        spring创建的是一个无参构造对象-->
    <bean id="someServlet" class="org.yixia.impl.SomeServletImpl"/>

<!--    创建一个非自定义对象-->
    <bean id="myDate"  class="java.util.Date"/>
    @Test
    public void test2(){
        //使用spring容器创建的对象
        //指定spring配置文件名称
        String config = "beans.xml";
        //2.创建spring容器对象,ApplicationContext
        //通过ApplicationContext获取容器
        //在spring容器创建之后,会自动创建配置文件中的所有对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);

        //获取配置文件中创建对象的个数
        int count = applicationContext.getBeanDefinitionCount();
        System.out.println("spring容器中创建对象的个数"+count);

        //获取配置文件中创建对象的名称
        String[] name = applicationContext.getBeanDefinitionNames();
        for(String str:name){
            System.out.println("spring创建的对象为"+str);
        }

        //getBean("xml文件中创建对象的id名")
        SomeServlet someServlet = (SomeServlet) applicationContext.getBean("someServlet");
        someServlet.doSome();
    }

给spring对象的属性进行赋值

  1. 在spring的配置文件中,使用标签和属性来完成,基于xml的di实现
  2. 使用Spring的注解完成属性赋值

di的语法分类

  1. set注入:spring调用类的set方法,set方法可以实现属性的赋值,
  2. 构造注入,spring调用类的有参构造方法,创建对象,并完成属性的赋值

xml配置文件set注入

<!--    set注入
        1.简单类型注入
                <bean id="myStudent" class="org.yixia.ba01.Student">
                    一个标签只能赋值一个属性
                    <property name="属性名称" value="属性的值"/>
                </bean>
-->
    <bean id="myStudent" class="org.yixia.ba01.Student">
        <property name="name" value="zhangsan"/>
        <property name="age" value="24"/>
    </bean>
	<!--    set注入
        1.简单类型注入
                <bean id="myStudent" class="org.yixia.ba01.Student">
                    一个标签只能赋值一个属性
                    <property name="属性名称" value="属性的值"/>
                </bean>
        2.引用数据类型注入
                    <bean id="myStudent" class="org.yixia.ba01.Student">
                        <property name="school" ref="bean中的id(对象的名称)"/>
                    </bean>
-->
    <bean id="myStudent" class="org.yixia.ba01.Student">
        <property name="name" value="zhangsan"/>
        <property name="age" value="24"/>
        <property name="school" ref="mySchool"/>
    </bean>

    <bean id="mySchool" class="org.yixia.ba01.School">
        <property name="address" value="河北"/>
        <property name="name" value="小张传媒"/>
    </bean>
<!--    构造方法注入
         <constructor-arg>标签
         <constructor-arg>标签属性:
            name:构造方法的形参名
            index:表示构造参数的位置,参数从左到右的位置为0,1,2
            value:构造方法的形参类型是简单类型
            ref:构造方法的形参类型是应用类型
            
-->
    <bean id="myStudent" class="org.yixia.ba02.MyStudent">
        <constructor-arg name="name" value="lisi"/>
        <constructor-arg name="age" value="24"/>
        <constructor-arg name="mySchool" ref="mySchool"/>
    </bean>
    <bean id="mySchool" class="org.yixia.ba02.MySchool">
        <property name="address"    value="hebei"/>
        <property name="name"   value="xiaozhangjlb"/>
    </bean>
<!--   引用类型的自动注入
            根据某些规则给引用类型赋值
       1.byName(按名称注入):java类中引用类型的属性名和Spring容器中的(配置文件)<bean>的id名称一样
                              且数据类型是一致的
       2.byType(按类型注入):java类中引用类型的数据类型和Spring的容器中(配置文件)<bean>的class属性相同
                              是同源关系,
              同源关系:
                    1.Java中引用类型的数据类型和bean的class的值相同
                    2.Java中引用类型的数据类型和bean的class的值为父子关系
                    3.Java中引用类型的数据类型和bean的class的值为接口和实现类关系
            -->
<!--    <bean id="myStudent" class="org.yixia.ba03.Student" autowire="byName">
        <property name="name" value="zhangsan"/>
        <property name="age" value="24"/>
    </bean>
    <bean id="school" class="org.yixia.ba03.School">
        <property name="address" value="河北试试"/>
        <property name="name" value="小张传媒"/>
    </bean>-->
    <bean id="myStudent" class="org.yixia.ba03.Student" autowire="byType">
        <property name="name" value="xiaojin"/>
        <property name="age" value="24"/>
    </bean>
    <bean id="mySchool" class="org.yixia.ba03.School">
        <property name="address" value="河北会话"/>
        <property name="name" value="小张传媒"/>
    </bean>


注解注入

  1. 加入依赖
  2. 在类上加入注解
  3. 声明组件扫描器的标签,指明注解的位置
  4. 声明容器

包含关系的配置文件

<!--      包含关系的配置文件:可使用通配符  
          applicationContext表示主文件:包含其他配置文件,主配置文件一般不配置对象
            语法: <import resource="其他配置文件的路径"/>
            关键字:"classpath:"表示类路径(class文件所在的目录)
                    在spring的配置文件中要指定其他文件的位置,需要使用classpath,告诉spring到哪里去加载读取
    -->
        
        <import resource="classpath:ba04/spring_school.xml"/>
        <import resource="classpath:ba04/spring_student.xml"/>
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/*
* Component:创建对象的,等同于《bean》的功能
*   属性:value 设置对象的名称,也就是bean的id值
*         value的值是唯一的
*   位置:在类的上面
*
* 与component功能一致的注解:(给项目的对象分层的)
*  @Repository:放在Dao的实现类上,表示创建dao对象
*  @Service:放在Service的实现类上,做业务处理的,由事务等功能
*  @Controller:放在控制器上的,能够接受用户提供的参数,显示请求的处理结果
*
*/

//@Component(value = "myStudent")
//省略value
//@Component("myStudent")
//不指定对象名称,由spring默认提供,名称为类名的首字母小写
@Component
public class Student {

    /*
     *@Value:简单类型的属性赋值
     *  属性:value 是String类型,表示简单类型的属性值
     *  位置:定义在属性之上,无需set方法
     *       或者定义在set方法之上
     */

    @Value("ajin")
    private String name;
    @Value("50")
    private int age;
/*
* 引用类型
* @Autowired:spring提供的注解,实现引用类型赋值
* spring 中通过注解给引用类型赋值,采用自动注入,支持byName和byType
*    默认是byType类型注入
* 属性:required:boolean类型,默认为true 
*           true:赋值失败,报错
*           false:正常执行不赋值
* 位置:在属性上定义,无需set
*       或者set方法上面
*
* 如果要使用byName方式,需要做的是
*   1.在属性上面加入@Autowired
*   2.在属性上面加入@Qualifier(value="bean中的id"):表示使用注定的bean名称完成赋值
*
*/

/*    @Autowired(required = true)
    @Qualifier("school")
    private School school;*/

/*
 * 引用类型
 * @Resource:来自jdk中的注解,spring对此注解支持,可以给引用类型赋值
 *           使用的也是自动注入原理,支持byName。byType。默认ByName
 *           先使用byName原理,如果失败再使用byType
 * 位置:1.属性定义上面,无需set方法
 *      2.在set方法上面
 *
 * @Resource 只使用byName方式,需要增加一个属性 name
 *          name的值是bean的id
*/
    @Resource(name = "school")
    private School school;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }

    /*    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }*/
}

AOP面向切面编程
规范化的动态代理

动态代理
实现方式:
1.jdk动态代理,使用jdk中的proxy,Method,InvovationHander创建代理对象,jdk动态代理要求目标必须实现接口
cglib动态代理:第三方工具库,创建代理对象,原理是继承,通过继承目标类,创建子类,子类就是代理对象,要求目标类不能是final的,方法也不能是final的
功能:1.增强功能2.减少代码的重复 3.专注业务逻辑代码4.解耦合,事务和非业供能分开

术语

  1. Aspect:切面,表示增强的功能,非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证
  2. JoinPoint:连接点,连接业务方法和切面的位置,就是类中的业务方法
  3. Pointcut:切入点,指多个连接点方法的集合,多个方法
  4. 目标对象:给哪个类的方法增加功能,哪个类就是目标对象
  5. Advice:通知,通知表示切面功能执行的时间

一个切面的关键要素

  1. 切面的功能代码
  2. 切面的执行位置,使用Pointcut表示切面执行的位置
  3. 切面的执行时间:使用Advice表示时间,在目标方法之前还是之后

AspectJ专门的AOP框架
有五个注解实现(也可使用xml配置文件)

  1. @Before
  2. @AfterReturning
  3. @Around
  4. @AfterThrowing
  5. @After

AspectJ的切入点表达式
AspectJ定义了专门的表达式用于指定切入点,表达式的原型是:

execution(	modifiers-pattern?
			ret-type-pattern 
			declaring-type-pattern?name-pattern(param-patter) 
			throws-pattern?)
//execution(访问权限 方法返回值 方法声明(参数)异常类型)

在这里插入图片描述
jdk动态代理

  1. 有目标接口类
  2. 创建目标类
  3. 创建切面类
  4. 创建bean对象
  5. applicationContext配置文件中,创建声明自动代理生成器,代理对象是在内存中实现的
    aop:aspectj-autoproxy/
<!--加入依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Date;

/**
 * @Aspect:是aspectj中的注解
 *      作用:表示当前类是切面类
 *      切面类:似乎用来给业务方法增加功能的类,有切面功能的代码
 *      位置:类名上
 */
@Aspect
public class MyAspect {

    /**
     * 前置通知的:定义方法,方法是实现切面功能的
     * 方法的要求:
     * 1.公共方法:public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有
     *      如果有参数,参数不是自定义的,有几个参数类型可以使用
     */

    /**
     * @Before:前置通知注解
     *  属性:value,内容为切入点表达式,表是切面的功能执行的位置
     *  位置,方法的上面
     * 特点:  1.在目标方法之前执行
     *        2.不会改变目标方法的执行结果
     *        3.不会影响目标方法的执行
     */
    @Before("execution(public void com.yixia.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        System.out.println("前置通知,切面功能:在目标方法之前输出执行的时间:"+ new Date());
    }


    /**
     * 指定通知方法中的参数:JoinPoint
     * JoinPoint:业务方法,要加入切面功能的业务方法,
     *      作用:可以在通知方法中,获取方法执行时的信息,如方法名称,方法的实参
     *      要求:必须是第一个位置的参数,
     */
    @Before("execution(public void com.yixia.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore1(JoinPoint point){
        //获取方法完整的定义
        System.out.println("方法的签名(定义)="+point.getSignature());
        System.out.println("方法的名称="+point.getSignature().getName());
        //获取方法的实参
        Object[] args = point.getArgs();
        for (Object arg:args){
            System.out.println("参数="+arg);
        }

        System.out.println("前置通知,切面功能:在目标方法之前输出执行的时间:"+ new Date());
    }

    /**
     * 后置通知:有参数,其它同前置方法的要求
     *
     * @AfterReturning:后置通知注解
     *  属性:  1.value,内容为切入点表达式,表是切面的功能执行的位置
     *         2.returning 自定义的变量,表示目标方法的返回值
     *              自定义变量名必须和通知方法的形参名一样
     *
     *  位置,方法的上面
     * 特点:  1.在目标方法之后执行
     *        2.能够获得到目标方法的返回值,可以根据返回值做不同的处理
     *        3.可以修改返回值
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
                    returning = "res")
    public void myAfterReturning(Object res){
        System.out.println("后置通知:在目标方法之后执行的,获取返回值是"+res);
    }

    @AfterReturning(value = "execution(* *..SomeServiceImpl.doStudent(..))",
            returning = "res")
    public void myAfterReturningStudent(Object res){
        System.out.println("后置通知:在目标方法之后执行的,获取返回值是"+res);
        Student student = new Student();
        student.setName("xioajin");
        student.setAge(24);

        res = student;
    }

    /**
     * 环绕通知:
     *    方法定义格式:
     *      1,public
     *      2.必须有一个返回值,推荐使用Object
     *      3.方法名称自定义
     *      4.方法有参数,固定的参数,ProceedingJoinPoint
     *
     * @Around:环绕通知
     *      属性:value ,切入点表达式
     *      位置:在方法的定义上面
     * 特点:
     *      1。功能最强的通知
     *      2.在目标方法前和后都有增强功能;
     *      3.控制目标方法是否被调用执行
     *      4.修改原来的目标方法的执行结果,影响最后的调用结果
     *
     *    环绕通知等同于jak中的动态代理的 InvocationHandler接口
     *
     *    参数:ProceedingJoinPoint  就等于Method
     *          作用:执行目标方法的额执行结果,可以被修改
     *
     *    经常做事务
     * @param pjp
     * @return
     */
    @Around("execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        //实现环绕通知
        Object result = null;
        //在目标函数前
        System.out.println("环绕通知:在目标方法之前,输出时间:"+new Date());
        //目标方法调用
        result = pjp.proceed();//method.invoke(); object result = doFirst();
        //在目标方法之后
        System.out.println("环绕通知:在目标方法之后:提交事务");
        return result;
    }

    /**
     *  异常通知:
     *    方法定义格式:
     *      1,public
     *      2.没有返回值;
     *      3.方法名称自定义
     *      4.方法有一个Exception,如果有是JoinPoint,
     *
     * @AfterThrowing:异常通知
     *      属性:value ,切入点表达式
     *            throwing 自定义的变量,表示目标方法抛出的异常对象
     *                  变量名必须和方法的参数名一样
     *      位置:在方法的定义上面
     * 特点:
     *      1。在目标方法抛出异常时执行
     *      2.可以做异常的监控程序,监控目标方法执行时是不是有异常
     *              如果有异常可以发现

     */
    @AfterThrowing(value = "execution(* *..SomeService.doSecond(..))",
        throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知:方法发生异常时,执行="+ex.getMessage());
        //发送邮件,短信,通知开发人员
    }

    /**
     *  最终通知:
     *    方法定义格式:
     *      1,public
     *      2.没有返回值;
     *      3.方法名称自定义
     *      4.没有参数,如果有是JoinPoint,
     *
     * @After:异常通知
     *      属性:value ,切入点表达式
     *      位置:在方法的定义上面
     * 特点:
     *      1。总是会执行
     *      2.在目标方法之后执行
     */
    @After("myPointcut()")
    public void myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清工作的
    }


    /**
     * @Pointcut :定义和管理切入点,如果你的项目中有多个切入点表达式时重复的,可以复用。
     *              可以使用@Pointcut
     *      属性:value 切入点表达式
     *      位置:在自定义的方法上面
     *
     *      一般为私有的,无需外面调用
     *
     * 特点:当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名
     *      其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式
     */
    @Pointcut("execution(* *..SomeService.doThird(..))")
    public void myPointcut(){
        //无需代码
    }
}

cglib动态代理

目标类没有接口,使用cglib动态代理,spring框架会自动应用cglib
com.yixia.ba01.SomeServiceImpl…EnhancerBySpringCGLIB…758f9bee

如果想要在没有接口的情况下使用cglib动态代理
需要在applicationContext配置文件中,配置,就会使用cglib动态代理,cglib动态代理的速度快一点

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

spring整合mybatis
整合后,事务是自动提交的

配置文件的具体内容:spring整合mybatis框架

spring事务处理

1.使用事务管理器(一个接口和众多实现类)
接口PlatformTransactionManager,定义了事务的方法,
实现类:spring将每种数据库访问技术对应的事务对应对实现类都创建好了
只需要的配置文件中声明bean即可
2.说明方法需要的事务
(1)事务的隔离级别
(2)事务的超市时间
(3)事务的传播行为

spring框架中提供的事务处理方案

  1. 适合中小项目使用,注解方案
    spring框架自己用aop实现给业务增加事务的功能,使用@Transactional注解增加事务
    @Transactional是spring框架自己的注解,放在public方法的上面,表示当前方法具有事务。
    可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等。
<!--    spring的事务处理
        声明事务管理器
-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        连接数据库-->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

<!--   开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
        transaction-manager代表事务管理器的id
-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    /**
     * @Transactional:事务注解,自动使用环绕通知
     * @return
     */
    @Transactional(
            //传播行为
            propagation = Propagation.REQUIRED,
            //隔离级别
            isolation = Isolation.DEFAULT,
            //
            readOnly = false,
            //发生指定异常一点回滚
            rollbackFor = {
                    ClassNotFoundException.class,NullPointerException.class
            }
    )
    @Override
    public List<Student> queryStudent() {
        List<Student> students = studentDao.selectStudent();
        return students;
    }
  1. 适合大型项目,有很多的类,方法需要大量的配置事务,使用aspectJ框架功能,在spring配置文件中,声明类,方法需要的事务,这种方式业务方法和事务配置完全分离
    实现步骤:都是在xml配置文件中实现的
    1.使用aspectJ框架,需要加入依赖
    2.声明事务管理器对象
    3.声明方法的事务类型,(配置方法的事务属性,隔离级别,传播行为,超时)
    4.配置aop:指定哪类需要
<!--    声明式事务处理,和源代码完全分离的-->
<!--     1.声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        连接数据库-->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

<!--
        2.声明业务方法它的事务属性(隔离级别,传播行为i,超时)
        id:自定义名称,表示<tx:advice>和</tx:advice>之间的配置内容
-->
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--        tx:attributes:配置事务属性-->
        <tx:attributes>
<!--           tx:method: 给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
                    name:方法名称:1.完整的方法名称,不带有包和类
                                   2.方法可以使用通配符,8表示任意字符
                    还有其他属性,为事务的配置属性
                    rollback-for:指定的异常类名,发生异常一定回滚
-->
            <tx:method name="" propagation="" isolation="" rollback-for=""/>
        </tx:attributes>
    </tx:advice>
<!--    配置aop-->
    <aop:config>
<!--        配置切点点表达式:指定那些表中的类,要用事务
            id:自定义,切入点表示的名称,唯一值
            expression:切入点表达式,指定哪些类需要事务,aspectJ会创建代理对象
-->
        <aop:pointcut id="servicePt" expression="execution(* *(..))"/>

<!--        配置增强器:关联advice和pointcut
            advice-ref:通知,上面tx:advice那里的配置
            pointcut-ref:切入点表达式的id
-->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
    </aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十六进一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值