Spring5从基础入门到深入理解IOC、DI与AOP原理

文章目录

    • 1.Spring
      • 1.1简介
      • 1.2优点
      • 1.3组成
      • 1.4扩展
    • 2.IOC理论推导
      • 2.1实现程序进行理解
      • 2.2**IOC本质**
    • 3.HelloSpring
    • 4.IOC创建对象的方式
      • 4.1、使用无参构造方法来创建对象
      • 4.2、使用有参构造方法来创建对象
    • 5.Spring配置(重点)
      • 5.1别名
      • 5.2Bean的配置
      • 5.3import
    • 6.依赖注入(重点)
      • 6.1构造器注入
      • 6.2Set方式注入【重点】
      • 6.3拓展方式
      • 6.4bean的作用域
      • 【搭建环境步骤】
    • 7.Bean的自动装配(重点)
      • 7.1测试
      • 7.2ByName自动装配
      • 7.4使用注解自动装配
        • 7.4.1@Autowired
        • 7.4.2@Resource
    • 8.使用注解开发
    • 9.使用Java的方式配置Spring
    • 10.代理模式
      • 10.1静态代理
      • 10.2动态代理
    • 11.AOP
      • 11.1什么是AOP
      • 11.2AOP在Spring的作用
      • 11.3使用Spring实现AOP
    • 12.整合MyBatis
      • 12.1回忆MyBatis
      • 12.2Mybatis-spring
    • 13.声明式事务
      • 13.1回顾事务
      • 13.2具体步骤
      • 13.3Spring中的事务管理

Spring框架在Java开发中占有极其重要的地位,但是到底什么是Spring,Spring怎么使用以及为什么要使用Spring,接下来让我们详细的了解一下!!!

注:本篇文章大部分内容参照于B站狂神说老师的Spring5视频编写,仅供大家参考学习,重点内容已经做了标记,视频原地址为【狂神说Java】Spring5最新完整教程IDEA版通俗易懂,大家记得一键三连啊*.。(๑・∀・๑)*.

1.Spring

1.1简介

  • Spring:春天------->给软件行业带来了春天
  • 2002年,首次推出了Spring框架的雏形:interface21框架
  • Spring框架以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日正式发布了1.0版本
  • RodJohnson ,SpringFrameWork的创始人
  • Spring理念:使现有的技术更高价容易使用,本身是一个大杂烩,整合了现有的技术框架
  • SSH:Struct2+Spring+Hibernate
  • SSM:SpringMvc+Spring+Mybatis
  • 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/overview.html#overview
  • 官方下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/
  • GitHub地址:https://github.com/spring-projects/spring-framework
  • Maven地址

<dependency><groupId>org.springframeworkgroupId><artifactId>spring-webmvcartifactId><version>5.3.18version>
dependency>

<dependency><groupId>org.springframeworkgroupId><artifactId>spring-jdbcartifactId><version>5.3.18version>
dependency>

1.2优点

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级的,非入侵式的框架
  • 控制反转(IOC) 面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

1.3组成

1.4扩展

  • Spring Boot

    • 一个快速开发的脚手架

    • 基于Spring Boot可以快速的开发单个微服务

    • 约定大于配置

  • Spring Cloud

    • Spring Cloud是基于Spring Boot实现的

因为现在大多数公司都在使用SpringBooti进行快速开发,学习SpringBoot的前提,需要完全掌握Spring.及
SpringMVC!承上启下的作用!

2.IOC理论推导

2.1实现程序进行理解

  1. UserDao接口

    public interface UserDao {void getUser();
    }
    
  2. UserDaolmpl实现类

    public class UserDaoImpl implements UserDao {@Overridepublic void getUser() {System.out.println("默认获取用户的数据");}
    }
    
  3. UserService业务接口

    public interface UserService {void getUser();
    }
    
  4. UserServicelmpl业务实现类

public class UserServiceImpl implements UserService{private UserDao userDao = new UserDaoImpl();private UserDao userDao = new UserDaoMysqlImpl();@Overridepublic void getUser() {userDao.getUser();}
}

5.beans.xml


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="mysqlImpl" class="com.qjd.dao.UserDaoMysqlImpl"/><bean id="oracleImpl" class="com.qjd.dao.UserDaoOracleImpl"/><bean id="UserServiceImpl" class="com.qjd.service.UserServiceImpl"><property name="userDao" ref="mysqlImpl" />bean>beans>

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码 ,如果程序的代码量十分大,修改一次的成本代价十分昂贵

我们使用一个set接口实现,已经发生了革命性的变化

public class UserServiceImpl implements UserService{private UserDao userDao;
//利用set进行动态实现值的注入public void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void getUser() {userDao.getUser();}
}
  • ·之前,程序是主动创建对象!控制权在程序猿手上!
  • ·使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!

这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !

2.2IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

原文链接:https://blog.csdn.net/qq_33369905/article/details/106647330

3.HelloSpring

1、编写一个Hello实体类

public class Hello {private String str;public String getStr() {return str;}public void setStr(String str) {this.str = str;}@Overridepublic String toString() {return "Hello{" +"str='" + str + '\'' +'}';}
}

2、编写我们的spring文件 , 这里我们命名为beans.xml


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="hello" class="com.qjd.pojo.Hello"><property name="str" value="Spring"/>bean>beans>

3、测试

public class MyTest {public static void main(String[] args) {//使用xml加载就必须使用这句话//获取Spring的上下文对象ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");//我们的对象现在都在Spring中管理了,我们要使用,直接去里面取出来就可以了Hello hello =(Hello) context.getBean("hello");System.out.println(hello.toString());}
}

思考

  • Hello 对象是谁创建的 ? 【hello 对象是由Spring创建的】

  • Hello 对象的属性是怎么设置的 ? 【hello 对象的属性是由Spring容器设置的】

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的

  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

  • 依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收

现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所以总结IOC:对象由Spring创建,管理,装配

4.IOC创建对象的方式

4.1、使用无参构造方法来创建对象

4.2、使用有参构造方法来创建对象

  • 下标赋值

        <bean id="user" class="com.qjd.pojo.User"><constructor-arg index="0" value="qjd学Spring"/>bean>
    
  • 通过类型创建

        <bean id="user" class="com.qjd.pojo.User"><constructor-arg  type="java.lang.String" value="qjd一定能学会"/>bean>
    
  • 直接通过参数名

        <bean id="user" class="com.qjd.pojo.User"><constructor-arg name="name" value="qjd真的能学会"/>bean>
    

总结:在配置文件加载的时候,容器中的管理对象就已经初始化了

5.Spring配置(重点)

5.1别名


5.2Bean的配置


<bean id="UserT" class="com.qjd.pojo.UserT" name="user2 u2,u3;u4" ><property name="name" value="qjd"/>
bean>

5.3import

import一般用于团队开发使用,他可以将多个配置文件,导入合并为一个

将不同人编写bean合并在一个applicationContext.xml中




6.依赖注入(重点)

6.1构造器注入

参照4

6.2Set方式注入【重点】

  • 依赖注入(Dependency Injection,DI):set注入
  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

【搭建环境】

  1. 复杂类型-----Address

    public class Address {private String address;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "Address{" +"address='" + address + '\'' +'}';}
    }
    
  2. 真实测试对象----Student

    public class Student {private String name;private Address address;private String[] books;private List<String> hobbys;private Map<String,String> card;private Set<String> game;private String wife;@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", address=" + address.toString() +", books=" + Arrays.toString(books) +", hobbys=" + hobbys +", card=" + card +", game=" + game +", 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> getGame() {return game;}public void setGame(Set<String> game) {this.game = game;}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;}private Properties info;}
    
  3. beans.xml

    同5

  4. 测试类

    public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");Student student = (Student) context.getBean("student");
    //        System.out.println(student.getName());System.out.println(student.toString());
    /*
    * Student{
    * name='张三',
    * address=Address{address='大连'},
    * books=[三体, 西行纪, 魁拔],
    * hobbys=[唱, 跳, rap, 打篮球],
    * card={身份证=1234567890, 银行卡=0987654321},
    * game=[COD, CSGO, PUBG], wife='null',
    * info={学号=20030099, 性别=男, password=123456, url=........, driver=..., 姓名=李四, username=qjd}}
    * */}
    }
  5. 完善注入信息

    
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="address" class="com.qjd.pojo.Address"><property name="address" value="大连"/>bean><bean id="student" class="com.qjd.pojo.Student" ><property name="name" value="张三"/><property name="address" ref="address"/><property name="books"><array><value>三体value><value>西行纪value><value>魁拔value>array>property><property name="hobbys"><list><value>value><value>value><value>rapvalue><value>打篮球value>list>property><property name="card"><map><entry key="身份证" value="1234567890"/><entry key="银行卡" value="0987654321"/>map>property><property name="game"><set><value>CODvalue><value>CSGOvalue><value>PUBGvalue>set>property><property name="wife" ><null/>property><property name="info"><props><prop key="学号">20030099prop><prop key="性别">prop><prop key="姓名">李四prop><prop key="driver">...prop><prop key="url">........prop><prop key="username">qjdprop><prop key="password">123456prop>props>property>bean>beans>
    

6.3拓展方式

我们可以使用p命名和c命名空间进行注入


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:c="http://www.springframework.org/schema/c"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.qjd.pojo.User" p:name="王五" p:age="19"/><bean id="user2" class="com.qjd.pojo.User" c:age="18" c:name="王六"/>beans>

测试:

    @Testpublic void test2(){ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
//        User user =(User) context.getBean("user");User user1 = context.getBean("user2", User.class);System.out.println(user1);}

注意:p命名和c命名空间不能直接使用,需要导入xml约束

6.4bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象

  1. 单例模式(Spring默认机制,以下是显式表示)

    
    
  2. 原型模式:每次从容器中get的时候都会产生一个新对象

    
    
  3. request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境

【搭建环境步骤】

  1. 在pojo下创建实体类(包括复杂类型和真实测试对象)
  2. 在resources下编写beans.xml
  3. 测试

7.Bean的自动装配(重点)

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

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;(参照5)
  2. 在java中显式配置;
  3. 隐式的bean发现机制和自动装配【重要】

7.1测试

1.搭建环境: 一个人有两个宠物

7.2ByName自动装配



7.3ByType自动装配

    <bean id="cat" class="com.qjd.pojo.Cat"/><bean id="dog1" class="com.qjd.pojo.Dog"/><bean id="people" class="com.qjd.pojo.People" autowire="byType"><property name="name" value="老八"/>bean>

小结:

  • byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
  • bytypet的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

7.4使用注解自动装配

jdk1.5开始支持注解,spring2.5开始全面支持注解

要使用注解须知:

  • 导入约束:context约束

  • 配置注解的支持(切记不要忘记); context:annotation-config/

    
    <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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/>beans>
    
7.4.1@Autowired

直接在属性上使用即可,也可以在set方式上使用

使用@Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname

测试:

1、将User类中的set方法去掉,使用@Autowired注解

public class User {@Autowiredprivate Cat cat;@Autowiredprivate Dog dog;private String str;public Cat getCat() {return cat;}public Dog getDog() {return dog;}public String getStr() {return str;}
}

2、此时配置文件内容

<context:annotation-config/><bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

3、测试结果成功

科普:

@Nullable:字段标记了这个注解,说明这个字段可以为null

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

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

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byType的方式自动装配
  • @Qualifier不能单独使用

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以
使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!

测试

1、配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

2、没有加Qualifier测试,直接报错

3、在属性上添加Qualifier注解

@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;

测试,成功输出!

7.4.2@Resource
  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

测试:

1、实体类

public class User {//如果允许对象为null,设置required = false,默认为true@Resource(name = "cat2")private Cat cat;@Resourceprivate Dog dog;private String str;
}

2、beans.xml

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

3、测试成功

4、修改配置文件

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>

5、实体类只保留注解

@Resource
private Cat cat;
@Resource
private Dog dog;

测试结果:成功

结论:先进行byName查找,失败;再进行byType查找,成功。

小结
@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在属性字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

8.使用注解开发

在spring4之后,想要使用注解形式,必须得要引入aop的包

在配置文件当中,还得要引入一个context约束


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/>beans>
  1. bean

    import org.springframework.stereotype.Component;
    //等价于
    //@Component 组件
    @Component
    public class User {public String name="张三";
    }
    
  2. 属性如何注入

    public class User {//等价于 //        //    //也可以放在set方法上@Value("陆毅")public String name;
    }
    
  3. 衍生的注解

    @Component有几个衍生注解,我们在web开发中,会按照MVC三层架构分层

    ​ 1.dao【@Repository】

    ​ 2.service【@Service】

    ​ 3.controller【@Controller】

    这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean,只是在不同的包下使用

  4. 自动装配

    1.@Autowired:自动装配通过类型,名字如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")去配置@Autowired的使用2.@Nullable:字段标记了这个注解,说明这个字段可以为null3.@Resource:自动装配通过名字,类型
    
  5. 作用域

    @Scope("")
    
  6. 小结

    XML与注解比较

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

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

  • xml管理Bean

  • 注解完成属性注入

  • 使用过程中, 可以不用扫描,扫描是为了类上的注解

  • 一定要开启注解支持

     
    <context:component-scan base-package="com.qjd"/>
    <context:annotation-config/>
    

9.使用Java的方式配置Spring

我们现在要完全不使用Spring的xml配置了,全权交给Java来做

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

测试:spring-07-appconfig

1.pojo(实体类)

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这里这个注解的意思是说明这个类被Spring接管,注册到了容器中
@Component
public class User {private String name;public String getName() {return name;}//给属性注入值@Value("okkk")public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}
}

2.config(相当于配置文件)

//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component,
// @Configuration代表这是一个配置类,就类似于beans.xml
@Configuration
@ComponentScan("com.qjd")
@Import(QjdConfig2.class)
public class QjdConfig {
//注册一个bean就相当于之前写的一个bean标签,
// 这个方法的名字相当于id,返回值相当于class@Beanpublic User getUser(){return new User();//就是返回要注入到bean的对象}}

3.MyTest

public class MyTest {public static void main(String[] args) {//如果完全使用了配置类的方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(QjdConfig.class);//这里取得是方法名User user = context.getBean("getUser", User.class);System.out.println(user.getName());}
}

10.代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理! 【SpringAOP和SpringMVC必问】

学习代理模式会大量用到反射的知识,如果反射的知识遗忘可以参考以下文章:
Java—注解与反射

代理模式:

  • 静态代理
  • 动态代理

10.1静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 访问代理对象的人

测试:spring-08-proxy

  1. 接口

    //租房
    public interface Rent {public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host  implements Rent{public void rent(){System.out.println("房东要出租房子");}}
    
  3. 代理角色

    public class Proxy implements Rent{private Host host;public Proxy(Host host) {this.host = host;}public Proxy() {}@Overridepublic void rent() {seeHouse();host.rent();hetong();fare();}//看房public void seeHouse(){System.out.println("中介领你看房");}//收中介费public void fare(){System.out.println("收中介费");}//签合同public void hetong(){System.out.println("签租赁合同");}
    }
    
  4. 客户端访问代理角色

    public class Client {public static void main(String[] args) {//房东要出租房子Host host = new Host();//去找代理,代理帮房东,一般会有附属操作Proxy proxy = new Proxy(host);proxy.rent();}
    }
    

静态代理的好处:

可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .

公共的业务由代理来完成 . 实现了业务的分工 ,

公共业务发生扩展时变得更加集中和方便 .

缺点 :

类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

10.2动态代理

动态代理的角色和静态代理的一样 .

动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

  • 基于接口的动态代理----JDK动态代理

  • 基于类的动态代理–cglib

现在用的比较多的是 javasist 来生成动态代理 .

我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看

【InvocationHandler:调用处理程序】

代码实现:

  • Rent . java 即抽象角色

    //租房
    public interface Rent {public void rent();
    }
    
  • Host . java 即真实角色

    //房东
    public class Host implements Rent {public void rent(){System.out.println("房东要出租房子");}}
    
  • ProxyInvocationHandler. java 即代理角色

    //我们会用这个类自动生成代理类
    public class ProxyInvocationHandler  implements InvocationHandler {//被代理的接口private Rent rent;public void setRent(Rent rent) {this.rent = rent;}//生成得到代理类public Object getProxy(){return  Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this );}//处理代理实例,并返回结果@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//动态代理的本质就是使用反射机制实现seeHouse();Object result = method.invoke(rent, args);fare();return result;}public void seeHouse(){System.out.println("中介带看房子");}public void fare(){System.out.println("收中介费");}
    }
    
  • Client . java

    public class Client {public static void main(String[] args) {//真实角色Host host = new Host();//代理角色:现在没有ProxyInvocationHandler pih = new ProxyInvocationHandler();//用过调用程序处理角色来处理我们要调用的接口对象pih.setRent(host);//这里的Proxy就是动态生成的,我们并没有写Rent proxy = (Rent) pih.getProxy();proxy.rent();}
    }
    

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、

深化理解:

​ 我们来使用动态代理实现代理我们后面写的UserService!

​ 我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

//我们会用这个类自动生成代理类
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 );}//处理代理实例,并返回结果@Overridepublic 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.println("执行了"+msg+"方法");}}

测试:

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.update();}
}

动态代理的好处

  • 静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .

  • 公共的业务由代理来完成 . 实现了业务的分工 ,

  • 公共业务发生扩展时变得更加集中和方便 .

  • 一个动态代理 , 一般代理某一类业务

  • 一个动态代理可以代理多个类,代理的是接口

11.AOP

11.1什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

11.2AOP在Spring的作用

提供声明式事务;允许用户自定义切面

以下名词需要了解下:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

  • 目标(Target):被通知对象。

  • 代理(Proxy):向目标对象应用通知之后创建的对象。

  • 切入点(PointCut):切面通知 执行的 “地点”的定义。

  • 连接点(JointPoint):与切入点匹配的执行点。

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

11.3使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!


<dependency><groupId>org.aspectjgroupId><artifactId>aspectjweaverartifactId><version>1.9.4version>
dependency>

方式一:使用Spring的API接口【主要是SpringAPI接口实现】

代码实现:

首先编写我们的业务接口和实现类

public interface UserService {public void add();public void delete();public void update();public void select();
}
public class UserServiceImpl implements UserService{@Overridepublic void add() {System.out.println("增加了一个用户");}@Overridepublic void delete() {System.out.println("删除了一个用户");}@Overridepublic void update() {System.out.println("修改了一个用户");}@Overridepublic void select() {System.out.println("查询了一个用户");}
}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log  implements MethodBeforeAdvice {//method:要执行目标的方法//Object:参数//target:目标对象@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");}
}
public class AfterLog implements AfterReturningAdvice {//returnValue:返回值@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);}
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .


<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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"
><bean id="userService" class="com.qjd.service.UserServiceImpl"/><bean id="log" class="com.qjd.log.Log"/><bean id="after" class="com.qjd.log.AfterLog"/>

<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.qjd.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/><aop:advisor advice-ref="after" pointcut-ref="pointcut"/>aop:config>
beans>

测试

public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");//动态代理的是接口:注意这里只能使用强转UserService userService = (UserService) context.getBean("userService");userService.add();}
}

结果:

方式二:使用自定义类来实现AOP【主要是切面定义】

在新包下自定义一个类

public class DiyPointCut {public void  before(){System.out.println("=========方法执行前=============");}public void  after(){System.out.println("=========方法执行后=============");}
}

beans.xml

    <!-- 方式二:自定义类   --><bean id="diy" class="com.qjd.diy.DiyPointCut"/><aop:config>
<!--自定义切面:ref要引用的类--><aop:aspect ref="diy">
<!-- 切入点--><aop:pointcut id="point" expression="execution(* com.qjd.service.UserServiceImpl.*(..))"/>
<!--通知--><aop:before method="before" pointcut-ref="point"/><aop:after method="after" pointcut-ref="point"/></aop:aspect></aop:config>

MyTest不变测试结果如下

方式三:使用注解实现AOP

自定义一个类

@Aspect       //标注这个类是一个切面
public class AnnotationPointCut {@Before("execution(* com.qjd.service.UserServiceImpl.*(..))")public void before(){System.out.println("=========方法执行前=============");}@After("execution(* com.qjd.service.UserServiceImpl.*(..))")public void  after(){System.out.println("=========方法执行后=============");}//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点@Around("execution(* com.qjd.service.UserServiceImpl.*(..))")public void around(ProceedingJoinPoint jp) throws Throwable{System.out.println("环绕前");Signature signature = jp.getSignature();//获得签名System.out.println("signature"+signature);//执行方法Object proceed = jp.proceed();System.out.println("环绕后");}}

beans.xml

<bean id="annotationPointCut" class="com.qjd.diy.AnnotationPointCut"/><aop:aspectj-autoproxy/>

结果;

12.整合MyBatis

步骤:

  1. 导入相关jar包

    junit

<dependency><groupId>junitgroupId><artifactId>junitartifactId><version>4.12version>
dependency>

mybatis

<dependency><groupId>org.mybatisgroupId><artifactId>mybatisartifactId><version>3.5.2version>
dependency>

mysql-connector-java

<dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>5.1.47version>
dependency>

spring相关

<dependency><groupId>org.springframeworkgroupId><artifactId>spring-webmvcartifactId><version>5.1.10.RELEASEversion>
dependency>
<dependency><groupId>org.springframeworkgroupId><artifactId>spring-jdbcartifactId><version>5.1.10.RELEASEversion>
dependency>

aspectJ AOP 织入器


<dependency><groupId>org.aspectjgroupId><artifactId>aspectjweaverartifactId><version>1.9.4version>
dependency>

mybatis-spring整合包 【重点】

<dependency><groupId>org.mybatisgroupId><artifactId>mybatis-springartifactId><version>2.0.2version>
dependency>

配置Maven静态资源过滤问题!

<build><resources><resource><directory>src/main/javadirectory><includes><include>**/*.propertiesinclude><include>**/*.xmlinclude>includes><filtering>truefiltering>resource>resources>
build>

2.编写配置文件

3.测试

12.1回忆MyBatis

1.编写实体类

2.编写核心配置文件

3.编写接口

4.编写Mapper.xml

5.测试

12.2Mybatis-spring

核心;


<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/qjd/mapper/*.xml"/>
bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg index="0" ref="sqlSessionFactory"/>
bean>

<bean id="userMapper" class="com.qjd.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/>
bean>

1.编写数据源配置

2.sqlSessionFactory

3.sqlSessionTempiate

4.需要给接口加实现类

5.将自己写的实现类注入到Spring中,测试使用

大体步骤如下:

13.声明式事务

13.1回顾事务

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
  • 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

  • 原子性(atomicity)

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

  • 一致性(consistency)

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

  • 隔离性(isolation)

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

  • 持久性(durability)

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

13.2具体步骤

1.新建项目在pom.xml导入依赖

<dependencies><dependency><groupId>junitgroupId><artifactId>junitartifactId><version>4.12version>dependency><dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>8.0.29version>dependency><dependency><groupId>org.mybatisgroupId><artifactId>mybatisartifactId><version>3.5.1version>dependency><dependency><groupId>org.springframeworkgroupId><artifactId>spring-webmvcartifactId><version>5.1.10.RELEASEversion>dependency><dependency><groupId>org.springframeworkgroupId><artifactId>spring-jdbcartifactId><version>5.1.10.RELEASEversion>dependency><dependency><groupId>org.aspectjgroupId><artifactId>aspectjweaverartifactId><version>1.9.4version>dependency><dependency><groupId>org.mybatisgroupId><artifactId>mybatis-springartifactId><version>2.0.2version>dependency><dependency><groupId>org.projectlombokgroupId><artifactId>lombokartifactId><version>1.18.24version>dependency>dependencies><build><resources><resource><directory>src/main/resourcesdirectory><includes><include>**/*.propertiesinclude><include>**/*.xmlinclude>includes><filtering>truefiltering>resource><resource><directory>src/main/javadirectory><includes><include>**/*.propertiesinclude><include>**/*.xmlinclude>includes><filtering>truefiltering>resource>resources>build>

2.建pojo包并建立实体类User

@Data     //使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法
public class User {private int id;private String name;private String pwd;
}

3.mapper包下建立UserMapper接口

public interface UserMapper {public List<User> selectUser();
}

4.导入mybatis配置文件


DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>
<typeAliases><package name="com.qjd.pojo"/>typeAliases>



configuration>

5.整合mybatis------spring-mapper.xml(dao)

  • 配置数据源
  • sqlSessionFactory
  • sqlSession

<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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF8"/><property name="username" value="root"/><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/qjd/mapper/*.xml"/>bean><bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg index="0" ref="sqlSessionFactory"/>bean><bean id="userMapper" class="com.qjd.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/>bean>beans>

6.把UserMapper.xml放在与接口同包


DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.qjd.mapper.UserMapper"><select id="selectUser" resultType="user">select *from mybatis.userselect>
mapper>

7.把实现类UserMapperImpl放在mapper下于接口同包(这里使用第一种实现方法,就是不继承的那一种)

public class UserMapperImpl implements UserMapper{//在原来我们的所有操作都使用SqlSession来执行,现在都使用SqlSessionTemplateprivate SqlSessionTemplate sqlSession;public void setSqlSession(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}@Overridepublic List<User> selectUser() {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.selectUser();}
}

扩展:以下是另一种方法

public class UserMapperImpl2  extends SqlSessionDaoSupport implements UserMapper{@Overridepublic List<User> selectUser() {return getSqlSession().getMapper(UserMapper.class).selectUser();}
}

8.整合配置文件application.xml


<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><import resource="spring-mapper.xml"/><bean id="userMapper" class="com.qjd.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/>bean>beans>

9.MyTest测试

public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");UserMapper userMapper = context.getBean("userMapper", UserMapper.class);for (User user : userMapper.selectUser()) {System.out.println(user);}}}

10.测试结果-----成功

13.3Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

  • 编程式事务管理

将事务管理代码嵌到业务方法中来控制事务的提交和回滚

缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

  • 声明式事务管理

一般情况下比编程式事务好用。

将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。

将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

为什么需要配置事务?

  • 如果不配置,就需要我们手动提交控制事务;

  • 如果不配置,可能存在数据提交不一致的情况

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

到这里关于Spring5的内容就结束了,如果有遗漏和错误欢迎大家提出,我会第一时间改正!!!
最后,我在这里附上Spring中常用的注解,使用注解开发在今后会很常见,所以大家一定要记住常用的注解
##常用注解说明

1.@Autowired:自动装配通过类型,名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)去配置@Autowired的使用

2.@Nullable:字段标记了这个注解,说明这个字段可以为null

3.@Resource:自动装配通过名字,类型

4.@Component:组件,这里这个注解的意思是说明这个类被Spring接管,注册到了容器中,类似
@ComponentScan:扫描
@Component有几个衍生注解,我们在web开发中,会按照MVC三层架构分层

  ​     1.dao【@Repository】​     2.service【@Service】​     3.controller【@Controller】这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean,只是在不同的包下使用

5.@Value(“”):给属性注入值,通常写在属性或者set方法上

6.@Configuration代表这是一个配置类,就类似于beans.xml

7.@Import(QjdConfig2.class):引入另一个类

8.@Bean:下面的方法名字相当于id,返回值相当于class

9.@Aspect: 标注这个类是一个切面


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部