spring FramkWork知识点整理

Spring FramkWrok

1.介绍

  • Spring是一个IOC(DI)和AOP容器框架
  • 轻量级:Spring是非侵入性的 开发不依赖Spring的API
  • 包含:
    • 依赖注入 (DI)
    • 面向切面编程 (AOP)

2.spring 的helloworld

  • applicationContext.xml中配置

    <!--    配置bean -->
        <bean id="helloworld" class="bean.HelloWorld">
    <!--        name 对应类中set方法的名-->
            <property name="name2" value="Spring"></property>
        </bean>
    
    
  • Main方法中

    //        HelloWorld helloWorld = new HelloWorld();
    //        helloWorld.setName("JIAT");
            //以上可以由spring完成
    
            //1.创建Spring的IOC对象:会对xml中设置的bean创建对象调用构造方法,并且对property标签中的属性赋值
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            //2.从IOC容器中获取Bean实例
    //        HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloworld"); //使用id获取
            HelloWorld helloWorld = ctx.getBean(HelloWorld.class);  //使用类型获取(同类型的bean无法区分)
            //3.调用
            helloWorld.hello();
    
    

3.javaBean的注入

IOC & DI (使用的是工厂模式)

IOC: 反转资源获取的方向 原方向:组件向容器请求资源,容器返回资源;反方向:容器主动的将资源给到组件,组件要做的是选择一种合适的方式来接受资源

DI: IOC的另一种表述方式,组件以一些定义好的方式,接受来自如容器的资源注入

1.工厂方法注入bean

  • IOC容器的初始化,使用beanfactory的子接口ApplicationContext,ApplicationContext有两个主要的实现类ClassPathXmlApplicationContext(从类路径下加载配置文件)和FileSystemXmlApplicationContext(从文件系统下加载配置文件)
  • 从IOC容器中获取Bean实例
    • HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloworld"); //使用id获取
    • HelloWorld helloWorld = ctx.getBean(HelloWorld.class); //使用类型获取(同类型的bean无法区分)

2.依赖注入方式

1.在xml中bean标签属性

  • Class: bean的全类名,通过反射的方式在IOC容器中创建bean,所以要求bean中必须有无参构造器
  • Id: 标识容器中的bean,id唯一

2.注入方式

属性注入和构造器注入常用,工厂方法注入很少且不推荐

  • 属性注入:利用setter方法注入bean属性值或者依赖的对象。

使用 例子如上 (需要无参构造器)

  • 构造方法注入:通过构造方法注入,保证bean在实例化之后就可以使用,在中声明属性,没有name属性(该方法无需无参构造器)
 <bean id="car" class="bean.CAR">
        <constructor-arg value="Audi" index="0"></constructor-arg>
        <constructor-arg value="ShangHai" index="1"></constructor-arg>
        <constructor-arg value="300000" type="double"></constructor-arg>  //price
    </bean>

<!--    使用构造器注入属性值可以指定参数的位置(index)和参数的类型(type)以区分重载构造器-->
    <bean id="car2" class="bean.CAR">
        <constructor-arg value="Baoma" type="java.lang.String"></constructor-arg>
        <constructor-arg value="SHANGHAI" type="java.lang.String"></constructor-arg>
        <constructor-arg value="240" type="int"></constructor-arg> //对应maxSpeed
    </bean>
public CAR(String brand, String corp, double price) {
    this.brand = brand;
    this.corp = corp;
    this.price = price;
}


public CAR(String brand, String corp, int maxSpeed) {
    this.brand = brand;
    this.corp = corp;
    this.maxSpeed = maxSpeed;
}

细节

字面值:可以直接使用value或者constructor-arg标签中子标签value的方式,若字面值中包括特殊字符,可以使用把字面值包裹起来

<bean id="car2" class="bean.CAR">
    <constructor-arg value="Baoma" type="java.lang.String"></constructor-arg>
    <constructor-arg  type="java.lang.String">
        <value><![CDATA[<SHANGHAI>]]></value>
    </constructor-arg>
    <constructor-arg value="240" type="int"></constructor-arg>
</bean>
  • 引用其他的bean:

    在property中使用ref直接引用其他bean的id

    <property name="car"  ref="car2"></property>
    

    结果如下

    Person{name='Tom', age=24, car=CAR{brand='Baoma', corp='', price=0.0, maxSpeed=240}}

  • 创建内部bean(无法被其他的外部bean引用)

        <bean id="person" class="bean.Person">
            <property name="name" value="Tom"></property>
            <property name="age" value="24"></property>
    <!--        内部bean-->
            <property name="car">
                <bean class="bean.CAR">
                    <constructor-arg value="Ford"></constructor-arg>
                    <constructor-arg value="China"></constructor-arg>
                    <constructor-arg value="200"></constructor-arg>
                </bean>
            </property>
        </bean>
    
    
  • 注入参数详解:null值和级联属性

    可以使用专用的为属性赋空值

    <constructor-arg><null/></constructor-arg>
    

    级联属性赋值:在对应的类中,要有该属性的set方法

    <property name="car"  ref="car2"></property>    //需要有这一句先初始化,否则异常
    <property name="car.price" value="300000"></property>
    
  • 集合属性

    (list和数组)

    <entry key = “AA" value-ref = “car">

  • 配置Properties,每个标签必须定义key属性

<bean id="dataSourse" class="DAO.DataSourse">
    <property name="properties">
        <props>
            <prop key="user">root</prop>
            <prop key="password">123</prop>
            <prop key="jdbcUrl">jdbc:mysql:///test</prop>
            <prop key="driverClass">com.mysql.jdbc.Driver</prop>
        </props>
    </property>
</bean>
  • 单独配置集合bean,以可以被其他bean引用、

    <!--    配置独立的集合bean-->
        <util:list id="cars">
            <ref bean ="car"/>
            <ref bean ="car2"/>
        </util:list>
        <bean id="person4" class="bean.PersonC">
            <property name="name" value="Jack"></property>
            <property name="age" value="20"></property>
            <property name="cars"  ref="cars"></property>
        </bean>
    
  • 使用p命名空间(需要声明)

    xmlns:p="http://www.springframework.org/schema/p"
    
    <!--    通过p命名空间为bean的属性赋值-->
        <bean id="person5" class="bean.PersonC" p:age="30" p:name="Queen" p:cars-ref="cars"></bean>
    
  • 使用外部属性文件

    Properties

    user =root
    password =
    driverclass = com.mysql.jdbc.Driver
    jdbcurl = jdbc:mysql:///test
    

    导入文件属性

    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    

    获取文件属性用${}

    <bean id="dataSourse" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
        <property name="driverClass" value="${driverclass}"></property>
        <property name="jdbcUrl" value="${jdbcurl}"></property>
    </bean>
    

3.自动装配

IOC容器可以自动装配bean,需要做的就是在bean的autowire属性里指定自动装配的模式

  • ByType:根据类型自动装配,如果容器中有多个与目标bean类型一致的,将无法判定,不能执行自动装配
  • ByName:根据名称自动装配,不许将目标bean的名称和属性名设置的完全相同
  • Constructor:通过构造器自动装配:当bean中存在多个构造器时很复杂
<bean id="address" class="Autowire.Address" p:city="BeiJing" p:street="HuiLongGuan"></bean>
<bean id="car" class="Autowire.Car" p:brand="Audi" p:price="300000"></bean>
<bean id="person" class="Autowire.Person" p:name="Tom" autowire="byName"></bean>
//adderss和car的beanid都必须与person类中的属性名相同 没有匹配的赋空值
public class Person {
    private String name;
    private Address address;
    private Car car;

缺点:

自动装配会配置bean中的所有属性,不够灵活

4.java bean

1.bean之间的关系

继承

    <bean id="address" class="relation.Address" p:city="BeiJing" p:street="WuDaoKou" ></bean>
<!--    继承,使用parent指定父bean的id-->
    <bean id="address2"  parent="address" p:street="DaZhongSi" ></bean>
定义了abstract的bean不能被实例化,只能作为模板被继承
    <bean id="address" class="relation.Address" p:city="BeiJing" p:street="WuDaoKou" abstract="true"></bean>
<!--    继承,使用parent指定父bean的id-->
    <bean id="address2"  parent="address" p:street="DaZhongSi" ></bean>

若某一个bean没有指定class属性,则该bean必须是一个抽象bean

<bean id="address"  p:city="BeiJing" p:street="WuDaoKou" abstract="true"></bean>

依赖

<!--配置时,必须有一个关联的car,就是说person这个bean依赖于car这个bean,否则会报错-->
    <bean id="car" class="relation.Car" p:brand="Audi" p:price="300000"></bean>
    <bean id="person" class="relation.Person" p:name="Tom" p:address-ref="address2" depends-on="car"></bean>

依赖多个时,通过逗号 空格的方式配置bean名称

2.Bean的作用域

默认为单例: scope="singleton"
<!--    使用bean的scope属性来配置bean的作用域
         singleton:默认值,容器初始时创建bean实例,在整个容器的生命周期内只创建这一个bean,即为单例
         prototype:原型的。容器初始化时不创建实例,而在每次请求时都创建一个新的bean实例,并返回
-->
    <bean id="car" class="relation.Car" scope="prototype">
        <property name="brand" value="Audi"></property>
        <property name="price" value="300000"></property>
    </bean>
public static void main(String[] args) {
//单例创建
    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");
    //原型创建
Car car = (Car) ctx.getBean("car");
    Car car2 = (Car) ctx.getBean("car");
    System.out.println(car==car2);
}

5.SPEL

1.介绍

Spring表达式语言:是一个支持运行时查询和操作对象图的强大的表达式语言

语法类似EL:SpEL使用#{}作为定界符,所有在大括号里的字符都被认为是SpEL

2. 用途:

  • 通过Bean的id对bean进行引用
  • 调用方法以及引用对象中的属性
  • 计算表达式的值
  • 正则表达式的匹配

3.应用

  • 引用其他对象

    <bean id="person1" class="Autowire.Person">
        <property name="name" value="Mike"></property>
        <property name="address" value="#{address}"></property>
        <property name="car" value="#{car}"></property>
    </bean>
    
  • 引用其他对象的属性

    <bean id="car1" class="Autowire.Car">
        <property name="brand" value="#{car.brand}"></property>
    </bean>
    
  • 支持算数运算符

    字符串+连接
    <property name="city" value="#{adress.city+'--'+adress.street}"></property>
    
  • 逻辑运算符:and,or,not,| , !

  • 比较运算符(< , > , <= , >= , == , lt , gt , eq , le , ge)

    If-else:表达式 ? ‘’ : ‘’
    <property name="info" value="#{car.price>300000 ? '金领' : '白领'}"></property>
    
  • 调用静态方法或静态属性

    <!--        使用类的静态属性-->
            <property name="tyrePerimeter" value="#{T(java.lang.Math).PI * 80}"></propert>
    

6.IOC容器管理

1.IOC容器可以管理Bean的生命周期

<bean id="car" class="cycyle.Car" init-method="init" destroy-method="destroy">
    <property name="brand" value="Audi"></property>
</bean>
  • Init和destory为类中自己定义的方法,作为初始化方法和关闭
  • 当为单例时,容器创建就调用init方法,容器关闭即 close()时,执行destroy
  • 当为原型时,只有实例化时才会调用init,每次实例化都会执行一次,容器关闭时也不会执行destroy

2.创建bean后置处理器(不需要指定id)

Bean后置处理器允许在调用初始化方法前后对bean进行额外的处理,对IOC容器中的所有bean实例逐一进行处理,而非单一实例

配置

<!--    配置bean的后置处理器-->
    <bean class="cycyle.MyBeanPostProcessor"></bean>

继承BeanPostProcessor(bean为bean对象,beanName为bean配置的id)

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After init...."+ bean+","+beanName);
        if ("car".equals(beanName)){     //过滤bean
           Car car = new Car();
           car.setBrand("BaoMa");
           return car;
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before init..." + bean+","+beanName);
        return bean;
    }
}

7.开发中配置bean的几种方式

1.通过工厂方法配置bean

1.静态工厂

public class StaticCarFactory {
    //直接调用某一个类的静态方法,就可以返回bean的实例
    private static Map<String, Car> cars = new HashMap<String, Car>();

    static {
        cars.put("audi" , new Car("audi",300000));
        cars.put("ford" , new Car("ford",400000));
    }
    //静态工厂方法
    public static Car getCar(String name){
        return cars.get(name);
    }
}
<!--    通过静态工厂方法来配置bean,不是配置静态工厂方法实例
            Class指向静态工厂全类名
       Factory-method指向静态方法
            constructor-arg用来传入静态方法参数
-->
    <bean id="car1" class="factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg value="audi"></constructor-arg>  //静态方法参数输入
    </bean>

2.实例工厂

//实例工厂 的 方法,即先创建工厂本身再调用工厂的实例方法
public class InstanceCarFactory {
    private Map<String,Car> cars = null;

    public InstanceCarFactory() {
        cars = new HashMap<String, Car>();
        cars.put("audi",new Car("audi",300000));
        cars.put("ford",new Car("ford",400000));
    }

    public Car getCar(String brand){    //静态方法
        return cars.get(brand);
    }
}
<!--    配置工厂的实例
            Factory-bean 指向实例工厂方法的全类名
            Factory-method 指向实例工厂对象的方法
            Constructor-arg  传入方法参数
-->
    <bean id="carFactory" class="factory.InstanceCarFactory"></bean>
<!--通过实例工厂方法来配置bean-->
    <bean id="car2" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg value="ford"></constructor-arg>
    </bean>

2.通过FactoryBean

<!--    通过FactoryBean来配置Bean的实例
         calss 指向FactoryBean的全类名
         property 配置FactoryBean的属性
         但实际返回的实例确实FactoryBean的getObject()方法返回的实例
-->
    <bean id="car" class="factorybean.CarFactoryBean">
        <property name="brand" value="BaoMa"></property>
    </bean>
//自定义的FactoryBean需要实现FactoryBean<T>接口
public class CarFactoryBean implements FactoryBean<Car> {
    private String brand;
    public void setBrand(String brand) {
        this.brand = brand;
    }

    //返回bean的对象
    @Override
    public Car getObject() throws Exception {
        return new Car(brand,500000);
    }
    //返回bean的类型
    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    //返回是否为单例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

3.基于注解的方式

1.Spring能从classpath下自动扫描,侦测和实例化具有特定注解的组件。

特定组件包括:

  • @Component:基本注解,表示了一个受Spring管理的组件

  • @Respository:标识持久层组件

  • @Service:标识服务层(业务层)组件

  • @Controller:标识表现层组件

    @Service         //注解
    public class UserService {
        public  void add(){
            System.out.println("UserService add...");
        }
    }
    

对于扫描到的组件,Spring有默认的命名策略:使用非限定类名,第一个字母小写,也可以在注解中通过value属性值标识组件名称

@Repository("userRepository")   //自定义名字

2.当在组件类上使用了特定注解之后,还需要在Spring的配置文件中声明context:component-scan

<!--    指定spring IOC容器扫描的包-->
    <context:component-scan base-package="xxx"></context:component-scan>
<!--    base-package:指定spring IOC容器扫描的包
         resource-pattern:过滤作用,只扫描指定的资源
         <context:include-filter> 子节点表示要包含的目标类
         use-default-filters:默认为true,包涵repository、controller和service,实现只包含某个类需要与<context:include-filter>搭配use-default-filters="false"
         <context:exclude-filter> 子节点表示要排除在外的目标类
         annotation:expression识别的是某个类
         assignable:expression识别的是某个接口,指向他的所有实现类
-->
<!--    <context:component-scan-->
<!--            base-package="annotation"-->
<!--            resource-pattern="repository/*.class"></context:component-scan>-->
    <context:component-scan base-package="annotation" use-default-filters="false">
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
<!--        <context:exclude-filter type="assignable" expression="annotation.repository.UserRepository"/>-->
        <context:include-filter type="assignable" expression="annotation.repository.UserRepository"/>
    </context:component-scan>

3.基于注解来配置Bean的属性

context:component-scan元素会自动注册AutowiredAnnotationBeanPostProcessor实例,该实例可以自动装配具有@Autowired 和 @Resource 、@Inject 注解的属性

@Autowired 注解自动装配具有兼容类型的单个bean属性

普通的字段以及一切具有参数的方法都可以应用@Autowired注解

private UserService userService;
@Autowired
public void setUserService(UserService userService) {
    this.userService = userService;
}
@Autowired
private UserService userService;

默认情况下,所有使用该注解的属性都需要被设置,当Spring找不到匹配的bean装配属性时,会抛出异常,若某一属性允许不被设置,可以设置注解的required属性为false

@Autowired(required = false)
private UserService userService;

当有多个类型相同的bean时,该注解会先找和注解中需要的bean名称相同的bean,若没有名称相同的bean且有多个相同类型,则会报错

  1. 装配的时候定义相同名字

  2. 利用@Qualifier的注解指定装配哪个bean:该注解还可以加到入参前面

@Autowired
@Qualifier("userRepositoryImpl")
private UserRepository userRepository;

@Resource 、@Inject与@Autowired相类似

@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称

@Inject 注解按类型匹配注入的bean,但没有required属性

4.Spring 4.x泛型依赖注入:

在泛型父类中建立关系,在子类中也有这个关系

public class BaseService<T> {
    @Autowired     //父类中建立联系,其子类无需再进行装配
    protected  BaseRespository<T> respository;

    public void add(){
        System.out.println("add...");
        System.out.println(respository);
    }
}

8.在web中的应用

Spring在web应用中使用需要额外加入jar包:

Spring-web 和Spring-webmvc

创建IOC容器:(核心思路)

  1. 非WEB应用在main方法中直接创建

  2. 实际上,Spring配置文件的名字和位置也是可以配置的!将其配置到当前web应用的初始化参数中

<!--    配置Spring配置文件的名称和位置-->
    <context-param>
        <param-name>configLocation</param-name>
        <param-value>applicationContext.xml</param-value>
    </context-param>

<!--    启动IOC容器的监听器-->
    <listener>
        <listener-class>Listeners.SpringServletContextListener</listener-class>
    </listener>
 
  1. 应该在web应用被服务器加载时就创建IOC容器:----监听器ServletContextListener init方法

  2. 在web应用的其他组件中如何来访问IOC容器

创建IOC容器后将其放入ServletContext(即application域中)的一个属性中

public void contextInitialized(ServletContextEvent sce) {
   //获取初始化在web配置文件中的Spring配置文件的名称和位置
   ServletContext servletContext = sce.getServletContext();
   String config = servletContext.getInitParameter("configLocation");

   //创建IOC容器
   ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
   //放入ServletContext中
   servletContext.setAttribute("ApplicationContext",ctx);

 }

调用

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1.从application域对象中得到IOC容器的引用
    ServletContext servletContext = getServletContext();
    ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute("ApplicationContext");
    //2.从IOC容器中得到需要的bean
    Person person= (Person) ctx.getBean("person");
    person.hello();
}

使用Spring封装好的接口来获取IOC

新建applicationContext.xml文件

在web配置文件中一下配置:org.springframework.web.context.ContextLoaderListener为Spring提供的API,已经完成了以上监听器的核心步骤,只需要直接从ServletContext中获取就好

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

获取IOC:使用WebApplicationContextUtils. getWebApplicationContext(application)获取

  //从application中获取IOC实例
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);

//从IOC容器中得到bean
Person person = ctx.getBean(Person.class);

//使用bean
person.hello();

9.Spring AOP切面编程

1.介绍

应用:

可以解决由于非业务需求(日志和验证等 )加入而引起的代码混乱

可以解决修改需求时,需要修改多个模块中相同代码,即解决代码分散的问题

2.实现

使用动态代理:使用一个代理将对象包装起来

业务代码接口-------(实现类)----------à 业务代码(类加载器)

定义一个代理,传入代理的对象(即业务代码) 完成一个返回上述类的方法

//业务代码,即需要代理的类加载器
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    @Override
    public int add(int i, int j) {
        int result = i+j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i-j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i*j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i/j;
        return result;
    }
}
//业务接口
public interface ArithmeticCalculator {
    int add(int i ,int j);
    int sub(int i ,int j);
    int mul(int i ,int j);
    int div(int i ,int j);
}
//代理器
public class ArithmeticCalculatorLoggingProxy {
    //要代理的对象
    private ArithmeticCalculator target;

    public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
        this.target = target;
    }

    public ArithmeticCalculator getLoggingProxy(){
        ArithmeticCalculator proxy = null;
        //代理对象由哪一个类加载器负责加载
        ClassLoader loader = target.getClass().getClassLoader();
        //代理对象的类型,即其中有哪些方法
        Class[] interfaces = new Class[]{ArithmeticCalculator.class};

        //当调用代理对象其中的方法是,该执行的代码
        InvocationHandler h = new InvocationHandler() {
            //o : 正在返回的代理对象,一般情况下,在invoke方法中的不使用该对象
            //method:正在被调用的方法
            //args:调用方法时传入的参数

            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                String methodName = method.getName();
                System.out.println("The method "+methodName+" begins with "+ Arrays.asList(objects));
                int result = (int) method.invoke(target,objects);
                System.out.println("The method "+methodName+" ends with "+result);
                return result;
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces,h);
        return  proxy;
    }
}

调用:

ArithmeticCalculator target = new ArithmeticCalculatorImpl();
ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();  //proxy为代理的加载器

3.AOP的概念描述

  • AOP的主要编程对象是切面,而切面模块化横切关注点,在应用AOP编程时,需要定义公共功能,但是可以明确定义这个功能在哪里,以什么方式应用,而不必修改受影响的类,如此横切关注点就模块化到特殊的对象(切面)里
  • 连接点:程序执行的某个特定位置,是个物理存在,即某个方法执行前,执行后,或抛出异常后等的点。由两个信息确认,一个是方法表示的程序执行点,一个是相对点的方位;即add()方法的连接点:执行点为add(),方位为add()方法执行前(后)的位置(一个类多个)
  • 切点:AOP通过切点定位到特定的连接点,切点通过org.springframework.aop.Pointcut描述

4.基于注解的方式配置AOP

1.AspectJ

AspectJ:最完整最流行的AOP框架

在Spring2.0以上的版本中,可以使用基于AspectJ注解或基于XML配置的AOP

要在SpringIOC容器中启用AspectJ注解支持,只要在Bean配置文件中定义一个空的XML元素aop:aspectj-autoproxy

<!--    使切面中的注解产生作用,调用目标方法时,自动的为方法所在的类生成代理对象,在目标方法执行前执行切面中的方法-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2.定义切面类

带有@Aspect和@Component注解的类

通知有5个:Execution表达式@Before("execution(public int AOPImpl.ArithmeticCalculator.*(int ,int))")

修饰符 类型 包名 类名 方法名 (可以用*号来代替任意修饰符任意类型任意方法任意类,任意参数用..)

  • @Before 前置通知,方法执行前执行

    //将这个类声明为切面:
    //1.将该类放入到IOC容器中
    //2.再声明为一个切面
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        //日志切面
        //声明该方法是个前置通知:在目标方法开始之前执行
        @Before("execution(public int AOPImpl.ArithmeticCalculator.*(int ,int))")
        public void beforeMethod(JoinPoint joinPoint){   //JoinPoint 连接点,可以获取目标方法信息
            String methodName = joinPoint.getSignature().getName();    //获取方法名
            List<Object> args = Arrays.asList(joinPoint.getArgs());     //获取输入参数
            System.out.println("The method"+methodName+ "begins with" + args);
        }
    }
    
  • @After 后置通知,方法执行后执行

    //后置通知,无论是否异常都可以执行,无法访问目标方法执行的结果
    @After("execution(public int AOPImpl.ArithmeticCalculator.*(int,int ))")
    public  void afterMethod(JoinPoint joinPoint){
        String methodName =joinPoint.getSignature().getName();
        System.out.println("The method "+methodName+ " ends.");
    
    }
    
    
  • @AfterReturning 返回通知,在方法返回结果之后执行

    /返回通知可以访问到方法的返回值,出现异常不会返回,定义一个returning
    @AfterReturning(value = "execution(public int AOPImpl.ArithmeticCalculator.*(..))",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method "+methodName+ " ends with "+result);
    }
    
  • @AfterThrowing 异常通知,在方法抛出异常之后执行

    //在目标方法出现异常时,会执行的代码,而且可以访问到一场对象,可以指定在出现特定异常时执行通知代码
    @AfterThrowing(value = "execution(public int AOPImpl.ArithmeticCalculator.*(..))",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Exception ex){  //定义ex的类型可以指定特定异常
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method "+methodName+ " occurs excetion "+ex);
    }
    
  • @Around 环绕通知,围绕着方法执行

    //环绕通知需要携带ProceedingJoinPoint类型的参数
    //环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
    //且环绕通知必须有返回值,返回值即为目标方法的返回值
    @Around("execution(public int AOPImpl.ArithmeticCalculator.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd){
        Object result = null;
        //执行目标方法
        try {
            //前置通知
            System.out.println("前置通知");
            result = pjd.proceed();
            System.out.println("返回通知");
        } catch (Throwable throwable) {
            //异常通知
            System.out.println("异常通知");
            //throw new RuntimeException(throwable); 抛出异常
        }
        //后置通知
        System.out.println("后置通知");
        return result;
    }
    

    3.切面的优先级

    • 切面的优先级(@Order 注解在相同目标方法的切点方法上)

    • 在切面上注解@Order(1) 数字越小优先级越高

    4.重用切面表达式

    //定义一个方法,用于声明切入点表达式,一般的,该方法中再不需要填入其他的代码
    //使用@Pointcut来声明切入点表达式
    //后面的其他通知直接使用方法名来引用当前的切入点表达式
    @Pointcut("execution(public int AOPImpl.ArithmeticCalculator.*(..))")
    public void declareJointPointExprssion(){}
    

    引用

    @Before("declareJointPointExprssion()")
    

5.基于xml配置文件来配置AOP(不需要注解)

<!--    配置bean-->
    <bean id="arithmeticCalculator" class="AOPXML.ArithmeticCalculatorImpl"></bean>

<!--    配置切面的bean-->
    <bean id="loggingAspect " class="AOPXML.LoggingAspect"></bean>

<!--    配置AOP-->
<aop:config>
<!--        配置切点表达式-->
    <aop:pointcut id="pointcut" expression="execution(public int AOPXML.ArithmeticCalculator.*(..))"/>
<!--        配置切面及通知-->
    <aop:aspect ref="loggingAspect " order="1"> <!-- 指定切面方法和优先级--> 
        <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"></aop:after-throwing>
        <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
    </aop:aspect>
</aop:config>

10.Spring对JDBC的支持

1.JDBC Template

  • 配置连接池

    <!--    导入资源文件-->     
    <context:property-placeholder location="db.properties"/> 
    <!--    配置c3p0数据源-->     
    <bean id="dataSourse" class="com.mchange.v2.c3p0.ComboPooledDataSource">         
        <property name="user" value="${jdbc.user}"></property>         
        <property name="password" value="${jdbc.password}"></property>        
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>         
        <property name="driverClass" value="${jdbc.diverClass}"></property>         
        <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>         
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>     
    </bean>
    
  • 配置jdbcTemplate

<!--    配置Spring 的JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSourse"></property>
    </bean>
  • Dao中直接调用jdbcTemplate的方法
@Repository    //将DAO配置到IOC容器中
public class EmployeeDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
  1. update(String sql, @Nullable Object... args)
//测试Update方法:执行INSERT,UPDATE,DELETE
public void testUpdate(){
    String sql = "UPDATE employees SET last_name=? WHERE id =?";
    jdbcTemplate.update(sql,"Jacke",4);
}
  1. batchUpdate(String sql, List<Object[]> batchArgs)
//测试batchUpdate()方法:执行update的批量更新
//batchArgs为一个Object[]的List类型:因为修改一条记录需要一个Object数组
public void testBatcheUpdate(){
    String sql = "INSERT INTO employees(last_name,email,dept_id) VALUES(?,?,?)";
    List<Object[]> batchArgs = new ArrayList<>();

    batchArgs.add(new Object[]{"世文","aa@qq.com",1});
    batchArgs.add(new Object[]{"kaixin","bb@qq.com",3});
    batchArgs.add(new Object[]{"CC","cc@qq.com",2});
    jdbcTemplate.batchUpdate(sql,batchArgs);
}
  1. queryForObject(String sql, RowMapper rowMapper, @Nullable Object... args)
//从数据库中获取一条记录,实际得到一个对应的对象
//不是调用queryForObject(String sql, Class<T> requiredType, Object... args) 方法
//调用queryForObject(String sql, RowMapper<T> rowMapper, Object... args)方法
//1.其中RowMapper指定如何去映射结果集的行,常用的实现类为BeanPropertyRowMapper
//2.使用SQL中列的别名完成列名和类的属性名的映射(一一对应)类似last_name--> lastName
//3.不支持级联属性,JdbcTemplate到底是一个Jdbc的小工具,不是ORM框架
public void testQueryForObject(){
    String sql = "SELECT id,last_name lastName,email FROM employees WHERE id = ?";
    RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
    Employee employee = jdbcTemplate.queryForObject(sql,rowMapper,1);
    System.out.println(employee);

}
  1. query(String sql, RowMapper rowMapper, @Nullable Object... args)
//查询实体类的实体
public  void testQueryForList(){
    String sql = "SELECT id,last_name lastName,email FROM employees WHERE id > ?";
    RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
    List<Employee> employees = jdbcTemplate.query(sql,rowMapper,4);
    System.out.println(employees);
} 

5.queryForObject(String sql, Class requiredType)

//获取单个列的值,或做统计查询
//使用queryForObject(String sql, Class<T> requiredType) 方法
public void testQueryForObject2(){
    String sql  = "SELECT count(id) FROM employees";
    long count = jdbcTemplate.queryForObject(sql,Long.class);
    System.out.println(count);
}

JDBCTemplate类被设计成为线程安全的,所以可以在IOC容器中去声明它的单个实例,并将这个实例注入到所有的DAO实例中

2.NamedParameterJdbcTemplate

在Spring JDBC模板中使用具名参数,可以取代sql语句中的?占位符(当语句中多个参数时可以使代码容易维护),在NamedParameterJdbcTemplate中得到支持

  • Xml中进行配置:
<!--    配置NamedParameterJdbcTemplate,该对象可以使用具名参数,其没有无参构造器,必须制定参数-->
    <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="dataSourse"></constructor-arg>
    </bean>
  • 两种方式 ,一种使用map,一种使用SqlParameterSource
//可以为参数命名,与map中的键名一一对应,在参数多的时候不受顺序影响,便于维护
public void testNamedPatameterJdbcTemplate(){
    String sql = "INSERT INTO employees(last_name,email,dept_id) VALUES(:ln,:email,:deptid)";

    Map<String,Object> paramMap = new HashMap<>();
    paramMap.put("email" ,"jdifj@qq.com");
    paramMap.put("deptid",3);
    paramMap.put("ln" ,2);
    namedParameterJdbcTemplate.update(sql,paramMap);
}
//使用具名参数时,可以使用update(String sql, SqlParameterSource paramSource)方法进行更新操作
//1.语句中的参数名与对象属性名一致
//2.使用SqlParameterSource的BeanPropertySqlParameterSource实现类作为参数
public void testNamedPatameterJdbcTemplate2() {
    String sql = "INSERT INTO employees(last_name,email,dept_id) VALUES(:lastName,:email,:dpid)";

    Employee employee = new Employee();
    employee.setLastName("jdif");
    employee.setEmail("XIE@162.COM");
    employee.setDpid(3);

    SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employee);
    namedParameterJdbcTemplate.update(sql,paramSource);
}

11.Spring中的事务管理

1.事务

用来确保数据的完整性和一致性,他是一系列动作,被当作一个单独的工作单元,要么全部完成,要么全部不起作用,例如交易的时候,库存和用户余额的变化是同步的,要么一起完成,要么都不变

Spring的核心事务管理抽象:事务管理器

2.声明式事务:

配置文件

<!--创建命名空间-->
xmlns:tx="http://www.springframework.org/schema/tx"

配置:

<!--    配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSourse"></constructor-arg>
    </bean>
<!--    启用事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

在启用事务的方法上加事务注解

//添加事务注解
@Transactional
@Override
public void purchase(String username, String isbn) {
    //1.获取书的单价
    int price = bookShopDao.findBookPriceByIsbn(isbn);
    //2.更新书的库存
    bookShopDao.updateBookStock(isbn);
    //3.更新用户余额
    bookShopDao.updateUserAccount(username,price);
}

3.事务的属性

​ 事务的传播行为,事务的隔离级别,事务的回滚,只读,事务的过期时间

1.传播行为

传播行为:一个事务方法被另一个事务方法调用时,必须指定事务应该如何运行在另一个事务中,例如:方法可能继续在现有的事务中运行,也可能开启一个新事务,并在自己的事务中运行

由传播属性指定,Spring定义了7种类传播行为:

  • REQUIRED(默认) 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事物,并在自己的事务中运行 (整体停止,只要事务中的任意事务停止整个停止 exp:不能结账全部不买)
  • REQUIRED_NEW 当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起 (事务中的事务通过时,部分代码正常运行 exp:不足结账,按账单顺序能买的买,不能则停止)
  • SUPPORTS 如果有事务在运行,当前方法则在这个事务内运行,否则它可以不运行在事务中
  • NOT_SUPPORTS 不能运行在事务中,如果有运行的事务,将它挂起
  • MANDATORY 必须运行在事务中,如果没有正在运行的事务,抛出异常
  • NEVER 不能运行在事务中,如果有运行的事务,抛出异常
  • NESTED 如果有事务在运行,当前方法就应该在这个事务的嵌套事务中运行,否则,就启动一个事务,在自己的事务中运行

添加传播行为:

//propagation指定事物的传播行为,即当前事务方法被另外一个事务方法调用时
@Transactional(propagation = Propagation.REQUIRED)

2.隔离级别

最常用的isolation = Isolation.READ_COMMITTED

3.回滚(出现异常回滚使操作停止,一般不设置)

默认情况下Spring的声明式事务对所有的运行时异常进行回滚,也可以通过对应属性设置

noRollbackFor 设置那个异常不回滚

noRollbackFor = {UserAccountException.class}

4.只读

readOnly 指定是否只读,表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务,只读取数据库值的方法需要设置true

5.过期时间

​ timeout 指定强制回滚之前事务可以占用的时间,当方法运行时间超过事务设置的过期时间,将会强制回滚,以s为单位

timeout = 3    //过期时间三秒

4.使用xml来配置事务

  • 先正常配置好bean
<!--    配置bean-->
    <bean id="bookShopDao" class="txxml.BookShopDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <bean id="bookShopService2" class="txxml.serviceImpl.BookShopServiceImpl">
        <property name="bookShopDao" ref="bookShopDao"></property>
    </bean>

    <bean id="cashier" class="txxml.serviceImpl.CashierImpl">
        <property name="bookShopService" ref="bookShopService2"></property>
    </bean>
  • 配置管理器

    <!--    配置事务管理器-->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <constructor-arg ref="dataSourse"></constructor-arg>
        </bean>
    
  • 配置事务属性

<!--    配置事务属性-->
    <tx:advice id="tdAdvice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
<!--            根据方法名指定事务的属性-->
            <tx:method name="purchase" propagation="REQUIRES_NEW"/>
            <tx:method name="get*" read-only="true"></tx:method>
            <tx:method name="find*" read-only="true"></tx:method>
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>

<!--    配置事务切入点,以及吧事务切入点和事务属性关联起来-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* txxml.service.*.*(..))"/>
        <aop:advisor advice-ref="tdAdvice" pointcut-ref="txPointCut"></aop:advisor>
    </aop:config>
posted @ 2020-07-31 10:22  J,IAT  阅读(71)  评论(0编辑  收藏  举报