zoukankan      html  css  js  c++  java
  • 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>
    
  • 相关阅读:
    scp上传服务器文件
    svn一次添加所有未添加的文件
    vue 去掉#和拼接参数
    vuex状态管理
    获取页面iframe里的元素
    angular 中ng-bind-html 、$scope服务
    心态崩了
    day 8
    day 7
    day6 angularjs学习
  • 原文地址:https://www.cnblogs.com/JIATCODE/p/13408209.html
Copyright © 2011-2022 走看看