Spring学习

JavaSpring学习

前言:

  • spring译为春天,给软件行业带来了春天
  • 2002年:首次推出了Spring框架雏形——>interface21框架
  • Spring框架即以interface21框架为基础经过重新设计,并不断丰富其内涵,于2004年03月24日发布了1.0正式版本
  • spring理念:是现有技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架(解决企业开发的复杂性)
  • SSH:Struct2 + Spring + Hibernate
  • SSM: SpringMvc + Spring + Mybatis

下载Spring:

1.官方下载地址:https://repo.spring.io/ui/native/libs-release-local/org/springframework/spring/4.3.2.RELEASE/ (4.3.2版本较为成熟)

2.官网下载详细步骤:

spring官网——Project——Spring Framework——点击github标志(那只小猫猫)——下滑在一堆文字中找到Spring Framework Artifactsrk的链接——进入链接后在一堆文字中找到https://repo.spring.io链接——点击进入Artifacts——目录中找到libs-release-local——打开目录找到org——打开org目录找到springframework——打开目录后找到spring——选择版本后鼠标右击——选择Native Browser——点击第一个进行下载

由于下载地址现托管于GitHub官网,但是GitHub常常访问超时或者较为慢,加速访问github官网的方法:
1.打开hosts文件:
hosts路径:C:\Windows\System32\drivers\etc\hosts
2.修改管理员身份管理权限:
文件右击选择属性-安全-选择身份(user)-写入权限打勾
3.在hosts文件尾部复制如下:
199.232.69.194 github.global.ssl.fastly.net
140.82.112.4 github.com
(一般情况够用)
或者
140.82.112.4 github.com
140.82.113.3 gist.github.com
199.232.69.194 github.global.ssl.fastly.net
185.199.108.153 assets-cdn.github.com(有四个ip,使用自己速度最快的IP,在ipaddress查ssets-cdn.github.comIP)
199.232.68.133 raw.githubusercontent.com
199.232.68.133 cloud.githubusercontent.com
199.232.68.133 camo.githubusercontent.com
199.232.68.133 avatars0.githubusercontent.com
199.232.68.133 avatars1.githubusercontent.com
199.232.68.133 avatars2.githubusercontent.com
199.232.68.133 avatars3.githubusercontent.com
199.232.68.133 avatars4.githubusercontent.com
199.232.68.133 avatars5.githubusercontent.com
199.232.68.133 avatars6.githubusercontent.com
199.232.68.133 avatars7.githubusercontent.com
199.232.68.133 avatars8.githubusercontent.com

4.刷新DNS:在dos窗口输入 ipconfig /flushdns

Spring导入

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.2.0.RELEASE</version>
</dependency>

Spring的优点:

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

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

Spring中文文档

Spring Boot 中文文档

Spring Framework 中文文档

Spring Cloud 中文文档

Spring Security 中文文档

Spring Session 中文文档

Spring AMQP 中文文档

Spring Data

链接来源:https://www.jianshu.com/p/b3da0c8a22fe

Spring组成

  • 核心容器(SpringCore)
    核心容器提供Spring框架的基本功能。spring以bean的方式组织和管理Java应用的各个组件及其关系,spring使用BeanFactory来产生和管理Bean,是工厂模式的实现,BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开

  • 应用上下文(Spring Context)
    Spring上下文是一个配置文件,向spring提供上下文信息,spring上下文包括企业服务、、、、

  • Spring面向切面编程(Spring AOP)

    AOP(Aspect Oriented Programming)
    通过配置管理特性,SpringAOP模块直接将面向方法的编程功能集成在了Spring框架中,Spring管理的任何对象都支持AOP,SpringAOP模块基于Spring的应用程序中的对象提供了事务管理服务,通过使用SpringAOP,不用依赖EJB组件,就可以将声明性事务管理集成在应用程序中

  • JDBC和DAO模块(Spring DAO)
    Dao(Data Access Object)JDBC、DAO的抽象层,提供了有意义的异常层次结构实现,可用该结构来管理异常处理,和不同数据库提供商抛出的错误信息,异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。

  • 对象实体映射(Spring ORM)
    ORM(Object Relational Mapping)
    Spring插入了若干个ORM框架,提供了ORM对象的关系工具,其中包括Hibernate,JDO和IBatisSQL Map等,所有这些都遵从Spring的通用事务和DAO异常层次结构

  • Web模块(Spring Web)
    web上下文模块建立应用程序上下文模块之上,基于web的应用程序提供了上下文,所以spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作

  • MVC模块(SpringWebMVC)
    MVC(Model View Controller)
    MVC框架是一个全功能的构建Web应用程序的MVC实现,通过策略接口,MVC框架编程高度可配置的,MVC容纳了大量视图技术,其中包括JSP,POI等,模型由JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码,由c的事情。spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境,spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用

Spring Boot

  • 一个快速开发的脚手架。
  • 基于SpringBoot可以快速的开发单个微服务
  • 约定大于配置

Spring Cloud

  • SpringCloud是基于SpringBoot实现的。

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

弊端:发展太久之后,违背了原来的理念,配置十分繁琐,配置地狱

Spring IOC容器理论

IOC底层:工厂设计模式+反射/注解+XML配置文件

  • IoC 也称为“依赖注入”(DI)
  • 控制反转(IOC)就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)
  • 我们用依赖注入(Dependency Injection)这种方式来实现控制反转。所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。

1.1、IoC能做什么

IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

1.2、IOC和DI

DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即**由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IOC容器注入应用程序某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”,

2.1、IOC(控制反转)

首先想说说IOC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IOC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,**所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。由此,底层核心实现的设计模式为:工厂设计模式

2.2、DI(依赖注入)

IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

原来写业务的步骤都需要如下

  1. UserDao 接口
  2. UserDaoImpl 实现类
  3. UserService业 务接口
  4. UserServiceImpl 业务实现类

spring使用

使用spring来创建对象,在spring这些都成为Bean

  • 一般创建对象的方式:

    类型 变量名 = new 类型();

  • spring创建对象:

    <bean id ="test" class="全路径">
    	<property name="str"  value="spring">
    </bean>
    <bean id ="***" class="全路径">
    <property name="str"  ref="test">
    </bean> 
    
  
id:相当于变量名,
  
class:相当于new 的对象,
  
property: 相当于给对象中的属性设置一个值。
  
name:是属性名,
  
value:就是该属性的准确值 ,
  
ref:是引用Spring容器中创建的对象
  


对象由spring创建 

对象的属性由Spring容器设置

这个过程就叫做控制反转

控制:谁来控制对象的创建,传统引用程序的对象是由程序本身控制创建的,使用spring后,对象是右spring来创建的

反转:程序本身不创建对象,而是被动的接受对像

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

IOC是一种编程思想,由主动的编程编程被动的接受。

可以通过new ClassPathXmlApplicationContext去游览一下底层源码

总而言之:IOC就是对象由spring创建,管理,装配

可以没有get必须由set方法,否则报错



## IOC创建对象的方式

**DI 存在两个主要变体基于构造函数的依赖注入nstructor-injection)和基于 Setter 的依赖注入。**

### 1.使用无参构造创建对象,默认!

​```xml
xml<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- services -->
    <bean id="teacher" class="com.stu.pojo.Teacher">
        <property name="tid" value="2"/>
        <property name="sname" value="李老师"/>
    </bean>

    <bean id="Student" class="com.stu.pojo.Student">
        <property name="id" value="1" />
        <property name="name" value="张三"/>
        <property name="teacher" ref="teacher">
        </property>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    
</beans>

当存在有参构造时(不存在无参构造函数),报错!

Caused by: java.lang.NoSuchMethodException: com.stu.pojo.Student.()

⒉.假设我们要使用有参构造创建对象。

  1. 第一种,下标赋值1

    <bean id="Student" class="com.stu.pojo.Student">
       <constructor-arg index="0" value="1"/>
          <constructor-arg index="1" value="张三"/>
          <constructor-arg index="2" ref="teacher"/>
           <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    
  2. 第二种方式:通过类型创建,不建议使用!

    <bean id="Student" class="com.stu.pojo.Student">
         <constructor-arg type="int" value="1"/>
            <constructor-arg type="java.lang.String" value="李四"/>
            <constructor-arg type="com.stu.pojo.Teacher" ref="teacher"/>
            <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    
  3. 第三种,直接通过参数名来设置(推荐)

    <bean id="Student" class="com.stu.pojo.Student">
            <constructor-arg name="id" value="1"/>
         <constructor-arg name="name" value="李四"/>
            <constructor-arg name="teacher" ref="teacher"/>
            <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    

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

<?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">

    <!-- services -->
    <bean id="teacher" class="com.stu.pojo.Teacher">
        <property name="tid" value="2"/>
        <property name="sname" value="李老师"/>
    </bean>

<!--    <bean id="Student" class="com.stu.pojo.Student">-->
<!--        <constructor-arg index="0" value="1"/>-->
<!--        <constructor-arg index="1" value="张三"/>-->
<!--        <constructor-arg index="2" ref="teacher"/>-->
<!--        &lt;!&ndash; additional collaborators and configuration for this bean go here &ndash;&gt;-->
<!--    </bean>-->

<!--    <bean id="Student" class="com.stu.pojo.Student">-->
<!--        <constructor-arg type="int" value="1"/>-->
<!--        <constructor-arg type="java.lang.String" value="李四"/>-->
<!--        <constructor-arg type="com.stu.pojo.Teacher" ref="teacher"/>-->
<!--        &lt;!&ndash; additional collaborators and configuration for this bean go here &ndash;&gt;-->
<!--    </bean>-->
    <bean id="Student" class="com.stu.pojo.Student">
        <constructor-arg name="id" value="1"/>
        <constructor-arg name="name" value="李四"/>
        <constructor-arg name="teacher" ref="teacher"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

</beans>

4,p/c命名空间

p-namespace:

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

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="masterkaoli"/>

</beans>

c-namespace

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

    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- traditional declaration -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
        <constructor-arg value="[emailprotected]"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="[emailprotected]"/>

</beans>

c/p

<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">
    <!-- services -->
    <bean id="taecher" class="com.stu.pojo.Teacher"
          c:tid="1"
          c:tname="李老师"
    />
    <bean id="student" class="com.stu.pojo.Student"
          p:id="1"
          p:name="张三"
          p:teacher-ref="taecher"
    />
        <!-- additional collaborators and configuration for this bean go here -->
</beans>

Student{id=1, name=‘张三’, teacher=Teacher{tid=1, tname=‘李老师’}}

方法注入(set注入)

在大多数应用场景中,容器中的大多数 bean 是singletons。当单例 Bean 需要与另一个单例 Bean 协作时,或者非单例 Bean 需要与另一个非单例 Bean 协作时,通常可以通过将一个 Bean 定义为另一个 Bean 的属性来处理依赖性。当 bean 的生命周期不同时会出现问题。假设单例 bean A 需要使用非单例(原型)bean B,也许在 A 的每个方法调用上都使用。容器只创建一次单例 bean A,因此只有一次机会来设置属性。每次需要一个容器时,容器都无法为 bean A 提供一个新的 bean B 实例。

一个解决方案是放弃某些控制反转。您可以通过实现ApplicationContextAware接口来使 bean A 知道容器,并在每次 bean A 需要它时对容器进行 getBean(“ B”)调用询问(通常是新的)bean B 实例。以下是此方法的示例:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

前面的内容是不理想的,因为业务代码知道并耦合到 Spring 框架。方法注入是 Spring IoC 容器的一项高级功能,它允许以干净的方式处理此用例。

查找方法注入

查找方法注入是容器覆盖* container 受管 bean *上的方法的能力,以返回容器中另一个命名 bean 的查找结果。如上节所述,查找通常涉及原型 bean。 Spring 框架通过使用从 CGLIB 库生成字节码来动态生成覆盖该方法的子类来实现此方法注入。

Note

  • 为了使此动态子类起作用,Spring Bean 容器将子类化的类不能为final,并且要覆盖的方法也不能为final
  • 对具有abstract方法的类进行单元测试需要您自己对该类进行子类化,并提供abstract方法的存根实现。
  • 组件扫描也需要具体的方法,这需要具体的类别。
  • 另一个关键限制是,查找方法不适用于工厂方法,特别是不适用于配置类中的@Bean方法,因为在这种情况下容器不负责创建实例,因此无法在其上创建运行时生成的子类。苍蝇。

查看前面的代码片段中的CommandManager类,您会发现 Spring 容器将动态覆盖createCommand()方法的实现。您的CommandManager类将没有任何 Spring 依赖项,如在重做的示例中所示:

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

在包含要注入的方法的 Client 端类(在本例中为CommandManager)中,要注入的方法需要以下形式的签名:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果方法是abstract,则动态生成的子类将实现该方法。否则,动态生成的子类将覆盖原始类中定义的具体方法。例如:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

只要需要* myCommand * bean 的新实例,标识为* commandManager *的 bean 就会调用其自己的方法createCommand()。如果确实需要myCommand bean,则必须小心将其部署为原型。如果它是singleton,则每次都返回myCommand bean 的相同实例。

或者,在基于 Comments 的组件模型中,您可以通过@LookupComments 声明一个查找方法:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

或者,更习惯地说,您可能依赖于针对查找方法的声明返回类型来解析目标 bean:

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

请注意,您通常会使用具体的存根实现来声明此类带 Comments 的查找方法,以使其与 Spring 的组件扫描规则兼容,在默认情况下,抽象类将被忽略。在显式注册或显式导入的 Bean 类的情况下,此限制不适用。

Tip

访问范围不同的目标 bean 的另一种方法是ObjectFactory/Provider注入点。签出称为“范围 bean 作为依赖项”的部分

有兴趣的 Reader 还可以找到ServiceLocatorFactoryBean(在org.springframework.beans.factory.config包中)。

任意方法替换

与查找方法注入相比,方法注入的一种不太有用的形式是能够用另一种方法实现替换托管 bean 中的任意方法。用户可以安全地跳过本节的其余部分,直到实际需要该功能为止。

借助基于 XML 的配置元数据,您可以使用replaced-method元素将现有的方法实现替换为已部署的 Bean。考虑下面的类,该类带有一个我们要重写的方法 computeValue:

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

实现org.springframework.beans.factory.support.MethodReplacer接口的类提供了新的方法定义。

/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

用于部署原始类并指定方法覆盖的 Bean 定义如下所示:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

您可以在<replaced-method/>元素中使用一个或多个包含的<arg-type/>元素来指示要覆盖的方法的方法签名。仅当方法重载且类中存在多个变体时,才需要对参数签名。为了方便起见,参数的类型字符串可以是完全限定类型名称的子字符串。例如,以下所有都匹配java.lang.String

java.lang.String
String
Str

由于参数的数量通常足以区分每个可能的选择,因此该快捷方式通过允许您仅键入将与参数类型匹配的最短字符串,可以节省大量 Importing。

Spring配置

1.别名

使用alias 标签

<! --别名,如果添加了别名,我们也可以使用别名获取到这个对象–>

使用name属性

直接使用bean的name属性更为强大,而且name可以同时取多个别名

2、Bean的配置
id : bean的唯一标识符,也就是相当于我们学的对象名class : bean对象所对应的全限定名:包名+类型

name :也是别名,而且name可以同时取多个别名

-->

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

3、import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import|将所有的beans.xml合并为一个总的applicationContext.xml

<import resource="beans.xm1" />
<import resource="beans2.xm1" />
<import resource="beans3.xm7" />

使用的时候,直接使用总的配置就可以了

当然,也可以通过ClassPathXmlApplicationContext()传入多个xml文件

如: ClassPathXmlApplicationContext(“bean1.xml”,“bean2.xml”,“bean3.xml”…)

依赖注入

bean的作用域

ScopeDescription
singleton(默认值)每个 Spring IoC 容器将单个 bean 定义范围限制到单个对象实例。
prototype将单个 bean 定义的作用域限定为任意数量的对象实例。
request将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期;也就是说,每个 HTTP 请求都有一个自己的 bean 实例,它是在单个 bean 定义的后面创建的。仅在可感知网络的 Spring ApplicationContext中有效。
session将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
globalSession将单个 bean 定义的作用域限定为全局 HTTP Session的生命周期。通常仅在 Portlet 上下文中使用时才有效。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。

从 Spring 3.0 开始,线程作用域可用,但默认情况下未注册

单例 ,原型,请求,会话,全局会话,应用程序和 WebSocket 范围

<?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">

    <!-- services -->

    <bean id="Teacher" class="com.stu.pojo.Teacher" scope="session">
        <property name="tid" value="1" />
        <property name="sname" value="张三"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>


</beans>

简单区分单例和原型:

单例:您在单个 Spring 容器中为特定类定义一个 bean,则 Spring 容器将创建该 bean 定义所定义的类的一个并且仅一个实例,单例作用域是 Spring *中的默认作用域

原型:每次对特定 bean 发出请求时,bean 部署的非单一原型范围都会导致创建一个新 bean 实例

bean的自动装配

Spring 容器可以autowire合作 bean 之间的关系。您可以允许 Spring 通过检查ApplicationContext的内容来自动为您的 bean 解决协作者(其他 bean)。自动装配具有以下优点:

  • 自动装配可以大大减少指定属性或构造函数参数的需要。
  • 随着对象的 Developing,自动装配可以更新配置。例如,如果您需要向类中添加一个依赖项,则无需修改配置即可自动满足该依赖项。因此,自动装配在开发过程中特别有用,而不必担心当代码库变得更稳定时切换到显式接线的选择。

使用基于 XML 的配置元数据[2]时,可以使用<bean/>元素的autowire属性为 bean 定义指定自动装配模式。自动装配功能具有四种模式。您指定自动装配* per * bean,从而可以选择要自动装配的对象。

自动装配模式

ModeExplanation
no(默认)无自动装配。 Bean 引用必须通过ref元素定义。对于大型部署,建议不要更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。
byName按属性名称自动布线。 Spring 寻找与需要自动装配的属性同名的 bean。例如,如果将一个 bean 定义设置为按名称自动装配,并且包含一个* master 属性(即,它具有一个 setMaster(…)*方法),那么 Spring 将查找一个名为master的 bean 定义,并使用它来设置属性。
byType如果容器中恰好存在一个该属性类型的 bean,则允许自动装配该属性。如果存在多个错误,则会引发致命异常,这表明您可能不对该 bean 使用* byType *自动装配。如果没有匹配的 bean,则什么也没有发生。该属性未设置。
constructor与* byType *类似,但适用于构造函数参数。如果容器中不存在构造函数参数类型的一个 bean,则将引发致命错误。

使用* byType constructor 自动装配模式,您可以装配数组和类型集合。在这种情况下,将提供与预期类型匹配的容器中的所有自动装配线候选者,以满足相关性。如果期望的键类型为String,则可以自动为强类型的 Maps 连线。自动装配的 Maps 值将由与期望类型匹配的所有 bean 实例组成,并且 Maps 键将包含相应的 bean 名称。

您可以将自动装配行为与依赖检查结合起来,后者在自动装配完成后执行。

自动装配的局限性和缺点

当在项目中一致使用自动装配时,自动装配效果最佳。如果通常不使用自动装配,那么使用开发人员仅连接一个或两个 bean 定义可能会使开发人员感到困惑。

考虑自动装配的局限性和缺点:

  • propertyconstructor-arg设置中的显式依赖项始终会覆盖自动装配。您不能自动连接所谓的simple属性,例如基元,StringsClasses(以及此类简单属性的数组)。此限制是设计使然。
  • 自动装配不如显式接线精确。尽管如上表所述,Spring 会谨慎地避免猜测,以免产生可能导致意外结果的歧义,但不再显式地记录 SpringManagement 对象之间的关系。
  • 接线信息可能不适用于可能从 Spring 容器生成文档的工具。
  • 容器内的多个 bean 定义可能与要自动装配的 setter 方法或构造函数参数指定的类型匹配。对于数组,集合或 Map,这不一定是问题。但是,对于期望单个值的依赖项,不会任意解决此歧义。如果没有唯一的 bean 定义可用,则引发异常。

在++后一种情况下,您有几种选择:

  • 放弃自动布线,转而使用明确的布线。
  • 如下一节所述,通过将 Bean 定义的autowire-candidate属性设置为false来避免自动装配。
  • 通过将其<bean/>元素的primary属性设置为true,将单个 bean 定义指定为* primary *候选对象。
  • 如[第 7.9 节“基于 Comments 的容器配置”中所述,通过基于 Comments 的配置实现更细粒度的控件。
从自动装配中排除 bean

在每个 bean 的基础上,您可以从自动装配中排除一个 bean。使用 Spring 的 XML 格式,将<bean/>元素的autowire-candidate属性设置为false;容器使特定的 bean 定义对自动装配基础结构不可用(包括 Comments 样式配置,例如@Autowired)。

Note

autowire-candidate属性旨在仅影响基于类型的自动装配。它不会影响名称的显式引用,即使指定的 bean 未标记为自动装配候选,名称也将得到解析。因此,如果名称匹配,按名称自动装配仍将注入 Bean。

您还可以基于与 Bean 名称的模式匹配来限制自动装配候选。顶级<beans/>元素在其default-autowire-candidates属性内接受一个或多个模式。例如,要将自动装配候选状态限制为名称以* Repository 结尾的任何 bean,请提供 Repository 值。要提供多种模式,请在以逗号分隔的列表中定义它们。 Bean 定义autowire-candidate属性的truefalse的显式值始终优先,并且对于此类 Bean,模式匹配规则不适用。

这些技术对于您不希望通过自动装配将其注入其他 bean 的 bean 非常有用。这并不意味

Bean的自动装配

  • 自动装配是Spring满足bean依赖一种方式!
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种装配的方式
1.在xml中显示的配置

<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的 bean id !
byType:会自动在容器上下文中查找,和自己对象属性类型和同的bean!
-->
   <bean id="teacher" class="com.stu.pojo.Teacher">
        <property name="tid" value="2"/>
        <property name="sname" value="李老师"/>
    </bean>
    <bean id="Student" class="com.stu.pojo.Student" autowire="byType">
        <property name="name" value="ss"/>
        <property name="id" value="1"/>

        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

注( autowire-candidate :autowire候选描述:指示在寻找匹配的候选项以满足另一个bean的自动连接需求时是否应考虑此bean。请注意,这不会影响按名称显式引用,即使指定的bean未标记为autowire候选项,也会解决此问题。)

小结:

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

2.在java中显示配置

3.隐式的自动装配bean [重点]

注解实现自动装配(@Autowired)

jdk1.5支持的注解,Spring2.5就支持注解了 !
The introduction of annotation-based configuration raised the question of whether this approach is “better”
than XML.
要使用注解须知:
1.导入约束: context约束 :xmIns:context=“http://www. springframework. org/schema/context”
2.配置注解的支持: <context:annotation-config />

<?xml version="1.0" encoding="UTF-8"?>
<beans xmIns="http://www.springframework.org/schema/beans"
xmIns:xsi="http://www.w3.org/2001/xMLschema-instance"
xmIns:context="http://www.springframework.org/schema/context"
xsi :schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http ://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config />  <!--开启自动配置注释的支持-->
</beans>

@Autowired
直接在属性上使用即可!也可以在set方式上使用!
使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在I0C (Spring) 容器中存在,且符
合名字byname!
科普:
@Nu11ab1e 字段标记了这个注解,说明这个字段可以为null:
pub1ic @interface Autowired {
boolean requiredO) default true;
}

@Quanlifier

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解[@Autowired]完成的时候、我们可以
使用@Qulifierlvalue=xx"法配置@Autowired的使用,指定一个唯一的bean对象注入!

@Qualifier限定哪个bean应该被自动注入。当Spring无法判断出哪个bean应该被注入时,@Qualifier注解有助于消除歧义bean的自动注入。参见下面的例子,

public class Staff{    
    @Autowired    
    private user user;
}

我们有两个bean定义为Person类的实例。

<bean id="staff"class="com.test.Staff"/>

<bean id="user1"class="com.test.Us er">
<property name="name"value="zhangsan"/></bean>

<bean id="user2"class="com.test.User">
<property name="name"value="lisi"/></bean>

Spring 不知道哪个bean应该自动注入,运行上面的例子时,抛出如下异常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.test.User] is defined: expected single matching bean but found2: [user1, user2]

要解决以上问题,你需要使用@Quanlifier注解告诉Spring 哪个bean应该被autowired的。

public class Staff
{
    @Autowired
    @Qualifier("user1")
    private User user;
}


@Resource注解

public class People {

@Resource(name = "cat2")
private cat cat;

@Resource
private Dog dog;
}

小结:
@Resource和@ Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @ Autowired通过byname的方式实现,而且必须要求这个对象存在! [常用]
  • @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现! 如果两个都找不到的情况
    下,就报错! [常用]
  • 执行顺序不同:@Autowired通过byType的方式实现

使用注解开发

xml与注解比较

  • xml更加万能,适用于任何场合!维护简单方便
  • 注解不是自己类使用不了,维护相对复杂!
  • xml与注解最佳实践:
    1. xml用来管理bean
    2. 注解只负责完成属性的注入
    3. 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持 <context : component-scan base -package=" com. stu"/>
      <context: annotation-config/>
<?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:cotext="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
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解支持 -->
    <bean id="teacher" class="com.stu.pojo.Teacher">
        <property name="sname" value="李老师"/>
        <property name="tid" value="1"/>
    </bean>
    <bean id="student" class="com.stu.pojo.Student" autowire="byType">
        <property name="id" value="1"/>
        <property name="name" value="ddd"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <cotext:component-scan base-package="com.stu.pojo"/>
    <cotext:annotation-config/>
</beans>

package com.stu.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component  //将该类交给Spring托管
public class User {
    @Value("ss")
    String name;
    @Value("1")
    int id;
    //@Qualifier(value="com.stu.pojo.Student")
    @Autowired() @Qualifier(value = "student")
    Student student;
    //@Qualifier(value = "com.stu.pojo.Teacher")
    @Autowired() @Qualifier(value = "teacher")
    Teacher teacher;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", student=" + student +
                ", teacher=" + teacher +
                '}';
    }
}

改进xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cotext="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
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解支持 -->
    <cotext:component-scan base-package="com.stu.pojo"/>
    <cotext:annotation-config/>
</beans>

负责开启即可

package com.stu.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class User {
    @Value("ss")
    String name;
    @Value("1")
    int id;
    //@Qualifier(value = "com.stu.pojo.Student")
    @Autowired()
    //@Qualifier(value = "student")
    Student student;
    //@Qualifier(value = "com.stu.pojo.Teacher")
    @Autowired()
    //@Qualifier(value = "teacher")
    Teacher teacher;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", student=" + student +
                ", teacher=" + teacher +
                '}';
    }
}

使用JavaConfig的方式配置spring

完全不使用Spring的xml配置,全权交给Java来做!

JavaConfig是Spring的一个子项目,在Spring4之后,他成为一个核心功能

@Configuration 是一个类级别的注释,指示对象是bean定义的来源。 @Configuration classes通过公共 @Bean 注释方法声明bean 。 @Bean 对 @Configuration 类上的方法的调用也可用于定义bean间依赖项。

当bean彼此依赖时,表达该依赖关系就像让一个bean方法调用另一个bean一样简单,如下例所示

这种声明bean间依赖关系的@Bean方法只有在@Configuration类中声明方法时才有效。您不能使用普通@Component类声明bean间依赖项。

实体类

package com.stu.pojo;

import org.springframework.beans.factory.annotation.Value;

public class User {
    int id;
    String name;

    public int getId() {
        return id;
    }
    @Value("1")
    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    @Value("dff")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Config类

package com.stu.config;

import com.stu.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//相当于<bean id="teacher" class="com.stu.pojo.Teacher">
//意思是这个类被Spring接管了,注册到容器中心中了,本质仍然是@Component @Configuration代表这是一个配置类和bean.xml一样
@Configuration 
public class StuConfig {
    /*
    @Bean
    注册一个bean,就相当于之前所写的一个Bean标签
    这里的方法名就是bean标签的id属性
    这里的方法返回值就相当于bean标签的class属性
    
    */
    @Bean
    public User getUser(){
        return new User();
    }
}

测试

package com.stu.TestConfig;

import com.stu.config.StuConfig;
import com.stu.pojo.User;
import javafx.application.Application;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import sun.awt.AppContext;

public class TestConfig {
    @Test
    public void test(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(StuConfig.class);
        User getUser = (User)context.getBean("getUser"); //方法名就是bean的名字
        System.out.println(getUser);

    }
}

代理模式

静态代理

角色分析:

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

就是在不修改原来代码的基础上,对原有的功能 进行增强,所以才有了代理类。动态代理也是这个原理

代理模式的好处: .
●可以使真实角色的操作更加纯粹!不用去关注一 -些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!
缺点:|
●个真实角色就会产生-个代理角色;代码量会翻倍开发效率会变低~ (动态代理可以解决此问题)

代码步骤:
1.接口
2.真实角色
3.代理角色.
4.客户端访问代理角色

当需要在service增加业务功能时,使用代理进行横向开发,修改源代码是大忌!

动态代理:

●动态代理和静态代理角色一样
●动态代理的代理类是动态生成的,不是我们直接写好的!
●动态代理分为两大类:基于接口的动态代理,基于类的动态代理
。基于接口-- JDK动态代理 [我们在这里使用]
。基于类: cglib
。java字节码实现 : javasist

需要了解两个类: Proxy: 代理,InvocationHandler: 调用处理程序

动态代理的好处:
●可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!
●一个动态代理类代理的是-个接口,-般就是对应的一类业务

动态生成代理类:

//等我们会川这个类。日动生成化理必!
public class ProxyInvocationHand1er 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[]
10g(method. getName());
object result = method . invoke(target, args);
return result;
}
public void log(String msg){
System. out . print1n("执行了"+msg+"方法" );
}

}
import com.stu.demo02.UserService;
import com.stu.demoe2.UserServiceImp1;
public class Client {
public static void main(String[] args) {
//点实加色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHand1er
pih. setTarget(userService); //设置要化理的对象
//动态生成代理贵
UserService proxy = (UserService) pih. getProxy();
proxy.add();
}

AOP

面向方面的编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象的编程(OOP)。 OOP 中模块化的关键单位是类,而在 AOP 中模块化的单位是方面。方面使关注点模块化,例如跨多种类型和对象的事务 Management。 (在 AOP 文献中,此类关注点通常被称为“跨领域”关注点.)

Spring 的关键组件之一是* AOP framework *。尽管 Spring IoC 容器不依赖于 AOP,这意味着您不需要使用 AOP,但 AOP 是对 Spring IoC 的补充,以提供功能强大的中间件解决方案。

要理解切面编程,就需要先理解什么是切面。用刀把一个西瓜分成两瓣,切开的切口就是切面;炒菜,锅与炉子共同来完成炒菜,锅与炉子就是切面。web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。

概念

让我们首先定义一些重要的 AOP 概念和术语

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target(目标对象):织入 Advice 的目标对象.。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

体会:

​ 在AOP中切面就是与业务逻辑独立,但又垂直存在于业务逻辑的代码结构中的通用功能组合;切面与业务逻辑相交的点就是切点;连接点就是把业务逻辑离散化后的关键节点;切点属于连接点,是连接点的子集;Advice(增强)就是切面在切点上要执行的功能增加的具体操作;在切点上可以把要完成增强操作的目标对象(Target)连接到切面里,这个连接的方式就叫织入

Advice 的类型

before advice 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)

after return advice, 在一个 join point 正常返回后执行的 advice

after throwing advice, 当一个 join point 抛出异常后执行的 advice
after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
introduction,introduction可以为原有的对象增加新的属性和方法。

实现

方式一:spring的API接口实现(主要是SpringAPI接口实现)

导入依赖

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.10</version>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
  </dependency> 

Service

package com.stu.service;
public interface UserService {
    public void insert();
    public void delete();
    public void updata();
    public void select();
}

ServiceImpl

package com.stu.service;
public class UserServiceImpl implements UserService{
    @Override
    public void insert() {
        System.out.println("增加一个用户信息");
    }
    @Override
    public void delete() {
        System.out.println("删除一个用户信息");
    }
    @Override
    public void updata() {
        System.out.println("修改一个用户信息");
    }
    @Override
    public void select() {
        System.out.println("查看用户信息");

    }
}

前置实现:Log.java

package com.stu.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
//后置日志
public class Log implements MethodBeforeAdvice {
    @Override
    //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+ method.getName()+"被执行了");

    }
}

后置实现:AfterLog.java

package com.stu.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;
//前置日志
public class AfterLog implements AfterReturningAdvice {
    @Override
    //returnValue:返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法返回结果为:"+returnValue);

    }
}

环绕实现

xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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

">
    <!--方式一:使用原生Spring API接口 -->
    <!-- 注册bean -->
    <bean id="userServiceImpl" class="com.stu.service.UserServiceImpl"/>
    <bean id="log" class="com.stu.log.Log"/>
    <bean id="afterLog" class="com.stu.log.AfterLog"/>

    <!-- 配置AOP : 需要导入Aop约束-->
    <aop:config>
        <!-- 切入点:expression():表达式 execution(要执行的位置 *(修饰词) *(返回值) *(列名) *(方法名) *(参数)) ,*代表所有,..代表任意参数-->
        <aop:pointcut id="pointcut" expression="execution(* com.stu.service.UserServiceImpl.*(..))"/>
        <!--执行环绕 -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试代码

package com.stu.AopTest;

import com.stu.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理 代理的是接口
        UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
        userServiceImpl.insert();
    }
}

配置文件

<?xml version="1.e" encoding="UTF-8"?>
<beans xmlns= "http://ww. springframework。org/ schema/beans"
xmIns:xsi="http://ww.w3.org/ 2001/XMLSchema- instance"
xmlns:aop="http://ww. springframework . org/ schema/ aop"
xsi:schemaLocation="http://www. springframework .org/ schema/beans
https : //www. springframework .org/schema/beans/ spring- beans .xsd
http://ww. springframework. org/ schema/aop
https://ww. springframework.org/schena/aop/spring- aop.xsd")
<!--注bean-->
<bean id="userService" class=" com. kuang. service .UserServiceIepl"/>
<bean id="1og" class="com. kuang.1og.Log"/>
<bean id="afterlog" class="com. kuang. log.AfterLog"/>

<!--方式一 使用原生Spring API接口-->
<!--配oop:需要yAoop的约束-->
<aop:config>
<!--切入点: expression: 表达式execution( 要执行的位置! * * * * *) -->
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImp1.(..))"/>
<aop:advisor advice-ref=" 1og" pointcut -ref="pointcut"/>
<aop: advisor advice- ref="afterLog" pointcut = ref="pointcut"/>
</aop:config>
</beans>
方式二:使用自定义类实现AOP (主要是切面定义)

自定义类

package com.stu.log;
public class DIYLog {
    public void before(){
        System.out.println("执行方法前=============");
    }
    public void after(){
        System.out.println("执行方法后=============");
    }
}

xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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="userServiceImpl" class="com.stu.service.UserServiceImpl"/>
    <bean id="diyLog" class="com.stu.log.DIYLog"/>
    <aop:config>
        <!--aspect:切面
        自定义切面,ref 要引用的类
        -->
        <aop:aspect ref="diyLog">
            <!-- 切入点 -->
            <aop:pointcut id="point" expression="execution(* com.stu.service.UserServiceImpl.*(..))"/>
            <!-- 通知 -->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

结果:

执行方法前=============
增加一个用户信息
执行方法后=============

方法三:使用注解实现

xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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="userServiceImpl" class="com.stu.service.UserServiceImpl"/>
    <bean id="annLog" class="com.stu.log.AnnLog"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>
</beans>
package com.stu.log;

import org.aspectj.lang.ProceedingJoinPoint;
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 AnnLog {
    @Before("execution(* com.stu.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前============");
    }
    @After("execution(* com.stu.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后============");
    }
    @Around("execution(* com.stu.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("==========环绕前============");
        Object proceed = jp.proceed();
        System.out.println("==========环绕后============");
    }
}

测试

package com.stu.AopTest;

import com.stu.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理 代理的是接口
        UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
        userServiceImpl.delete();
    }
}

结果

环绕前==
方法执行前============
删除一个用户信息
方法执行后============
环绕后==

整合mybatis

导入相关依赖:

  • junit
  • mybatis
  • mysql数据库
  • spring相关
  • aop织入
  • mybatis-spring
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>
  <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.3.10</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.10</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>
  </dependencies>

整合方式一:

spring-dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--
 DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
        我们这使用Spring提供的JDBC
-->
    <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/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root" />
        <property name="password" value="root"/>
    </bean>

    <!--sqlSessionFactory
        在基础的MyBatis用法中是通过SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。
        而在 MyBatis-Spring中,则使用 SqlSessionFactoryBean 来创建。
   -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--绑定Mybatis配置文件 -->
        <property name="configLocation" value="classpath:Mybatis_Spring-config.xml"/>
        <property name="mapperLocations" value="classpath:com/stu/mapper/*.xml"/>
    </bean>

    <!--sqlSession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 由于SqlSessionTemplate没有set方法,只能用构造器注入-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <bean id="serviceMapper" class="com.stu.mapper.ServiceMapperImpl">
        <property name="sqlSession" ref="sqlSession"></property>
    </bean>
</beans>

ServiceMapperImpl

package com.stu.mapper;

import com.stu.pojo.Teacher;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class ServiceMapperImpl implements ServiceMapper{
    //在原来,我们的所有操作都使用sqlSession来执行,现在都是用SqlSessionTemplate
    private SqlSessionTemplate sqlSession;
    //注入sqlSession
    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
    public List<Teacher> selectTeacher(){
        ServiceMapper mapper = sqlSession.getMapper(ServiceMapper.class);
        return mapper.selectTeacher();
    }
}

ServiceMapper.xml

<?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.stu.mapper.ServiceMapper">
    <resultMap id="teacher" type="com.stu.pojo.Teacher">
        <result property="tname" column="name"/>
    </resultMap>
    <select id="selectTeacher"  resultMap="teacher">
        select * from school.teacher
    </select>
</mapper>

Mybatis_Spring-config.xml(这里本来可以直接省略该文件,但是一般可以用来作为配置文件的设置,来说明该项目是由spring整合mybatis)

<?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>
    <!-- 配置外部文件 -->
    <properties resource="db.properties">
    </properties>
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <typeAliases>
        <package name="com.stu.pojo"/>
    </typeAliases>
</configuration>

整合方式二(继承SqlSessionDaoSupport)

xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--
 DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
        我们这使用Spring提供的JDBC
-->
    <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/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root" />
        <property name="password" value="root"/>
    </bean>

   <!--sqlSessionFactory
        在基础的MyBatis用法中是通过SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。
        而在 MyBatis-Spring中,则使用 SqlSessionFactoryBean 来创建。
   -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--绑定Mybatis配置文件 -->
        <property name="configLocation" value="classpath:Mybatis_Spring-config.xml"/>
        <property name="mapperLocations" value="classpath:com/stu/mapper/*.xml"/>
    </bean>

<!--    &lt;!&ndash;sqlSession &ndash;&gt;-->
<!--    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
<!--        &lt;!&ndash; 由于SqlSessionTemplate没有set方法,只能用构造器注入&ndash;&gt;-->
<!--        <constructor-arg index="0" ref="sqlSessionFactory"/>-->
<!--    </bean>-->

    <bean id="serviceMapper" class="com.stu.mapper.ServiceMapperImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>
package com.stu.mapper;

import com.stu.pojo.Teacher;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class ServiceMapperImpl extends SqlSessionDaoSupport implements ServiceMapper{
    //SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。
    // 调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法

   // private SqlSessionTemplate sqlSession;
    //省略注入sqlSession
//    public void setSqlSession(SqlSessionTemplate sqlSession) {
//        this.sqlSession = sqlSession;
//    }

//    public List<Teacher> selectTeacher(){
//        ServiceMapper mapper = sqlSession.getMapper(ServiceMapper.class);
//        return mapper.selectTeacher();
//    }

    public List<Teacher> selectTeacher(){
        ServiceMapper mapper = getSqlSession().getMapper(ServiceMapper.class);
        return mapper.selectTeacher();
    }

}

相较于方式一,只修改了ServiceMapperImpl类,省略了set注入sqlSession

Spring声明式事务(transaction)

事务定义:用户定义的一个数据库操作序列,这些操作要么都做,要么都不做(要么都成功,要么都失败),是一个不可分割的工作单位

事务的ACID特性:

  • 原子性(Atonmicity):事务是数据库的逻辑工作单位,事务的诸多操作要么都做要么都不做
  • 一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态,与原子性密切相关
  • 隔离性(Isolation):一个事务不能被另一个事务干扰
  • 持久性(Durability):一个事务一旦提交,对数据库中数据改变是永久性的,其他操作或者故障不能影响其执行结果

Spring中的声明式事务管理: transaction --> tx (简写)
1)配置信息中引入事务的命名空间

xmlns:tx="http://www.springframework.org/schema/tx"
 http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd

2)启用事务:用注解驱动的方式来管理事务

        <tx:annotation-driven/>

3)在 spring.xml 中配置事务管理器 : id=" transcationManager "

 <!-- 5)配置事务管理器
     id="transactionManager" id名必须是transactionManager,不能更改
 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
  1. 在要使用事务的方法或类上,添加注解 @Transactional,spring会自动提交,回滚事务
    事务注解(@Transactional):
  • 加在方法上,表示此方法受事务管理(自动提交,回滚事务)
  • 加在类上,那么这个类中的所有方法都受事务管理

最后要注意的是,在业务方法中不要自己try-catch捕获异常,否则spring无法自动回滚事务

    @Transactional
    public void business1(){
        productMapper.delete(100000005);
        productMapper.delete(100000006);
        int i = 1/0;
        productMapper.delete(100000007);

}

@Transcational 的工作原理:
默认情况下,数据库处于自动提交模式,每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。这点,Spring会在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中将底层连接的自动提交特性设置为false

注解 @Transcational:

1.@Transcational

​ 默认情况下,只有方法出现的 是RuntimeException或Error以及它们的子类时(未检查异常),才会导致事务回滚

如果要改变默认情况
@Transactional(rollbackFor=异常类.class)
那么方法如果出现了该异常,或该异常的子类异常时,就会回滚

​ @Transactional(noRollbackFor=异常类.class)
​ 当遇到这种异常时,不会回滚事务
注意:在业务方法中不要自己try-catch捕获异常,否则spring无法自动回滚事务

  1. @Transactional(readOnly = true|false) :true表示只读(只有查询) false(会有增删改)
    设置为true,性能会有所提升,但是底层数据库驱动支持(对mysql支持)

  2. 事务超时设置:
    @Transactional(timeout=30) //默认是30秒

  3. 事务的传播行为: @Transcational (propagation=传播行为)

    ​ 只有两个业务类的方法相互调用时,传播行为才会有效
    ​ ProductService 商品业务类
    ​ @Transactional(propagation=REQUIRED)
    ​ biz1() { // 事务1
    ​ biz2();
    ​ }
    ​ OrderService 订单业务类
    ​ @Transactional(propagation=REQUIRES_NEW)
    ​ biz2(); // 事务2

    事务传播行为 说明
    @Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况)
    @Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务
    @Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
    @Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常
    @Transactional(propagation=Propagation.NEVER) 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
    @Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务

    事务隔离级别: @Transcational( isolation = " 事务的隔离级别 ")

​ 事务隔离级别 说明

@Transactional(isolation = Isolation.READ_UNCOMMITTED) 读取未提交数据(会出现脏读, 不可重复读),基本不使用

@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默认) 读取已提交数据(会出现不可重复读和幻读)

@Transactional(isolation = Isolation.REPEATABLE_READ) 可重复读(会出现幻读)

@Transactional(isolation = Isolation.SERIALIZABLE) 串行化

脏读 : 一个事务读取到另一事务未提交的更新数据
不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据。
可重复读:在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据
幻读 : 一个事务读到另一个事务已提交的insert数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值