Spring教程

Spring

1.介绍

官网:https://spring.io/

Spring 让每个人都可以更快、更轻松、更安全地编写 Java。Spring 对速度、简单性和生产力的关注使其成为世界上最受欢迎的Java 框架。

我们使用了 Spring 框架附带的许多工具,并获得了许多开箱即用的解决方案所带来的好处,并且不必担心编写大量额外的代码——这确实为我们节省了一些时间和能量。

2.特征

  • 核心技术:依赖注入、事件、资源、i18n、验证、数据绑定、类型转换、SpEL、AOP。
  • 测试:模拟对象、TestContext 框架、Spring MVC 测试、WebTestClient.
  • 数据访问:事务、DAO 支持、JDBC、ORM、编组 XML。
  • Spring MVCSpring WebFlux Web 框架。
  • 集成:远程处理、JMS、JCA、JMX、电子邮件、任务、调度、缓存。
  • 语言:Kotlin、Groovy、动态语言。

3.核心技术

IOC:控制反转,将对象的创建权交给了Spring去管理

DI:依赖注入,把数据给创建好的对象中的属性进行赋值

AOP:面向切面编程,底层是代理模式

4.Bean的创建

org.springframework.beansorg.springframework.context包是 Spring Framework 的 IoC 容器的基础。该 BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。 ApplicationContextBeanFactory的子接口。它补充说:

  • 更容易与 Spring 的 AOP 功能集成
  • 消息资源处理(用于国际化)
  • 活动发布
  • 应用层特定上下文,例如WebApplicationContext 用于 Web 应用程序的上下文。

org.springframework.context.ApplicationContext接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。

4.1无参构造创建

导入依赖

<!-- spring核心依赖 -->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

创建User实体类

package com.qf.pojo;

/**
 * User实体类
 */
public class User {

    private Integer id;
    private String name;
    private String password;

    public User() {
        System.out.println("无参构造");
    }
}

编写applicationContext.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
    该id属性是标识单个 bean 定义的字符串。(不能重复)
    该class属性定义 bean 的类型并使用完全限定的类名。
    -->
    <bean id="user" class="com.qf.pojo.User">
    </bean>

</beans>

编写测试类

package com.qf.test;

import com.qf.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试类
 */
public class SpringTest {

    @Test
    public void testUser() {
        //需要加载配置文件
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //根据bean标签中的id属性值,获取bean对象
        User user = (User)applicationContext.getBean("user");

        System.out.println(user);

    }
}
4.2工厂创建

创建工厂类

package com.qf.factory;

import com.qf.pojo.User;

/**
 * 通过实例方法获取User对象
 */
public class UserFactory {

    /**
     * 获取 User 对象
     * @return
     */
    public User getUser() {
        //return new User();

        //反射 + 配置文件
        try {
            return  (User)Class.forName("com.qf.pojo.User").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        //return null;
        //手动抛异常
        throw new RuntimeException("创建User对象异常");
    }


    /**
     * 通过静态方法获取 User 对象
     * @return
     */
    public static User getUserStatic() {
        //return new User();

        //反射 + 配置文件
        try {
            return  (User)Class.forName("com.qf.pojo.User").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        //return null;
        //手动抛异常
        throw new RuntimeException("创建User对象异常");
    }
}

配置applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
    该id属性是标识单个 bean 定义的字符串。(不能重复)
    该class属性定义 bean 的类型并使用完全限定的类名。
    -->
<!--    <bean id = "user" class = "com.qf.pojo.User">-->
<!--    </bean>-->

    <!-- 工厂中的实例方法创建Bean -->
<!--    <bean id = "userFactoryBean" class = "com.qf.factory.UserFactory"></bean>-->
<!--    <bean id = "user" factory-bean="userFactoryBean" factory-method="getUser"></bean>-->

    <!-- 工厂中的静态方法创建Bean -->
    <bean id="user" class="com.qf.factory.UserFactory" factory-method="getUserStatic"></bean>

</beans>
4.3简单工厂模式

Car

package com.qf.factorymode.simplefactory;

/**
 * Car 接口
 */
public interface Car {

    /**
     * 提供 run 方法
     */
    public void run();
}
package com.qf.factorymode.simplefactory;

/**
 * Car 实现类
 */
public class BaoSJ  implements Car {
    @Override
    public void run() {
        System.out.println("保时捷在飞驰...");
    }
}
package com.qf.factorymode.simplefactory;

/**
 * Car 的实现类
 */
public class FaLL implements Car {
    @Override
    public void run() {
        System.out.println("法拉利在飞驰...");
    }
}
package com.qf.factorymode.simplefactory;

/**
 * 简单工厂模式:代码集中,不符合 OCP 原则( open - close ):对代码的扩展是开放的,对代码的修改是关闭的
 * 车工厂
 */
public class CarFactory {

    //提供创建保时捷的方法
    public static BaoSJ creatBaoSJ() {
        try {
            return (BaoSJ)Class.forName("com.qf.factorymode.simplefactory.BaoSJ").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //提供创建法拉利的方法
    public static FaLL creatFaLL() {
        try {
            return (FaLL)Class.forName("com.qf.factorymode.simplefactory.FaLL").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试

package com.qf.factorymode.simplefactory;

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {

        //创建对象,调用方法
        //BaoSJ baoSJ = new BaoSJ();
        BaoSJ baoSJ = CarFactory.creatBaoSJ();
        baoSJ.run();

        //创建对象,调用方法
        //FaLL faLL = new FaLL();
        FaLL faLL = CarFactory.creatFaLL();
        faLL.run();

    }
}
4.4抽象工厂模式

Car

package com.qf.factorymode.abstractfactory;

/**
 * Car 接口
 */
public interface Car {

    /**
     * 提供 run 方法
     */
    public void run();
}
package com.qf.factorymode.abstractfactory;

/**
 * Car 实现类
 */
public class BaoSJ  implements Car {
    @Override
    public void run() {
        System.out.println("保时捷在飞驰...");
    }
}
package com.qf.factorymode.abstractfactory;

/**
 * Car 的实现类
 */
public class FaLL implements Car {
    @Override
    public void run() {
        System.out.println("法拉利在飞驰...");
    }
}

CarFactory

package com.qf.factorymode.abstractfactory;

/**
 * 抽象工厂模式:符合开闭原则
 * 车工厂
 */
public interface CarFactory {

    /**
     * 创建车的方法
     * @return
     */
    public Car createCar();

}
package com.qf.factorymode.abstractfactory;

/**
 * 保时捷的车工厂
 */
public class BaoSJFactory implements CarFactory{
    /**
     * 创建保时捷车
     * @return
     */
    @Override
    public Car createCar() {
        try {
            return (BaoSJ)Class.forName("com.qf.factorymode.abstractfactory.BaoSJ").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.qf.factorymode.abstractfactory;

/**
 * 法拉利车工厂
 */
public class FaLLFactory implements CarFactory{
    @Override
    public Car createCar() {
        try {
            return (FaLL)Class.forName("com.qf.factorymode.abstractfactory.FaLL").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试

package com.qf.factorymode.abstractfactory;

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {

        //创建对象,调用方法
        BaoSJFactory baoSJFactory = new BaoSJFactory();
        Car baoSJ = baoSJFactory.createCar();
        baoSJ.run();

        //创建对象,调用方法
        FaLLFactory faLLFactory = new FaLLFactory();
        Car faLL = faLLFactory.createCar();
        faLL.run();

    }
}

5.Bean的作用范围

5.1scope属性

bean标签的scope属性:指定当前bean的作用范围

取值:

singleton:单例( 默认值 )

prototype:多例

request:作用于Web应用的请求范围

session:作用于Web应用的会话范围

global-session:作用于集群环境Web应用的会话范围(全局会话)

<!--
    该id属性是标识单个 bean 定义的字符串。(不能重复)
    该class属性定义 bean 的类型并使用完全限定的类名。
    scope:常用的是单例(singleton)和多例(prototype)
-->
<bean id = "user" class = "com.qf.pojo.User" scope="prototype">
</bean>
5.2单例模式-懒汉式
package com.qf.singletonmode;

/**
 * 单例模式:懒汉式(在第一次调用的时候实例化自己)
 * 优势:第一次调用才会初始化,避免内存消耗
 * 劣势:必须加锁才能保证单例,加锁会影响效率
 */
public class SingletonLazy {

    //构造器私有化
    private SingletonLazy() {}

    //声明对象
    private static SingletonLazy singletonLazy = null;

    //实例化,线程安全
    public static synchronized SingletonLazy getSingletonLazy() {
        //判断
        if(null == singletonLazy) {
            singletonLazy = new SingletonLazy();
        }

        return singletonLazy;
    }

}
5.3单例模式-饿汉式
package com.qf.singletonmode;

/**
 * 饿汉式:比较常用,容易产生垃圾( GC回收 )
 * 优势:没有加锁,效率会提高
 * 劣势:类加载时就进行初始化,消耗内存
 */
public class SingletonHungry {

    //私有化构造器
    private SingletonHungry() {}

    //实例化
    private static SingletonHungry singletonHungry = new SingletonHungry();

    //方法
    public static SingletonHungry getSingletonHungry() {
        return singletonHungry;
    }
}
5.4单例模式-双重校验锁
package com.qf.singletonmode;

/**
 * 双重校验锁
 * 优势:安全,在多线程情况下保证较高的性能
 */
public class SingletonLock {

    //构造器私有化
    private SingletonLock() {}

    //声明
    private static SingletonLock singletonLock = null;

    //实例化方法
    public static SingletonLock getSingletonLock() {
        //先检查当前实例是否为空,如果不存在再进行同步
        if(null == singletonLock){
            //同步
            synchronized (SingletonLock.class) {
                //再次检查当前实例是否为空
                if(null == singletonLock){
                    //返回
                    singletonLock = new SingletonLock();
                }
            }
        }
        return singletonLock;
    }
}

6.Bean生命周期

<!--
    该id属性是标识单个 bean 定义的字符串。(不能重复)
    该class属性定义 bean 的类型并使用完全限定的类名。
    scope:常用的是单例(singleton)和多例(prototype)
    init-method:创建对象后执行的初始化方法
    destroy-method:对象销毁后执行(如果是多例模式下不执行)
    -->
<bean id = "user" class = "com.qf.pojo.User"
      scope="singleton" init-method="initUser" destroy-method="destroyUser">
</bean>
package com.qf.pojo;

/**
 * User实体类
 */
public class User {

    private Integer id;
    private String name;
    private String password;

    public User() {
        System.out.println("无参构造");
    }

    /**
     * 初始化方法,创建对象后执行
     */
    public void initUser() {
        System.out.println("User 初始化方法");
    }

    /**
     * 销毁方法,销毁spring容器中对象后执行
     */
    public void destroyUser() {
        System.out.println("User 销毁方法");
    }

}

测试

/**
 * 测试生命周期相关方法
 */
@Test
public void testLife() {
    //创建ClassPathXmlApplicationContext对象
    ClassPathXmlApplicationContext applicationContext
        = new ClassPathXmlApplicationContext("applicationContext.xml");

    //获取对象
    User user = (User) applicationContext.getBean("user");

    System.out.println(user);

    //关闭
    applicationContext.close();
}

7.依赖注入

DI:Dependency Injection:给创建对象中的属性赋值

IOC作用:降低程序间耦合(依赖关系)

依赖关系维护:以后都交给Spring进行管理

可注入类型:

基本数据类型以及包装类

String类

类类型( 其他Bean类型 )

复杂类型:集合,数组…

7.1set方法注入

applicationContext.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 初始化一个Car对象 -->
    <bean id="firstCar" class="com.qf.pojo.Car">
        <property name="cid" value="20001"></property>
        <property name="cname" value="保时捷"></property>
    </bean>
    <!--
    该id属性是标识单个 bean 定义的字符串。(不能重复)
    该class属性定义 bean 的类型并使用完全限定的类名。
    -->
    <bean id = "user" class = "com.qf.pojo.User">
        <!-- set方法赋值
        property表示当前对象的属性
        name:属性名
        value:给当前属性赋值
        ref:用于注入其他Bean对象(在spring容器中已经创建了)
        -->
        <property name="id" value="1001"></property>
        <property name="name" value="张三"></property>
        <property name="password" value="123"></property>
        <property name="car" ref="firstCar"></property>
    </bean>

</beans>

实体类

package com.qf.pojo;

import lombok.Data;

@Data
public class Car {

    private Integer cid;
    private String cname;

}
package com.qf.pojo;

import lombok.Data;

@Data
public class User {

    private Integer id;
    private String name;
    private String password;

    private Car car;
}

测试

package com.qf.test;

import com.qf.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试类
 */
public class SpringTest {

    @Test
    public void testUser(){

        //加载配置文件
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //创建Bean
        User user =(User)applicationContext.getBean("user");

        //输出
        System.out.println(user);
    }
}
7.2复杂类型注入

1.注入数组对应的标签:array

2.注入LIst以及Set集合的标签:list,set

3.注入Map以及Propertis的标签:map,properties

创建实体类CollectionVo

package com.qf.vo;

import lombok.Data;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * 数组以及集合类型的属性注入
 */
@Data
public class CollectionVo {

    private Integer [] arr;
    private List list;
    private Set set;
    private Map map;
    private Properties properties;

}

在applicationContext.xml文件中添加内容

<!-- 复杂类型注入 -->
<bean id="collectionVo" class="com.qf.vo.CollectionVo">
    <!-- 数组 -->
    <property name="arr">
        <array>
            <value>123</value>
            <value>456</value>
            <value>789</value>
        </array>
    </property>

    <!-- List -->
    <property name="list">
        <list>
            <value>jack</value>
            <value>jack</value>
            <value>rose</value>
            <ref bean="user"></ref>
        </list>
    </property>

    <!-- Set -->
    <property name="set">
        <set>
            <value>张三</value>
            <value>张三</value>
            <value>李四</value>
            <ref bean="user"></ref>
        </set>
    </property>

    <!-- Map -->
    <property name="map">
        <map>
            <entry key="1001" value="张三"></entry>
            <entry key-ref="user" value-ref="firstCar"></entry>
        </map>
    </property>

    <!-- Properties -->
    <property name="properties">
        <props>
            <prop key="username">root</prop>
            <prop key="password">root</prop>
            <prop key="url">jdbc:mysql:///db_name?serverTimezone=Asia/Shanghai</prop>
            <prop key="driverClassName">com.mysql.cj.jdbc.Driver</prop>
        </props>
    </property>

</bean>

测试

/**
 * 测试复杂类型注入
 */
@Test
public void testCollectionVo() {

    //加载配置文件
    ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");

    //创建Bean
    CollectionVo collectionVo = (CollectionVo) applicationContext.getBean("collectionVo");

    //输出
    System.out.println(collectionVo);
}
7.3构造器注入( 不常用 )

创建Car

package com.qf.pojo;

import lombok.Data;

@Data
public class Car {

    private Integer cid;
    private String cname;

    public Car(Integer cid, String cname) {
        this.cid = cid;
        this.cname = cname;
    }
}

创建User

package com.qf.pojo;

import lombok.Data;

@Data
public class User {

    private Integer id;
    private String name;
    private String password;

    private Car car;

    public User(Integer id, String name, String password, Car car) {
        this.id = id;
        this.name = name;
        this.password = password;
        this.car = car;

        System.out.println("第一个构造器");
    }

    /**
     * 把 id 和 name 交换了一下顺序
     * @param name
     * @param id
     * @param password
     * @param car
     */
    public User(String name, Integer id,String password, Car car) {
        this.id = id;
        this.name = name;
        this.password = password;
        this.car = car;

        System.out.println("第二个构造器");
    }
}

配置applicationContext.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 初始化一个Car对象,要和类中的构造器器匹配 -->
    <!--
     id和name用法相同
     -->
    <bean name="firstCar" class="com.qf.pojo.Car">
        <!-- 构造器注入
        name:获取构造器中指定参数的名称
        value:给构造器中的参数赋值(基本数据类型以及String类型)
        ref:给其他bean类型赋值
        type:指定注入的类型
        index:指定参数的位置,默认从0开始
        -->
        <constructor-arg name="cid" value="20001" type="java.lang.Integer" index="0"></constructor-arg>
        <constructor-arg name="cname" value="保时捷" type="java.lang.String" index="1"></constructor-arg>
    </bean>

    <!-- 初始化一个User对象,通过改变 index 来设置通过使用第一个构造器创建对象 -->
    <bean id = "user1" class = "com.qf.pojo.User">
        <constructor-arg name="id" value="1001" index="0"></constructor-arg>
        <constructor-arg name="name" value="张三" index="1"></constructor-arg>
        <constructor-arg name="password" value="123" index="2"></constructor-arg>
        <constructor-arg name="car" ref="firstCar" index="3"></constructor-arg>
    </bean>

    <!-- 初始化一个User对象,通过改变 index 来设置通过使用第二个构造器创建对象 -->
    <bean id = "user2" class = "com.qf.pojo.User">
        <constructor-arg name="id" value="1002" index="1"></constructor-arg>
        <constructor-arg name="name" value="李四" index="0"></constructor-arg>
        <constructor-arg name="password" value="456" index="2"></constructor-arg>
        <constructor-arg name="car" ref="firstCar" index="3"></constructor-arg>
    </bean>

</beans>

测试

package com.qf.test;

import com.qf.pojo.Car;
import com.qf.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

    @Test
    public void testCar() {

        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        Car car = (Car) applicationContext.getBean("firstCar");

        System.out.println(car);
    }

    @Test
    public void testUser() {

        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        User user1 = (User) applicationContext.getBean("user1");
        System.out.println(user1);

        User user2 = (User) applicationContext.getBean("user2");
        System.out.println(user2);
    }
}
7.4注解注入

注意:需要在applicationContext.xml文件中,导入context约束

Car

package com.qf.pojo;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * @Component 将对象放到 spring 容器中,相当于:<bean id = "" class = "" />
 * 如果一个注解的属性默认值是 value,在只使用 value 属性的时候可以省略不写
 * value属性:用于指定 bean 的 id,如果不写,默认值就是当前类名,首字母小写
 * 以下三个注解用法和 @Component 一样,为了区别不同层
 * @Controller:一般用于表现层 ( Web层 )
 * @Service:一般用于业务逻辑层 ( Service层 )
 * @Repository:一般用于持久层 ( Dao层 )
 *
 * @Value 给属性赋值,赋值类型为基本数据类型以及String,可以在属性以及方法上使用
 */
//@Component("myCar")
@Component
@Data
public class Car {

    @Value("20001")
    private Integer cid;

    //@Value("保时捷")
    private String cname;

    @Value("保时捷")
    public void setCname(String cname) {
        this.cname = cname;
    }
}

User

package com.qf.pojo;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
 * @Scope 表示当前对象默认单例( singleton ),可以设置为多例( prototype )
 * 相当于 <bean scope = "" />
 */
@Component
@Scope(value = "singleton")
@Data
public class User {

    @Value("1001")
    private Integer id;

    @Value("张三")
    private String name;

    @Value("123")
    private String password;

    /**
     * @Autowired 表示自动装配,如果 spring容器中有该类型的对象,则自动注入到当前属性中
     * @Qualifier 和 @Autowired 一起用,指定要注入具体对象的名称,value 属性:指定注入 bean 的 id
     * 如果只有一个对象,只使用 @Autowired,如果有多个同类型的对象,名称不能相同,使用 @Qualifier 选择具体的 Bean
     *
     *  @Resource 是javaEE中的注解,name 属性:通过名称指定注入的 bean,相当于 @Autowired + @Qualifier
     *  @Resource 不常用,如果不生效,则需要导入
     */
    @Autowired
    @Qualifier("otherCar")
    //@Resource(name = "otherCar")
    private Car car;


    /**
     *  @PostConstruct 相当于 <bean init-method = "" /> 表示初始化的方法( 构造器之后执行 )
     */
    @PostConstruct
    public void init(){
        System.out.println("User 初始化");
    }

    /**
     *  @PreDestroy 相当于 <bean destroy-method = "" /> 表示销毁的方法 ( 对象销毁之前执行 )
     */
    @PreDestroy
    public void destroy(){
        System.out.println("User 销毁");
    }
}

applicationContext.xml配置文件

<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

        <!-- 扫描对应包下的注解 -->
        <context:component-scan base-package="com.qf"></context:component-scan>

        <!-- 初始化一个 Car 对象 -->
        <bean name="otherCar" class="com.qf.pojo.Car">
                <property name="cid" value="30001"></property>
                <property name="cname" value="法拉利"></property>
        </bean>

</beans>

bean.xml

<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

        <!-- 导入其他的xml文件-->
        <import resource="applicationContext.xml"></import>

</beans>

测试

package com.qf.test;

import com.qf.pojo.Car;
import com.qf.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

    /**
     * 测试初始化 Car 对象相关注解的方法
     */
    @Test
    public void testCar() {

        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        Car car = (Car) applicationContext.getBean("car");

        System.out.println(car);
    }

    /**
     * 测试初始化 User 对象相关注解的方法
     */
    @Test
    public void testUser() {

        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        User user = (User) applicationContext.getBean("user");

        System.out.println(user);
    }

    /**
     * 测试作用范围关注解的方法
     */
    @Test
    public void testScope() {

        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        User user1 = (User) applicationContext.getBean("user");
        User user2 = (User) applicationContext.getBean("user");

        System.out.println(user1 == user2);
    }

    /**
     * 测试生命周期相关注解的方法
     */
    @Test
    public void testLife() {

        //通过其他配置文件测试
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("bean.xml");

        User user = (User) applicationContext.getBean("user");
        System.out.println(user);

        //关闭
        applicationContext.close();
    }

}

8.整合MyBatis 【重点】

8.1导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qf</groupId>
    <artifactId>spring-05</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.9</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.20</version>
        </dependency>
        <!-- 连接数据库 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <!-- mybatis 整合 spring 所需依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>

        <!-- 测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>


    </dependencies>

</project>
8.2User类
package com.qf.pojo;

import lombok.Data;

@Data
public class User {

    private Integer id;
    private String name;
    private String password;

}
8.3UserController
package com.qf.controller;


import com.qf.pojo.User;
import com.qf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.util.List;

/**
 * web层 -> @Controller
 */
@Controller
public class UserController {

    /**
     * 注入 UserService
     */
    @Autowired
    private UserService userService;

    /**
     * 查询所有用户
     * @return
     */
    public List<User> findAll(){
        return userService.findAll();
    }

}
8.4UserService
package com.qf.service;

import com.qf.pojo.User;

import java.util.List;

public interface UserService {
    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();
}
8.5UserServiceImpl
package com.qf.service.impl;

import com.qf.mapper.UserMapper;
import com.qf.pojo.User;
import com.qf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * service层 -> @Service
 */
@Service
public class UserServiceImpl implements UserService {

    /**
     * 注入 UserMapper
     */
    @Autowired
    private UserMapper userMapper;

    /**
     * 查询所有用户
     * @return
     */
    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }
}
8.6UserMapper
package com.qf.mapper;

import com.qf.pojo.User;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * dao层 -> @Repository
 */
@Repository
public interface UserMapper {

    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();
}
8.7UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.qf.mapper.UserMapper">
   
    <!-- orm映射 -->
    <resultMap id="userMap" type="com.qf.pojo.User">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="password" column="password"></result>
    </resultMap>
    
    <!-- sql片段 -->
    <sql id="baseSql">
        select id, name, password from t_user
    </sql>
    
    <!-- 查询所有用户 -->
    <select id="findAll" resultMap="userMap">
        <include refid="baseSql"></include>
    </select>
    
</mapper>
8.8db.properties
db.username = root
db.password = root
db.url = jdbc:mysql://localhost:3306/java2203?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.driver = com.mysql.cj.jdbc.Driver
8.9log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
8.10mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 配置日志 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

</configuration>
8.11applicationContext.xml
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

        <!-- 导入外部配置文件 db.properties -->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>

        <!-- 配置数据源对象 -->
        <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
            <!-- 导入 db.properties 中的值-->
            <property name="username" value="${db.username}"></property>
            <property name="password" value="${db.password}"></property>
            <property name="url" value="${db.url}"></property>
            <property name="driverClassName" value="${db.driver}"></property>
        </bean>

        <!-- 扫描对应包下的注解 -->
        <context:component-scan base-package="com.qf"></context:component-scan>

        <!-- 配置sqlSessionFactory -->
        <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 必选配置 -->
            <property name="dataSource" ref="datasource"></property>
            <!-- 非必选属性,根据自己需求去配置 -->
            <!-- 导入 mybatis-config.xml -->
            <property name="configLocation" value="classpath:mybatis-config.xml"></property>
            <!-- 导入 Mapper.xml 文件,classpath后面不能有空格 -->
            <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
        </bean>

        <!-- 扫描 Mapper 接口,生成代理对象 -->
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!-- 指定扫描的具体位置 -->
            <property name="basePackage" value="com.qf.mapper"></property>
        </bean>

</beans>
8.12测试
package com.qf.test;

import com.qf.controller.UserController;
import com.qf.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class SpringTest {

    /**
     * 测试查询所有用户
     */
    @Test
    public void test_findAll(){

        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        UserController userController = (UserController)applicationContext.getBean("userController");

        List<User> userList = userController.findAll();

        System.out.println(userList);
    }
}

9.数据源配置类

用于替换applicationContext.xml中数据源的相关配置

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

package com.qf.config;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.qf.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * @Configuration 表示服务器启动时加载当前类
 * @PropertySource 加载对应的配置文件
 */
@Configuration
@PropertySource(value = "classpath:db.properties")
public class DataSourceConfig {

    @Value("${db.username}")
    private String username;

    @Value("${db.password}")
    private String password;

    @Value("${db.url}")
    private String url;

    @Value("${db.driver}")
    private String driverClassName;

    /**
     * 初始化一个 Bean 对象 -> DataSource
     *
     * @Bean 将方法的返回值作为 bean 对象,放到 spring 容器中
     */
    @Bean("datasource")
    public DataSource getDataSource() {
        //设置数据源参数
        Properties properties = new Properties();
        properties.setProperty("username", username);
        properties.setProperty("password", password);
        properties.setProperty("url", url);
        properties.setProperty("driverClassName", driverClassName);

        DataSource dataSource = null;

        try {
            //创建数据源对象
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //返回
        return dataSource;
    }

}

10.分页

10.1导入依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.0</version>
</dependency>
10.2第一种方式在mybatis-config.xml中配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 配置日志 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <!-- 分页插件 -->
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 数据库方言,指定对应的数据库进行分页 -->
            <property name="helperDialect" value="mysql"/>
            <!-- 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页 -->
            <property name="reasonable" value="true"/>
            <!-- 支持通过 Mapper 接口参数来传递分页参数 -->
            <property name="supportMethodsArguments" value="true"/>
        </plugin>
    </plugins>

</configuration>
10.3第二种方式在applicationContext.xml中配置
 <!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 必选配置 -->
    <property name="dataSource" ref="datasource"></property>
    <!-- 非必选属性,根据自己需求去配置 -->
    <!-- 导入 mybatis-config.xml -->
    <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    <!-- 导入 Mapper.xml 文件,classpath后面不能有空格 -->
    <property name="mapperLocations" value="classpath:mapper/*.xml"></property>

    <!-- 配置分页 -->
    <property name="plugins">
        <array>
            <bean class="com.github.pagehelper.PageInterceptor">
                <property name="properties">
                    <!--使用下面的方式配置参数,一行配置一个 -->
                    <value>
                        helperDialect = mysql
                        reasonable = true
                        supportMethodsArguments = true
                    </value>
                </property>
            </bean>
        </array>
    </property>

</bean>
10.4 测试
/**
 * 测试分页查询
 */
@Test
public void test_findByPage(){

    ClassPathXmlApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");

    UserController userController = (UserController)applicationContext.getBean("userController");

    PageInfo pageInfo = userController.findByPage(4, 2);

    System.out.println(pageInfo.getList());
}

9.AOP

Aspect Oriented Programing 面向切面编程

springAOP:在程序运行期通过动态代理的方式向目标类(接口),织入增强的代码,为目标类(接口)中的方法添加额外的功能

采取是横向抽取机制,取代了我们传统的纵向继承方式重复性的代码

底层原理:代理模式

9.1装饰器模式

装饰器模式:对象本身增强

代理模式:代理对象(代理过程)增强

9.1.1Info
package com.qf.decorator;

/**
 * 抽象构件
 * 抽象类:可以有抽象方法,也可以有普通方法,但是一旦写了抽象方法,这个类一定为抽象类
 */
public abstract class Info {

    /**
     * 自我介绍
     */
    public abstract void info();

}
9.1.2PersonInfo
package com.qf.decorator;

/**
 * 具体构件
 * 继承Info,实现方法
 */
public class PersonInfo extends Info{
    @Override
    public void info() {
        System.out.println("自我介绍");
    }
}
9.1.3Decorator
package com.qf.decorator;

/**
 * 装饰器
 * 抽象装饰
 * 继承Info,实现方法,可以通过其子类扩展具体构件的功能
 */
public abstract class Decorator extends Info{

    private Info info;

    //传入被装饰对象
    public Decorator(Info info){
        this.info = info;
    }

    @Override
    public void info() {
        //调用自我介绍的方法
        info.info();
    }
}
9.1.4Singer
package com.qf.decorator;

/**
 * 具体装饰
 * 继承抽象装饰
 */
public class Singer extends Decorator{

    //调用父类构造器
    public Singer(Info info) {
        super(info);
    }

    //自己的方法
    public void singing(){
        System.out.println("唱歌");
    }

    //重写方法,达到增强的目的
    @Override
    public void info() {
        super.info();
        singing();
    }
}
9.1.5Dancer
package com.qf.decorator;

/**
 * 具体装饰
 * 继承抽象装饰
 */
public class Dancer extends Decorator{

    public Dancer(Info info) {
        super(info);
    }

    public void dancing(){
        System.out.println("跳舞");
    }

    @Override
    public void info() {
        super.info();
        dancing();
    }
}
9.1.6Magic
package com.qf.decorator;

/**
 * 具体装饰
 * 继承抽象装饰
 */
public class Magic extends Decorator{

    public Magic(Info info) {
        super(info);
    }

    public void magic(){
        System.out.println("变魔术");
    }

    @Override
    public void info() {
        super.info();
        magic();
    }
}
9.1.7测试
package com.qf.decorator;

public class Test {
    public static void main(String[] args) {

        //单独测试
//        Info personInfo = new PersonInfo();
//        personInfo.info();
//        System.out.println("----------------------");
//
//        Decorator singer = new Singer(personInfo);
//        singer.info();
//        System.out.println("----------------------");
//
//        Dancer dancer = new Dancer(personInfo);
//        dancer.info();
//        System.out.println("----------------------");


        //增强测试
        Info personInfo = new PersonInfo();
        Decorator singer = new Singer(personInfo);//第一次增强
        Dancer dancer = new Dancer(singer);//第二次增强
        Magic magic1 = new Magic(dancer);//第三次增强
        Magic magic2 = new Magic(magic1);//第四次增强

        magic2.info();
    }
}
9.2代理模式

通过代理类对象,为目标类对象添加功能

分类:静态代理和动态代理

静态代理:需要实现接口中的方法,进行增强,代理的功能代码有冗余,维护性较差

动态代理:在不实现接口中所有方法,对接口中的指定的方法进行增强

9.2.1静态代理

Rent

package com.qf.proxy.demo1;

/**
 * 接口
 * 出租房子
 */
public interface Rent {

    //出租房子
    public void rent();

    //其他的方法
    //public void test();
}

Owner

package com.qf.proxy.demo1;

/**
 * 房东
 */
public class Owner implements Rent{
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

OwnerProxy

package com.qf.proxy.demo1;

/**
 * 房东的代理对象
 * 静态代理
 */
public class OwnerProxy implements Rent{

    //房东对象
    private Owner owner;

    public OwnerProxy(Owner owner) {
        this.owner = owner;
    }

    public void publish(){
        System.out.println("发布租房信息");
    }

    public void seeHouse(){
        System.out.println("带租户看房子");
    }

    @Override
    public void rent() {
        publish();
        owner.rent();
        seeHouse();
    }
}

测试

package com.qf.proxy.demo1;

public class Test {
    public static void main(String[] args) {

        Owner owner = new Owner();
        owner.rent();

        System.out.println("----------------");

        OwnerProxy ownerProxy = new OwnerProxy(owner);
        ownerProxy.rent();

    }
}
9.2.2动态代理

分为:JDK动态代理 和 CGLIB动态代理

JDK动态代理:基于接口的动态代理,被代理对象必须实现接口

CGLIB动态代理 :基于子类的动态代理,对目标对象进行继承代理

JDK动态代理

Rent

package com.qf.proxy.demo2;

/**
 * 接口
 * 出租房子
 */
public interface Rent {

    //出租房子
    public void rent();

    //其他的方法
    public void test();
}

Owner

package com.qf.proxy.demo2;

/**
 * 房东
 */
public class Owner implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }

    @Override
    public void test() {
        System.out.println("测试方法");
    }
}

RentJdkProxy

package com.qf.proxy.demo2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理
 * 核心实现 InvocationHandler 接口,调用 invoke 方法
 */
public class RentJdkProxy implements InvocationHandler {

    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理对象
    public Rent getRent(){
        return (Rent)Proxy.newProxyInstance(
                rent.getClass().getClassLoader(), //类加载器
                rent.getClass().getInterfaces(),//接口列表
                this
        );
    }

    //对接口中的方法进行增强(扩展),不需要实现接口中的所有方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //声明变量做为方法的返回值
        Object result = null;

        //判断方法名,对其进行增强
        if("rent".equals(method.getName())){
            publish();
            result = method.invoke(rent,args);
            seeHouse();
        } else {
            result = method.invoke(rent,args);
        }

        return result;
    }


    public void publish(){
        System.out.println("发布租房信息");
    }

    public void seeHouse(){
        System.out.println("带租户看房子");
    }
}

测试

package com.qf.proxy.demo2;

public class Test {
    public static void main(String[] args) {

        Owner owner = new Owner();
        owner.rent();

        System.out.println("-------------------");

        //创建RentJdkProxy对象
        RentJdkProxy rentJdkProxy = new RentJdkProxy();
        //给属性赋值
        rentJdkProxy.setRent(owner);

        //获取代理对象
        Rent proxyRent = rentJdkProxy.getRent();
        proxyRent.rent();

        proxyRent.test();

    }
}
9.3Spring中的AOP

面向切面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP),OOP的延伸

对目标对象中的多个不同方法进行不同程度的增强

AOP的术语:
Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法,spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):可以在运行期为类动态地添加一些方法或Field。
Target(目标对象):代理的目标对象。
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
Proxy(代理):一个类被AOP织入增强后,就产生一个代理类。
Aspect(切面):是切入点和通知(引介)的结合

9.4XML配置AOP
9.4.1导入依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>

    <!-- aop -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.9.1</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>

</dependencies>
9.4.2UserService
package com.qf.service;

/**
 * 目标对象 target
 */
public interface UserService {

    //没有增强的方法,叫做连接点 JoinPoint
    //被增强的方法,叫做切入点 PointCut
    //增强的代码,叫做通知
    public void add();

    public void delete();

    public void update();

    public void query();
}
9.4.3UserServiceImpl
package com.qf.service.impl;

import com.qf.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("add");
        int i = 1/0;//算数异常
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void query() {
        System.out.println("query");
    }
}
9.4.4MyAdvice
package com.qf.advice;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 通知类:增强的代码
 * 方法名以及功能可以是任意的
 */
public class MyAdvice {

    public void before(){
        System.out.println("前置通知,目标对象调用方法前执行");
    }

    public void after(){
        System.out.println("后置通知(最终通知),目标对象调用方法后执行,无论目标对象方法是否发生异常都会执行");
    }

    public void after_returning(){
        System.out.println("后置通知,目标对象调用方法后执行,目标对象方法发生异常则不执行");
    }

    public void after_throwing(){
        System.out.println("异常通知,目标对象调用方法发生异常时执行");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知,目标对象调用方法之前");
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知,目标对象调用方法之后");
    }

}
9.4.5applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->


        <!-- 配置userService对象,对该对象中的方法进行增强 -->
        <bean id="userService" class="com.qf.service.impl.UserServiceImpl"></bean>

        <!-- 配置通知 -->
        <bean id="myAdvice" class="com.qf.advice.MyAdvice"></bean>


        <!-- 配置aop -->
        <!-- 默认使用JDK动态代理,通过 proxy-target-class="true" 可以设置使用CGLIB动态代理 -->
        <aop:config proxy-target-class="true">
            <!-- 配置切点
             expression:表达式,指定哪些方法是切入点,对那些进行增强
             -->
<!--            <aop:pointcut id="pc" expression="execution(public void com.qf.service.impl.UserServiceImpl.add())"/>-->
            <aop:pointcut id="pc" expression="execution(* com.qf.service.impl.*ServiceImpl.*(..))"/>

            <!-- 配置切面,把通知配置到切点上 -->
            <aop:aspect ref="myAdvice">
                <!-- aop:before 前置通知,method 表示增强的代码的方法名 -->
                <aop:before method="before" pointcut-ref="pc"></aop:before>

                <!-- 后置通知(最终通知),目标对象调用方法后执行,无论目标对象方法是否发生异常都会执行 -->
                <aop:after method="after" pointcut-ref="pc"></aop:after>

                <!-- 后置通知,目标对象调用方法后执行,目标对象方法发生异常则不执行 -->
                <aop:after-returning method="after_returning" pointcut-ref="pc"></aop:after-returning>

                <!-- 异常通知,目标对象调用方法发生异常时执行 -->
                <aop:after-throwing method="after_throwing" pointcut-ref="pc"></aop:after-throwing>

                <!-- 环绕通知,目标方法调用之前和之后都会执行 -->
                <aop:around method="around" pointcut-ref="pc"></aop:around>

            </aop:aspect>

        </aop:config>

</beans>
9.4.6测试
package com.qf.test;

import com.qf.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

    @Test
    public void testAop(){

        ClassPathXmlApplicationContext applicationContext
                = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserService userService = (UserService)applicationContext.getBean("userService");

        userService.add();

        //userService.delete();
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值