Spring

一、IOC

  1. IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
    描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。

    控制: 创建对象,对象的属性赋值,对象之间的关系管理。
    反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器
    代替开发人员管理对象。创建对象,
    给属性赋值。

    正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。

  2. 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。

  3. IoC的技术实现 ,
    DI 是ioc的技术实现,
    DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,
    赋值,查找都由容器内部实现。

  4. spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。

    spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。

二、容器配置文件

<!--告诉spring创建对象
        声明bean , 就是告诉spring要创建某个类的对象
        id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
        class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

        spring就完成 SomeService someService = new SomeServiceImpl();
        spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
           springMap.put(id的值, 对象);
           例如 springMap.put("someService", new SomeServiceImpl());

        一个bean标签声明一个对象。
    -->
<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl" />

三、创建容器对象

//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config="beans.xml";
//2.创建表示spring容器的对象, ApplicationContext
// ApplicationContext就是表示Spring容器,通过容器获取对象了
// ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);

//从容器中获取某个对象, 你要调用对象的方法
//getBean("配置文件中的bean的id值")
SomeService service = (SomeService) ac.getBean("someService");

//使用spring创建好的对象
service.doSome();

//获取容器中对象的数量
int nums = ac.getBeanDefinitionCount();

//获取容器中对象中对象的名称
String names[] = ac.getBeanDefinitionNames();

四、注入

  1. 简单类型注入

    <!--声明student对象
            注入:就是赋值的意思
            简单类型: spring中规定java的基本数据类型和String都是简单类型。
            di:给属性赋值
            1. set注入(设值注入) :spring调用类的set方法, 你可以在set方法中完成属性赋值
             1)简单类型的set注入
                <bean id="xx" class="yyy">
                   <property name="属性名字" value="此属性的值"/>
                   一个property只能给一个属性赋值
                   <property....>
                </bean>
     -->
    <bean id="myStudent" class="com.bjpowernode.ba01.Student" >
            <property name="name" value="李四lisi" /><!--setName("李四")-->
            <property name="age" value="22" /><!--setAge(21)-->
            <property name="email" value="lisi@qq.com" /><!--setEmail("lisi@qq.com")-->
    </bean>
    
    
  2. 引用类型注入

    <!--
     2) 引用类型的set注入 : spring调用类的set方法
               <bean id="xxx" class="yyy">
                  <property name="属性名称" ref="bean的id(对象的名称)" />
               </bean>
     -->
     <!--声明School对象-->
    <bean id="mySchool" class="com.bjpowernode.ba02.School">
            <property name="name" value="北京大学"/>
            <property name="address" value="北京的海淀区" />
    </bean>
    
  3. 有参构造注入

    <!-- 2.构造注入:spring调用类有参数构造方法,在创建对象的同时,在构造方法中给属性赋值。
              构造注入使用 <constructor-arg> 标签
              <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数。
              <constructor-arg> 标签属性:
                 name:表示构造方法的形参名
                 index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序
                 value:构造方法的形参类型是简单类型的,使用value
                 ref:构造方法的形参类型是引用类型的,使用ref
    -->
    <!--使用name属性实现构造注入-->
    <bean id="myStudent" class="com.bjpowernode.ba03.Student" >
        <constructor-arg name="myage" value="20" />
        <constructor-arg name="mySchool" ref="myXueXiao" />
        <constructor-arg name="myname" value="周良"/>
    </bean>
    
    <!--使用index属性-->
    <bean id="myStudent2" class="com.bjpowernode.ba03.Student">
        <constructor-arg index="1" value="22" />
        <constructor-arg index="0" value="李四" />
        <constructor-arg index="2" ref="myXueXiao" />
    </bean>
    
    <!--省略index-->
    <bean id="myStudent3" class="com.bjpowernode.ba03.Student">
        <constructor-arg  value="张强强" />
        <constructor-arg  value="22" />
        <constructor-arg  ref="myXueXiao" />
    </bean>
    <!--声明School对象-->
    <bean id="myXueXiao" class="com.bjpowernode.ba03.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京的海淀区" />
    </bean>
    
    <!--创建File,使用构造注入-->
    <bean id="myfile" class="java.io.File">
        <constructor-arg name="parent" value="D:\course\JavaProjects\spring-course\ch01-hello-spring" />
        <constructor-arg name="child" value="readme.txt" />
    </bean>
    
  4. 按名称自动注入

    <!--
    1.byName(按名称注入) : java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,
                                  且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。
             语法:
             <bean id="xx" class="yyy" autowire="byName">
                简单类型属性赋值
             </bean>
    
    -->
    <!--byName-->
        <bean id="myStudent" class="com.bjpowernode.ba04.Student"  autowire="byName">
            <property name="name" value="李四" />
            <property name="age" value="26" />
            <!--引用类型-->
            <!--<property name="school" ref="mySchool" />-->
        </bean>
    
        <!--声明School对象-->
        <bean id="school" class="com.bjpowernode.ba04.School">
            <property name="name" value="清华大学"/>
            <property name="address" value="北京的海淀区" />
        </bean>
    
    
  5. 按类型注入

    <!--
    2.byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性
                                  是同源关系的,这样的bean能够赋值给引用类型
             同源就是一类的意思:
              1.java类中引用类型的数据类型和bean的class的值是一样的。
              2.java类中引用类型的数据类型和bean的class的值父子类关系的。
              3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的
             语法:
             <bean id="xx" class="yyy" autowire="byType">
                简单类型属性赋值
             </bean>
              注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的,
              多余一个是错误的
    
     <!--byType-->
    <bean id="myStudent" class="com.bjpowernode.ba05.Student"  autowire="byType">
        <property name="name" value="张飒" />
        <property name="age" value="26" />
        <!--引用类型-->
        <!--<property name="school" ref="mySchool" />-->
    </bean>
    
    <!--声明School对象-->
    <bean id="mySchool" class="com.bjpowernode.ba05.School">
        <property name="name" value="人民大学"/>
        <property name="address" value="北京的海淀区" />
    </bean>
    
    <!--声明School的子类-->
    <!--<bean id="primarySchool" class="com.bjpowernode.ba05.PrimarySchool">
        <property name="name" value="北京小学" />
        <property name="address" value="北京的大兴区" />
    </bean>-->
    -->
    

五、多个配置文件整合

<!--
         包含关系的配置文件:
         spring-total表示主配置文件 : 包含其他的配置文件的,主配置文件一般是不定义对象的。
         语法:<import resource="其他配置文件的路径" />
         关键字:"classpath:" 表示类路径(class文件所在的目录),
               在spring的配置文件中要指定其他文件的位置, 需要使用classpath,告诉spring到哪去加载读取文件。
    -->

    <!--加载的是文件列表-->

    <import resource="classpath:ba06/spring-school.xml" />
    <import resource="classpath:ba06/spring-student.xml" />


    <!--
       在包含关系的配置文件中,可以通配符(*:表示任意字符)
       注意: 主的配置文件名称不能包含在通配符的范围内(不能叫做spring-total.xml)
    -->
    <import resource="classpath:ba06/spring-*.xml" />

六、注解

  1. 组件扫描器

    <!--声明组件扫描器(component-scan),组件就是java对象
            base-package:指定注解在你的项目中的包名。
            component-scan工作方式: spring会扫描遍历base-package指定的包,
               把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。
    
           加入了component-scan标签,配置文件的变化:
            1.加入一个新的约束文件spring-context.xsd
            2.给这个新的约束文件起个命名空间的名称
    -->
    <context:component-scan base-package="com.bjpowernode.ba02" />
    
    <!--指定多个包的三种方式-->
    <!--第一种方式:使用多次组件扫描器,指定不同的包-->
    <context:component-scan base-package="com.bjpowernode.ba01"/>
    <context:component-scan base-package="com.bjpowernode.ba02"/>
    
    <!--第二种方式:使用分隔符(;或,)分隔多个包名-->
    <context:component-scan base-package="com.bjpowernode.ba01;com.bjpowernode.ba02" />
    
    <!--第三种方式:指定父包-->
    <context:component-scan base-package="com.bjpowernode" />
    
    
    
    <!--加载属性配置文件-->
    <context:property-placeholder location="classpath:test.properties" />
    
  2. @Component

  3. @Respotory

  4. @Service

  5. @Controller

    /**
     * @Component: 创建对象的, 等同于<bean>的功能
     *     属性:value 就是对象的名称,也就是bean的id值,
     *          value的值是唯一的,创建的对象在整个spring容器中就一个
     *     位置:在类的上面
     *
     *  @Component(value = "myStudent")等同于
     *   <bean id="myStudent" class="com.bjpowernode.ba01.Student" />
     *
     *  spring中和@Component功能一致,创建对象的注解还有:
     *  1.@Repository(用在持久层类的上面) : 放在dao的实现类上面,
     *               表示创建dao对象,dao对象是能访问数据库的。
     *  2.@Service(用在业务层类的上面):放在service的实现类上面,
     *              创建service对象,service对象是做业务处理,可以有事务等功能的。
     *  3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,
     *              控制器对象,能够接受用户提交的参数,显示请求的处理结果。
     *  以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。
     *  @Repository,@Service,@Controller是给项目的对象分层的。
     *
     *
     */
    //使用value属性,指定对象名称
    //@Component(value = "myStudent")
    
    //省略value
    @Component("myStudent")
    
    //不指定对象名称,由spring提供默认名称: 类名的首字母小写
    //@Component
    public class Student {
    
        private String name;
        private Integer age;
    
        public Student() {
            System.out.println("==student无参数构造方法===");
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
  6. @Value

    @Component("myStudent")
    public class Student {
    
        /**
         * @Value: 简单类型的属性赋值
         *   属性: value 是String类型的,表示简单类型的属性值
         *   位置: 1.在属性定义的上面,无需set方法,推荐使用。
         *         2.在set方法的上面
         */
        //@Value("李四" )
        @Value("${myname}") //使用属性配置文件中的数据
        private String name;
    
        @Value("${myage}")  //使用属性配置文件中的数据
        private Integer age;
    
        public Student() {
            System.out.println("==student无参数构造方法===");
        }
    
        public void setName(String name) {
            this.name = name;
        }
        //@Value("30")
        public void setAge(Integer age) {
            System.out.println("setAge:"+age);
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
  7. @Autowired

    @Component("myStudent")
    public class Student {
        @Value("李四" )
        private String name;
        private Integer age;
    
        /**
         * 引用类型
         * @Autowired: spring框架提供的注解,实现引用类型的赋值。
         * spring中通过注解给引用类型赋值,使用的是自动注入原理 ,支持byName, byType
         * @Autowired:默认使用的是byType自动注入。
         *
         *  位置:1)在属性定义的上面,无需set方法, 推荐使用
         *       2)在set方法的上面
         * 
         * 属性:required ,是一个boolean类型的,默认true
         *       required=true:表示引用类型赋值失败,程序报错,并终止执行。
         *       required=false:引用类型如果赋值失败, 程序正常执行,引用类型是null
         * 
         * 如果要使用byName方式,需要做的是:
         *  1.在属性上面加入@Autowired
         *  2.在属性上面加入@Qualifier(value="bean的id") :表示使用指定名称的bean完成赋值。
         */
        @Autowired(required="true")
        @Qualifier(value="bean的id")
        private School school;
    
        public Student() {
            System.out.println("==student无参数构造方法===");
        }
    
        public void setName(String name) {
            this.name = name;
        }
        @Value("30")
        public void setAge(Integer age) {
            System.out.println("setAge:"+age);
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", school=" + school +
                    '}';
        }
    }
    
  8. @ReSource

    @Component("myStudent")
    public class Student {
    
        @Value("李四" )
        private String name;
        private Integer age;
    
        /**
         * 引用类型
         * @Resource: 来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
         *            使用的也是自动注入原理,支持byName, byType .默认是byName
         *  位置: 1.在属性定义的上面,无需set方法,推荐使用。
         *        2.在set方法的上面
         */
        //默认是byName: 先使用byName自动注入,如果byName赋值失败,再使用byType
    	/**
    	     @Resource只使用byName方式,需要增加一个属性 name
     	* 	name的值是bean的id(名称)
    	*/
        @Resource
        private School school;
    
        public Student() {
            System.out.println("==student无参数构造方法===");
        }
    
        public void setName(String name) {
            this.name = name;
        }
        @Value("30")
        public void setAge(Integer age) {
            System.out.println("setAge:"+age);
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", school=" + school +
                    '}';
        }
    }
    

七、aop简介

  1. 怎么理解面向切面编程 ?
    1)需要在分析项目功能时,找出切面。
    2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
    3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

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

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

  3. 说一个切面有三个关键的要素:
    1)切面的功能代码,切面干什么
    2)切面的执行位置,使用Pointcut表示切面执行的位置
    3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

八 aspectj切入点表达式

  1. execution(访问修饰符 返回值,包名.类名.方法名称(方法的参数)异常)
  2. *表示0或多个任意字符
  3. 。。用在方法参数中,表示任意多个参数,用在包名后,表示当前包及其任意子包路径
  4. +用在类明后,表示当前类及其子类,用在接口后,表示当前接口及其实现类

九、aspectj通知注解

  1. 声明自动代理生成器

    <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
            创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象
            所以目标对象就是被修改后的代理对象.
    
            aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。
        -->
        <aop:aspectj-autoproxy />
    
    
        <!--
           如果你期望目标类有接口,使用cglib代理
           proxy-target-class="true":告诉框架,要使用cglib动态代理
        -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
  2. @Before

    /**
     *  @Aspect : 是aspectj框架中的注解。
     *     作用:表示当前类是切面类。
     *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
     *     位置:在类定义的上面
     */
    @Aspect
    public class MyAspect {
        /**
         * 定义方法,方法是实现切面功能的。
         * 方法的定义要求:
         * 1.公共方法 public
         * 2.方法没有返回值
         * 3.方法名称自定义
         * 4.方法可以有参数,也可以没有参数。
         *   如果有参数,参数不是自定义的,有几个参数类型可以使用。
         */
    
    
        /**
         * @Before: 前置通知注解
         *   属性:value ,是切入点表达式,表示切面的功能执行的位置。
         *   位置:在方法的上面
         * 特点:
         *  1.在目标方法之前先执行的
         *  2.不会改变目标方法的执行结果
         *  3.不会影响目标方法的执行。
         */
    	/**
         * 指定通知方法中的参数 : JoinPoint
         * JoinPoint:业务方法,要加入切面功能的业务方法
         *    作用是:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。
         *    如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
         *    这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数
         */
        @Before(value = "execution(void *..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("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
        }
    }
    
    
  3. @AfterReturning

    @Aspect
    public class MyAspect {
        /**
         * 后置通知定义方法,方法是实现切面功能的。
         * 方法的定义要求:
         * 1.公共方法 public
         * 2.方法没有返回值
         * 3.方法名称自定义
         * 4.方法有参数的,推荐是Object ,参数名自定义
         */
    
        /**
         * @AfterReturning:后置通知
         *    属性:1.value 切入点表达式
         *         2.returning 自定义的变量,表示目标方法的返回值的。
         *          自定义变量名必须和通知方法的形参名一样。
         *    位置:在方法定义的上面
         * 特点:
         *  1。在目标方法之后执行的。
         *  2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
         *      Object res = doOther();
         *  3. 可以修改这个返回值
         *
         *  后置通知的执行
         *    Object res = doOther();
         *    参数传递: 传值, 传引用
         *    myAfterReturing(res);
         *    System.out.println("res="+res)
         *
         */
        @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
                        returning = "res")
        public void myAfterReturing(  JoinPoint jp  ,Object res ){
            // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
            System.out.println("后置通知:方法的定义"+ jp.getSignature());
            System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
            if(res.equals("abcd")){
                //做一些功能
            } else{
                //做其它功能
            }
    
            //修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
            if( res != null){
                res = "Hello Aspectj";
            }
    
        }
    
  4. @Around

    @Aspect
    public class MyAspect {
        /**
         * 环绕通知方法的定义格式
         *  1.public
         *  2.必须有一个返回值,推荐使用Object
         *  3.方法名称自定义
         *  4.方法有参数,固定的参数 ProceedingJoinPoint
         */
    
        /**
         * @Around: 环绕通知
         *    属性:value 切入点表达式
         *    位置:在方法的定义什么
         * 特点:
         *   1.它是功能最强的通知
         *   2.在目标方法的前和后都能增强功能。
         *   3.控制目标方法是否被调用执行
         *   4.修改原来的目标方法的执行结果。 影响最后的调用结果
         *
         *  环绕通知,等同于jdk动态代理的,InvocationHandler接口
         *
         *  参数:  ProceedingJoinPoint 就等同于 Method
         *         作用:执行目标方法的
         *  返回值: 就是目标方法的执行结果,可以被修改。
         *
         *  环绕通知: 经常做事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务
         */
        @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
        public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
    
            String name = "";
            //获取第一个参数值
            Object args [] = pjp.getArgs();
            if( args!= null && args.length > 1){
                  Object arg=  args[0];
                  name =(String)arg;
            }
    
            //实现环绕通知
            Object result = null;
            System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
            //1.目标方法调用
            if( "zhangsan".equals(name)){
                //符合条件,调用目标方法
                result = pjp.proceed(); //method.invoke(); Object result = doFirst();
    
            }
    
            System.out.println("环绕通知:在目标方法之后,提交事务");
            //2.在目标方法的前或者后加入功能
    
            //修改目标方法的执行结果, 影响方法最后的调用结果
            if( result != null){
                  result = "Hello AspectJ AOP";
            }
    
            //返回目标方法的执行结果
            return result;
        }
    }
    
  5. @AfterThrowing

    @Aspect
    public class MyAspect {
        /**
         * 异常通知方法的定义格式
         *  1.public
         *  2.没有返回值
         *  3.方法名称自定义
         *  4.方法有个一个Exception, 如果还有是JoinPoint,
         */
    
        /**
         * @AfterThrowing:异常通知
         *     属性:1. value 切入点表达式
         *          2. throwinng 自定义的变量,表示目标方法抛出的异常对象。
         *             变量名必须和方法的参数名一样
         * 特点:
         *   1. 在目标方法抛出异常时执行的
         *   2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。
         *      如果有异常,可以发送邮件,短信进行通知
         *
         *  执行就是:
         *   try{
         *       SomeServiceImpl.doSecond(..)
         *   }catch(Exception e){
         *       myAfterThrowing(e);
         *   }
         */
        @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",
                throwing = "ex")
        public void myAfterThrowing(Exception ex) {
            System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());
            //发送邮件,短信,通知开发人员
        }
    
    }
    
  6. @After

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

    @Aspect
    public class MyAspect {
    
    
        @After(value = "mypt()")
        public  void  myAfter(){
            System.out.println("执行最终通知,总是会被执行的代码");
            //一般做资源清除工作的。
         }
    
        @Before(value = "mypt()")
        public  void  myBefore(){
            System.out.println("前置通知,在目标方法之前先执行的");
        }
    
        /**
         * @Pointcut: 定义和管理切入点, 如果你的项目中有多个切入点表达式是重复的,可以复用的。
         *            可以使用@Pointcut
         *    属性:value 切入点表达式
         *    位置:在自定义的方法上面
         * 特点:
         *   当使用@Pointcut定义在一个方法的上面 ,此时这个方法的名称就是切入点表达式的别名。
         *   其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
         */
        @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" )
        private void mypt(){
            //无需代码,
        }
    
    }
    

十、整合mybatis

  1. 声明数据源

    <!--
           把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
           spring知道jdbc.properties文件的位置
        -->
        <context:property-placeholder location="classpath:jdbc.properties" />
    
        <!--声明数据源DataSource, 作用是连接数据库的-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <!--set注入给DruidDataSource提供连接数据库信息 -->
            <!--    使用属性配置文件中的数据,语法 ${key} -->
            <property name="url" value="${jdbc.url}" /><!--setUrl()-->
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.passwd}" />
            <property name="maxActive" value="${jdbc.max}" />
        </bean>
    
  2. 声明sqlSessionFactory类

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
            SqlSessionFactory  sqlSessionFactory = new ..
        -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--set注入,把数据库连接池付给了dataSource属性-->
            <property name="dataSource" ref="myDataSource" />
            <!--mybatis主配置文件的位置
               configLocation属性是Resource类型,读取配置文件
               它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
            -->
            <property name="configLocation" value="classpath:mybatis.xml" />
        </bean>
    
    
  3. 配置dao类

    <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
            MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--指定SqlSessionFactory对象的id-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <!--指定包名, 包名是dao接口所在的包名。
                MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
                一次getMapper()方法,得到每个接口的dao对象。
                创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
            -->
            <property name="basePackage" value="com.bjpowernode.dao"/>
        </bean>
    

十一、事务

  1. 事务管理器
    事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback

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

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

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

    mybatis访问数据库—spring创建好的是DataSourceTransactionManager
    hibernate访问数据库----spring创建的是HibernateTransactionManager

    声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用声明就可以了
    例如,你要使用mybatis访问数据库,你应该在xml配置文件中
    <bean id=“xxx" class="…DataSourceTransactionManager">

  2. 事务的隔离级别
    事务的隔离级别:有4个值。
    DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
    ➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
    ➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
    ➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
    ➢ SERIALIZABLE:串行化。不存在并发问题。

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

  4. 事务的传播行为 : 控制业务方法是不是有事务的, 是什么样的事务的。
    7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。

     	PROPAGATION_REQUIRED   (会接受外部事务)
     	PROPAGATION_REQUIRES_NEW (会创建新事务)
     	PROPAGATION_SUPPORTS (支持事务)
     	以上三个需要掌握的
    
     	PROPAGATION_MANDATORY
     	PROPAGATION_NESTED
     	PROPAGATION_NEVER
     	PROPAGATION_NOT_SUPPORTED
    
  5. 事务提交事务,回滚事务的时机

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

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

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

  6. 配置事务管理器和开启事务注解驱动

    <!--使用spring的事务处理-->
        <!--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" />
    
  7. 给方法添加注解

    /**
         *
         * rollbackFor:表示发生指定的异常一定回滚.
         *   处理逻辑是:
         *     1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
         *         如果异常在rollbackFor列表中,不管是什么类型的异常,一定回滚。
         *     2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
         *         如果是一定回滚。
         *
         */
       /* @Transactional(
                propagation = Propagation.REQUIRED,
                isolation = Isolation.DEFAULT,
                readOnly = false,
                rollbackFor = {
                        NullPointerException.class,  NotEnoughException.class
                }
        )*/
    
        //使用的是事务控制的默认值, 默认的传播行为是REQUIRED,默认的隔离级别DEFAULT
        //默认抛出运行时异常,回滚事务。
        @Transactional
        @Override
        public void buy(Integer goodsId, Integer nums) {
    
  8. 配置文件添加事务

    <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <!--tx:attributes:配置事务属性-->
            <tx:attributes>
                <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
                    name:方法名称,1)完整的方法名称,不带有包和类。
                                  2)方法可以使用通配符,* 表示任意字符
                    propagation:传播行为,枚举值
                    isolation:隔离级别
                    rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
                -->
                <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                           rollback-for="java.lang.NullPointerException,com.bjpowernode.excep.NotEnoughException"/>
    
                <!--使用通配符,指定很多的方法-->
                <tx:method name="add*" propagation="REQUIRES_NEW" />
                <!--指定修改方法-->
                <tx:method name="modify*" />
                <!--删除方法-->
                <tx:method name="remove*" />
                <!--查询方法,query,search,find-->
                <tx:method name="*" propagation="SUPPORTS" read-only="true" />
            </tx:attributes>
        </tx:advice>
    
        <!--配置aop-->
        <aop:config>
            <!--配置切入点表达式:指定哪些包中类,要使用事务
                id:切入点表达式的名称,唯一值
                expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
    
                com.bjpowernode.service
                com.crm.service
                com.service
            -->
            <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
    
            <!--配置增强器:关联adivce和pointcut
               advice-ref:通知,上面tx:advice哪里的配置
               pointcut-ref:切入点表达式的id
            -->
            <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
        </aop:config>
    

十二、监听器

  1. web.xml文件配置

    <!--注册监听器ContextLoaderListener
            监听器被创建对象后,会读取/WEB-INF/spring.xml
            为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
            /WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
    
            可以修改默认的文件位置,使用context-param重新指定文件的位置
    
    
            配置监听器:目的是创建容器对象,创建了容器对象, 就能把spring.xml配置文件中的所有对象都创建好。
            用户发起请求就可以直接使用对象了。
        -->
        <context-param>
            <!-- contextConfigLocation:表示配置文件的路径  -->
            <param-name>contextConfigLocation</param-name>
            <!--自定义配置文件的路径-->
            <param-value>classpath:spring.xml</param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
  2. 拿到容器对象

    //创建spring的容器对象
            //String config= "spring.xml";
            //ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
            WebApplicationContext ctx = null;
            //获取ServletContext中的容器对象,创建好的容器对象,拿来就用
            /*String key =  WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
            Object attr  = getServletContext().getAttribute(key);
            if( attr != null){
                ctx = (WebApplicationContext)attr;
            }*/
    
            //使用框架中的方法,获取容器对象
            ServletContext sc = getServletContext();
            ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
            System.out.println("容器对象的信息========"+ctx);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值