本文档根据B站视频进行spring部分的学习记录
Spring5最新完整教程IDEA版
1.Spring简介
spring理念: 使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
spring总结: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
2.spring组成及拓展 spring Boot 构建一切 一个快速开发的脚手架 基于SpringBoot可以快速开发单个微服务 约定大于配置 spring Cloud 协调一切 SpringCloud是基于SpringBoot实现的 spring Cloud Data Flow 连接一切
学习springBoot的前提,需要完全掌握spring及springMVC
3.IOC理论推导 1.UserDao接口 2.UserDaoImpl实现类 3.UserService业务接口 4.UserServiceImpl业务实现类
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码,如果程序 代码量十分大,修改一次的成本代价十分高昂
我们使用一个set接口实现,已经发生了革命性的变化
1 2 3 4 5 6 private UserDao userDao;public void setUserDao (UserDao userDao) { this .userDao = userDao; }
之前程序是主动创建对象,控制权在程序员手上
使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象
这种思想,从本质上解决了问题,不用再去管理对象的创建了,系统的耦合性大大降低,可以更加专注于业务的实现上 这是IOC的原型
4.IOC本质 控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法 没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制, 控制反转后将对象的创建转移给第三方,简单来说就是:获得依赖对象的方式反转了
IOC是Spring框架的核心内容,使用多种方式完美的实现了IOC,可以使用XML配置,也可以使用注解 Spring容器在初始化的时,先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从 IOC容器中取出需要的对象
采用xml方式配置Bean的时候,Bean的定义信息是和现实分离的,而采用注解的方式可以把两者何为一体,Bean的定义信息直接以 注解的形式定义在实现类中,从而达到了零配置的目的
控制反转时一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的时IOC容器,其实现 方法是依赖注入(Dependency Injection,DI)
5.HelloSpring 创建一个spring项目
beans的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?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 ="hello" class ="pojo.Hello" > <property name ="str" value ="Spring" /> </bean > </beans >
测试方法
1 2 3 4 5 6 7 public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Hello hello = (Hello) context.getBean("hello" ); System.out.println(hello.toString()); }
pojo对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Hello { private String name; public String getName () { return name; } public void setName (String name) { this .name = name; } public void show () { System.out.println("hello" + name); } }
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用spring后,对象是由spring来创建的 反转:程序本身不创建对象,而变成被动的接收对象 依赖注入:就是利用set方法来进行注入的 IOC是一种编程思想,由主动的编程变成被动的接收 要实现不同的操作,不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改 所谓IOC一句话搞定,对象由spring来创建,管理,装配
6.IOC创建对象方式 1.使用无参构造创建对象,默认 2.使用有参构造创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class User { private String name; public User (String name) { this .name = name; } public String getName () { return name; } public String setName () { this .name = name; } public void show () { sout("name=" +name); } }
beans.xml实现:
1 2 3 4 5 6 7 8 9 10 11 12 - 2.1 下标赋值 <bean id ="user" class ="com.kuang.User" > <constructor-arg index ="0" value ="testValue" </bean> - 2.2 通过类型创建(不建议使用) <bean id ="user" class ="com.kuang.User" > <constructor-arg type ="java.lang.String" value ="testValue" </bean> - 2.3 直接通过参数名来设置 <bean id ="user" class ="com.kuang.User" > <constructor-arg name ="name" value ="testValue" </bean>
测试类:
1 2 3 4 5 6 7 public class MyTest () { psvm(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); User user = (User)context.getBean("user" ); user.show(); } }
总结: 在配置文件加载的时候,容器(可以理解为spirng中加载的bean的context)中管理的对象就已经初始化了
7.Spring配置说明 1.别名 示例:
1 2 3 4 5 <bean id ="user" class ="marktest.demo.User" > <constructor-arg name ="name" value ="marktest2333" > </constructor-arg > </bean > <alias name ="user" alias ="user2" />
测试类:
1 2 3 4 5 6 7 public class MyTest () { psvm(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); User user = (User)context.getBean("user2" ); user.show(); } }
2.Bean的配置 示例:
id: bean的唯一标识符,也就是相当于对象名 class: bean对象所对应的全限定名: 包名 + 类名 name: 也是别名,而且name 可以同时取多个别名(逗号,空格,分号都可以进行分隔)
1 2 3 <bean id ="user" class ="marktest.demo.User" name ="user3,u3 u4;u5" > <constructor-arg name ="name" value ="marktest3222" /> </bean >
3.import 这个import一般用于团队开发,可以将多个配置文件,导入合并为一个 不同的人负责不同的类进行开发,不同的类需要注册在不同的bean中,可以利用import将所有人的beans.xml合并成一个总的 例如:
新建一个applicationContext.xml
1 2 3 <import resource ="beans.xml" /> <import resource ="beans2.xml" /> <import resource ="beans3.xml" />
8.DI依赖注入环境 1.构造器注入 参考前面IOC创建对象方式
2.set方式注入[重点] 依赖注入:本质是Set注入
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性,由容器来注入
[测试环境搭建]
1.创建两个实体类
Address.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.marktest.pojo;public class Address { @Override public String toString () { return "Address{" + "address='" + address + '\'' + '}' ; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public Address () { } public Address (String address) { this .address = address; } private String address; }
Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package com.marktest.pojo;import java.util.*;public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String, String> card; private Set<String> games; private String wife; private Properties info; @Override public String toString () { return "Student{" + "name='" + name + '\'' + ", address=" + address.toString() + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}' ; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Address getAddress () { return address; } public void setAddress (Address address) { this .address = address; } public String[] getBooks() { return books; } public void setBooks (String[] books) { this .books = books; } public List<String> getHobbys () { return hobbys; } public void setHobbys (List<String> hobbys) { this .hobbys = hobbys; } public Map<String, String> getCard () { return card; } public void setCard (Map<String, String> card) { this .card = card; } public Set<String> getGames () { return games; } public void setGames (Set<String> games) { this .games = games; } public String getWife () { return wife; } public void setWife (String wife) { this .wife = wife; } public Properties getInfo () { return info; } public void setInfo (Properties info) { this .info = info; } }
2.创建beans文件
1 2 3 4 5 6 7 8 9 10 <?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:util ="http://www.springframework.org/schema/util" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd" > <bean id ="student" class ="com.marktest.pojo.Student" > <property name ="name" value ="marktest1122" /> </bean > </beans >
3.创建测试类文件
1 2 3 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Student student = (Student) context.getBean("student" ); System.out.printf(student.toString());
4.完善注入信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <?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:util ="http://www.springframework.org/schema/util" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd" > <bean id ="address" class ="com.marktest.pojo.Address" > <property name ="address" value ="内蒙" > </property > </bean > <bean id ="student" class ="com.marktest.pojo.Student" > <property name ="name" value ="marktest1122" /> <property name ="address" ref ="address" /> <property name ="books" > <array > <value > 红楼梦</value > <value > 水浒传</value > <value > 西游记</value > <value > 三国演艺</value > </array > </property > <property name ="hobbys" > <list > <value > 听歌</value > <value > 看电影</value > <value > 敲代码</value > </list > </property > <property name ="card" > <map > <entry key ="身份证" value ="123123123123123" /> <entry key ="银行看" value ="321312321312312" /> </map > </property > <property name ="games" > <set > <value > LOL</value > <value > COC</value > <value > BOB</value > </set > </property > <property name ="wife" > <null /> </property > <property name ="info" > <props > <prop key ="学号" > 123321001</prop > <prop key ="性别" > 男</prop > <prop key ="姓名" > 李华</prop > </props > </property > </bean > </beans >
3.拓展方式注入 我们可以使用p命名空间和c命名空间进行注入
使用
创建一个User实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.marktest.pojo;public class User { private String name; private int age; public User () { } public User (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "user{" + "name='" + name + '\'' + ", age=" + age + '}' ; } }
创建userbeans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <?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:p ="http://www.springframework.org/schema/p" xmlns:c ="http://www.springframework.org/schema/c" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd " > <bean id ="user" class ="com.marktest.pojo.User" p:age ="40" p:name ="jack" /> <bean id ="user2" class ="com.marktest.pojo.User" c:age ="18" c:name ="Fuck" scope ="prototype" /> </beans >
测试
1 2 3 4 5 6 @Test public void test2 () { ApplicationContext context3 = new ClassPathXmlApplicationContext("userbeans.xml" ); User user4 = context3.getBean("user2" , User.class ) ; System.out.printf(user4); }
注意点:
p命名和c命令空间不能直接使用,需要导入xml约束
1 2 xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
9.依赖注入之set注入 参考set方式注入
10.c命名空间和p命名空间注入 参考拓展方式注入
11.Bean的作用域 使用@test来检查Bean作用域的
1 2 3 4 5 6 7 @Test public void test2 () { ApplicationContext context3 = new ClassPathXmlApplicationContext("userbeans.xml" ); User user4 = context3.getBean("user2" , User.class ) ; User user5 = context3.getBean("user2" , User.class ) ; System.out.printf(String.valueOf(user4==user5)); }
12.自动装配Bean
在spring中有三种装配方式: 1.在xml中显示的配置 示例:
1 2 3 4 5 6 7 <bean id ="cat" class ="com.markup.pojo.Cat" /> <bean id ="dog" class ="com.markup.pojo.Dog" /> <bean id ="people" class ="com.markup.pojo.People" > <property name ="name" value ="markup" /> <property name ="dog" ref ="dog" /> <property name ="cat" ref ="cat" /> </bean >
在xml中显示的配置,如果有引用bean的话需要用ref参数将上面通过bean创建的参数做引用 2.在java中显示装配 3.隐式的自动装配bean[重要] 可以通过ByName和ByType的方式来实现自动装配
测试环境搭建
1.环境搭建: 一个人有两个宠物
2.ByName自动装配
示例:
1 2 3 4 5 <bean id ="cat" class ="com.markup.pojo.Cat" /> <bean id ="dog" class ="com.markup.pojo.Dog" /> <bean id ="people" class ="com.markup.pojo.People" autowire ="byName" > <property name ="name" value ="markup" /> </bean >
byName: 会自动在容器上下文中查找和自己对象set方法后面的值对应的beanID
3.ByType自动装配
示例:
1 2 3 4 5 <bean class ="com.markup.pojo.Cat" /> <bean class ="com.markup.pojo.Dog" /> <bean id ="people" class ="com.markup.pojo.People" autowire ="byType" > <property name ="name" value ="markup" /> </bean >
byType: 会自动在容器上下文中查找和自己对象属性类型相同的bean
小结:
ByName的时候,需要保证所有的bean的ID唯一,并且这个bean需要和自动注入的属性的set方法的值一致 ByType的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
4.使用注解实现自动装配
JDK 1.5 版本支持的注解, spring 2.5 支持的注解
1.导入约束 context约束
2.配置注解的支持[重要] context:annotation-config/
示例代码:
1 2 3 4 5 6 7 8 9 10 11 <?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://http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd " > <context:annotation-config /> </beans >
注解关键字:
@Autowired注解 [spring方式]
直接在属性上使用即可,也可以在set方式上使用 使用@Autowired我们可以不用编写set方法了,前提是这个自动装配的属性在IOC(spring)容器中存在,且符合名字byName. 测试代码:
1 2 3 4 5 6 7 public class People { @Autowired private Cat cat; @Autowired private Dog dog; private String name; }
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解[@Autowired]完成的时候,我们可以 使用@Qualifier(value = “xxx”)去配置@Autowired的使用,执行一个唯一的bean对象注入. 测试代码:
1 2 3 4 5 6 7 8 9 public class People { @Autowired @Qualifier (value = "cat111" ) private Cat cat; @Autowired @Qualifier (value = "dog222" ) private Dog dog; private String name; }
@Resource注解 [java方式]
测试代码:
1 2 3 4 5 6 7 8 public class People { @Resource (name = "cat" ) private Cat cat; @Resource private Dog dog; private String name; }
小结
@Resource和@Autowired的区别: 都是用来自动装配的,都可以放在属性字段上 @Autowired 通过byType的方式实现,并且必须要求这个对象存在 [常用] @Resource 默认通过byName的方式实现,如果找不到,则通过byType实现,如果两个都找不到的情况下,就报错 [常用] 执行顺序不同: @Autowired 通过byType的方式实现;@Resource 默认通过byName的方式实现.
13.注解实现主动装配 参考 4.使用注解实现自动装配
14.Spring注解开发 在spring4之后要使用注解开发,必须要保证aop的包导入了 在使用注解需要导入context约束,增加注解的支持
1 2 3 4 5 6 7 8 9 10 11 12 <?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:annotation-config /> </beans >
1.bean
2.属性如何注入
@Component: 组件,放在类上,说明这个类被spring管理了
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Component public class User { public String name; @Value ("fuckjack" ) public void setName (String name) { this .name = name; } }
3.衍生的注解
@Component有几个衍生注解,在web开发中,会按照mvc三层加枸分层
dao [@Repository]
service [@Service]
controller [@Controller]
这四个注解功能都是一样的,都是代表将某个类注解到spring容器中,装配bean
4.自动装配
@Autowired: 自动装配通过类型,名字 如果@Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=”xxx”) @Nullable: 字段标记了这个注解,说明这个字段可以为null @Resource: 自动装配通过名字,类型
5.作用域
1 2 3 4 5 6 7 8 9 10 11 @Component @Scope ("prototype" ) public class User { public String name; @Value ("fuckjack" ) public void setName (String name) { this .name = name; } }
6.小结
常用依赖:
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency >
xml与注解:
xml更加万能,适用于任何场合,维护简单方便
注解 不是自己的类使用不了,维护相对复杂
xml与注解最佳实现:
xml 用来管理bean
注解只负责完成属性的注入
在使用的过程中,只需要注意一个问题,必须让注解生效,就需要开启注解的支持
1 2 3 <context:component-scan base-package ="com.markup" /> <context:annotation-config />
15.使用JavaConfig实现配置 现在完全不使用spring的xml配置了,全交给java来做 JavaConfig是spring的一个子功能 示例:
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class User { private String name; @Override public String toString () { return "User{" + "name='" + name + '\'' + '}' ; } public String getName () { return name; } @Value ("MARKUP" ) public void setName (String name) { this .name = name; } }
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration @ComponentScan ("com.markup.pojo" )@Import (MarkConfig2.class ) public class MarkConfig { @Bean public User getUser () { return new User(); } }
测试类
1 2 3 4 5 6 7 8 public class Mytest { public static void main (String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MarkConfig.class ) ; User getUser = context.getBean("getUser" , User.class ) ; System.out.println(getUser.getName()); } }
这种纯java的配置方式,在sprin中随处可见.
16.内容回顾 spring的发展历程到配置的过程从xml配置到注解开发
17.静态代理模式 代理模式 为什么要学习代理模式? 因为这就是springAOP的底层[springAOP和springMVC] 代理模式的分类
静态代理模式 角色分析:
抽象角色: 一般会使用接口或者抽象类来解决
真实角色: 被代理的角色
代理角色: 代理真实角色,一般代理之后会做一些附属操作
客户: 访问代理对象的人
代码步骤:
1.接口
1 2 3 4 public interface Rent { public void rent () ; }
2.真实角色
1 2 3 4 5 6 public class Host implements Rent { public void rent () { System.out.println("房东要出租房子" ); } }
3.代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Proxy implements Rent { private Host host; public Proxy () { } public Proxy (Host host) { this .host = host; } public void rent () { seeHouse(); host.rent(); hetong(); fare(); } public void seeHouse () { System.out.printf("中介带你看房" ); } public void fare () { System.out.printf("收中介费" ); } public void hetong () { System.out.printf("签租赁合同" ); } }
4.客户端访问代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Client { public static void main (String[] args) { Host host = new Host(); Proxy proxy = new Proxy(host); proxy.rent(); } }
代理模式的好处 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务 公共业务交给代理角色,实现了业务的分工 公共业务发生扩展的时候,方便集中管理
代理的缺点: 一个真实的角色就会产生一个代理角色;代码量会翻倍,开发效率会变低
18.静态代理模式再理解 1.接口
1 2 3 4 5 6 public interface UserService { public void add () ; public void delete () ; public void update () ; public void query () ; }
2.真实角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class UserServiceImpl implements UserService { public void add () { System.out.printf("增加了一个用户" ); } public void delete () { System.out.printf("删除了一个用户" ); } public void update () { System.out.printf("修改了一个用户" ); } public void query () { System.out.printf("查询了一个用户" ); } }
3.代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class UserServiceProxy implements UserService { private UserService userService; public UserServiceProxy () { } public void setUserService (UserService userService) { this .userService = userService; } public void add () { log("add" ); userService.add(); } public void delete () { log("delete" ); userService.delete(); } public void update () { log("update" ); userService.update(); } public void query () { log("query" ); userService.query(); } public void log (String msg) { System.out.println("[DEBUG]使用了" + msg+ "方法 " ); } }
4.客户端访问代理角色
1 2 3 4 5 6 7 8 9 10 public class Client { public static void main (String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.delete(); } }
19.动态代理模式详解 动态代理和静态代理的角色一样 动态代理的代理类是动态生成的,不是我们直接写好的 动态代理分为两大类:基于接口的动态代理,基于类的动态代理 基于接口: JDK动态代理 基于类: cglib java字节码实现: javasist
需要了解两个类: Proxy:提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类. InvocationHandler:是由代理实例的,调用处理程序实现的接口
动态代理的好处: 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务 公共业务交给代理角色,实现了业务的分工 公共业务发生扩展的时候,方便集中管理 一个动态代理类代理的是一个接口,一般就是对应的一类业务 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
1.接口
1 2 3 4 5 6 public interface UserService { public void add () ; public void delete () ; public void update () ; public void query () ; }
2.真实角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class UserServiceImpl implements UserService { public void add () { System.out.printf("增加了一个用户" ); } public void delete () { System.out.printf("删除了一个用户" ); } public void update () { System.out.printf("修改了一个用户" ); } public void query () { System.out.printf("查询了一个用户" ); } }
3.代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class ProxyInvocationHandler implements InvocationHandler { private Object target; public void setTarget (Object target) { this .target = target; } public Object getProxy () { return Proxy.newProxyInstance(this .getClass().getClassLoader(), target.getClass().getInterfaces(),this ); } public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); Object result = method.invoke(target, args); return result; } public void log (String msg) { System.out.printf("执行了" + msg + "方法" ); } }
4.客户端访问代理角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Client { public static void main (String[] args) { UserServiceImpl userService = new UserServiceImpl(); ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setTarget(userService); UserService proxy = (UserService) pih.getProxy(); proxy.delete(); } }
20.AOP实现方式一 1.什么是AOP
AOP(Aspect Oriented Programming)意为: 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要的内容,是函数式编程的一种衍生泛型,利用AOP可以对 业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高了开发的效率
2.AOP在Spring中的作用
提供声明式事务,允许用户自定义切面 横切关注点: 跨越应用程序多个模块的方法或功能,即与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等 切面(Aspect) 横切关注点,被模块化的特殊对象,它是一个类 通知(Advice) 切面必须要完成的工作,它是类中的一个方法 目标(Target) 被通知的对象 代理(Proxy) 向目标对象应用通知之后创建的对象 切入点(PointCut) 切面通知执行的”地点”的定义 连接点(JoinPoint) 与切入点匹配的执行点
SpringAOP中,通过advice定义横切逻辑,Spring中支持5种类型的Advice: 通知类型 连接点 实现接口 前置通知 方法前 org.springframework.aop.MethodBeforeAdvice 后置通知 方法后 org.springframework.aop.AfterReturningAdvice 环绕通知 方法前后 org.aopallicance.intercept.MethodInterceptor 异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice 引介通知 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor 即AOP在不改变原有代码的情况下,去增加新的功能
3.使用Spring实现AOP
[重点] 使用AOP织入,需要导入一个依赖包
1 2 3 4 5 <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.4</version > </dependency >
通过给增删改查方法添加方法执行前后打印的方式来实践对AOP的理解
方式一:使用Spring的api接口 [spring API接口实现]
1.新建一个接口UserService
1 2 3 4 5 6 7 8 package com.markup.service;public interface UserService { public void add () ; public void delete () ; public void update () ; public void quert () ; }
2.新建接口的实现UserServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.markup.service;public class UserServiceImpl implements UserService { public void add () { System.out.printf("增加了一个用户" ); } public void delete () { System.out.printf("删除了一个用户" ); } public void update () { System.out.printf("修改了一个用户" ); } public void quert () { System.out.printf("查询了一个用户" ); } }
3.添加检查方法执行时的Log方法
1 2 3 4 5 6 7 8 9 10 11 12 import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class Log implements MethodBeforeAdvice { public void before (Method method, Object[] args, Object target) throws Throwable { System.out.printf(target.getClass().getName()+"的" +method.getName()+"被执行了" ); } }
4.添加方法执行后的AfterLog方法
1 2 3 4 5 6 7 8 9 10 11 import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class AfterLog implements AfterReturningAdvice { public void afterReturning (Object returnValue, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了" + method.getName() + "方法,返回结果为: " + returnValue); } }
5.编写Spring Aop的配置文件 applicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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" 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" > <bean id ="userService" class ="com.markup.service.UserServiceImpl" /> <bean id ="log" class ="com.markup.log.Log" /> <bean id ="afterLog" class ="com.markup.log.AfterLog" /> <aop:config > <aop:pointcut id ="pointcut" expression ="execution(* com.markup.service.UserServiceImpl.*(..))" /> <aop:advisor advice-ref ="log" pointcut-ref ="pointcut" /> <aop:advisor advice-ref ="afterLog" pointcut-ref ="pointcut" /> </aop:config > </beans >
6.测试结果 Mytest
1 2 3 4 5 6 7 8 9 10 11 12 import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Mytest { public static void main (String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml" ); UserService userService = (UserService) context.getBean("userService" ); userService.add(); } }
方式二:使用自定义类来实现api接口 [主要是切面定义]
21.AOP实现方式二 自定义类实现aop接口
1.自定义一个切入点
1 2 3 4 5 6 7 8 9 public class DiyPointCut { public void before () { System.out.printf("======方法执行前======" ); } public void after () { System.out.printf("======方法执行后======" ); } }
2.修改之前的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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" 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" > <bean id ="userService" class ="com.markup.service.UserServiceImpl" /> <bean id ="log" class ="com.markup.log.Log" /> <bean id ="afterLog" class ="com.markup.log.AfterLog" /> <bean id ="diy" class ="com.markup.diy.DiyPointCut" /> <aop:config > <aop:aspect ref ="diy" > <aop:pointcut id ="point" expression ="execution(* com.markup.service.UserServiceImpl.*(..))" /> <aop:before method ="before" pointcut-ref ="point" /> <aop:after method ="after" pointcut-ref ="point" /> </aop:aspect > </aop:config > </beans >
3.之前的测试文件不变,执行之后即可发现在方法执行前后都有打印输出
22.注解实现AOP 方式三:使用注解方式实现AOP
1.新建注解使用的切入点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspect public class AnnotationPointCut { @Before ("execution(* com.markup.service.UserServiceImpl.*(..))" ) public void before () { System.out.printf("------方法执行前------" ); } @After ("execution(* com.markup.service.UserServiceImpl.*(..))" ) public void after () { System.out.printf("-----方法执行后-------" ); } @Around ("execution(* com.markup.service.UserServiceImpl.*(..))" ) public void around (ProceedingJoinPoint pjp) throws Throwable { System.out.printf("环绕前" ); Signature signature = pjp.getSignature(); System.out.println("signature" + signature); Object proceed = pjp.proceed(); System.out.printf("环绕后" ); } }
2.修改之前的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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" 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" > <bean id ="userService" class ="com.markup.service.UserServiceImpl" /> <bean id ="log" class ="com.markup.log.Log" /> <bean id ="afterLog" class ="com.markup.log.AfterLog" /> <bean id ="annotationPointCut" class ="com.markup.diy.AnnotationPointCut" /> <aop:aspectj-autoproxy /> </beans >
3.执行测试结果Mytest
23.回顾Mybatis 步骤:
导入相关jar包 junit mybatis mysql spring aop织入 mybatis-spring [new]
编写配置文件
测试
回忆mybatis
0.导包编写pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 <?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" > <parent > <artifactId > com.kuang.</artifactId > <groupId > spring-study</groupId > <version > 1.0-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > spring-10-mybatis</artifactId > <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.2</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.2.10.RELEASE</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.4</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 2.0.2</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.16.10</version > </dependency > </dependencies > <build > <resources > <resource > <directory > src/main/java</directory > <includes > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build > </project >
1.编写实体类
1 2 3 4 5 6 7 8 9 10 package com.kuang.pojo;import lombok.Data;@Data public class User { private int id; private String name; private String pwd; }
2.编写核心配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?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 > <typeAliases > <package name ="com.kuang.pojo" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/mybatis?useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="admin" /> <property name ="password" value ="123456" /> </dataSource > </environment > </environments > <mappers > <mapper class ="com.kuang.mapper.UserMapper" /> </mappers > </configuration >
3.编写接口
1 2 3 4 5 6 7 8 9 package com.kuang.mapper;import com.kuang.pojo.User;import java.util.List;public interface UserMapper { public List<User> selectUser () ; }
4.编写Mapper.xml, 注册接口
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.kuang.mapper.UserMapper" > <select id ="selectUser" resultType ="User" > select * from mybatis.user; </select > </mapper >
5.测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import com.kuang.mapper.UserMapper;import com.kuang.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class Mytest { @Test public void test1 () throws IOException { String resources = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resources); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper mapper = sqlSession.getMapper(UserMapper.class ) ; List<User> users = mapper.selectUser(); for (User user : users) { System.out.println(user); } } }
24.整合Mybatis方式一 Mybatis-Spring测试修改前面回顾Mybatis的代码
1.编写数据源配置
创建spring-dao.xml的文件,配置dataSource 创建对应的数据源之后,可以清理mybatis-config.xml中的数据源配置信息,通过spring来管理数据源
1 2 3 4 5 6 7 8 9 <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?useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="admin" /> <property name ="password" value ="123456" /> </bean >
2.sqlSessionFactory
要和Spring一起使用Mybatis,需要在Spring应用上下文中定义至少两样东西:一个SqlSessionFactory和至少一个数据映射器类
在MyBatis-Spring中,可使用SqlSessionFactoryBean来创建SqlSessionFactory,要配置这个工厂bean,只需要把下面代码放在Spring的XML配置文件中
1 2 3 4 5 6 7 <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="configLocation" value ="classpath:mybatis-config.xml" /> <property name ="mapperLocations" value ="classpath:com/kuang/mapper/*.xml" /> </bean >
3.sqlSessionTemplate
就是我们需要管理的sqlSession
1 2 3 4 5 <bean id ="sqlSession" class ="org.mybatis.spring.SqlSessionTemplate" > <constructor-arg index ="0" ref ="sqlSessionFactory" /> </bean >
4.需要给接口加实现类
创建一个UserMapperImpl实现类,spring无法直接创建mybatis的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class UserMapperImpl implements UserMapper { private SqlSessionTemplate sqlSession; public void setSqlSession (SqlSessionTemplate sqlSession) { this .sqlSession = sqlSession; } public List<User> selectUser () { UserMapper mapper = sqlSession.getMapper(UserMapper.class ) ; return mapper.selectUser(); } }
5.将自己写的实现类注入mybatis
1 2 3 <bean id ="userMapper" class ="com.kuang.mapper.UserMapperImpl" > <property name ="sqlSession" ref ="sqlSession" /> </bean >
6.测试
1 2 3 4 5 6 7 8 @Test public void test () { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-dao.xml" ); UserMapper userMapper = applicationContext.getBean("userMapper" , UserMapper.class ) ; for (User user : userMapper.selectUser()) { System.out.println(user); } }
完整spring-dao.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?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" 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" > <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?useUnicode=true& characterEncoding=UTF-8" /> <property name ="username" value ="admin" /> <property name ="password" value ="123456" /> </bean > <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="configLocation" value ="classpath:mybatis-config.xml" /> <property name ="mapperLocations" value ="classpath:com/kuang/mapper/*.xml" /> </bean > <bean id ="sqlSession" class ="org.mybatis.spring.SqlSessionTemplate" > <constructor-arg index ="0" ref ="sqlSessionFactory" /> </bean > <bean id ="userMapper" class ="com.kuang.mapper.UserMapperImpl" > <property name ="sqlSession" ref ="sqlSession" /> </bean > </beans >
修改后的mybatis-config.xml文件
1 2 3 4 5 6 7 8 9 10 <?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 > <typeAliases > <package name ="com.kuang.pojo" /> </typeAliases > </configuration >
25.整合Mybatis方式二 通过继承SqlSessionDaoSupport直接返回sqlSession
1 2 3 4 5 public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser () { return getSqlSession().getMapper(UserMapper.class ).selectUser () ; } }
配置applicationContext.xml文件
1 2 3 4 5 6 7 8 9 <import resource ="spring-dao.xml" /> <bean id ="userMapper" class ="com.kuang.mapper.UserMapperImpl" > <property name ="sqlSession" ref ="sqlSession" /> </bean > <bean id ="userMapper2" class ="com.kuang.mapper.UserMapperImpl2" > <property name ="sqlSessionFactory" ref ="sqlSessionFactory" /> </bean >
测试结果:
1 2 3 4 5 6 7 8 @Test public void test2 () { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml" ); UserMapper userMapper = applicationContext.getBean("userMapper2" , UserMapper.class ) ; for (User user : userMapper.selectUser()) { System.out.println(user); } }
26.事务回顾 回顾事务
把一组业务当成一个业务来做,要么都成功,要么都失败
事务在项目开发中,十分的重要,涉及到数据一致性的问题,不能马虎
确保完整性和一致性
事务的ACID原则
原子性
一致性
隔离性:多个业务可能操作同一个资源,防止数据损坏
持久性:事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中
27.Spring声明式事务 声明式事务:AOP
编程式事务: 需要在代码中,进行事务的管理
在配置文件中声明事务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <constructor-arg ref ="dataSource" /> </bean > <tx:advice id ="txAdvice" transaction-manager ="transactionManager" > <tx:attributes > <tx:method name ="add" propagation ="REQUIRED" /> <tx:method name ="delete" propagation ="REQUIRED" /> <tx:method name ="update" propagation ="REQUIRED" /> <tx:method name ="query" read-only ="true" /> <tx:method name ="*" propagation ="REQUIRED" /> </tx:attributes > </tx:advice > <aop:config > <aop:pointcut id ="txPointCut" expression ="execution(* com.kuang12.mapper.*.*(..))" /> <aop:advisor advice-ref ="txAdvice" pointcut-ref ="txPointCut" /> </aop:config >
自定义mapper中添加错误的方法 delete写成deletes
1 2 3 4 5 6 7 <insert id ="addUser" parameterType ="User" > insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd}); </insert > <delete id ="deleteUser" parameterType ="int" > deletes from mybatis.user where id = #{id} </delete >
在测试中提交事务的时候,要么一起成功,要么一起失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser () { User user = new User(5 , "jiji" , "123123" ); UserMapper mapper = getSqlSession().getMapper(UserMapper.class ) ; mapper.addUser(user); mapper.deleteUser(1 ); return getSqlSession().getMapper(UserMapper.class ).selectUser () ; } public int addUser (User user) { return getSqlSession().getMapper(UserMapper.class ).addUser (user ) ; } public int deleteUser (int id) { return getSqlSession().getMapper(UserMapper.class ).deleteUser (id ) ; } }
28.回顾和总结 附录 注解说明 @Autowired: 自动装配通过类型名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=”xxx”)来指定对象
@Nullable: 字段标记了这个注解,说明这个字段可以为null
@Rresource: 自动装配通过名字,类型
@Component: 组件,放在类上,说明这个类被Spring管理了,就是bean