哪个网站免费做简历,响应式布局与自适应布局区别,付费网站建设,建站免费平台文章目录 spring61. 一些基本的概念、优势2. 入门案例实现maven聚合工程创建步骤分析实现过程 3. IoC#xff08;Inversion of Control#xff09;基于xml的bean环境搭建获取bean获取接口创建实现类依赖注入 setter注入 和 构造器注入原生方式的setter注入原生方式的构造器注… 文章目录 spring61. 一些基本的概念、优势2. 入门案例实现maven聚合工程创建步骤分析实现过程 3. IoCInversion of Control基于xml的bean环境搭建获取bean获取接口创建实现类依赖注入 setter注入 和 构造器注入原生方式的setter注入原生方式的构造器注入使用bean的方式进行依赖注入步骤 对象类型属性赋值步骤引入外部属性文件步骤bean的八大生命周期重要 基于xml的bean自动装配基于注解的bean自动装配极其重要使用注解创建对象 手动实现IoC实现过程手动实现一个IoC反射手动实现属性注入过程 4. 面向切面编程静态代理图解静态代理术语动态代理动态代理的实现过程 概念相关术语AOP思想综述基于注解方式配置AOP通知类型及其使用bean配置文件测试文件报错信息解决重用切入点表达式 5. 单元测试6. 事务JdbcTemplate对象操作Mysql数据库Spring事务处理前置知识准备环境利用JdbcTemplate对象完成对数据库的增加、修改、删除操作利用JdbcTemplate对象完成对数据库的查询操作报错信息解决 事务处理概念特性 声明式事务管理基于注解实现准备环境基于上面的JDBC示例进行添加目录结构如下BookControllerBookDaoBookDaoImplBookServiceBookServiceImplTestBookTx 分析 开启事务步骤使用注解的方式事务注解的一些属性 Resoource资源操作UrlResource实现类访问url资源ClassPathResourceDemo访问本地资源项目内资源loadAndReadUrlResource访问磁盘内文件 国际化i18n数据校验通过Validator接口实现数据校验基于注解校验基于方法校验自定义校验 AOT 导语感觉自己记得可能不全所以我找了一篇优秀的笔记 双手奉上链接https://lixx.cn/archives/spring6-2 https://lixx.cn/categories/spring6
spring6
1. 一些基本的概念、优势
主流轻量级、开源 框架
广义划分以SpringFirework为核心的Spring技术栈
狭义划分专指SpringFireWork
两大核心Ioc和AOP
特点
非侵入式功能组件使用注解进行标记简洁。控制反转自动创建对象让我们享受资源注入。面向切面编程在不修改源代码的基础上增强代码功能。组件化拆分复杂应用为多个组件共同实现一站式在集合许多依赖
2. 入门案例实现
maven聚合工程创建步骤
父工程 - spring6子模块 - spring-first 引入spring相关依赖
dependencies!--引入spring-context依赖--dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion6.1.10/version/dependency!--测试依赖junit--dependencygroupIdorg.junit.jupiter/groupIdartifactIdjunit-jupiter-api/artifactIdversion5.10.2/version/dependency
/dependencies创建类定义属性和方法
package com.itchen.spring6;public class User {public void add(){System.out.println(add ...);}public static void main(String[] args) {User user new User();user.add();}
}按照spring要求创建一个配置文件.xml 在配置文件中创建相应的配置信息
?xml version1.0 encodingUTF-8?
!--下面是一些对标签的约束--
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!--完成对User对象的创建id属性:唯一标识 为了方便一般使用类的首字母小写class:要创建的类的对象的全路径(包名称 类名称)--bean iduser classcom.itchen.spring6.User/bean
/beans最终测试
package com.itchen.spring6;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestUser {Testpublic void testUserObject(){// 加载spring配置文件对象创建ApplicationContext context new ClassPathXmlApplicationContext(bean.xml);// 获取创建的对象User user (User)context.getBean(user);System.out.println(user);// 使用对象的调用方法进行测试user.add();}
}输出了user对象的全路径和user的地址
分析实现过程
执行了实体类的无参数构造方法如何返回创建的对象 加载bean.xml配置文件对xml文件进行解析操作获取标签的属性值使用反射根据类的全路径创建对象对象存放在项目的一个Map集合中格式key,value
3. IoCInversion of Control
Spring通过loC容器来管理所有Java对象的实例化和初始化控制对象与对象之间的依赖关系。我们将由loC容器管理的Java对象称为Spring Bean它与使用关键字new创建的Java对象没有任何区别。
将对象的创建和对象与对象的关系都交给了容器进行管理。
基于xml的bean环境搭建
将子模块pom文件中的依赖放在父模块中子模块依然可以调用相应的依赖创建资源文件.xml和java实体类
获取bean
实体类user.java
package com.itchen.spring6.iocxml;public class User {private String name;private Integer age;public void run(){System.out.println(run ...);}
}bean资源文件获取 注意此时的class类型的bean只能有一个
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean iduser classcom.itchen.spring6.iocxml.User/bean
/beans测试获取bean的三种方式
package com.itchen.spring6.iocxml;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestUser {public static void main(String[] args) {ApplicationContext context new ClassPathXmlApplicationContext(bean.xml);// 根据对象获取beanUser user (User)context.getBean(user);System.out.println(根据id获取bean user);// 根据类型获取beanUser user2 (User)context.getBean(User.class);System.out.println(根据类型获取bean user2);// 根据id和类型获取beanUser user3 (User)context.getBean(user, User.class);System.out.println(根据id和类型获取bean user3);}
}获取接口创建实现类
创建接口声明需要实现的方法在实体类中实现接口。在bean.xml中配置相应的impl实现类
bean idUSerDaoImpl classcom.itchen.spring6.iocxml.bean.USerDaoImpl/bean注意这里的bean必须是唯一的。一个接口只能由一个impl实体类来实现
依赖注入 setter注入 和 构造器注入
原生方式的setter注入
先在类中定义基本的set和get方法然后在其他类中创建类的对象调用其setget方法来传值。
原生方式的构造器注入
先定义基本的有参构造器和无参构造器然后在创建对象的时候在构造器中传入相对应的值。
使用bean的方式进行依赖注入步骤
创建一个类在其中定义基本属性生成set方法在spring中进行配置
bean idbook classcom.itchen.spring6.iocxml.di.Bookproperty namebname valuejava后端开发/propertyproperty nameauthor value陈志伟/property
/bean测试
Test
public void testSetter(){// 1. 加载配置文件ApplicationContext context new ClassPathXmlApplicationContext(bean-di.xml);Book book context.getBean(book, Book.class);System.out.println(book);
}or
创建类定义属性生成有参数的构造方法进行配置
bean idBookConstructor classcom.itchen.spring6.iocxml.di.Bookconstructor-arg namebname value前端开发/constructor-argconstructor-arg nameauthor value陈志伟/constructor-arg
/bean测试
注意setter注入调用的是无参构造方法而构造器注入调用的是有参构造方法
对象类型属性赋值步骤
准备工作创建两个类班和学生学生中定义班的对象。定义其中的构造方法、和get、set方法创建bean.xml名字任意
!--第一种方式引入外部bean的方式--
bean idclass classcom.itchen.spring6.iocxml.ditest.Classproperty namecname value241班/property
/bean
bean idstudent classcom.itchen.spring6.iocxml.ditest.Studentproperty namesname valueAim/propertyproperty nameage value22/propertyproperty namecls refclass/property
/bean!--第二种方式引入内部bean的方式--
bean idstudent2 classcom.itchen.spring6.iocxml.ditest.Studentproperty namesname valueMary/propertyproperty nameage value20/property!--内部bean--property nameclsbean idclass2 classcom.itchen.spring6.iocxml.ditest.Classproperty namecname value242班/property/bean/property
/bean!--第三种方式级联属性赋值--
bean idcls3 classcom.itchen.spring6.iocxml.ditest.Classproperty namecname value144班/property/beanbean idstudent3 classcom.itchen.spring6.iocxml.ditest.Studentproperty namesname valueTom/propertyproperty nameage value28/propertyproperty namecls refclass/property!--级联再赋值--property namecls.cname value141班/property/bean!--第四种方式数组注入--
!--注入数组类型的属性--
bean idclss classcom.itchen.spring6.iocxml.ditest.Classproperty namecname value143班/property
/bean
bean idstudent classcom.itchen.spring6.iocxml.ditest.Student!--普通--property namesname valueAim/propertyproperty nameage value22/property!--对象--property namecls refclss/property!--数组--property namelovesarrayvalue吃饭/valuevalue睡觉/valuevalue玩jj/value/array/property
/bean!--第五种方式集合类型属性的注入--
bean idstuden1 classcom.itchen.spring6.iocxml.ditest.Studentproperty namesname valueAim/propertyproperty nameage value19/property
/bean
bean idstuden2 classcom.itchen.spring6.iocxml.ditest.Studentproperty namesname valueMary/propertyproperty nameage value19/property
/bean
bean idstuden3 classcom.itchen.spring6.iocxml.ditest.Studentproperty namesname valueTom/propertyproperty nameage value19/property
/bean
bean idcls classcom.itchen.spring6.iocxml.ditest.Classproperty namecname value142班/propertyproperty namestuListlistref beanstuden1/refref beanstuden2/refref beanstuden3/ref/list/property
/bean注意因为这里的包含关系变了需要修改一下测试方法
public static void main(String[] args) {ApplicationContext context new ClassPathXmlApplicationContext(bean-diarr.xml);// 学生的对象Student stu context.getBean(student,Student.class);stu.work();
}!--第六种方式map类型属性注入--
!--1. 创建两个对象用bean标签进行配置2. 注入它的普通类型的属性3. 在学生的bean中注入map集合的属性
--
bean idteacher classcom.itchen.spring6.iocxml.dimap.Teacherproperty nametid value1000/propertyproperty nametname value小李/property
/bean
bean idstudent classcom.itchen.spring6.iocxml.dimap.Studentproperty namesid value2000/propertyproperty namesname value小明/propertyproperty nameteacherMapmapentrykeyvalue10010/value/key!--这里的键值对的值值如果是一个普通的值可以使用value--ref beanteacher/ref/entry/map/property
/bean!--第七种方式集合类型的属性注入--
!--1. 创建三个对象2. 注入普通类型的属性3. 使用“util类型” 定义4. 在学生bean中引入3.的bean
--bean idstudent classcom.itchen.spring6.iocxml.dimap.Studentproperty namesid value10000/propertyproperty namesname value张三/property!--注入list和map类型属性--property namelessonList reflessonList/propertyproperty nameteacherMap refteacherMap/property
/beanutil:list idlessonListref beanlesson1/refref beanlesson2/ref
/util:list
util:map idteacherMapentrykeyvalue10010/value/keyref beanteacher1/ref/entryentrykeyvalue10086/value/keyref beanteacher2/ref/entry
/util:mapbean idlesson1 classcom.itchen.spring6.iocxml.dimap.Lessonproperty namelessonName valuejava开发/property
/bean
bean idlesson2 classcom.itchen.spring6.iocxml.dimap.Lessonproperty namelessonName value前端开发/property
/beanbean idteacher1 classcom.itchen.spring6.iocxml.dimap.Teacherproperty nametname value西门庆/propertyproperty nametid value1000/property
/bean
bean idteacher2 classcom.itchen.spring6.iocxml.dimap.Teacherproperty nametname value欧阳弧/propertyproperty nametid value2000/property
/bean注意第七种方式在使用的时候需要引入标签相应的命名空间
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:utilhttp://www.springframework.org/schema/utilxsi:schemaLocationhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd引入外部属性文件步骤
引入外部属性文件的相关依赖【pom文件】创建一个外部的属性文件。定义数据信息用户名、密码、地址。【.properties文件】创建spring的配置文件引入context的命名空间。引入属性文件使用表达式完成注入。
!--引入外部文件 完成数据库的信息注入--
bean iddruidDataSource classcom.alibaba.druid.pool.DruidDataSourceproperty nameurl value${jdbc.url}/propertyproperty nameusername value${jdbc.user}/propertyproperty namepassword value${jdbc.password}/propertyproperty namedriverClassName value${jdbc.driver}/property
/beanbean的八大生命周期重要
可以在配置文件中【bean.xml文件】使用参数的形式制定bean对象的初始化函数和销毁函数。
bean对象的创建调用无参构造方法给bean对象设置相关属性bean后置处理器初始化之前bean对象初始化调用制定的初始化方法bean后置处理器初始化之后bean对象创建完成了可以使用了bean对象销毁配置制定的销毁方法IoC容器关闭
基于xml的bean自动装配
!--根据类型自动装配--
bean iduserController classcom.itchen.spring6.iocxml.auto.Controller.UserController autowirebyType/bean
bean iduserService classcom.itchen.spring6.iocxml.auto.Service.UserServiceImpl autowirebyType/bean
bean iduserDao classcom.itchen.spring6.iocxml.auto.dao.UserDaoImpl autowirebyType/bean!--根据名称自动装配--
bean iduserController classcom.itchen.spring6.iocxml.auto.Controller.UserController autowirebyName/bean
bean iduserService classcom.itchen.spring6.iocxml.auto.Service.UserServiceImpl autowirebyName/bean
bean iduserDao classcom.itchen.spring6.iocxml.auto.dao.UserDaoImpl autowirebyName/bean
!--这时需要保证类的名字和id的名字一模一样--基于注解的bean自动装配极其重要
使用注解创建对象
引入依赖开启组件扫描 context:component-scan base-packagecom.itchen.spring6/context:component-scan 被扫描的类用component修饰自动注入的属性用Autowired修饰使用注解定义bean
注解说明Component该注解用于描述 Spring 中的 Bean它是一个泛化的概念仅仅表示容器中的一个组件Bean并且可以作用在应用的任何层次例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。Repository该注解用于将数据访问层Dao 层的类标识为 Spring 中的 Bean其功能与 Component 相同。Service该注解通常作用在业务层Service 层用于将业务层的类标识为 Spring 中的 Bean其功能与 Component 相同。Controller该注解通常作用在控制层如SpringMVC 的 Controller用于将控制层的类标识为 Spring 中的 Bean其功能与 Component 相同。
手动实现IoC
主要使用java的反射机制和注解进行实现
实现过程
创建子模块创建测试类创建两个注解 约定注解Bean Di 创建bean容器的接口ApplicationContext定义方法返回对象实现bean的容器接口 返回对象根据包的规则加载bean设置包的扫描规则
手动实现一个IoC反射
loadBean步骤 // 1. 判断当前是否是文件夹 // 2. 获取文件夹里面的所有内容 // 3. 文件夹里面为空直接返回 // 4. 如果文件夹里面不为空 // 遍历得到的每个file对象 重复3. 4. // 5. 如果是文件得到包路径 类名称 // 6. 判断文件是否是.class类型如果是则去掉后缀 // 7. 如果有Bean注解使用反射实例化 // 将实例化结果放入map集合中 package com.itchen.bean;import com.itchen.annotation.Bean;import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext{// 创建map集合用于创建bean的实例对象private static MapClass,Object beanFactory new HashMap();private static String rootPath;// 返回对象Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}// 设置包的扫描规则// 创建有参构造传递包路径public static void pathDemo1(String basePackage){// 把点替换为反斜杠String packagePath basePackage.replaceAll(\\., \\\\);// 获取包的绝对路径try {EnumerationURL dirs Thread.currentThread().getContextClassLoader().getResources(packagePath);while(dirs.hasMoreElements()){URL url dirs.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 (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}// 包扫描过程实例化beanprivate static void loadBean(File fileParent) throws ClassNotFoundException {// 1. 判断当前是否是文件夹if (fileParent.isDirectory()) {// 2. 获取文件夹里面的所有内容File[] childrenFiles fileParent.listFiles();// 3. 文件夹里面为空直接返回if (childrenFiles null || childrenFiles.length 0) {return;}// 4. 如果文件夹里面不为空// 遍历得到的每个file对象 重复3. 4.for (File child : childrenFiles) {if (child.isDirectory()) {loadBean(child);} else {// 5. 如果是文件得到包路径 类名称String pathWithClass child.getAbsolutePath().substring(rootPath.length() - 1);// 6. 判断文件是否是.class类型如果是则去掉后缀String fullName pathWithClass.replaceAll(\\\\, .).replace(.class, );try {Class? aClass Class.forName(fullName);// 7. 如果有Bean注解使用反射实例化if (!aClass.isInterface()) {Bean annotation aClass.getAnnotation(Bean.class);if (annotation ! null) {Object instance aClass.newInstance();if (aClass.getInterfaces().length 0) {System.out.println(正在加载【 aClass.getInterfaces()[0] 】,实例对象是 instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);} else {System.out.println(正在加载【 aClass.getName() 】,实例对象是 instance.getClass().getName());beanFactory.put(aClass, instance);}}}// 将实例化结果放入map集合中}catch (Exception e){e.printStackTrace();}}}}}
}手动实现属性注入过程
// 1. 实例化对象在beanFactory的map集合里面
// 遍历map集合// 2. 获取所有的value值获取每个对象的属性// 3. 遍历对象属性的数组得到每个属性// 4. 判断是否有Di注解// 5. 如果有将对象注入package com.itchen.bean;import com.itchen.annotation.Bean;
import com.itchen.annotation.Di;
import com.itchen.bean.ApplicationContext;import java.io.File;
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;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMapClass, Object beanFactory new HashMap();private static String rootPath;Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName basePackage.replaceAll(\\., \\\\);EnumerationURL dirs Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url dirs.nextElement();String filePath URLDecoder.decode(url.getFile(),utf-8);rootPath filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}// 属性的注入loadDi();}private void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles fileParent.listFiles();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)) {// com.xinzhi.dao.UserDao//去掉.class后缀并且把 \ 替换成 .String fullName pathWithClass.replaceAll(\\\\, .).replace(.class, );try {Class? aClass Class.forName(fullName);//把非接口的类实例化放在map中if(!aClass.isInterface()){Bean annotation aClass.getAnnotation(Bean.class);if(annotation ! null){Object instance aClass.newInstance();//判断一下有没有接口if(aClass.getInterfaces().length 0) {//如果有接口把接口的class当成key实例对象当成valueSystem.out.println(正在加载【 aClass.getInterfaces()[0] 】,实例对象是 instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class当成key实例对象当成valueSystem.out.println(正在加载【 aClass.getName() 】,实例对象是 instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}// 属性注入private void loadDi() {for(Map.EntryClass,Object entry : beanFactory.entrySet()){//就是咱们放在容器的对象Object obj entry.getValue();Class? aClass obj.getClass();Field[] declaredFields aClass.getDeclaredFields();for (Field field : declaredFields){Di annotation field.getAnnotation(Di.class);if( annotation ! null ){field.setAccessible(true);try {System.out.println(正在给【obj.getClass().getName()】属性【 field.getName() 】注入值【 beanFactory.get(field.getType()).getClass().getName() 】);field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}}
}4. 面向切面编程
核心业务任务日志 – 代码冗余
解决用代理模式将其分离分离任务日志 ----------------- 解耦
静态代理图解 静态代理术语
代理将非核心代码分离出来后封装这些非逻辑代理、对象、方法
目标被代理套用的非核心代码类、对象、方法
动态代理
原因因为静态代理不具备代码的灵活性所以引入了动态代理
动态代理的实现过程
package com.itchen.spring6.aop.example;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;public class ProxyFactory {// 目标对象private Object target;public ProxyFactory(Object obj){target obj;}// 返回代理对象的方法public Object getProxy(){/*** 参数1. 类加载器* 参数2. 目标对象实现的所有接口* 参数3. 设置代理修改目标函数的方法* */ClassLoader classLoader target.getClass().getClassLoader();Class?[] interfaces target.getClass().getInterfaces();InvocationHandler invocationHandler new InvocationHandler(){Override/*** 参数1. 代理对象* 参数2. 需要重写目标对象中的方法啊* 参数3. 目标方法中的参数* */public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println([动态代理][日志] method.getName()参数 Arrays.toString(args));Object result method.invoke(target, args);System.out.println([动态代理][日志] method.getName()结果 result);return result;}};return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);}
}自行补充计算器的基类和计算器接口类
// 测试类
package com.itchen.spring6.aop.example;public class TestCaculator {public static void main(String[] args) {// 1. 创建代理对象ProxyFactory proxyFactory new ProxyFactory(new CalculatorImpl());Calculator proxy (Calculator) proxyFactory.getProxy();proxy.add(1,2);proxy.mul(2,4);}
}概念
AOPAspect Oriented Programming是一种设计思想是软件设计领域中的面向切面编程它是面向对象编程的一种补充和完善它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性同时提高了开发的效率。
相关术语
横切关注点 项目中相同的需要被提取的部分 通知增强 需要增加的功能通知方法横切关注点的实现通知类型五种 切面 通知 封装通知方法的类eg. 一个切面类包含前置通知、返回通知、后置通知等 目标 被代理的对象 代理 向目标对象应用通知后创建代理对象 连接点 能使用通知的地方 切入点 实际去增强方法的位置
AOP思想综述
将特定的代码封装到切面类中将切面类用于一个新的目标方法最终实现 方法的增强方法的增强好抽象呀
基于注解方式配置AOP
通知类型及其使用
package com.itchen.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;Aspect // 切面类
Component // 在spring的ioc容器中进行管理
public class LogAspect {// 设置切入点和通知类型Before(value pointCut())public void beforeMethod(JoinPoint joinPoint){String methodName joinPoint.getSignature().getName();String args Arrays.toString(joinPoint.getArgs());System.out.println(Logger--前置通知方法名methodName参数args);}After(execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..)))public void afterMethod(JoinPoint joinPoint){String methodName joinPoint.getSignature().getName();System.out.println(Logger--后置通知方法名methodName);}AfterReturning(value execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..)), returning result)public void afterReturningMethod(JoinPoint joinPoint, Object result){String methodName joinPoint.getSignature().getName();System.out.println(Logger--返回通知方法名methodName结果result);}AfterThrowing(value execution(* com.itchen.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(execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..)))public Object aroundMethod(ProceedingJoinPoint joinPoint){String methodName joinPoint.getSignature().getName();String args Arrays.toString(joinPoint.getArgs());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.itchen.spring6.aop.annoaop.CalculatorImpl.*(..)))public void pointCut(){}
}bean配置文件
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd!--开启组件的扫描--context:component-scan base-packagecom.itchen.spring6.aop.annoaop/context:component-scan!--开启自动代理--aop:aspectj-autoproxy /
/beans测试文件
package com.itchen.spring6.aop.annoaop;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestAop {Testpublic void testAdd(){ApplicationContext context new ClassPathXmlApplicationContext(bean.xml);Calculator calculator context.getBean(Calculator.class);calculator.add(1,1);}
}报错信息解决
NoSuchBeanDefinitionException没有在实现类中加入Component注解创建bean
重用切入点表达式
通知注解后面的参数重复书写所以可以用注解将参数定义为一个类似宏的东西
5. 单元测试
Junit依赖自动注入方便测试
整合Junit5 和 Junit4
// 不整合的时候测试
public class TestUser {public static void main(String[] args) {ApplicationContext context new ClassPathXmlApplicationContext(bean-auto.xml);UserController userController context.getBean(userController, UserController.class);userController.addUser();}
}// 整合Junit5
SpringJUnitConfig(locations classpath:bean.xml)
public class SpringTestJunit5 {Autowiredprivate User user;// 测试方法Testpublic void testUser(){System.out.println(user);user.run();}
}// 整合Junit4
RunWith(SpringJUnit4ClassRunner.class)
ContextConfiguration(classpath:bean.xml)
public class SpringTestJunit4 {Autowiredprivate User user;Testpublic void testUser4(){System.out.println(user);user.run();}
}6. 事务
JdbcTemplate对象操作Mysql数据库Spring事务处理前置知识
准备环境
导入依赖
!--spring jdbc Spring 持久化层支持jar包--
dependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion6.0.2/version
/dependency
!-- MySQL驱动 --
dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.30/version
/dependency
!-- 数据源 --
dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.2.15/version
/dependency
!--spring对junit的支持相关依赖--
dependencygroupIdorg.springframework/groupIdartifactIdspring-test/artifactIdversion6.0.2/version
/dependency!--junit5测试--
dependencygroupIdorg.junit.jupiter/groupIdartifactIdjunit-jupiter-api/artifactIdversion5.9.0/version
/dependency配置jdbc.properties配置文件连接数据库
jdbc.userroot
jdbc.passwordroot
jdbc.urljdbc:mysql://localhost:3306/spring?characterEncodingutf8useSSLfalse
jdbc.drivercom.mysql.cj.jdbc.Driver配置bean.xml配置JdbcTemplate
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd!-- 导入外部属性文件 --context:property-placeholder locationclasspath:jdbc.properties /!-- 配置数据源 --bean iddruidDataSource classcom.alibaba.druid.pool.DruidDataSourceproperty nameurl value${jdbc.url}/property namedriverClassName value${jdbc.driver}/property nameusername value${jdbc.user}/property namepassword value${jdbc.password}//bean!-- 配置 JdbcTemplate --bean idjdbcTemplate classorg.springframework.jdbc.core.JdbcTemplate!-- 装配数据源 --property namedataSource refdruidDataSource//bean
/beans利用JdbcTemplate对象完成对数据库的增加、修改、删除操作
测试代码
package com.itchen.spring6.jdbc;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;SpringJUnitConfig(locations classpath:beans.xml)
public class JdbcTemplateTest {Autowiredprivate JdbcTemplate jdbcTemplate;// 添加、修改、删除操作Testpublic void testUpdate(){// 添加数据String sql insert into t_emp values(null,?,?,?);Object[] params {洛萨,32,男};
// 添加数据
// int rows jdbcTemplate.update(sql,params);
// System.out.println(影响了 rows 行);String sql2 update t_emp set name? where id?;// 修改id为行数据的名字字段
// int row jdbcTemplate.update(sql2,666,3);
// System.out.println(影响了 row 行);// 删除id为的一行String sql3 delete from t_emp where id?;
// int row3 jdbcTemplate.update(sql3,3);
// System.out.println(影响了 row3 行);}
}利用JdbcTemplate对象完成对数据库的查询操作
package com.itchen.spring6.jdbc;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;import java.util.List;SpringJUnitConfig(locations classpath:beans.xml)
public class jdbcTemplateTest2 {Autowiredprivate JdbcTemplate jdbcTemplate;/*** queryForObject* */// 查询并获取数据库的第一行数据Testpublic void testSelectObject1(){String sql select * from t_emp where id?;Emp person jdbcTemplate.queryForObject(sql, (rs, rowNum) - {Emp emp new Emp();emp.setId(rs.getInt(id));emp.setName(rs.getString(name));emp.setAge(rs.getInt(age));emp.setSex(rs.getString(sex));return emp;}, 1);System.out.println(person);}/*** queryForObject* */Testpublic void testSelectObject2(){String sql select * from t_emp where id?;// 利用BeanPropertyRowMapper方法自动创建对象接收数据库数据Emp emp jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Emp.class),2);System.out.println(emp);}/*** query* */Testpublic void testSelectList(){String sql select * from t_emp;ListEmp list jdbcTemplate.query(sql, new BeanPropertyRowMapper(Emp.class));System.out.println(list);}/*** queryForObject* *///查询单行单列的值Testpublic void selectCount(){String sql select count(id) from t_emp;Integer count jdbcTemplate.queryForObject(sql, Integer.class);System.out.println(count);}
}报错信息解决
BadSqlGrammarException没有按照规定的sql语法进行传递参数信息
事务处理
概念
eg.银行转账要成功都成功要失败都失败
特性
原子性要成功都成功
一致性操作前和操作后总量一致
隔离性多个事务进行操作对各个事务之间没有影响
持久性提交就永久保存
声明式事务管理基于注解实现
准备环境基于上面的JDBC示例进行添加目录结构如下 BookController
package com.itchen.spring.tx.controller;import com.itchen.spring.tx.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;Controller
public class BookController {Autowiredprivate BookService bookService;// 买书的方法 图书的id 、 用户的idpublic void buyBook(Integer bookId,Integer userId){// 调用Service方法bookService.buyBook(bookId,userId);}
}BookDao
package com.itchen.spring.tx.dao;public interface BookDao {Integer getBookPriceByBookId(Integer bookId);void updateStock(Integer bookId);void updateUserBalance(Integer userId,Integer price);
}BookDaoImpl
package com.itchen.spring.tx.dao;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;Repository
public class BookDaoImpl implements BookDao{Autowiredprivate JdbcTemplate jdbcTemplate;Integer price;Overridepublic Integer getBookPriceByBookId(Integer bookId) {String sql select price from t_book where book_id?;price jdbcTemplate.queryForObject(sql, Integer.class, bookId);return price;}Overridepublic void updateStock(Integer bookId) {String sql update t_book set stockstock-1 where book_id?;jdbcTemplate.update(sql,bookId);}Overridepublic void updateUserBalance(Integer userId,Integer price) {String sql update t_user set balancebalance-? where user_id?;jdbcTemplate.update(sql,price,userId);}
}BookService
package com.itchen.spring.tx.service;public interface BookService {void buyBook(Integer bookId, Integer userId);
}BookServiceImpl
package com.itchen.spring.tx.service;import com.itchen.spring.tx.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;Service
public class BookServiceImpl implements BookService{Autowiredprivate BookDao bookDao;Overridepublic void buyBook(Integer bookId, Integer userId) {// 根据图书id查询图书的价格Integer price bookDao.getBookPriceByBookId(bookId);// 更新图书表中的库存量bookDao.updateStock(bookId);// 更新用户余额bookDao.updateUserBalance(userId,price);}
}TestBookTx
package com.itchen.spring.tx;import com.itchen.spring.tx.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;SpringJUnitConfig(locations classpath:beans.xml)
public class TestBookTx {Autowiredprivate BookController bookController;Testpublic void testBuyBook(){bookController.buyBook(1,1);}
}分析
将买书一个方法作为一个大的方法在Controller中调用。然后再Service将这个方法分解成多个数据库操作。最后将数据库的各个操作在dao层进行实现。揉碎了喂到嘴里
问题这里如果用户的余额不足仍然会执行图书出库操作但是应为没有事物的回滚报错的步骤不会执行。
开启事务步骤使用注解的方式
在bean中添加开启事务的注解驱动在业务逻辑层Service层添加事务注解Transactional**【这个注解可以加在方法上也可以加在类上】**
运行结果在用户余额不足的情况下依然报错但是不会执行图书出库操作
事务注解的一些属性
参数描述readOnly是否只读表示是否只能进行查询操作timeout设置超时时间-1表示永不超时单位是秒noRollbackFor事务策略设置哪些异常不回滚见文章头连接隔离级别解决脏读、不可重读、虚读等见文章头连接传播行为事务之间的相互调用使用哪一个事务进行逻辑处理
脏读两个都未提交但是都能看见对方的修改
不可重复一个提交了另一个修改了没有提交的能看到修改完成之后的数据
虚读一个提交了另一个做完了添加没有提交之后能读到自己添加的数据
Resoource资源操作
Resource接口提供了对低级资源访问的抽象方法接口有多个实现类供给给我们来访问低级资源
UrlResource实现类访问url资源
// 演示urlResource访问网络资源
public class UrlResourceDemo {public static void main(String[] args) {// http前缀loadUrlResource(https://www.baidu.com/);// file前缀 放在项目的根路径loadUrlResource(file:itchen.txt);}// 访问前缀httppublic static void loadUrlResource(String path){// 创建Resource接口的实现类的对象try {// 获取资源UrlResource url new UrlResource(path);System.out.println(url.getFilename());System.out.println(url.getURI());System.out.println(url.getDescription());System.out.println(url.getInputStream().read());} catch (Exception e) {throw new RuntimeException(e);}// 获取资源信息}
}ClassPathResourceDemo访问本地资源项目内资源
// 访问类路径下的资源
public class ClassPathResourceDemo {public static void main(String[] args) {loadClasspathResource(itchen.txt);}public static void loadClasspathResource(String path){// 创建的对象ClassPathResource resource new ClassPathResource(path);System.out.println(resource.getFilename());System.out.println(resource.getDescription());// 获取文件内容try {InputStream in resource.getInputStream();byte[] b new byte[1024];while(in.read(b) ! -1){System.out.println(new String(b));}} catch (IOException e) {throw new RuntimeException(e);}}
}
loadAndReadUrlResource访问磁盘内文件
public class FileSystemResourceDemo {public static void loadAndReadUrlResource(String path) throws Exception{//相对路径FileSystemResource resource new FileSystemResource(itchen.txt);//绝对路径//FileSystemResource resource new FileSystemResource(D:\\itchen.txt);// 获取文件名System.out.println(resource.getFileName resource.getFilename());// 获取文件描述System.out.println(resource.getDescription resource.getDescription());//获取文件内容InputStream in resource.getInputStream();byte[] b new byte[1024];while(in.read(b)!-1) {System.out.println(new String(b));}}public static void main(String[] args) throws Exception {loadAndReadUrlResource(itchen.txt);}
}其他的实现类见文档头链接
国际化i18n
调用不同的配置文件示例
Test
public void test1(){ResourceBundle bundle ResourceBundle.getBundle(itchen, new Locale(zh, CN));String value1 bundle.getString(test);System.out.println(value1);
}
Test
public void test2() {ResourceBundle bundle ResourceBundle.getBundle(itchen, new Locale(en, GB));String value1 bundle.getString(test);System.out.println(value1);
}数据校验
通过Validator接口实现数据校验
public class TestMethod1 {public static void main(String[] args) {//创建person对象Person person new Person();person.setName(lucy);person.setAge(-1);// 创建Person对应的DataBinderDataBinder binder new DataBinder(person);// 设置校验binder.setValidator(new PersonValidator());// 由于Person对象中的属性为空所以校验不通过binder.validate();//输出结果BindingResult results binder.getBindingResult();System.out.println(results.getAllErrors());}
}测试
public class PersonValidator implements Validator {// 校验类型确认Overridepublic boolean supports(Class? clazz) {return Person.class.equals(clazz);}// 校验逻辑Overridepublic void validate(Object object, Errors errors) {ValidationUtils.rejectIfEmpty(errors, name, name.empty);Person p (Person) object;if (p.getAge() 0) {errors.rejectValue(age, error value 0);} else if (p.getAge() 110) {errors.rejectValue(age, error value too old);}}
}基于注解校验
基于方法校验
自定义校验
其他的实现类见文档头链接
AOT
AOT先关概念 JIT动态编译边运行边编译 AOT运行前编译提前编译