Spring6教程

发布时间 2023-12-13 11:09:56作者: 西芹-小汤圆

入门

使用Maven引入Spring6基础依赖环境:

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.2</version>
        </dependency>
</dependencies>

Bean.xml文件完成对象创建,<bean id="user" class="com.atguigu.spring6.User"></bean>,id是唯一标识,class是对象所在类的全路径。

在项目中调用该对象的步骤:

//加载Spring配置文件,创建对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//获取创建的对象
User user = (User) context.getBean("user");
//使用对象调用方法进行测试
user.add();

创建过程中调用了类的无参数构造方法,根据bean.xml文件的class属性利用反射创建对象,创建好的对象则放在一个Map中。

Log4j2

Apache Logj2是一个开源的日志记录组件,使用非常的广泛。在工程中以易用方便代替了System.out等打印语句,它是JAVA下最流行的日志输入工具。

Log4j2的组成:

  1. 日志信息的优先级,日志信息的优先级从高到低有TRACE<DEBUG<INFO<WARN<ERROR<FATAL。这些级别分别用来指定这条日志信息的重要程度;级别高的会自动屏蔽级别低的日志。
  2. 日志信息的输出目的地。
  3. 日志信息的输出格式。

引入Log4j2依赖:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.19.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>2.19.0</version>
</dependency>

引入依赖文件后,需要在类的根路径(resources文件内)下提供log4j2.xml文件,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <loggers>
        <!--选择日志的级别-->
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="log"/>
        </root>
    </loggers>

    <appenders>
        <!--输出日志信息到控制台-->
        <console name="spring6log" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="log" fileName="D:/java/尚硅谷2023最新版spring6课程/log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>

        <!-- 这个会打印出所有的信息,
            每次大小超过size,
            则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
            作为存档-->
        <RollingFile name="RollingFile" fileName="D:/java/尚硅谷2023最新版spring6课程/log/app.log"
                     filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <SizeBasedTriggeringPolicy size="50MB"/>
            <!-- DefaultRolloverStrategy属性如不设置,
            则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </appenders>
</configuration>

写入日志:

//创建对象,记得导入的是org.slf4j.*
private Logger logger = LoggerFactory.getLogger(TestUser.class);
//写入日志
logger.info("执行调用成功。。");

容器:IoC

loC是Inversion of Control的简写,译为"控制反转",它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。

Spring通过loc容器来管理所有Java对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由IoC容器管理的Java对象称为Spring Bean,它与使用关键字new创建的Java对象没有任何区别T

Spring提供了IoC容器的两种实现方式:1、BeanFactory这是IoC容器的基本实现,是Spring内部使用的接口。面向Spring本身,不提供给开发人员使用。2、ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:对象instanceof指定的类型的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。因此当bean唯一时,可以根据接口来获取实现类。

遇到Maven导入包失败的时候可以检查一下仓库的路径设置,因为设置文件是改过的,因此使用原来的设置可能会出现导包不成功的情况。

遇到java: 程序包com.sun.tools.javac不存在的问题可以到JDK8中导入tools.jar文件,详细教程

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的 bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

自动装箱方式有byTypebyName两种,可以通过Bean标签中的autowire属性设置。

基于xml方式管理Bean

获取Bean的方式

  1. 根据ID获取Bean。User user = (User) context.getBean("user");
  2. 根据类型获取Bean。User user2 = (User) context.getBean(User.class);。当根据类型获取Bean时,要求IoC容器内指定的Bean有且只有一个,否则会出现错误。
  3. 根据ID和类型获取Bean。User user3 = (User) context.getBean("user",User.class);

依赖注入方式

DI(Dependency Injection):依赖注入,依赖注入实现了控制反转的思想。依赖注入:指Springt创建对象的过程中,将对象依赖属性通过配置进行注入。

  1. 通过setter注入:
<bean id="book" class="com.atguigu.spring6.iocxml.di.Book">
        <property name="bname" value="前端开发"></property>
        <property name="author" value="尚硅谷"></property>
</bean>
  1. 通过构造器注入:
<bean id="bookCon" class="com.atguigu.spring6.iocxml.di.Book">
        <constructor-arg name="bname" value="java"></constructor-arg>
        <constructor-arg name="author" value="尚硅谷"></constructor-arg>
</bean>
  1. P命名空间注入:
<!--首先声明一个P命名空间-->
xmlns:p="http://www.springframework.org/schema/p"
<!--    P命名空间注入-->
<bean id="studentp" class="com.atguigu.spring6.iocxml.dimap.Student"
p:sid="100" p:sname="mary" p:lessonList-ref="lessonList" p:teacherMap-ref="teacherMap">

特殊值处理

  1. 空值,加上null标签。<property name="other"><null/></property>
  2. xml实体,即<>符号,需要使用&lt和&gt进行转义。
  3. CDATA区,需要写特殊字符的时候可以使用。<property name="other"><value><![CDATA[a<b]]></value></property>

特殊类型注入

  1. 对象类型属性注入
<!--引入外部bean,使用ref指向外部bean的ID值-->
<bean id="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
    <property name="dname" value="保安部"></property>
</bean>
<bean id="emp" class="com.atguigu.spring6.iocxml.ditest.Emp">
    <property name="dept" ref="dept"></property>
</bean>

<!--内部bean注入,在bean中属性再建一个bean-->
<property name="dept">
    <bean id="dept2" class="com.atguigu.spring6.iocxml.ditest.Dept">
        <property name="dname" value="财务部"></property>
    </bean>
</property>

<!--级联注入,通过ref属性获取到bean后修改值-->
<bean id="dept3" class="com.atguigu.spring6.iocxml.ditest.Dept">
    <property name="dname" value="技术部"></property>
</bean>
<bean id="emp3" class="com.atguigu.spring6.iocxml.ditest.Emp">
    <property name="dept" ref="dept3"></property>
    <property name="dept.dname" value="测试部"></property>
</bean>
  1. 数组类型属性注入
<property name="loves">
    <array>
        <value>吃饭</value>
        <value>睡觉</value>
        <value>敲代码</value>
    </array>
</property>
  1. 集合类型属性注入
<bean id="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
    <property name="empList">
            <list>
              <!--因为数组里面的值是对象,因此不适用value属性,而是使用ref-->
                <ref bean="empone"></ref>
                <ref bean="emptwo"></ref>
            </list>
    </property>
</bean>
  1. Map类型属性注入
<property name="teacherMap">
    <map>
        <entry>
            <key><value>100</value></key>
            <ref bean="teacher"></ref>
        </entry>
    </map>
</property>
  1. 引用集合类型的bean,需要在上面添加命名空间。
<property name="lessonList" ref="lessonList"></property>
<!--在外边写好,然后使用ref引用-->
<util:list id="lessonList">
    <ref bean="lessonone"></ref>
    <ref bean="lessontwo"></ref>
</util:list>
  1. 引用外部属性注入,首先引入数据库相关依赖,然后创建外部属性文件,properties格式,最后创建Spring配置文件,引入context命名空间,引入属性文件,使用表达式完成注入。
<!--添加context命名空间-->
<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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--引用外部属性文件-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="driverClassName" value="${jdbc.driver}"></property>
</bean>

作用域

取值 含义 创建对象的时机
singleton 在IoC容器中,这个bean的对象始终为单实例 IoC容器初始化时
prototype bean在IoC容器中有多个实例 获取bean时
request 在一个请求范围内有效
session 在一个会话范围内有效

生命周期

初始化方法和销毁方法都可以在bean标签的属性中设置。

  1. bean对象创建(调用无参构造器)。
  2. 给bean对象设置属性
  3. bean的后置处理器(初始化之前),由一个实现BeanPostProcessor接口的类执行,后置处理器对整个IoC容器生效。配置文件:<bean id="myBeanProcessor" class="com.atguigu.spring6.iocxml.life.MyBeanPost"></bean>
  4. bean对象初始化(需在配置bean时指定初始化方法)
  5. bean的后置处理器(初始化之后)
  6. bean对象就绪可以使用
  7. bean对象销毁(需在配置bean时指定销毁方法),当使用close()方法时进行销毁。
  8. IoC容器关闭

基于注解管理Bean(重点)

从Java5开始,Java增加了对注解(Annotation)的支持,它是代码中的一种特殊标记。可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。Spring从2.5版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化Spring的XML配置。 Spring通过注解实现自动装配的步骤如下:1.引入依赖 2.开启组件扫描 3.使用注解定义Bean 4.依赖注入。

组件扫描:Spring默认不使用注解装配Bean,因此我们需要在Spring的XML配置中,通过context:component--scan元素开启Spring Beans的自动扫描功能。开启此功能后,Spring会自动从扫描指定的包(base-package属性设置)及其子包下的所有类,如果类上使用了@Component注解,就将该类装配到容器中。

注解

注解 说明
@Component 该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应类上即可。
@Repository 该注解用于将数据访问层(Dao层)的类标识为Spring中的Bean,其功能与@Component相同。
@Service 该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
@Controller 该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为 Spring中的Bean,其功能与@Component相同。

@Autowired注入

  1. 属性注入,在需要注入的属性上写@Autowired注解。
  2. set方法注入。
  3. 构造方法注入。
  4. 形参上注入,把@Autowired写到方法对应的形参上。
  5. 只有一个构造方法时,可以不使用注解。
  6. @Autowired注解和@Qualifier注解联合,因为@Autowired默认是使用byType注解的,加上@Qualifier注解则是byName。

@Resource注入

@Resource注入是JDK扩展包,默认使用byName方式装配,@Autowired是Spring框架定义的且默使用byType方式装配。

@Resource注解用在属性上、setter方法上。

@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖。如果是JDK8的话不需要额外引入依赖。高于DK11或低于JDK8需要引入以下依赖。(这个Maven导入不成功,暂时没解决这个问题)

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

全注解开发

//创建一个配置类,然后指定扫描的目录
@Configuration //配置类
@ComponentScan("com.atguigu.spring6")
public class SpringConfig {
}
//在程序中选择配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

原理-手写IoC

获取Class对象三种方式:1、类名.class;2、对象.getClass();3、Class.forName("类的全路径")。

获取私有方法时需要使用declare方法,而且获取后需要设置允许访问私有属性。

package com.atguigu.bean;

import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNameAware;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * ClassName: AnnotationApplicationContext
 * Package: com.atguigu.bean
 * Description:
 *
 * @Author: xiqin
 * @Create: 2023 /4/27 - 15:39
 * @Version: v1.0
 */
public class AnnotationApplicationContext implements ApplicationContext{

    private Map<Class,Object> beanFactory = new HashMap<>();
    private static String rootPath;

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    /**
     * 创建有参数构造,传递包路径,设置包扫描规则.
     *
     * @param basePackage the base package
     */
    public AnnotationApplicationContext(String basePackage){
        try {
            //1.把.替换成\
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //2.获取包绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                //获取到的路径需要解码
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面的路径部分,截取前面的不变部分作为常量
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());

                System.out.println(filePath);
                //包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //属性注入
        loadDi();
    }



    private void loadBean(File file) throws Exception {
        //1.判断当前是否是文件夹
        if(file.isDirectory()){
            //2.获取文件夹里面的所有内容
            File[] childrenFiles = file.listFiles();

            //3。判断文件夹是否为空,不为空则遍历里面的内容
            if(childrenFiles == null || childrenFiles.length == 0){
                return;
            }

            for(File child : childrenFiles){
                //如果仍是文件夹则进行递归调用
                if(child.isDirectory()){
                    loadBean(child);
                } else {
                    //得到包路径+类名称部分,把前面常量部分截取出来
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //判断当前文件夹是否为.class文件
                    if(pathWithClass.contains(".class")){
                        String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class","");
                        Class<?> clazz = Class.forName(allName);
                        if(!clazz.isInterface()){
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null){
                                Object instance = clazz.getConstructor().newInstance();
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                } else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void loadDi() {
        //实例化对象已经在beanFactory的Map集合中
        //1.遍历Map集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for(Map.Entry<Class, Object> entry : entries){
            //2.获取Map集合中的每个对象,获取到对象属性
            Object obj = entry.getValue();
            Class<?> clazz = obj.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            //3.遍历得到每个对象属性数组
            for(Field field : declaredFields){
                //4.判断属性是否有@Di注解,有则进行注入
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    field.setAccessible(true);
                    try {
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }

        }
    }
}

面向切面:AOP

代理模式要解决问题是要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。将核心代码和附加代码分离,难点在于两类代码相互交错。

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。使用@Order()注解可以控制切面的优先级:@Order(较小的数):优先级高;@Order(较大的数):优先级低。

代理模式

它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来一一解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

截图

动态代理

package com.atguigu.spring6.aop.example;

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

/**
 * ClassName: ProxyFactory
 * Package: com.atguigu.spring6.aop.example
 * Description:动态代理实现
 *
 * @Author: xiqin
 * @Create: 2023/4/27 - 19:01
 * @Version: v1.0
 */
public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 返回代理对象
     *
     * @return the object
     */
    public Object getProxy(){
        //动态生成代理类的类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //目标对象实现的所有接口的Class类型数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //设置代理对象实现目标对象方法的过程
        InvocationHandler invocationHandler = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("调用前输出");

                //调用目标方法
                Object result = method.invoke(target, args);

                System.out.println("方法调用后输出");

                return result;
            }
        };

        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

//使用时调用即可
public class TestCal {
  public static void main(String[] args) {
    //CalculatorImpl()是目标代码,需要传入
    ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
    Calculator proxy = (Calculator)proxyFactory.getProxy();
    proxy.add(1,2);
  }
}

相关术语

  1. 横切关注点:分散在每个各个模块中解决同样的问题,如用户验证、日志管理、事务处理、数据缓存都属于横切关注点。从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

  2. 通知(增强):想要增强的功能,比如安全,事务,日志等。每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。五种通知类型:前置@Before();返回@AfterReturing();异常@AfterThrowing();后置@After();环绕@Around()。括号中间填入切入点表达式配置切入点,表达式可以通过@Pointcut()进行重用。

    截图

  3. 切面:封装通知方法的类。

  4. 目标:被代理的,目标对象。

  5. 代理:向目标对象应用通知之后创建的代理对象。

  6. 连接点:Spring中允许你使用通知的地方。

  7. 切入点:Spring的AOP技术可以通过切入点定位到特定的连接点。通俗说,要实际去增强的方法。

动态代理分类

  1. JDK代理:有接口,使用JDK动态代理,生成接口实现类代理对象,代理对象和目标对象实现同样的接口。
  2. cglib代理:没有接口,使用cglib动态代理,生成子类代理对象,通过继承被代理的目标类。

AspectJ:是AOP思想的一种实现。本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了Aspect中的注解。

package com.atguigu.spring6.aop.annoaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * ClassName: LogAspect
 * Package: com.atguigu.spring6.aop.annoaop
 * Description:
 *
 * @Author: xiqin
 * @Create: 2023 /5/5 - 17:04
 * @Version: v1.0
 */
@Aspect
@Component
public class LogAspect {

    //设置切入点和通知类型
    //前置方法
    @Before(value = "execution(public int com.atguigu.spring6.aop.annoaop.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("logger-->前置通知,方法名称"+methodName+"参数:"+ Arrays.toString(args));
    }

    @After(value = "pointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("logger-->后置通知,方法名称"+methodName+"参数:"+ Arrays.toString(args));

    }

    @AfterReturning(value = "execution(* com.atguigu.spring6.aop.annoaop.CalculatorImpl.*(..))",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("logger-->返回通知,方法名称"+methodName+"目标方法返回值:"+ result);
    }

    @AfterThrowing(value = "execution(* com.atguigu.spring6.aop.annoaop.CalculatorImpl.*(..))",throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("logger-->异常通知,方法名称"+methodName+"异常信息"+ex);

    }

    @Around(value = "execution(* com.atguigu.spring6.aop.annoaop.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String argString = Arrays.toString(args);
        Object result = null;
        try{
            System.out.println("环绕通知==目标方法之前执行");
            //调用目标方法
            result = joinPoint.proceed();
            System.out.println("环绕通知==目标方法返回值之后");
        }catch (Throwable throwable){
            throwable.printStackTrace();
            System.out.println("环绕通知==目标方法出现异常执行");
        } finally {
            System.out.println("环绕通知==目标方法执行完毕");
        }
        return result;

    }

    //重用切入点表达式
    @Pointcut(value = "execution(* com.atguigu.spring6.aop.annoaop.CalculatorImpl.*(..))")
    public void pointCut(){}
}

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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.atguigu.spring6.aop.annoaop"></context:component-scan>

    <!--开启aspectj自动代理,为目标对象生成代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.atguigu.spring6.aop.xmlaop"></context:component-scan>


<!--    配置aop五种通知类型-->
    <aop:config>
<!--        配置切面类-->
        <aop:aspect ref="logAspect">
<!--            配置切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.atguigu.spring6.aop.xmlaop.CalculatorImpl.*(..))"/>
<!--            配置具体方法-->
            <aop:before method="afterMethod" pointcut-ref="pointcut"></aop:before>
            <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
            <aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="pointcut"></aop:after-returning>
            <aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointcut"></aop:after-throwing>
            <aop:around method="aroundMethod" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

单元测试:JUint

@SpringJUnitConfig(locations = "classpath:bean.xml")
public class SpringTestJunit5{
    //注入
    @Autowired
    private User user;

    //测试方法
    @Test
    public void testUser(){
        System.out.println(user);
        user.run();
    }
}
//Junit4的注解修改为下面两个即可,另外引入的Test类路径中没有api
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")

事务

查询语句:Emp empResult = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(Emp.class),1);

<!--事务注解开启-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

@Transactional()注解可以设置事务,可以设置回滚策略,隔离级别,传播行为。

事务的传播行为:在service类中有a方法和b方法,a方法上有事务,b方法上也有事务,当a方法执行过程中调用了b方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。

配置类的编写

@Configuration //配置类
@ComponentScan("com.atguigu.spring6.tx")
@EnableTransactionManagement //开启事务管理
public class SpringConfig {
    @Bean
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("159123zxc");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        return dataSource;
    }

    @Bean(name="jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

}

Spring Resourcers

Spring的Resource声明了访问low-level资源的能力。

ClassPathResource用来访问类加载路径下的资源,相对于其他的Resource实现类,其主要优势是方便访问类加载路径里的资源,尤其对于Web应用,ClassPathResource可自动搜索位于classes下的资源文件,无须使用绝对路径访问。

Spring提供如下两个标志性接口:1.ResourceLoader:该接口实现类的实例可以获得一个Resource实例。2.ResourceLoaderAware:该接口实现类的实例将获得一个ResourceLoader的引用,这样就可以使用依赖注入。