Spring总结(IOC、AOP原理以及Spring事务)

1|0一、概述

1、Spring是一个开源免费且轻量级的框架 , 非侵入式的 .

2、控制反转 IoC , 面向切面 Aop

3 、对事物的支持 , 对框架的支持

一句话概括:

Spring 是一个轻量级的控制反转(IOC),面向切面(AOP)的框架

2|0二、Spring下载

Sprig官网:Spring.io 去到官网即可下载

GA:稳定通用版本、SNAPSHOT : 快照版本

Spring历史版本下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5wlF38o-1642581545693)(Spring笔记.assets/image-20220116181734040.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EwSGeX8B-1642581545694)(Spring笔记.assets/image-20220116182005242.png)]

Spring5 Runtime 结构示意图

、

Core 就是我们的Spring的核心 ,如下就是我们如果使用Spring必须导入的Jar包 ,commons-logging必须但不属于Spring;

、

3|0三、IOC 控制反转

3|11、IOC底层原理

IOC就是通过 工厂模式 + 反射 + XML解析 实现

<!--第一步:配置XML文件,配置创建对象--> <bean id = dao class = "com.sqx.UserDao"></bean>
//第二步就是,通过解析配置文件,创建工厂对象,进而创建对象! class UserDaoFactory{ public static UserDao getDao(){ String CalssVlaue = class属性值 // 1、进行xml解析 Calss clazz = Class.forName(CalssVlaue) ; // 2、通过反射创建对象 return (UserDao)clazz.newInstance() ; } }

我们通常所说的IOC容器本质上就是一个对象工厂,而我们的Spring提供IOC容器实现的两种方式(也就是两个接口,如下)

1、BeanFactory : IOC容器的基本实现,是Spring内部使用的接口,不提供给开发人员使用 ;

  • 加载配置文件的时候,不会创建对象,而是当我们的使用的时候才回去创建对象!

2、ApplicationContext : BeanFactory的子接口提供更多更强大的功能,一般由开发人员进行使用 ; 【推荐】

  • 加载配置文件的时候,会把配置文件中的对象进行创建!

ApplicationContext接口的实现类

查看ApplicationContext接口的继承结构

区别:

  • ClassPathXmlApplicaitonContext : 加载的是我们的src路径下的资源文件
  • FileSyustemXmlApplicationContext: 加载的就是我们系统盘也就是电脑的绝对路径(全路径):如:D:// …

总结

我们通过Spring提供的两种接口 BeanFactory 和 ApplicationContext ,以及其具体的实现类通过读取我们的xml文件创建我们的对象工厂,也就是IOC容器!接下来我们的IOC容器通过反射的方式创建出对象!

3|22、IOC创建对象的方式

IOC有两种创建对象的方式: 方式一:无参构造 + set注入属性值 ,方式二:有参构造

1|01、无参构造(默认)

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.kuang.pojo.User"> <!--可以理解为 User user = new User--> <property name="name" value="kuangshen"/> </bean> <bean id="ServiceImpl" class="com.sqx.service.impl.UserServiceImpl"> <!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写--> <!--引用另外一个bean , 不是用value 而是用 ref--> <property name="userDao" ref="OracleImpl"/> </bean> </beans>

1|02、有参构造

<!-- 第一种根据index参数下标设置 --> <bean id="userT" class="com.sqx.pojo.UserT"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="0" value="songqixiang"/> </bean> <!-- 第二种根据参数名字设置 --> <bean id="userT" class="com.sqx.pojo.UserT"> <!-- name指参数名 --> <constructor-arg name="name" value="songqixiang"/> </bean> <!-- 第三种根据参数类型设置 --> <bean id="userT" class="com.sqx.pojo.UserT"> <constructor-arg type="java.lang.String" value="songqixiang"/> </bean>

1|03、DI依赖注入

DI : 依赖注入(Dependency Injection)也称属性注入 ;

方式一 :使用set方法进行注入 : 我们使用无参构造创建对象,这个类就会通过set方法注入属性,完成对象的创建!

方式二 :使用有参构造进行注入:我们使用有参构造创建的对象对通过构造方法赋值 !

扩展 :p命名和c命名注入

1、P命名空间注入 : 需要在头文件中加入约束文件

导入约束 : xmlns:p="http://www.springframework.org/schema/p" <!--P(属性: properties)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.sqx.pojo.User" p:name="狂神" p:age="18"/>

2、c 命名空间注入 : 需要在头文件中加入约束文件

导入约束 : xmlns:c="http://www.springframework.org/schema/c" <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.sqx.pojo.User" c:name="狂神" c:age="18"/>

其实p命名注入本质上就是我们的set注入,c命名注入本质上则是我们的构造器注入

1|03.1、注入空值和特殊符号

向我们的对象属性中注入空值

<bean id="user" class="com.sqx.pojo.User"> <property name="name" value="songqixiang"/> <property name="address" > <null/> </property> <!--User对象的adderss属性注入空值--> </bean>

属性值中注入特殊符号

<bean id="user" class="com.sqx.pojo.User"> <property name="name" value="songqixiang"/> <property name="address" value="<<南京>>"/> <!--User对象的adderss属性注入特殊值,<>是特殊符号,导致报错--> <!--解决办法1:&lt;&gt;转义--> <property name="address" value="&lt;&gt;<<南京>>"/> <!--解决方法2:将特殊内容写在CDATA中去--> <property name="address"> <value> <![CDATA[<<南京>>]]> </value> </property> </bean>

3|33、IOC中Bean的类型

IOC创建的Bean分为两种:FactoryBean(工厂Bean) 和 Bean(普通Bean)

FactoryBean 和 Bean的区别:

  • 普通Bean:就是在配置文件中定义的什么类型,经过IOC容器创建后返回的就是什么类型

    <bean id="user" class="com.sqx.pojo.User"> <property name="name" value="songqixiang"/> <!--普通Bean的创建--> </bean>
  • 工厂Bean:则是IOC创建的类型可以不是xml中定义的类型

    <!--FactoryBean的创建 : 1、让我门的MyBean实现FactoryBean接口,2、并且重写接口中方法--> <bean id="myBean" class="com.sqx.factoryBean.MyBean"> </bean>

    myBean实现FactoryBean

    public class MyBean implements FactoryBean<User> { /*** * 定义返回Bean的类型 * @return * @throws Exception */ public User getObject() throws Exception { //返回类型为一个User,标识这个工厂类只生产User User user = new User(); user.setName("sqx") ; return user; } public Class<?> getObjectType() { return null; } public boolean isSingleton() { //返回值类型 return false; } }

    通过IOC获取这个MyBean,进行测试 :

    @Test public void testFactoryBean(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User myBean = (User) context.getBean("myBean"); System.out.println(myBean); // com.sqx.pojo.User@64f6106c 发现MyBean获取的是一个User类 }

3|44、Bean的作用域

在Spring里面,设置创建Bean实例是单实例(默认)还是多实例

通过标签中scop属性设置我们Bean的作用域,如下:

区别:

  • singleton:单例模式,表示Spring的IOC容器当中只能存在一个Bean实例,默认的。
  • prototype:多实例模式,表示每次从IOC容器当中取一个Bean实例的时候,都是一个新的Bean 。
  • request:每次创建对象,都放在我们的Request域当中(很少用)。
  • session:每次创建对象,都放在我们的session域当中(很少用)。

扩展

关于singletonprototype还存在一个区别就是:

当我们的scop=singleton的时候我们的对象是会在ApplicaitionContex加载xml文件的时候创建的;

当我们的scop=prototype的时候我们的对象不是在加载xml的时候创建的,而是在调用getBean方法的时候创建的!

3|55、Bean的生命周期

也就是我们Bean对象从创建到销毁的过程

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和其他bean引用(调用set方法)
  3. 调用bean的初始化方法(需要进行配置初始化方法)
  4. bean可以使用了(对象获取到了)
  5. 当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)

1|0演示bean的生命周期

1、编写一个Orders类,为其设置set方法,

public class Orders { private String oname ; public Orders() { System.out.println("第一步:执行无参构造,构建bean的实例"); } public void setOname(String oname) { this.oname = oname; System.out.println("第二步:执行set方法进行属性注入"); } //创建一个执行的初始化方法 public void initMethod(){ System.out.println("第三步:执行我们bean的初始化方法"); } //创建一个bean销毁时执行的方法(只有当对象销毁的时候才会调用) public void destroyMethod(){ System.out.println("第五步:执行我们bean的销毁方法"); } }

2、编写xml文件

<bean id="orders" class="com.sqx.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean>

3、编写测试方法,对生命周期进行测试

@Test public void testBean(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans1.xml") ; Orders orders = (Orders)context.getBean("orders"); System.out.println("第四步:获取我们的创建的bean实例"); System.out.println(orders); //让我们bean实例销毁(IOC容器关闭,其中的实例销毁) ((ClassPathXmlApplicationContext) context).close(); }

4、查看测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZaHOsYoZ-1642581545696)(Spring笔记.assets/image-20220117180210511.png)]

上述的五步就是我们Bean的生命周期,但是是不考虑后置处理器的情况下是五步,如果考虑的话是如下七步

考虑后置处理器

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和其他bean引用(调用set方法)
  3. 将我们bean实例传入我们的后置处理器方法postProcessBeforeInitialization()
  4. 调用bean的初始化方法(需要进行配置初始化方法)
  5. 将我们bean实例传入我们的后置处理器方法postProcessAfterInitialization()
  6. bean可以使用了(对象获取到了)当容器关闭的时候,
  7. 调用bean的销毁方法(需要进行配置销毁的方法)

让我们来创建后置处理器来实现我们的3、5步骤

我们只需创建一个后置处理器,然后将处理器注入到我们的IOC容器当中即可,我们的Spring会默认的将其加在IOC容器中的所有bean!

1、创建后置处理器:创建一个类实现BeanPostProcessor接口,并重写两个方法

public class MyBeanPost implements BeanPostProcessor { /** * bean初始化之前执行的方法!(第三步) * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("bean初始化之前执行的方法!"); return bean; } /*** * bean初始化之后执行的方法!(第五步骤) * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("bean初始化之后执行的方法!"); return bean; } }

2、注入到我们的IOC容器

<!--配置后置处理器--> <bean id="myBeanPost" class="com.sqx.bean.MyBeanPost"></bean>

测试添加后置处理器的执行结果

在这里插入图片描述

可以看到我们的Bean的生命周期就变为七步了!

3|66、自动装配

我们上边说的是如何将对象注入到IOC容器当中,自动装配简单来说就是自动的从IOC容器中去取对象!

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IOC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。

推荐不使用xml配置开启自动装配, 而是使用注解,无论是xml还是注解开启自动装配,都需要先了解一下自动装配的类型:byName 和 byType 这两类,自动装配的类型就是我们从IOC容器中按照什么找到我们所需的bean

1|06.1、自动装配的类型

自动装配的类型分为两种:byName 和 byType 这两类

  • byName:需要保证所有bean的id是唯一的,并且这个bean需要和自动注入的属性的set方法的名字一致
  • byType:需要保证所有bean的class是唯一的,并且这个bean需要和自动注入的属性的类型一致

知道了自动装配类型的区别,那么就可以学习开启自动装配的两种方式了,如下 :

1|06.2、自动装配的方式

开启自动装配的方式有,xml配置 和 注解 两种方式!

前提:需要自动装配的属性必须是引用数据类型!

1|01、XML配置开启自动装配

<bean id="user" class="com.kuang.pojo.User" autowire="byName"> <!--通过byName的方式--> <property name="str" value="qinjiang"/> </bean> <bean id="user" class="com.kuang.pojo.User" autowire="byType"> <!--通过byType的方式--> <property name="str" value="qinjiang"/> </bean> <!-- 注意 : 我们通过xml,开启自动装配的意思: 比如我们呢为User类开启自动装配,装配类型为过byName的方式,会将User类中的所有引用数据类型实现byNmae自动装配, 而非引用数据类型,还需要我们手动的赋值! -->

1|02、注解开启自动装配

注解开启有如下常用注解,将如下主注解放在需要自动装配的属性上即可!

  • @Autowired : 默认是按照类型(ByType)自动装配,如果出现相同类型,可以通过搭配**@Qualifire**指定bean的名称,实现ByName自动装配,@Autowired是Spring的注解!

    public class TestServiceImpl { // 下面两种@Autowired只要使用一种即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) { // 用于属性的方法上 this.userDao = userDao; } } // 实现按照名字自动装配 public class TestServiceImpl { @Autowired @Qualifier("userDao") private UserDao userDao; }
  • @Resource:默认是按照名字(ByName)自动装配的,@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性的值解析为bean的名字,而type属性的值则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。而@Resource是我们Java的原生注解!

    public class TestServiceImpl { // 下面两种@Resource只要使用一种即可 @Resource(name="userDao") private UserDao userDao; // 用于字段上 @Resource(name="userDao") public void setUserDao(UserDao userDao) { // 用于属性的setter方法上 this.userDao = userDao; } }

自动装配就这么简单!

扩展一下

@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。

//如果允许对象为null,设置required = false,默认为true @Autowired(required = false) private Cat cat;

3|77、注解实现DI和对象的创建

前面我们一直通过在xml标签的形式创建对象,接下来我们介绍一下注解的方式创建对象!

1|01、注解实现对象的创建

Spring提供如下四种注解来实现对象的创建

  • @Component
  • @Controller
  • @Service
  • @Repository

以上四个注解的作用是一样的,都可以用来创建bean,这样分只是为了更好的进行分层。

使用注解的具体步骤:

1、引入AOP的依赖(我们如果使用Maven导入webmvc会自动导入aop)

在这里插入图片描述

2、开启组件扫描(注解扫描,为有注解的类创建对象)

2.1、引入context名称空间:

<?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"> <!--指定注解扫描包--> <context:component-scan base-package="com.kuang.pojo"/> </beans>

2.2、配置扫描哪些包下的注解

<!--指定注解扫描包--> <context:component-scan base-package="com.sqx.pojo"/>

3、在指定包下编写类,增加注解

//@Component(value="user") value属性完全可以省略不写! // 相当于配置文件中 <bean id="user" class="当前注解的类"/> @Component //默认创建对象的名称就是类型的首字母小写! 例如 class:UserServcice ---> id:userService public class User { public String name = "sqx"; }

4、测试

@Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); User user = (User) applicationContext.getBean("user"); System.out.println(user.name); }

5、开启组件扫描的细节

这一块内容知道就好,通过两个例子演示

  • 样例一:
<!--开启组件扫描,默认扫描com.sqx包下的所含的所有注解,创建对象!--> <context:component-scan base-package="com.sqx" ></context:component-scan> <!--同样是开启组件扫描,而不是扫描com.sqx包下的所有注解,而是只扫描Controller注解,然后创建对象--> <context:component-scan base-package="com.sqx" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
  • 样例二:
<!--同样是开启组件扫描,而不是扫描com.sqx包下的所有注解,而是扫描com.sqx包下的全部注解除了Controller注解,然后创建对象--> <context:component-scan base-package="com.sqx" use-default-filters="false"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>

1|02、注解实现DI(属性注入)

引用数据类型的属性,通过@Atowired、@Quilifire、@Resource 实现,而基本数据类如何实现DI呢?

@Value :注入普通类型属性

  • 一个样例即可学会:
@Component("user") // 相当于配置文件中 <bean id="user" class="当前注解的类"/> public class User { @Value("宋淇祥") // 相当于配置文件中 <property name="name" value="宋淇祥"/> public String name; }

小结一下

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

3|88、通过JavaConfig创建对象

基于Java类进行配置

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

1、编写一个实体类,Dog

@Component //将这个类标注为Spring的一个组件,放到容器中! public class Dog { public String name = "dog"; }

2、新建一个config配置包,编写一个MyConfig配置类

@Configuration //代表这是一个配置类 public class MyConfig { @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id! public Dog dog(){ return new Dog(); } }

3、测试

@Test public void test2(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class); Dog dog = (Dog) applicationContext.getBean("dog"); System.out.println(dog.name); }

关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!

4|0四、AOP 面向切面

面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分耦合度降低,提高开发效率!

通俗描述:在不修改源代码的方式,在主干功能当中添加新功能!

、

4|11、AOP底层原理

AOP的底层就是动态代理,而其动态代理也根据情况的不同分为JDK动态代理CGLIB动态代理

  • 有接口,JDK动态代理

  • 无接口,CGLIB动态代理

4|22、JDK动态代理的实现

JDK动态代理是在有接口的情况下使用,所以我们需要先创建接口实现类的代理对象,这一步通过Proxy的方法可以实现!

Proxyjava.lang.reflect.Proxy

在这里插入图片描述

方法有三个参数:

1、类加载器

2、增强方法所在的类,这个类的接口 ,支持多个接口

3、实现这个接口InvocationHandler ,创建代理对象,写增强方法

具体的实现步骤

1、编写一个接口,UserDao

public interface UserDao { int add(int a, int b) ; String update(String id) ; }

2、创建接口的实现类,UserDaoImpl

public class UserDaoImpl implements UserDao { public int add(int a, int b) { return a + b ; } public String update(String id) { return id ; } }

3、通过Proxy创建代理类,实现方法增强

public class JDKProxy { public static void main(String[] args) { //创建接口实现类的代理对象 Class[] interfaces = { UserDao.class } ; UserDaoImpl userDaoImpl = new UserDaoImpl(); UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImpl));// invoke方法自动调用 int result = dao.add(1,2) ; System.out.println("result:"+result); } } //创建代理对象的代码 class UserDaoProxy implements InvocationHandler{ //1、创建的是谁的代理对象,把谁传进来 //有参构造传递 private Object obj ; public UserDaoProxy(Object obj) { this.obj = obj ; } //增强的逻辑 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前做处理 System.out.println("方法之前执行..." + method.getName() + "传递的参数..," + Arrays.toString(args)); //被增强的方法执行 Object res = method.invoke(obj, args); //方法之后做处理 System.out.println("方法之后执行..." + obj); return res; } }

4、测试结果,实现方法增强!

4|33、AOP术语

介绍一下AOP当中的的相关术语,例如:发动机术语是引擎

1、连接点 :类里面的哪些方法可以被增强,这些方法称为连接点 ;

2、切入点:实际被真正增强的方法,称为切入点 ;

3、通知(增强):实际增强的逻辑部分就称为通知 ;通知也分5种:前置通知 、后置通知 、环绕通知、异常通知、最终通知!

4、切面是一个动作,指的是将通知应用到切入点的过程!

4|44、AOP操作(AspectJ)

Spring框架一般都是基于Aspectj实现AOP操作!

1、什么是AspectJ ?

  • AspectJ不是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作!

2、基于Aspectj实现AOP操作的方式

  • 基于xml配置文件实现
  • 基于注解的方式实现(推荐)

1|04.1、实现AOP操作的准备

如下开始使用Spring 和 AjpectJ 来实现AOP的操作!

1、环境准备:Ajpectj + Spring

<!--Spring的依赖,默认会引入aop--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <!--AspectJ--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>

2、切入点表达式

  • 切入点表达式的作用:知道对哪个类里面的哪个方法进行增强
  • 语法结构:
execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表])) //类全路径:类的全限定名

例1:对com.sqx.dao.BookDao类里面的add方法进行增强

execution(* com.sqx.dao.BookDao.add(..)) // * 表示任意权限修饰符

例2:对com.sqx.dao.BookDao类里面的所有方法进行增强

execution(* com.sqx.dao.BookDao.*(..)) // * 表示任意权限修饰符

例3:对com.sqx.dao包里面的所有类,类里面的所有方法进行增强

execution(* com.sqx.dao.*.*(..)) // * 表示任意权限修饰符

1|04.2、AOP操作(AspectJ注解)

通过AspectJ注解的方式实现AOP操作

1、创建一个类,在类中定义方法

public class User { public void add(){ System.out.println("add方法执行...."); } }

2、创建增强类(编写增强逻辑)

/*** * User增强类 */ public class UserProxy { //前置通知 public void before(){ System.out.println("before......."); } }

3、进行通知的配置

3.1、在Spring配置文件,开启注解扫描

<!--开启注解扫描--> <context:component-scan base-package="com.sqx"/>

3.2、使用注解创建User和ProxyUser:也就是在类的上边加上@Component

@Component public class User { ... }

3.3、在增强类上添加一个注解 @AspectJ

/*** * User增强类 */ @Component @Aspect //为增强类生成一个代理对象 public class UserProxy { //前置通知 public void before(){ System.out.println("before......."); } }

3.4、在Spring配置中开启生成代理对象:同时需要引入aop的命名空间

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--开启AspectJ生成代理的对象:为带有@Aspectj的类创建代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>

3.5、配置不同类型的通知

1、在增强类的里面,在作为通知方法上面添加通知类型注解,使用切点表达式配置

/*** * User增强类 */ @Component @Aspect //生成增强类的代理对象 public class UserProxy { //前置通知 @Before(value = "execution(* com.sqx.annot.User.add())") // 表示方法作为前置通知切入到add()方法 public void before(){ System.out.println("before......."); } }

2、进行测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mel8kEKT-1642581545698)(Spring笔记.assets/image-20220118160342292.png)]

方法增强成功!

4、剩余的配置细节

对以上通知类型都进行测试,我么看一下有什么问题?

在这里插入图片描述

①、我们发现,我们为一个切点多次增强,切入点表达式出现冗余,因此,我们可以将切点表达式抽取出来,生成一个公共切入点

1、定义公共切入点:

@Pointcut(value = "execution(* com.sqx.annot.User.add())") public void pointDemo(){ }

2、使用公共切入点:

//前置通知 @Before(value = "pointDemo()") public void before(){ System.out.println("before......."); }

以上就解决了我们的切入点冗余的问题!

②、如果出现如下情况:多个增强类对同一个方法进行增强,我们需要制定其优先级

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jfUljM5d-1642581545700)(Spring笔记.assets/image-20220118164509560.png)]

我们通过@Order注解来设置增强类优先级:这个值越小优先级越高!

@Order(3) public class UserProxy {} @Order(1) public class PersonProxy {}

执行测试,查看结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JbK38Mkh-1642581545700)(Spring笔记.assets/image-20220118164856265.png)]

1|04.3、AOP操作(AspectJ配置文件)

通过xml配置文件的方式实现AOP操作(很少使用,了解即可!)

5|0五、Spring事务

事务一般添加到JavaEE三层结构里面的Service层(业务逻辑层)

Spring事务管理的两种方式:1、编程式事务管理 2、声明式事务管理(推荐)

区别:编程式事务管理,是我们通过代码保证我们的事务,显得比较臃肿,因此推荐声明式事务管理

5|11、编程式事务管理

如下是编程式事务的一个演示:

//模拟一下转账操作,小明向小红转账300 ; public void accountMony(){ try{ UserDao.addmony() //小明的余额添加300 UserDao.reducemony() //小红的余额减去300 }catche(Exception){ //回滚 } }

5|22、声明式事务管理

声明式事务的使用方式有如两种:

  • 基于注解的方式【方便、简单、推荐】
  • 基于xml配置文件方式

声明式事务的底层,使用的就是我们的AOP原理

Spring事务管理的一些API :

PlatformTransactionManager //事务管理器的接口,根据整合不同的框架采不同的实现类(事务管理器)

、

如果我们的Spring整合了Mybatis 、jdbcTemplate都是使用的DataSourceTransactionManger这个事务管理器!

1|02.1、注解方式实现

1、配置Spring的事务管理器

<!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean>

2、在Spring配置文件,开启事务注解

<!--在头文件添加如下命名空间tx--> xmlns:tx="http://www.springframework.org/schema/context" http://www.springframework.org/schema/tx https://www.springframework.org/schema/aop/spring-tx.xsd
<!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3、在Service类上面(获取Service类里面的方法上面)添加事务注解

  • @Transactional,这个注解可以加在类上边,也可以加在方法上面
  • 如果把这个注解添加在类上面:这个类里面所有方法都添加事务
  • 如果把这个注解添加在方法上面:为这个方法添加事务
@Transactional public class UserServiceImpl { } public class UserServiceImpl { @Transactional void UpdateUserInfo(User User){ } }

到此,我们的声明式事务,通过注解的方式就已经开启了!

@Transactional扩展 : 了解一个这个注解的一些属性参数

、

常用的有如下:

  • propagation :事务的传播行为

    指的是事务方法(对数据库表中数据发生改变的方法,统称事务方法),之间的相互调用!

    @Transactional public void add() { update() ; //调用update方法 这时候引出的一个问题add方法中开启了事务,但是调用了update方法,事务是如何管理的! } public void upodate() {}

    事务的传播行为有七种:

    参考文章:https://blog.csdn.net/weixin_39625809/article/details/80707695

    、

  • isolation:事务隔离级别

    • 在此处主要是指的多个开启事务的方法的并发问题,本质还是跟我们MySQL中的事务隔离级别一致!
  • timeout:超时时间

    • 事务需要在一定时间内进行提交,如果说在这一段时间内不提交,就会发生回滚!
    • 默认值是-1 , 设置时间以秒为单位
  • readOnly:是否只读

    • 读:查询操作,写:增删改操作’
    • 默认值为false , 表示当前事务当中可以查询可以查询和增删改操作
    • 设置为true , 当前事务中只支持读操作
  • rollbackFor:回滚

    • 设置出现哪些异常,执行回滚
  • noRollbackFor:不回滚

    • 设置出现哪些异常,不执行回滚

1|02.2、xml配置文件方式实现

实际开发,我们依然是用注解,但是xml配置方式仍需了解一下!

前提:声明式事务本质还是aop实现的,我们注解只是代替了我们手动切入事务的这个步骤,xml则需要我们手动的完成这个步骤!

1、依然是配置我们事务管理器

<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean>

2、配置我们的通知

<!--配置通知--> <tx:advice id="txadvice"> <!--配置事务参数--> <tx:attributes> <!--指定哪种规则的方法上面添加事务--> <!--name可看做切入点中的accounMoney方法添加事务--> <tx:method name="accountMoney" propagation="REQUIRED"/> <!-- 方法名为account开头的方法,添加事务 <tx:method name="account*"/> --> </tx:attributes> </tx:advice>

3、配置切入点和切面

<!--配置切入点和切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* com.sqx.service.UserService.*(..))"/> <!--配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>

xml配置文件方式实现声明式事务完成!

1|02.3、完全注解实现

通过JavaConfig的方式配置我们所有信息,无需要xml文件

@Configuration @ComponentScan(basePackages = "com.sqx") @EnableTransactionManagement // 开启事务管理 public class MyConfig { //创建一个数据库的连接池对象 @Bean public DruidDataSource getDruidDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } //创建一个JdbcTemplate对象 @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource){ //此处会按照类型自动装配,接收DataSource对象 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate ; } //创建事务管理对象 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager ; } }

需要注意:

、

事务相关就到此完结!

6|0六、Spring整合日志框架

Spring5 框架自带了通用的日志封装,而且也能整合其他日志工具 如 : Log4j 、Log4j2

由于Spring5 移除了Log4j CofigerListener(使用Log4j的核心),如果需要Log4j 要将Spinrg5降为4版本,所以官方建议使用Log4j2

参考文章:https://blog.csdn.net/zjh2016/article/details/103170578

6|11、整合Log4j2日志

1、引入相关依赖

需要导入如下的4个依赖,才能使用Log4j2

<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.8.0-alpha2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.10.0</version> </dependency>

2、创建Log4j2.xml配置文件

配置都是如下:

<?xml version="1.0" encoding="UTF-8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出--> <configuration status="WARN"> <!--先定义所有的appender--> <appenders> <!--输出日志信息到控制台--> <console name="Console" target="SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出--> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> </loggers> </configuration>

到这里我们Log4j2就已经开启了,但是如果我们呢想要手动使用Log4j2呢?

3、进行如图操作就好了

就如下两步即可!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7v5Q8RD-1642581545701)(Spring笔记.assets/image-20220119151515027.png)]

private static final Logger log = LoggerFactory.getLogger(MyTest.class) ; log.info("hello Log4j2"); log.debug("hello Log4j2"); log.warn("hello Log4j2"); log.error("hello Log4j2"); log.trace("hello Log4j2");

完结!

7|0七、Spring整合单元测试框架

常见的单元测试框架JUnit4 、JUnit5 我们的Spring5 都是支持的,我们逐个测试!

参考文章:https://blog.csdn.net/sinat_32336967/article/details/112275292

前提:先准备Spring5的环境

<!--Start--Spring5的关键Jar包依赖--Start--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.5.RELEASE</version> <scope>test</scope> </dependency>

7|11、整合JUnit4测试

1、导入相关依赖

<!--Start--Junit4的Jar包依赖--Start--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--End--Junit4的Jar包依赖--End-->

2、Spring5+Junit4单元测试

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classpath = "beans.xml") public class AppConfigurationTest { @Autowired private RestTemplate restTemplate; @org.junit.Test public void restTemplate() { System.out.println(restTemplate); ResponseEntity<String> res = restTemplate.getForEntity("https://www.baidu.com", String.class); System.out.println(res.getBody()); } }

7|22、整合JUnit5测试

1、引入相关依赖

<!--JUnit5--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <!--RELEASE表示获取最新版本,此文写作时是5.7.0--> <version>RELEASE</version> <scope>compile</scope> </dependency>

2、Spring5+Junit5单元测试代码

//下面两个注解可以用@SpringJunitConfig(AppConfiguration.class)替代 @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = AppConfiguration.class) //加载的JavaConig配置类,也可以classpath = "beans.xml" public class AppConfigurationTest { @Autowired private RestTemplate restTemplate; @org.junit.jupiter.api.Test public void restTemplate() { System.out.println(">>>>>>>>>"+restTemplate); ResponseEntity<String> res = restTemplate.getForEntity("https://www.jd.com", String.class); System.out.println(res.getBody()); } }

7|33、总结

不同点

1|01、使用注解不同

Spring5+Junit4时,使用如下两个注解:

  • @RunWith(SpringJUnit4ClassRunner.class)
  • @org.junit.Test

Spring5+Junit5时,使用如下两个注解:

  • @ExtendWith(SpringExtension.class)
  • @org.junit.jupiter.api.Test

1|02、整合时使用的启动类不同

  • 整合Junit4时是:org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  • 整合Junit5时是:org.springframework.test.context.junit.jupiter.SpringExtension;

两者均是spring-test包中提供的类。

相同点

加载配置类或者配置文件均使用注解 :

@ContextConfiguration(classes = AppConfiguration.class) 或者 @ContextConfiguration(classpath = “beans.xml”)

注意事项:在使用Spring5时,Junit版本最低为4.12,否则就会报错。


__EOF__

本文作者宋淇祥
本文链接https://www.cnblogs.com/qxsong/p/15837230.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   爪洼ing  阅读(183)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示