zoukankan      html  css  js  c++  java
  • Spring框架基础

    1         Spring框架

    1.1           Spring的基本概念

    是一个轻量级的框架,提供基础的开发包,包括消息、web通讯、数据库、大数据、授权、手机应用、session管理等。

    Maven

    Maven是用来管理项目的依赖、编译、文档等信息。以前使用第三方类库需要复制类库到指定的目录下,可能此jar包还要依赖于其他的jar包,需要不断的去下载复制。Maven通过一个配置文件配置类库的groupid组件唯一标识,artifactId项目的唯一标识,version项目的版本信息等。Marven会根据pom.xml中配置的denpendency依赖项,从marven中心库下载相关依赖包到.m2。

    AOP(Aspect Oriented Programming)面向切面编程

           面前切面编程其实一种封装机制,一个切面提供输入和输出接口,程序的流程经过切面。例如银行的转账、查询、取款流程中都有一个验证的操作,可以将验证操作独立成一个切面,转账、查询、取款流程都要经过这个切面去验证。

    依赖注入(dependency injection)和控制反转(Inversion of Control

    控制反转实际上是一种解耦的机制,在开发大型项目时,肯定要创建多个类,而且类之间相互依赖错综复杂,往往牵一发而动全身,为了解除类之间的耦合,而采用IOC进行解耦,实现各个模块之间的独立开发,协同工作。例如机械手表中各个齿轮之间相互耦合,是一种强耦合的关系,做一个齿轮要考虑其他的齿轮怎么的设计参数,稍有偏差就会出现很大的误差。而电子手表中则是解耦成各个模块:显示、供电、计算模块等。各个模块可以单独开发,再通过通用的接口连接在一起。这就是一种解耦的思路。IOC相当于在各个类之间的连接器,实现将各个类的对象注入到其他类的对象中。

                           

    例如在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。

    public class Human {

        ...

        Father father;

        ...

        public Human() {

            father = new Father();

        }

    }

    仔细看这段代码我们会发现存在一些问题:

    (1). 如果现在要改变 father 生成方式,如需要用new Father(String name)初始化 father,需要修改 Human 代码;

    (2). 如果想测试不同 Father 对象对 Human 的影响很困难,因为 father 的初始化被写死在了 Human 的构造函数中;

    (3). 如果new Father()过程非常缓慢,单测时我们希望用已经初始化好的 father 对象 Mock 掉这个过程也很困难。

    依赖注入 上面将依赖在构造函数中直接初始化是一种 Hard init 方式,弊端在于两个类不够独立,不方便测试。我们还有另外一种 Init 方式,如下:

    public class Human {

        ...

        Father father;

        ...

        public Human(Father father) {

            this.father = father;

        }

    }

    上面代码中,我们将 father 对象作为构造函数的一个参数传入。在调用 Human 的构造方法之前外部就已经初始化好了 Father 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入

    现在我们发现上面 1 中存在的两个问题都很好解决了,简单的说依赖注入主要有两个好处:

    (1). 解耦,将依赖之间解耦。

    (2). 因为已经解耦,所以方便做单元测试,尤其是 Mock 测试。

    控制反转的名称由来

    软件系统在没有引入IOC容器之前,对象human依赖于对象father,那么对象human在初始化或者运行到某一点的时候,自己必须主动去创建对象father或者使用已经创建的对象father。无论是创建还是使用对象father,控制权都在自己手上。软件系统在引入IOC容器之后,这种情形就完全改变了,由于IOC容器的加入,对象human与对象father之间失去了直接联系,而是通过接口传入对象。相当于是把已经创建好的对象father通过接口注入human中,human对象失去了创建对象father的权利,反而是一种被动的接收别人创建好的对象,所以称为控制反转。也被称为依赖注入。

    Spring为了降低对象之间的耦合关系,采用了一个bean工厂来创建bean对象,通过配置文件或者注解去声明需要作为bean对象的类,以及类之间的依赖关系。Bean工厂创建bean对象,并根据配置的从属关系,将对象注入到另外一个对象属性中。

     

    1.2           注解

    注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。假设很多人考驾照,教练在有些学员身上贴一些绿牌子、黄牌子,贴绿牌子的表示送礼送得比较多的,贴黄牌子的学员表示送礼送得比较少的,不贴牌子的学员表示没有送过礼的,通过这个牌子就可以标识出不同的学员教官在考核时一看,哦,这个学员是有牌子的,是送过礼给他的,优先让有牌子的学员过,此时这个牌子就是一个注解一个牌子就是一个注解的实例对象,实实在在存在的牌子就是一个实实在在的注解对象。

    1.2.1            JDK1.5之后内部提供的三个注解

    (1)@Deprecated 意思是“废弃的,过时的”

    (2)@Override 意思是“重写、覆盖”

    (3)@SuppressWarnings 意思是“压缩警告”

    注解实例:

    package cn.gacl.annotation;

    public class AnnotationTest {

        /* @param args*/

       (1) @SuppressWarnings(":deprecation")

        //这里就是注解,称为压缩警告,这是JDK内部自带的一个注解,一个注解就是一个类,在这里使用了这个注解就是创建了SuppressWarnings类的一个实例对象

        public static void main(String[] args) {

            System.runFinalizersOnExit(true);

            //The method runFinalizersOnExit(boolean) from the type System is deprecated(过时的,废弃的)

            //这里的runFinalizersOnExit()方法画了一条横线表示此方法已经过时了,不建议使用了

        }

       (2) @Deprecated //这也是JDK内部自带的一个注解,意思就是说这个方法已经废弃了,不建议使用了

        public static void sayHello(){

            System.out.println("hi,孤傲苍狼");

        }

        (3)@Override //这也是JDK1.5之后内部提供的一个注解,意思就是要重写(覆盖)JDK内部的toString()方法

        public String toString(){

            return "孤傲苍狼";

        }

    }

    1.2.2            自定义注解

    定义注解

    package cn.gacl.annotation;

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    @Retention(RetentionPolicy.RUNTIME)//设置注解的生命周期

    //Retention注解决定MyAnnotation注解的生命周期

    /*

     * @Retention(RetentionPolicy.SOURCE)

     * 这个注解的意思是让MyAnnotation注解只在java源文件中存在,编译成.class文件后注解就不存在了

     * @Retention(RetentionPolicy.CLASS)

     * 这个注解的意思是让MyAnnotation注解在java源文件(.java文件)中存在,编译成.class文件后注解也还存在,

     * 被MyAnnotation注解类标识的类被类加载器加载到内存中后MyAnnotation注解就不存在了

     */

    @Target( { ElementType.METHOD, ElementType.TYPE })//设置注解的作用域

    //Target注解决定MyAnnotation注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分

    public @interface MyAnnotation {}//定义注解

    使用注解

    package cn.gacl.annotation;

    @MyAnnotation

    //这里是将新创建好的注解类MyAnnotation标记到AnnotaionTest类上

    public class AnnotationUse {

        public static void main(String[] args) {

            // 这里是检查Annotation类是否有注解,这里需要使用反射才能完成对Annotation类的检查

            if (AnnotationUse.class.isAnnotationPresent(MyAnnotation.class)) {

                /*

                 * MyAnnotation是一个类,这个类的实例对象annotation是通过反射得到的,这个实例对象是如何创建的呢?

                 * 一旦在某个类上使用了@MyAnnotation,那么这个MyAnnotation类的实例对象annotation就会被创建出来了

                 */

                MyAnnotation annotation = (MyAnnotation) AnnotationUse.class

                        .getAnnotation(MyAnnotation.class);

                System.out.println(annotation);// 打印MyAnnotation对象,这里输出的结果为:@cn.itcast.day2.MyAnnotation()

            }

        }

    }

    1.2.3            为注解增加属性

    定义添加属性

    package cn.gacl.annotation;

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    @Retention(RetentionPolicy.RUNTIME)

    //Retention注解决定MyAnnotation注解的生命周期

    @Target( { ElementType.METHOD, ElementType.TYPE })

    public @interface MyAnnotation {

        /* 定义基本属性* @return*/

    String color();//新增属性color

    String name() default "blues";//新增属性name并设置默认值blues

    }

    package cn.gacl.annotation;

    @MyAnnotation(color="red")//应用MyAnnotation注解的color属性

    //@MyAnnotation(color="red",name=”blues”)

    public class MyAnnotationTest {

        public static void main(String[] args) {

            /**

             * 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法

             */

            MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);

            System.out.println(annotation.color());//输出color属性:red

            System.out.println(annotation.name());//输出默认属性blues

           

        }

    }

    1.2.4            Java API中三种注解的定义

    (1)@Deprecated

    Java API中是这样定义的@Deprecated的

    @Documented

    2 @Retention(value=RUNTIME)

    3 public @interface Deprecated

    (2)@Override

    Java API中是这样定义的@Override的

    1 @Target(value=METHOD)

    2 @Retention(value=SOURCE)

    3 public @interface Override

    @Override是给javac(java编译器)看的,编译完以后就@Override注解就没有价值了,@Override注解在源代码中有用,编译成.class文件后@Override注解就没有用了,因此@Override的Retention的属性值是RetentionPolicy.SOURCE

    (3)@SuppressWarnings

    Java API中是这样定义的@SuppressWarnings的

    @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})

     @Retention(value=SOURCE)

    public @interface SuppressWarnings

    1.3           spring常用注解

    1.3.1            spring使用配置文件的问题

    传统的spring是定义类和和类中定义成员类的引用和set、get接口。同时在配置文件中配置类和成员类之间的依赖关系。要传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:
    1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。
    2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。
    为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

    不使用注解:

    先看一个不使用注解的Spring示例,在这个示例的基础上,改成注解版本的,这样也能看出使用与不使用注解之间的区别,先定义一个老虎:

     

    package com.spring.model;

     

    public class Tiger {

       

        private String tigerName="TigerKing";

       

        public String toString(){

            return "TigerName:"+tigerName;

        }

    }

     

    再定义一个猴子:

     

    package com.spring.model;

     

    public class Monkey {

       

        private String monkeyName = "MonkeyKing";

       

        public String toString(){

            return "MonkeyName:" + monkeyName;

        }

     

    }

     

    定义一个动物园:

     

    package com.spring.model;

     

    public class Zoo {

        private Tiger tiger;

        private Monkey monkey;

       

        public Tiger getTiger() {

            return tiger;

        }

        public void setTiger(Tiger tiger) {

            this.tiger = tiger;

        }

        public Monkey getMonkey() {

            return monkey;

        }

        public void setMonkey(Monkey monkey) {

            this.monkey = monkey;

        }

       

        public String toString(){

            return tiger + " " + monkey;

        }

       

    }

     

    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"

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

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

        xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-3.0.xsd

        ">

       

         <bean id="zoo" class="com.spring.model.Zoo" >

            <property name="tiger" ref="tiger" />

            <property name="monkey" ref="monkey" />

        </bean>

       

        <bean id="tiger" class="com.spring.model.Tiger" />

        <bean id="monkey" class="com.spring.model.Monkey" />

     

    </beans>

     

    测试方法:

     

    public class TestAnnotation {

        /**

         * 不使用注解

         */

        @Test

        public void test(){

            //读取配置文件

            ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext2.xml");

            Zoo zoo=(Zoo) ctx.getBean("zoo");

            System.out.println(zoo.toString());

        }

    }

     

    1.3.2            采用spring注解自动装备bean

    采用注解之后,不需要再配置文件中配置类之间的依赖关系,也无需定义set和get接口,只需将类定义为bean,让spring能够识别bean,然后在需要注入的引用前面加上@Autowired注解,IOC就会自动的按照类型匹配的方式查找bean,将bean对象注入到引用中。

    @Autowired

    @Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。

    @Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

    因此,引入@Autowired注解,先看一下spring配置文件怎么写:

     

     1 <?xml version="1.0" encoding="UTF-8"?>

     2 <beans

     3     xmlns="http://www.springframework.org/schema/beans"

     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     5     xmlns:p="http://www.springframework.org/schema/p"

     6     xmlns:context="http://www.springframework.org/schema/context"

     7     xsi:schemaLocation="http://www.springframework.org/schema/beans

     8     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

     9     http://www.springframework.org/schema/context

    10     http://www.springframework.org/schema/context/spring-context-3.0.xsd

    11     ">

    12    

    13     <context:component-scan base-package="com.spring" />

    14    

    15     <bean id="zoo" class="com.spring.model.Zoo" />

    16     <bean id="tiger" class="com.spring.model.Tiger" />

    17     <bean id="monkey" class="com.spring.model.Monkey" />

    18

    19 </beans>

     

    注意第13行,使用必须告诉spring一下我要使用注解了,告诉的方式有很多,<context:component-scan base-package="xxx" />是一种最简单的,spring会自动扫描xxx路径下的注解。

    看到第15行,原来zoo里面应当注入两个属性tiger、monkey,现在不需要注入了。再看下,Zoo.java也很方便,把getter/setter都可以去掉:

     

    package com.spring.model;

     

    import org.springframework.beans.factory.annotation.Autowired;

     

    public class Zoo {

       

        @Autowired

        private Tiger tiger;//自动注入对象

       

        @Autowired

        private Monkey monkey;// //自动注入对象

       

        public String toString(){

            return tiger + " " + monkey;

        }

       

    }

     

    这里@Autowired注解的意思就是,当Spring发现@Autowired注解时,将自动在代码上下文中找到和其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。

    如果将16.17行去掉,ioc就无法找到tiger和monkey的bean。@Autowired注解要去寻找的是一个Bean,Tiger和Monkey的Bean定义都给去掉了,自然就不是一个Bean了,Spring容器找不到也很好理解。那么,如果属性找不到我不想让Spring容器抛出异常,而就是显示null,可以吗?可以的,其实异常信息里面也给出了提示了,就是将@Autowired注解的required属性设置为false即可:

     

    package com.spring.model;

     

    import org.springframework.beans.factory.annotation.Autowired;

     

    public class Zoo {

       

        @Autowired(required=false)

        private Tiger tiger;

       

        @Autowired(required=false)

        private Monkey monkey;

       

        public String toString(){

            return tiger + " " + monkey;

        }

       

    }

     

    此时,找不到tiger、monkey两个属性,Spring容器不再抛出异常而是认为这两个属性为null。

    1.3.3            Qualifier(指定注入Bean的名称)

    定义一个接口,有两个类实现了它,在使用接口引用自动注入时,因为有两个实现类,所以无法知道注入哪个类,所以要用Qualifier指定实现类的名称。那么如果容器中有一个以上匹配的Bean,则可以通过Qualifier注解限定Bean的名称,看下面的例子:

    定义一个Car接口:

    package com.spring.service;

     

    public interface ICar {

       

        public String getCarName();

    }

    两个实现类BMWCar和BenzCar:

     

    package com.spring.service.impl;

     

    import com.spring.service.ICar;

     

    public class BMWCar implements ICar{

       

        public String getCarName(){

            return "BMW car";

        }

    }

     

     

    package com.spring.service.impl;

     

    import com.spring.service.ICar;

     

    public class BenzCar implements ICar{

       

        public String getCarName(){

            return "Benz car";

        }

    }

     

    再写一个CarFactory,引用car(这里先不用@Qualifier注解):

     

    package com.spring.model;

     

    import org.springframework.beans.factory.annotation.Autowired;

     

    import com.spring.service.ICar;

     

    public class CarFactory {

       

        @Autowired

        private ICar car;

       

        public String toString(){

            return car.getCarName();

        }

       

    }

     

    配置文件:

     

    <?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:p="http://www.springframework.org/schema/p"

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

        xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-3.0.xsd

        ">

       

        <context:component-scan base-package="com.spring" />

       

        <!-- Autowired注解配合Qualifier注解 -->

        <bean id="carFactory" class="com.spring.model.CarFactory" />

        <bean id="bmwCar" class="com.spring.service.impl.BMWCar" />

        <bean id="benz" class="com.spring.service.impl.BenzCar" />

       

    </beans>

     

    测试方法:

     

    /**

     * Autowired注解配合Qualifier注解

     */

    @Test

    public void test1(){

        //读取配置文件

        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext2.xml");

        CarFactory carFactory=(CarFactory) ctx.getBean("carFactory");

        System.out.println(carFactory.toString());

    }

     

    运行一下,不用说,一定是报错的,Car接口有两个实现类,Spring并不知道应当引用哪个实现类。所以要用Qualifier指定。

    package com.spring.model;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.beans.factory.annotation.Qualifier;

    import com.spring.service.ICar;

     

    public class CarFactory {

       

        @Autowired

        @Qualifier("bmwCar")

        private ICar car;

       

        public String toString(){

            return car.getCarName();

        }

       

    }

    1.3.4            @Resource与@Autowired的异同

    @Resource注解与@Autowired注解作用非常相似,这个就简单说了,看例子:

     

    package com.spring.model;

     

    import javax.annotation.Resource;

     

    public class Zoo1 {

       

        @Resource(name="tiger")

        private Tiger tiger;

       

        @Resource(type=Monkey.class)

        private Monkey monkey;

       

        public String toString(){

            return tiger + " " + monkey;

        }

    }

     

    这是详细一些的用法,说一下@Resource的装配顺序:
    (1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
    (2)、指定了name或者type则根据指定的类型去匹配bean
    (3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错然后,区分一下@Autowired和@Resource两个注解的区别:
    (1)、@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
    (2)、@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
    Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

    1.3.5            @Service注解申明类为bean

    上面这个例子, spring的配置文件里面还有15行~17行申明三个类为bean,如果直接在类的定义前面使用注解@service,ioc就会将该类标记为bean。还可以使用下一步的简化是把这三个bean也给去掉,使得spring配置文件里面只有一个自动扫描的标签,增强Java代码的内聚性并进一步减少配置文件。

    要继续简化,可以使用@Service。先看一下配置文件,当然是全部删除了:

     

    <?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:p="http://www.springframework.org/schema/p"

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

        xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-3.0.xsd

        ">

       

        <context:component-scan base-package="com.spring" />

       

    </beans>

     

    然后在类的定义前面加上注解@Service

     

    package com.spring.model;

     

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.stereotype.Service;

     

    @Service//申明为bean,默认标识为小写的zoo

    public class Zoo {

       

        @Autowired

        private Tiger tiger;

       

        @Autowired

        private Monkey monkey;

       

        public String toString(){

            return tiger + " " + monkey;

        }

       

    }

     

    默认是在Spring容器中Zoo.java存在的形式就是"zoo",即可以通过ApplicationContext的getBean("zoo")方法来得到Zoo.java。@Service注解,其实做了两件事情:
    (1)、声明Zoo.java是一个bean,这点很重要,因为Zoo.java是一个bean,其他的类才可以使用@Autowired将Zoo作为一个成员变量自动注入。
    (2)、Zoo.java在bean中的id是"zoo",即类名且首字母小写。

    如果,我不想用这种形式怎么办,就想让Zoo.java在Spring容器中的名字叫做"Zoo",可以的:

     

    package com.spring.model;

     

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.context.annotation.Scope;

    import org.springframework.stereotype.Service;

     

    @Service("Zoo")//申明bean的标识为指定的名称Zoo

    @Scope("prototype")

    public class Zoo {

       

        @Autowired

        private Tiger tiger;

       

        @Autowired

        private Monkey monkey;

       

        public String toString(){

            return tiger + " " + monkey;

        }

       

    }

     

    这样,就可以通过ApplicationContext的getBean("Zoo")方法来得到Zoo.java了。

    这里我还多加了一个@Scope注解,应该很好理解。因为Spring默认产生的bean是单例的,假如我不想使用单例怎么办,xml文件里面可以在bean里面配置scope属性。注解也是一样,配置@Scope即可,默认是"singleton"即单例,"prototype"表示原型即每次都会new一个新的出来。

    1.3.6            Scope注解创建bean的方式和生命周期

    作用

    Scope设置对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。

    取值说明

    有singleton、prototype、request,session和global session五种取值。 

    (1)singleton (单一实例)

     此取值时表明容器中创建时只存在一个实例,所有引用此bean都是单一实例。singleton类型的bean定义从容器启动到第一次被请求而实例化开始,只要容器不销毁或退出,该类型的bean的单一实例就会一直存活。生命周期为IOC存在周期。

    (2)prototype每次重新创建

    spring容器在进行输出prototype的bean对象时,会每次都重新生成一个新的对象给请求方,生命周期为对象的使用周期。 

    (3)request

    web程序给每一个http request新建一个Bean实例,通常是和XmlWebApplicationContext共同使用。

    <bean id ="requestPrecessor" class="...RequestPrecessor"   scope="request" />

    Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束,如同java webrequest的生命周期。简单来讲,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。

     4session

    Web程序中用于保存用户的登录信息对象,对于这种放到session中的信息,我们可以使用如下形式的制定scope为session:

    <bean id ="userPreferences" class="...UserPreferences"   scope="session" />

    Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,比request scope的bean会存活更长的时间,其他的方面没区别,如java web中session的生命周期。

     (5)global session

    <bean id ="userPreferences" class="...UserPreferences"   scope="globalsession" />

    global session只有应用在基于porlet的web应用程序中才有意义,它映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待。

     scope配置方式

    (1)    使用配置文件配置

    <bean id ="userPreferences" class="...UserPreferences"   scope="session" />

    (2)    使用注解配置

    @Scope默认情况使用的是singleton,若是其他类型需要指定。

    @Scope(”prototype”)

     

    1.3.7            @Value注解

    @Value的作用是通过注解将常量、配置文件中的值、其他bean的属性值注入到变量中,作为变量的初始值。

    (1)常量注入

    @Value("normal")

        private String normal; // 注入普通字符串

        @Value("classpath:com/hry/spring/configinject/config.txt")

        private Resource resourceFile; // 注入文件资源

     

        @Value("http://www.baidu.com")

    private Resource testUrl; // 注入URL资源

    bean属性、系统属性、表达式注入@Value("#{}")

    bean属性注入需要注入者和被注入者属于同一个IOC容器,或者父子IOC容器关系,在同一个作用域内。

        @Value("#{beanInject.another}")

    private String fromAnotherBean; // 注入其他Bean属性:注入beanInject对象的属性another,类具体定义见下面

    @Value("#{systemProperties['os.name']}")

        private String systemPropertiesName; // 注入操作系统属性

     

        @Value("#{ T(java.lang.Math).random() * 100.0 }")

    private double randomNumber; //注入表达式结果

     

    (3)配置文件属性注入@Value("${}")

    @Value("#{}")读取配置文件中的值,注入到变量中去。配置文件分为默认配置文件application.properties和自定义配置文件。

    •application.properties。application.properties在spring boot启动时默认加载此文件

    •自定义属性文件。自定义属性文件通过@PropertySource加载。@PropertySource可以同时加载多个文件,也可以加载单个文件。如果相同第一个属性文件和第二属性文件存在相同key,则最后一个属性文件里的key启作用。加载文件的路径也可以配置变量,如下文的${anotherfile.configinject},此值定义在第一个属性文件config.properties

    第一个属性文件config.properties内容如下: 
    ${anotherfile.configinject}作为第二个属性文件加载路径的变量值

    book.name=bookName

    anotherfile.configinject=placeholder

    第二个属性文件config_placeholder.properties内容如下:

    book.name.placeholder=bookNamePlaceholder

    下面通过@Value(“${app.name}”)语法将属性文件的值注入bean属性值,详细代码见:

     

    @Component

    // 引入自定义配置文件。

    @PropertySource({"classpath:com/hry/spring/configinject/config.properties",

     // 引入自定义配置文件。${anotherfile.configinject}则是config.properties文件中的第二个属性值,会被替换为config_placeholder.properties。

       "classpath:com/hry/spring/configinject/config_${anotherfile.configinject}.properties"})

    public class ConfigurationFileInject{

        @Value("${app.name}")

        private String appName; // 这里的值来自application.properties,spring boot启动时默认加载此文件

     

        @Value("${book.name}")

        private String bookName; // 注入第一个配置文件config.properties的第一个属性

        @Value("${book.name.placeholder}")

        private String bookNamePlaceholder; // 注入第二个配置外部文件属性

    }

     

    1.3.8            @profile和@activeprofile

    程序在不同的测试环境中,使用的配置信息可能不一样,例如采用不同的数据库。在不同的环境中使用不同的数据库配置,需要修改配置文件,所有可以直接写成多个配置文件,通过注解@Profile(value = "dev")来申明不同的配置类,通过注解@ActiveProfiles("dev")来指定采用的是哪一个配置类,通过注解的形式选用不同的环境配置。

    (1)通过设定Environment的ActiveProfiles来设定当前Context需要使用的配置环境,在开发中使用@Profile注解类或者方法,达到不同情况实例化不同的bean。

    (2)通过设定jvm的spring.profiles.active参数来配置环境(例如:application-dev.properties/application-pro.properties,然后只需要在application.properties中制定dev还是pro就可以了)

    (3)web项目的话,一般在servlet的context parameter中设置。

    1.4           spring 事件(Application Event)

    1.5           Spring的AOP配置文件和注解实例解析

    AOP它利用一种称为"横切"的技术,将那些与核心业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。例如打印日志。与核心业务逻辑无关,但是却贯穿整个程序当中。所以使用AOP切面技术将日志和核心业务逻辑分离开来,通过配置文件或者注解配置切面,并为切面设置过滤条件,过滤条件是程序中的方法,表示调用程序中的这些方法时会调用日志切面的方法来打印日志,相当于是一种过滤符合条件就触发打印日志的机制。而不是将日志和程序中的方法写在一起。

    1.5.1            AOP配置文件方式

    通过配置文件设置切面,切入点,设置与切入点关联的触发方法,配置核心业务函数与触发函数之间的映射关系。一旦触发方法调用,就会进入切面,在触发方法前、后、异常等情况下执行相关函数。

    (1)为日志类LogAop配置切面信息,配置applicationContext.xml 中logAopBean是配置日志的bean,提供给IOC调用。aop:aspect id="logAspect"则是定义一个切面。

    <!-- 配置日志类logAopBean为bean ,提供给IOC使用-->

    <bean id="logAopBean" class="com.demo.common.aop.LogAop"></bean>

    <aop:config>

    <!—设置切面,logAopBean是日志类的名称 -->

            <aop:aspect id="logAspect" ref="logAopBean">

    <!—设置切入点, expression指定过滤条件,表示com.demo 内部的所有方法,id指定切入的方法,这个方法是一个空函数,只是用于后面关联日志类中要调用的其他方法-->

                <aop:pointcut expression="execution(* com.demo..*(..))" id="allMethod"/>

    <!—com.demo中的方法,在调用时,就会触发日志类中的函数,下面四个方法分别是调用前打日志,调用异常打日志,调用返回打日志,调用结束打日志,切入点allMethod和核心业务类中的方法关联,日志类中的方法和切入点方法关联。核心方法调用时,触发切入点,切入点根据方法的执行情况去执行对应的日志方法。 -->

                <aop:before method="before" pointcut-ref="allMethod" />

                <aop:after-throwing method="afterThrowing" pointcut-ref="allMethod" />

                <aop:after-returning method="afterReturn" pointcut-ref="allMethod" />

                <aop:after method="after" pointcut-ref="allMethod" />

            </aop:aspect>

        </aop:config>

    (2)定义日志类,实现日志类中前置、后置、异常、返回等方法。

    package com.demo.common.aop;

    import org.aspectj.lang.JoinPoint;

    import org.aspectj.lang.ProceedingJoinPoint;

    public class LogAop {

        public void before(JoinPoint call){

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println("开始执行:"+className+"."+methodName+"()方法...");

        }

        public void afterThrowing(JoinPoint call){

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println(className+"."+methodName+"()方法抛出了异常...");

        }

        public void afterReturn(JoinPoint call){

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println(className+"."+methodName+"()方法正常执行结束...");

        }

        public void after(JoinPoint call){

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println(className+"."+methodName+"()最终执行步骤(finally)...");

        }

        /*//用来做环绕通知的方法可以第一个参数定义为org.aspectj.lang.ProceedingJoinPoint类型 

        public Object doAround(ProceedingJoinPoint call) throws Throwable { 

            Object result = null; 

            this.before(call);//相当于前置通知 

            try { 

                result = call.proceed(); 

                this.afterReturn(call); //相当于后置通知 

            } catch (Throwable e) { 

                this.afterThrowing(call); //相当于异常抛出后通知 

                throw e;

            }finally{ 

                this.after(call);  //相当于最终通知 

            } 

            return result; 

        }*/

    }

    1.5.2            AOP注解方式

    使用注解去代替配置文件,告诉IOC容器,切面、切入点、触发函数和核心业务方法之间的映射关系。前置方法、后置方法、异常方法、正常返回方法。

    在配置文件中声明LogAnnotationAspect为logAspectBean,告诉IOC容器这是一个bean。

    <bean id="logAspectBean" class="com.demo.common.aop.LogAnnotationAspect"></bean>

        <aop:aspectj-autoproxy/>

    package com.demo.common.aop;

    import org.aspectj.lang.JoinPoint;

    import org.aspectj.lang.ProceedingJoinPoint;

    import org.aspectj.lang.annotation.After;

    import org.aspectj.lang.annotation.AfterReturning;

    import org.aspectj.lang.annotation.AfterThrowing;

    import org.aspectj.lang.annotation.Aspect;

    import org.aspectj.lang.annotation.Before;

    import org.aspectj.lang.annotation.Pointcut;

    @Aspect  //定义切面类 

    public class LogAnnotationAspect { 

        @SuppressWarnings("unused") 

        //定义切入点,提供一个方法,这个方法的名字就是切入点的id 

        @Pointcut("execution(* com.demo..*(..))")  //关联核心业务函数

        private void allMethod(){} 

        //针对指定的切入点表达式选择的切入点应用前置通知 

        @Before("allMethod()")   

        public void before(JoinPoint call) { 

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println("开始执行:"+className+"."+methodName+"()方法...");

        } 

        //访问命名切入点来应用后置通知 

        @AfterReturning("allMethod()") 

        public void afterReturn(JoinPoint call) { 

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println(className+"."+methodName+"()方法正常执行结束...");

        } 

        //应用最终通知 

        @After("allMethod()") 

        public void after(JoinPoint call) { 

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println(className+"."+methodName+"()最终执行步骤(finally)...");

        } 

        //应用异常抛出后通知 

        @AfterThrowing("allMethod()") 

        public void afterThrowing(JoinPoint call) { 

            String className = call.getTarget().getClass().getName();

            String methodName = call.getSignature().getName();

            System.out.println(className+"."+methodName+"()方法抛出了异常...");

        } 

        //应用周围通知 

        //@Around("allMethod()") 

        public Object doAround(ProceedingJoinPoint call) throws Throwable{ 

            Object result = null; 

            this.before(call);//相当于前置通知 

            try { 

                result = call.proceed(); 

                this.afterReturn(call); //相当于后置通知 

            } catch (Throwable e) { 

                this.afterThrowing(call);  //相当于异常抛出后通知 

                throw e; 

            }finally{ 

                this.after(call);  //相当于最终通知 

            } 

            return result; 

        } 

    }

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    百度云盘下载地址:

    链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg

    提取码:mc8l

    微信公众号获取最新的软件和视频介绍

    QStockView

  • 相关阅读:
    三十七、Java基础之JDBC
    三十六、Java基础之File类
    各种IoC框架下实现AOP
    Eclipse导出可执行Jar文件(包含第三方Jar包)
    设计模式(Patterns in Java)-解道
    MyBatis入门示例
    freemarker实例2
    freemarker小例子
    MyEclipse8.6 破解以及注册码
    myeclipse中java文件中文注释乱码问题
  • 原文地址:https://www.cnblogs.com/bclshuai/p/10509550.html
Copyright © 2011-2022 走看看