• spring 快速入门


    1.spring

    Spring 框架可以说是Java 世界最为成功的框架,在企业实际应用中,大部分的企业架构都基于Spring 框架。它的成功来自于理念,而不是技术,它最为核心的理念是IoC (控制反转)和AOP (面向切面编程),其中IoC 是Spring的基础,而AOP 则是其重要的功能,最为典型的当属数据库事务的使用。

    Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。

    1.1.优点

    1. 方便解耦,简化开发

      Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。

    2. AOP编程的支持

      Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。

    3. 声明式事务的支持

      只需要通过配置就可以完成对事务的管理,而无需手动编程。

    4. 方便程序的测试

      Spring对Junit4支持,可以通过注解方便的测试Spring程序。

    5. 方便集成各种优秀框架

      Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。

    6. 降低JavaEE API的使用难度

      Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

    1.2.缺点

    1. Spring明明一个很轻量级的框架,却给人感觉大而全
    2. Spring依赖反射,反射影响性能
    3. 使用门槛升高,入门Spring需要较长时间

    1.3.Spring框架的组成结构图

    Spring 总共大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被分别整合在核心容器(Core Container) 、 AOP(Aspect Oriented Programming)和设备支持(Instrmentation) 、数据访问与集成(Data Access/Integeration) 、 Web、 消息(Messaging) 、 Test等 6 个模块中。 以下是 Spring 5 的模块结构图:

    spring-overview

    组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

    1.3.1.核心容器

    Spring的核心容器是其他模块建立的基础,有spring-core、spring-beans、spring-context、spring-context-support和spring-expression(Spring表达式语言)等模块组成。

    spring-core 模块:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。

    spring-beans 模块:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。

    spring-context 模块:建立在Core和Beans模块的基础之上,提供一个框架式的对象访问方式,是访问定义和配置的任何对象的媒介。ApplicationContext接口是Context模块的焦点。

    spring-context-support 模块:支持整合第三方库到Spring应用程序上下文,特别是用于高速缓存(EhCache、JCache)和任务调度(CommonJ、Quartz)的支持。

    Spring-expression 模块:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对JSP2.1规范中规定的统一表达式语言(Unified EL)的扩展。该语言支持设置和获取属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及从Spring的IOC容器中以名称检索对象。它还支持列表投影、选择以及常用的列表聚合。

    1.3.2.AOP 和设备支持

    由spring-aop、 spring-aspects 和 spring-instrument等 3 个模块组成。

    spring-aop 模块:是 Spring 的另一个核心模块,提供了一个符合 AOP 要求的面向切面的编程实现。 作为继 OOP(面向对象编程) 后, 对程序员影响最大的编程思想之一, AOP 极大地开拓了人们对于编程的思路。 在 Spring 中, 以动态代理技术为基础,允许定义方法拦截器和切入点,将代码按照功能进行分离,以便干净地解耦。

    spring-aspects 模块:提供了与AspectJ的集成功能,AspectJ是一个功能强大且成熟的AOP框架。

    spring-instrument 模块:是 AOP 的一个支援模块, 提供了类植入(Instrumentation)支持和类加载器的实现,可以在特定的应用服务器中使用。主要作用是在 JVM 启用时, 生成一个代理类, 程序员通过代理类在运行时修改类的字节, 从而改变一个类的功能, 实现 AOP 的功能。

    1.3.3.数据访问与集成

    由 spring-jdbc、spring-orm、spring-oxm、spring-jms 和 spring-tx 等 5 个模块组成。

    spring-jdbc 模块:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC。主要是提供 JDBC 模板方式、 关系数据库对象化方式、 SimpleJdbc 方式、 事务管理来简化 JDBC 编程, 主要实现类是 JdbcTemplate、 SimpleJdbcTemplate 以及 NamedParameterJdbcTemplate。

    spring-orm 模块:是 ORM 框架支持模块, 主要集成 Hibernate, Java Persistence API (JPA) 和Java Data Objects (JDO) 用于资源管理、 数据访问对象(DAO)的实现和事务策略。

    spring-oxm 模块:主要提供一个抽象层以支撑 OXM(OXM 是 Object-to-XML-Mapping 的缩写, 它是一个 O/M-mapper, 将 java 对象映射成 XML 数据, 或者将 XML 数据映射成 java 对象) , 例如: JAXB,Castor,XMLBeans,JiBX 和 XStream 等。

    spring-jms模块(Java Messaging Service):指Java消息传递服务,包含用于生产和使用消息的功能。自Spring4.1以后,提供了与spring-messaging模块的集成。

    spring-tx 模块:事务模块,支持用于实现特殊接口和所有POJO(普通Java对象)类的编程和声明式事务管理。

    1.3.4.Web

    由spring-websocket、spring-webmvc、spring-web、portlet和spring-webflux模块等 5 个模块组成。

    spring-websocket 模块:Spring4.0以后新增的模块,实现双工异步通讯协议,实现了WebSocket和SocketJS,提供Socket通信和web端的推送功能。

    spring-webmvc 模块:也称为Web-Servlet模块,包含用于web应用程序的Spring MVC和REST Web Services实现。Spring MVC框架提供了领域模型代码和Web表单之间的清晰分离,并与Spring Framework的所有其他功能集成。

    spring-web 模块:提供了基本的Web开发集成功能,包括使用Servlet监听器初始化一个IOC容器以及Web应用上下文,自动载入WebApplicationContext特性的类,Struts集成类、文件上传的支持类、Filter类和大量辅助工具类。

    portlet 模块:实现web模块功能的聚合,类似于Servlet模块的功能,提供了Portlet环境下的MVC实现。

    spring-webflux 模块:是一个新的非堵塞函数式 Reactive Web 框架, 可以用来建立异步的, 非阻塞,事件驱动的服务, 并且扩展性非常好。

    1.3.5.消息(Messaging)

    即 spring-messaging 模块。

    spring-messaging 是从 Spring4 开始新加入的一个模块, 该模块提供了对消息传递体系结构和协议的支持。

    1.3.6.Test

    即 spring-test 模块。

    spring-test 模块主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。

    2.Spring核心ioc

    Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。就是不实例化了。先注入。

    谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取

    为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

    ps:控制反转是目标,依赖注入是手段。

    2.1.ioc容器

    image-20210830155718002

    IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。

    Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。

    Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。

    Spring 提供 2 种不同类型的 IoC 容器,即 BeanFactory 和 ApplicationContext 容器

    2.1.BeanFactory 容器

    BeanFactory 是最简单的容器,由 org.springframework.beans.factory.BeanFactory 接口定义,采用懒加载(lazy-load),所以容器启动比较快。BeanFactory 提供了容器最基本的功能。

    为了能够兼容 Spring 集成的第三方框架(如 BeanFactoryAware、InitializingBean、DisposableBean),所以目前仍然保留了该接口。

    简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。

    BeanFactory 接口有多个实现类,最常见的是 org.springframework.beans.factory.xml.XmlBeanFactory。使用 BeanFactory 需要创建 XmlBeanFactory 类的实例,通过 XmlBeanFactory 类的构造函数来传递 Resource 对象。如下所示。

    Resource resource = new ClassPathResource("applicationContext.xml");
    BeanFactory factory = new XmlBeanFactory(resource);  
    

    2.1.2. ApplicationContext 容器

    ApplicationContext 继承了 BeanFactory 接口,由 org.springframework.context.ApplicationContext 接口定义,对象在启动容器时加载。ApplicationContext 在 BeanFactory 的基础上增加了很多企业级功能,例如 AOP、国际化、事件支持等。

    ApplicationContext 接口有两个常用的实现类,具体如下。

    2.1.2.1.ClassPathXmlApplicationContext

    该类从类路径 ClassPath 中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
    

    在上述代码中,configLocation 参数用于指定 Spring 配置文件的名称和位置,如 Beans.xml。

    2.1.2.2.FileSystemXmlApplicationContext

    该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。

    ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
    

    它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext 不会从类路径中读取配置文件,而是通过参数指定配置文件的位置。即 FileSystemXmlApplicationContext 可以获取类路径之外的资源,如“F:/workspaces/Beans.xml”。

    2.1.2.3.AnnotationConfigApplicationContext

    读取用注解创建容器

    通常在 Java 项目中,会采用 ClassPathXmlApplicationContext 类实例化 ApplicationContext 容器的方式,而在 Web 项目中,ApplicationContext 容器的实例化工作会交由 Web 服务器完成。Web 服务器实例化 ApplicationContext 容器通常使用基于 ContextLoaderListener 实现的方式,它只需要在 web.xml 中添加如下代码:

    <!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!--spring将加载spring目录下的applicationContext.xml文件-->
        <param-value>
            classpath:spring/applicationContext.xml
        </param-value>
    </context-param>
    <!--指定以ContextLoaderListener方式启动Spring容器-->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    

    需要注意的是,BeanFactory 和 ApplicationContext 都是通过 XML 配置文件加载 Bean 的。

    二者的主要区别在于,如果 Bean 的某一个属性没有注入,使用 BeanFacotry 加载后,第一次调用 getBean() 方法时会抛出异常,而 ApplicationContext 则会在初始化时自检,这样有利于检查所依赖的属性是否注入。

    因此,在实际开发中,通常都选择使用 ApplicationContext,只有在系统资源较少时,才考虑使用 BeanFactory。

    2.2.使用ioc容器

    2.2.1.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"     xsi:schemaLocation="http://www.springframework.org/schema/beans      http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="user" class="com.wyl.pojo.User">
            <property name="name" value="王延领"/>
        </bean>
    </beans>
    

    2.2.2.pojo.User

    public class User { 
        private String name; 
        public User() {
            System.out.println("user无参构造方法");
        } 
        public void setName(String name) {
            this.name = name;
        } 
        public void show(){
            System.out.println("name="+ name );
        }
    }
    

    2.2.2.test

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //在执行getBean的时候, user已经创建好了 , 通过无参构造
        User user = (User) context.getBean("user");
        //调用对象的方法 .
        user.show();
    }
    

    2.3.bean

    2.3.1.定义

    由 Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息创建。可以把 Spring IoC 容器看作是一个大工厂,Bean 相当于工厂的产品,如果希望这个大工厂生产和管理 Bean,则需要告诉容器需要哪些 Bean,以及需要哪种方式装配 Bean。

    Spring 配置文件支持两种格式,即 XML 文件格式和 Properties 文件格式。

    • Properties 配置文件主要以 key-value 键值对的形式存在,只能赋值,不能进行其他操作,适用于简单的属性配置。

    • XML 配置文件是树形结构,相对于 Properties 文件来说更加灵活。XML 配置文件结构清晰,但是内容比较繁琐,适用于大型复杂的项目。

    通常情况下,Spring 的配置文件使用 XML 格式。XML 配置文件的根元素是 ,该元素包含了多个子元素 。每一个 元素都定义了一个 Bean,并描述了该 Bean 如何被装配到 Spring 容器中。

    2.3.2.创建

    2.3.2.1 默认方式

    无参

    <!-- 1. 默认构造函数,如果类中没有默认构造函数则无法创建对象;bean标签中只有id和class就默认使用构造函数创建对象 -->
    <bean id="userService" class="com.wyl.pojo.User"/>
    

    有参

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

    2.3.2.2 工厂类中的方法

    <!-- 2. 使用工厂中的方法创建对象;工厂中有一个方法可以创建对象,先创建工厂对象,通过factory-bean指向工厂,使用factory-method方法获取对象 -->
    <bean id="beanFactory" class="org.factory.BeanFactory"/>
    <bean id="userService" factory-bean="beanFactory" factory-method="getUserService"/>
    

    2.3.2.3 静态工厂中的静态方法

    <!-- 3. 使用静态工厂中的静态方法创建对象 -->
    <bean id="userService" class="org.factory.StaticBeanFactory" factory-method="getUserService"/>
    

    2.3.2.配置

    2.3.2.1.别名

    <!--  别名 : 如果添加了别名,我们也可以使用别名获取到这个对象 -->
    <alias name="User" alias="u1"></alias>
    

    2.3.2.2.bean 别名

    <!--
      bean标签常用属性:
    
    id属性:起名称,id属性值名称任意命名,不能包含特殊符号
    class属性:创建对象所在类的全路径
    name属性:功能和id属性一样的,但是在name属性值里面可以包含特殊符号
    scope属性
    singleton:默认值,单例
    prototype:多例
    request:创建对象把对象放到request域里面
    session:创建对象把对象放到session域里面
    globalSession:创建对象把对象放到globalSession里面
      -->
    <bean id="UserT" class="com.wyl.pojo.User" scope="singleton" name="u2 u21,u22;u23">
        <property name="name" value="123"/>
    </bean>
    

    2.3.2.2.import

    团队的合作通过import来实现 .

    <import resource="beans.xml"/>
    

    能将多个人开发的不同的配置xml文件整合到applicationContext.xml文件中,并且能够合适的去重。

    2.3.3.作用域

    <bean id="..." class="..." scope="singleton"/>
    

    Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 5 支持以下 6 种作用域。

    singleton

    默认值,单例模式,表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。

    prototype

    原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个 Bean 实例。

    request

    每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。

    session

    同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。

    application

    同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。
    类似于 singleton,不同的是,singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而同一个 Web 应用中可能会有多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。

    websocket

    websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效
    equest、session、application、websocket 和 global Session 作用域只能在 Web 环境下使用,如果使用 ClassPathXmlApplicationContext 加载这些作用域中的任意一个的 Bean,就会抛出以下异常。

    2.3.4.生命周期

    Bean的生命周期

    1. Spring 启动,查找并加载需要被 Spring 管理的 Bean,并实例化 Bean。

    2. 利用依赖注入完成 Bean 中所有属性值的配置注入。

    3. 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

    4. 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

    5. 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

    6. 如果 Bean 实现了 [BeanPostProcessor] 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

    7. 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。

    8. 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

    9. 如果 [BeanPostProcessor ]和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

    10. 如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理; 如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

    11. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

    2.3.4.1.单例

    public class UserBean {
    	private String name;  
        
        public UserBean(){  
            System.out.println("UserBean()构造函数");  
        }  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            System.out.println("setName()");  
            this.name = name;  
        }  
        public void init(){  
            System.out.println("this is init of UserBean");  
        }  
          
        public void destory(){  
            System.out.println("this is destory of UserBean " + this);  
        }  
    }
    
    
    <bean id="user_singleton" class="com.wyl.userBean" scope="singleton" 
    			init-method="init" destroy-method="destory" lazy-init="true"/>
    

    当scope="singleton",即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init="true"来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化.

    如果想对所有的默认单例bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示:

    <beans default-lazy-init="true">
    
    public class LifeTest {
    	@Test 
    	public void test() {
    		AbstractApplicationContext container = 
    		new ClassPathXmlApplicationContext("user.xml");
    		UserBean user = (UserBean)container.getBean("user_singleton");
    		System.out.println(user);
    		container.close();
    	}
    }
    
    

    UserBean()构造函数
    this is init of UserBean
    com.wyl.UserBean@573f2bb1
    ……
    this is destory of UserBeancom.wyl.UserBean@573f2bb1

    默认情况下,Spring在读取xml文件的时候,就会创建对象。在创建对象的时候先调用构造器[UserBean(),然后调用init-method属性值中所指定的方法。对象在被销毁的时候,会调用destroy-method属性值中所指定的方法.

    2.3.4.2.非单例管理的对象

    当scope="prototype"时,容器也会延迟初始化bean,Spring读取xml文件的时候,并不会立刻创建对象,而是在第一次请求该bean时才初始化(如调用getBean方法时)。

    在第一次请求每一个prototype的bean时,Spring容器都会调用其构造器创建这个对象,然后调用init-method属性值中所指定的方法。对象销毁的时候,Spring容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。

    <bean id="user_prototype" class="com.bean.UserBean" scope="prototype" init-method="init" destroy-method="destroy"/>
    
    public class UserTest {
    	@Test 
    	public void test() {
    		AbstractApplicationContext container = new ClassPathXmlApplicationContext("User.xml");
    		UserBean User1 = (UserBean)container.getBean("User_singleton");
    		System.out.println(User1);
    		
    		UserBean User2 = (UserBean)container.getBean("User_prototype");
    		System.out.println(User2);
    		container.close();
    	}
    }
    

    结果

    UserBean()构造函数
    this is init of UserBean
    com.wyl.UserBean@573f2bb1
    LifeBean()构造函数
    this is init of UserBean
    com.wyl.UserBean@5ae9a829
    ……
    this is destory of lifeBean com.wyl.UserBean@573f2bb1

    2.4.DI(依赖注入)

    依赖注入Dependency Injection,在解耦的过程中,我们将对象的创建交给Spring容器管理,当我们需要用其他类的对象,由Spring提供,我们只需在配置文件里声明即可。A类使用B类,就产生依赖关系,Spring给我们解决依赖关系就是依赖注入(DI)

    2.4.1.构造器注入

    private String name;
    private Integer age;
    private Date birthday;
    // 构造函数
    public UserServiceImpl(String name, Integer age, Date birthday) {
    this.name = name;
    this.age = age;
    this.birthday = birthday;
    }
    
    <!-- name:按字段名称辅助;index:字段索引,给第几个字段赋值;type:指定注入值的类型,该类型也是构造函数中某个或某些字段的类型; -->
    <!-- value:要注入的值,基本类型和String;ref:注入其他类型数据,指向外部bean对象;这个外部bean需要存在于Spring容器 -->
    <bean id="userService" class="org.service.impl.UserServiceImpl">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="age" value="12"/>
        <constructor-arg name="birthday" ref="date"/>
    </bean>
    <!-- 创建日期对象 -->
    <bean id="date" class="java.util.Date"/>
    

    2.4.2.Set方式注入

    private String name;
    private Integer age;
    private Date birthday;
    public void setName(String name) {
        this.name = name;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    
    <bean id="userService2" class="org.service.impl.UserServiceImpl2">
        <property name="name" value="李四"/>
        <property name="age" value="12"/>
        <property name="birthday" ref="date"/>
    </bean>
    <!-- 创建日期对象 -->
    <bean id="date" class="java.util.Date"/>
    

    2.4.3.对象类型注入

    <!-- 注入对象类型属性 -->
    <!-- 1 配置service和dao对象 -->
    <bean id="userDao" class="cn.ioc.UserDao"></bean>
    <bean id="userService" class="cn.ioc.UserService">
        <!-- 注入dao对象-->
        <property name="userDao" ref="userDao"></property>
    </bean>
    

    2.4.4.复杂类型注入

    <!-- 注入复杂类型属性值 -->
      <bean id="person" class="cn.property.Person">
        <!-- 数组 -->
        <property name="arrs">
           <list>
             <value>小王</value>
             <value>小马</value>
             <value>小宋</value>
           </list>
        </property>
        
        <!-- list -->
        <property name="list">
           <list>
             <value>小奥</value>
             <value>小金</value>
             <value>小普</value>
           </list>      
        </property>
    
        <!-- map -->
        <property name="map">
           <map>
             <entry key="aa" value="lucy"></entry>
             <entry key="bb" value="mary"></entry>
             <entry key="cc" value="tom"></entry>
           </map>
        </property>
    
        <!-- properties -->
        <property name="properties">
           <props>
             <prop key="driverclass">com.mysql.jdbc.Driver</prop>
             <prop key="username">root</prop>
           </props>
        </property>
      </bean>
    <!--set-->
    <property name="set">
                <set>
                    <value>LOL</value>
                    <value>COC</value>
                    <value>WOW</value>
                </set>
    </property>
    <!--null-->
    <property name="marne">
                <null/>
    </property>
    

    2.4.5.拓展方式注入

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

    p命名注入 property

    <!-- p命名空间注入,可以直接注入属性的值:property -->
    <bean id="User"  class="com.wyl.pojo.User" p:name ="老秦" p:age ="18"/>
    

    c命名空间注入 constructor

    <!-- c命名空间注入,通过构造器注入:construct-args -->
    <bean id="User2" class="com.wyl.pojo.User" c:age="18" c:name="老李"/>
    

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

    2.5.自动装配

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

    在Spring中有三种装配的方式

    1. 在xml中显示的配置

    2. 在java中显示配置

    3. 隐式的自动装配bean

      名称 说明
      no 默认值,表示不使用自动装配,Bean 依赖必须通过 ref 元素定义。
      byName 根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。
      byType 根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。
      constructor 类似于 byType,根据构造方法参数的数据类型,进行 byType 模式的自动装配。
      autodetect(3.0版本不支持) 如果 Bean 中有默认的构造方法,则用 constructor 模式,否则用 byType 模式。

    2.5.1.byName

    <!--
    byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid!
    -->
    <bean id="people" class="com.wyl.pojo.People" autowire="byName">
        <property name="name" value="wangyanling"/>
    </bean>
    

    2.5.2.byType

    <bean id="cat" class="com.wyl.pojo.Cat"/>
        <bean id="dog" class="com.wyl.pojo.Dog"/>
        <!--
        byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid!
        byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
        -->
        <bean id="people" class="com.wyl.pojo.People" autowire="byType">
            <property name="name" value="WANGAYNLING"/>
        </bean>
    

    2.5.3.注解

    jdk1.5支持的注解,Spring2.5就支持注解了!

    要使用注解须知:

    1. 导入约束 context约束

    2. 配置注解的支持: 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: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

    @Autowired是按类型自动转配的,不支持id匹配。byType
    需要导入 spring-aop的包!
    直接在属性上使用即可!也可以在set方式上使用!

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

     @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
    
    @Nullable     // 字段标记了这个注解,说明这个字段可以为null
    

    或者 如果显示定义了Autowired的required 的属性为false ,说明这个对象可以为null,允许为空

    autowired 注解应该是只能是别的,当注入 在IOC容器中该类型只有一个时,就通过byType进行装配,当注入容器存在多个同意类型的对象是,就是根据byName进行装配

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

    @Qualifier

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

    public class People {
        private String name;
        @Autowired
        @Qualifier("cat")
        private Cat cat;
        @Autowired
        @Qualifier("dog")
        private Dog dog;
    }
    

    @Resource注解

    • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
    • 其次再进行默认的byName方式进行装配;
    • 如果以上都不成功,则按byType的方式自动装配。
    • 都不成功,则报异常。
    public class People {
        private String name;
        @Resource(name = "cat")
        private Cat cat;
        @Resource(name = "dog")
        private Dog dog;
    

    小结
    @Autowired与@Resource异同:

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

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

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

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

    2.6.ioc注解

    @注解名称(属性名称=属性值)

    2.6.1. Spring使用的注解大全和解释

    注解 解释
    @Controller 组合注解(组合了@Component注解),应用在MVC层(控制层),DispatcherServlet会自动扫描注解了此注解的类,然后将web请求映射到注解了@RequestMapping的方法上。
    @Service 组合注解(组合了@Component注解),应用在service层(业务逻辑层)
    @Repository 组合注解(组合了@Component注解),应用在dao层(数据访问层)
    @Component 表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
    @Autowired Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入。)
    @Resource JSR-250提供的注解
    @Inject JSR-330提供的注解
    @Configuration 声明当前类是一个配置类(相当于一个Spring配置的xml文件)
    @ComponentScan 自动扫描指定包下所有使用@Service,@Component,@Controller,@Repository的类并注册
    @Bean 注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
    @Aspect 声明一个切面(就是说这是一个额外功能)
    @After 后置建言(advice),在原方法前执行。
    @Before 前置建言(advice),在原方法后执行。
    @Around 环绕建言(advice),在原方法执行前执行,在原方法执行后再执行(@Around可以实现其他两种advice)
    @PointCut 声明切点,即定义拦截规则,确定有哪些方法会被切入
    @Transactional 声明事务(一般默认配置即可满足要求,当然也可以自定义)
    @Cacheable 声明数据缓存
    @EnableAspectJAutoProxy 开启Spring对AspectJ的支持
    @Value 值得注入。经常与Sping EL表达式语言一起使用,注入普通字符,系统属性,表达式运算结果,其他Bean的属性,文件内容,网址请求内容,配置文件属性值等等
    @PropertySource 指定文件地址。提供了一种方便的、声明性的机制,用于向Spring的环境添加PropertySource。与@configuration类一起使用。
    @PostConstruct 标注在方法上,该方法在构造函数执行完成之后执行。
    @PreDestroy 标注在方法上,该方法在对象销毁之前执行。
    @Profile 表示当一个或多个指定的文件是活动的时,一个组件是有资格注册的。使用@Profile注解类或者方法,达到在不同情况下选择实例化不同的Bean。@Profile(“dev”)表示为dev时实例化。
    @EnableAsync 开启异步任务支持。注解在配置类上。
    @Async 注解在方法上标示这是一个异步方法,在类上标示这个类所有的方法都是异步方法。
    @EnableScheduling 注解在配置类上,开启对计划任务的支持。
    @Scheduled 注解在方法上,声明该方法是计划任务。支持多种类型的计划任务:cron,fixDelay,fixRate
    @Conditional 根据满足某一特定条件创建特定的Bean
    @Enable* 通过简单的@Enable来开启一项功能的支持。所有@Enable注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean(1.直接导入配置类2.依据条件选择配置类3.动态注册配置类)
    @RunWith 这个是Junit的注解,springboot集成了junit。一般在测试类里使用:@RunWith(SpringJUnit4ClassRunner.class) — SpringJUnit4ClassRunner在JUnit环境下提供Sprng TestContext Framework的功能
    @ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类:@ContextConfiguration(classes = {TestConfig.class(自定义的一个配置类)})
    @ActiveProfiles 用来声明活动的profile–@ActiveProfiles(“prod”(这个prod定义在配置类中))
    @EnableWebMvc 用在配置类上,开启SpringMvc的Mvc的一些默认配置:如ViewResolver,MessageConverter等。同时在自己定制SpringMvc的相关配置时需要做到两点:1.配置类继承WebMvcConfigurerAdapter类2.就是必须使用这个@EnableWebMvc注解。
    @RequestMapping 用来映射web请求(访问路径和参数),处理类和方法的。可以注解在类和方法上,注解在方法上的@RequestMapping路径会继承注解在类上的路径。同时支持Serlvet的request和response作为参数,也支持对request和response的媒体类型进行配置。其中有value(路径),produces(定义返回的媒体类型和字符集),method(指定请求方式)等属性。
    @ResponseBody 将返回值放在response体内。返回的是数据而不是页面
    @RequestBody 允许request的参数在request体中,而不是在直接链接在地址的后面。此注解放置在参数前。
    @PathVariable 放置在参数前,用来接受路径参数。
    @RestController 组合注解,组合了@Controller和@ResponseBody,当我们只开发一个和页面交互数据的控制层的时候可以使用此注解。
    @ControllerAdvice 用在类上,声明一个控制器建言,它也组合了@Component注解,会自动注册为Spring的Bean。
    @ExceptionHandler 用在方法上定义全局处理,通过他的value属性可以过滤拦截的条件:@ExceptionHandler(value=Exception.class)–表示拦截所有的Exception。
    @ModelAttribute 将键值对添加到全局,所有注解了@RequestMapping的方法可获得次键值对(就是在请求到达之前,往model里addAttribute一对name-value而已)。
    @InitBinder 通过@InitBinder注解定制WebDataBinder(用在方法上,方法有一个WebDataBinder作为参数,用WebDataBinder在方法内定制数据绑定,例如可以忽略request传过来的参数Id等)。
    @WebAppConfiguration 一般用在测试上,注解在类上,用来声明加载的ApplicationContext是一个WebApplicationContext。他的属性指定的是Web资源的位置,默认为src/main/webapp,我们可以修改为:@WebAppConfiguration(“src/main/resources”)。
    @EnableAutoConfiguration 此注释自动载入应用程序所需的所有Bean——这依赖于Spring Boot在类路径中的查找。该注解组合了@Import注解,@Import注解导入了EnableAutoCofigurationImportSelector类,它使用SpringFactoriesLoader.loaderFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包。而spring.factories里声明了有哪些自动配置。
    @SpingBootApplication SpringBoot的核心注解,主要目的是开启自动配置。它也是一个组合注解,主要组合了@Configurer,@EnableAutoConfiguration(核心)和@ComponentScan。可以通过@SpringBootApplication(exclude={想要关闭的自动配置的类名.class})来关闭特定的自动配置。
    @ImportResource 虽然Spring提倡零配置,但是还是提供了对xml文件的支持,这个注解就是用来加载xml配置的。例:@ImportResource({“classpath
    @ConfigurationProperties 将properties属性与一个Bean及其属性相关联,从而实现类型安全的配置。例:@ConfigurationProperties(prefix=”authot”,locations={“classpath
    @ConditionalOnBean 条件注解。当容器里有指定Bean的条件下。
    @ConditionalOnClass 条件注解。当类路径下有指定的类的条件下。
    @ConditionalOnExpression 条件注解。基于SpEL表达式作为判断条件。
    @ConditionalOnJava 条件注解。基于JVM版本作为判断条件。
    @ConditionalOnJndi 条件注解。在JNDI存在的条件下查找指定的位置。
    @ConditionalOnMissingBean 条件注解。当容器里没有指定Bean的情况下。
    @ConditionalOnMissingClass 条件注解。当类路径下没有指定的类的情况下。
    @ConditionalOnNotWebApplication 条件注解。当前项目不是web项目的条件下。
    @ConditionalOnResource 条件注解。类路径是否有指定的值。
    @ConditionalOnSingleCandidate 条件注解。当指定Bean在容器中只有一个,后者虽然有多个但是指定首选的Bean。
    @ConditionalOnWebApplication 条件注解。当前项目是web项目的情况下。
    @EnableConfigurationProperties 注解在类上,声明开启属性注入,使用@Autowired注入。例:@EnableConfigurationProperties(HttpEncodingProperties.class)。
    @AutoConfigureAfter 在指定的自动配置类之后再配置。例:@AutoConfigureAfter(WebMvcAutoConfiguration.class)

    2.6.1.1.创建对象的注解

    • @Component(标注当前类是Spring容器中的一个组件)
    • @Repository(一般用于持久层)
    • @Service(一般用于业务层)
    • @Controller(一般用于表现层)

    2.6.1.2、注入数据的注解

    • @Autowired:自动按类型注入,常用在变量上;如果容器中有唯一一个类型与注解的变量类型相同则可以自动注入成功。当有多个bean匹配则按照变量名称去查找,找不到则注入失败。

    • @Qualifier("userDaoImpl"):结合@Autowired使用,注入指定名称的bean;在类的成员变量上不能单独使用;在方法参数里使用可以单独使用;

    • @Resource:相当于@Autowired自动注入,而@Resource(name="xxx")注入指定的bean,相当于同时使用@Autowired和@Qualifier("userDaoImpl")两个注解。

      上面三个注解都只能注入其他的bean类型,不能注入基本数据类型和String和复杂类型;复杂类型只能通过xml文件来注入。

    • @Value:注入基本数据类型和String类型。指定数据的值,写法:${表达式}。

    2.6.1.3、改变作用范围的注解

    • @Scope:取值有singleton单例(默认)和prototype多例

    2.6.1.4、和生命周期相关注解

    @PostConstruct
    public void init() {
        System.out.println("初始化注解");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("销毁注解");
    }
    

    这两个注解和bean标签里面的init-method、destroy-method作用相同。

    2.6.1.5.新注解

    • @Configuration:作用在类上面标明当前类是一个配置类

    • @ComponentScan(basePackages = "com.wyl"):扫描包注解:相当于下面这一行配置

      <!--<context:component-scan base-package="com.wyl"/>-->
      
    • @Bean:在配置类中写在方法上,将方法返回的对象注入到Spring容器中。该注解的方法有参数时,会去容器中找bean对象,跟@Autowired注解一样的。

    • @PropertySource("classpath:db.properties"):指定数据库配置文件的位置

    • @Import:存在多个配置文件,用该注解引入其他配置文件。

    2.6.1.6.Spring测试注解

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = ApplicationConfig.class) 纯注解
    // @ContextConfiguration(locations = "classpath:ApplicationContext.xml") xml配置文件
    public class SpringTest {
    
        @Autowired
        private AccountServiceImpl accountService;
    
        @Test
        public void findAll(){
            List<Account> accountList = accountService.findAll();
            for (Account account : accountList) {
                System.out.println(account);
            }
        }
       
    }
    

    @RunWith(SpringJUnit4ClassRunner.class):替换掉原来junit的runner执行方法,使用Spring自己的执行方法。

    @ContextConfiguration(classes = ApplicationConfig.class):如果是使用注解创建Spring的容器使用classes;

    @ContextConfiguration(locations = "classpath:ApplicationContext.xml"):使用xml配置文件的方法

    2.6.2.基于xml方式创建bean

    public class User {
        private Integer id;
        private String name;
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans        
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="user" class="com.wyl.bean.User" >
            <property name="id" value="1"></property>
            <property name="name" value="wyl"></property>
        </bean>
    </beans>
    
    @test
    public void UserTest{
    ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("Bean.xml");
            User userInfo=(User)context.getBean("user");
            System.out.println(userInfo);
    }
    

    2.6.3.基于@Configuration 和@Bean 注解

    Configuration 配置类

    @Configuration
    public class MyTestConfig {
        //bean的id默认为方法名
        @Bean
        public User user(){
            User user =new User();
            user.setName("王延领");
            user.setId(2);
            return user;
        }
    }
    
    @test
    public void UserTest{
        AnnotationConfigApplicationContext context=new 							AnnotationConfigApplicationContext(MyTestConfig.class);
            User userInfo=(User)context.getBean("user");
            System.out.println(userInfo.toString());
            }
    

    3.Spring核心AOP

    AOP(Aspect Oriented Programming):面向切面编程,在不修改源代码的情况下增强代码的功能。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    image-20210901112213781

    3.1.AOP实现原理代理模式

    代理模式,创建一个代理对象实现和被对代理对象相同的接口,这样就拥有和被代理对象相同的功能,在这基础上增强原有的方法。

    • 静态代理,手动去实现一个代理类
    • 动态代理,通过反射动态的实现代理类

    3.1.1 静态代理

    步骤:

    1. 抽象角色 : 一般使用接口或者抽象类来实现

      public interface Rent {
          public void rent();
      }
      
    2. 真实角色 : 被代理的角色

      public class Host implements Rent{
          @Override
          public void rent() {
              System.out.println("房东出租房子!");
          }
      }
      
    3. 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .

      public class Proxy {
          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.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();
            //host.rent();
            Proxy proxy=new Proxy(host);
            proxy.rent();
        }
    }
    

    好处:

    • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
    • 公共的业务由代理来完成 . 实现了业务的分工 ,
    • 公共业务发生扩展时变得更加集中和方便 .

    缺点 :

    • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
      我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理

    3.1.2.动态代理

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

    动态代理分为两类 :

    1. 基于接口的动态代理----JDK动态代理

      //抽象角色:租房
      public interface Rent {
          public void rent();
      }
      //真实角色: 房东,房东要出租房子
      public class Host implements Rent{
          public void rent() {
              System.out.println("房屋出租");
          }
      }
      //代理:中介
      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);
          }
       
          // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
          // 处理代理实例上的方法调用并返回结果
          @Override
          public 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("收中介费");
          }
      }
      //租客
      public class Client {
       
          public static void main(String[] args) {
              //真实角色
              Host host = new Host();
              //代理实例的调用处理程序
              ProxyInvocationHandler pih = new ProxyInvocationHandler();
              pih.setRent(host); //将真实角色放置进去!
              Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
              proxy.rent();
          }
       
      }
      
    2. 基于类的动态代理–cglib

    // 被代理的对象
    Account account = new Account();
    Account o = (Account) Enhancer.create(account.getClass(), new MethodInterceptor() {
        /**
        * 被代理对象的方法执行前会执行
        * @param obj 被代理的对象
        * @param method 方法
        * @param objects 参数
        * @param methodProxy 当前执行方法的代理的对象
        * @return 和被代理对象的方法相同的返回值
        * @throws Throwable 异常
        */
        @Override
        public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("增强前...");
            Object invoke = method.invoke(account, objects);
            System.out.println("增强后...");
            return invoke;
        }
    });
    o.findAll();
    

    3.2.AOP术语

    • Joinpoint(连接点):指的是方法,可以被动态代理增强的方法就是连接点,Spring只支持方法类型的连接点
    • Pointcut(切入点):定义要对哪些Joinpoint连接点(方法)进行拦截增强功能。被增强的方法叫做切入点,所有的方法都可以看做是一个连接点。只有被增强了的方法才叫做切入点。
    • Advice(通知/增强):拦截到Jointpoint(连接点)之后要做的事情就是通知。通知的类型:前置通知、后置通知、最终通知、环绕通知、异常通知。
    • Introduction(引介):一种特殊的通知,在不修改代码的前提下,可以在运行期为类动态的添加一些方法或字段。
    • Target(目标对象):代理的目标对象
    • Weaving(织入):是把增强 应用到 目标对象来创建新的代理对象的过程(添加新功能代码的过程)。Spring采用的是动态代理织入,而AspectJ采用编译期和类装载织入。
    • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
    • Aspect(切面):是切入点和通知(引介)的结合。

    image-20210903141349489

    3.3.使用Spring实现Aop

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    

    3.3.1.通过 Spring API 实现

    //接口与业务
    public interface UserService {
     
        public void add();
     
        public void delete();
     
        public void update();
     
        public void search();
     
    } 
    public class UserServiceImpl implements UserService{
     
        @Override
        public void add() {
            System.out.println("增加用户");
        }
     
        @Override
        public void delete() {
            System.out.println("删除用户");
        }
     
        @Override
        public void update() {
            System.out.println("更新用户");
        }
     
        @Override
        public void search() {
            System.out.println("查询用户");
        }
    }
    
    //增强
    public class AfterLog implements AfterReturningAdvice {
        //returnValue 返回值
        //method被调用的方法
        //args 被调用的方法的对象的参数
        //target 被调用的目标对象
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("执行了" + target.getClass().getName()
            +"的"+method.getName()+"方法,"
            +"返回值:"+returnValue);
        }
    }
    
    <?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-->
        <bean id="userService" class="com.wyl.service.UserServiceImpl"/>
        <bean id="log" class="com.kuang.log.Log"/>
        <bean id="afterLog" class="com.kuang.log.AfterLog"/>
     
        <!--aop的配置-->
        <aop:config>
            <!--切入点  expression:表达式匹配要执行的方法-->
            <aop:pointcut id="pointcut" expression="execution(* com.wyl.service.UserServiceImpl.*(..))"/>
            <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
     
    </beans>
    
    public class MyTest {
        @Test
        public void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.search();
        }
    }
    

    3.3.2.自定义类来实现Aop

    //切入类
    public class DiyPointcut {
     
        public void before(){
            System.out.println("---------方法执行前---------");
        }
        public void after(){
            System.out.println("---------方法执行后---------");
        }
        
    }
    
    <!--第二种方式自定义实现-->
    <!--注册bean-->
    <bean id="diy" class="com.wyl.config.DiyPointcut"/
    <!--aop的配置-->
    <aop:config>
        <!--第二种方式:使用AOP的标签实现-->
        <aop:aspect ref="diy">
            <aop:pointcut id="diyPonitcut" expression="execution(* com.wyl.service.UserServiceImpl.*(..))"/>
            <aop:before pointcut-ref="diyPonitcut" method="before"/>
            <aop:after pointcut-ref="diyPonitcut" method="after"/>
        </aop:aspect>
    </aop:config>
    
    public class MyTest {
        @Test
        public void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.add();
        }
    }
    

    3.3.3.使用注解实现AOP

    //注解实现的增强类
    package com.wyl.config;
     
    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 AnnotationPointcut {
        @Before("execution(* com.wyl.service.UserServiceImpl.*(..))")
        public void before(){
            System.out.println("---------方法执行前---------");
        }
     
        @After("execution(* com.wyl.service.UserServiceImpl.*(..))")
        public void after(){
            System.out.println("---------方法执行后---------");
        }
     
        @Around("execution(* com.wyl.service.UserServiceImpl.*(..))")
        public void around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("环绕前");
            System.out.println("签名:"+jp.getSignature());
            //执行目标方法proceed
            Object proceed = jp.proceed();
            System.out.println("环绕后");
            System.out.println(proceed);
        }
    }
    

    4.事务和JdbcTemplate

    4.1.JdbcTemplate使用

    入门案例:

    // Spring自带的数据源
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/spring?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    JdbcTemplate template = new JdbcTemplate(dataSource);
    
    List<Account> accountList = template.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
    for (Account account : accountList) {
        System.out.println(account);
    }
    

    具体增删改查用法:

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    // 添加
    @Test
    public void insert(){
        String sql = "insert into account(name, money) VALUES (?,?)";
        Account account1 = new Account();
        account1.setName("迪迦");
        account1.setMoney(10000F);
        jdbcTemplate.update(sql, account1.getName(), account1.getMoney());
        find();
    }
    
    // 删除
    @Test
    public void delete(){
        String sql = "delete from account where id = ?";
        jdbcTemplate.update(sql, 6);
        find();
    }
    
    // 更新
    @Test
    public void update(){
        List<Account> accounts = jdbcTemplate.query("select * from account where id = ?", new BeanPropertyRowMapper<>(Account.class), 1);
        Account account = accounts.get(0);
        account.setName("泰罗");
        String sql = "update account set name = ? where id = ?";
        jdbcTemplate.update(sql, account.getName(),account.getId());
        find();
    }
    
    // 查询所有
    @Test
    public void find(){
        List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
        for (Account account : accountList) {
            System.out.println(account);
        }
    }
    
    // 查询一个bean
    @Override
    public Account findByName(String name) {
        String sql = "select * from account where name = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
    }
    
    // 查询一个Object
    @Test
    public void findOne(){
        String sql = "select count(id) from account";
        Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(integer);
    }
    

    4.2.Spring配置事务

    在Spring中有两种方法管理事务:声明式事务管理和编程式事务管理;

    • 声明式事务管理:

      1、xml配置文件式:

      1、配置事务管理器
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <property name="dataSource" ref="dataSource"/></bean>
      
      2、配置通知
      <!-- 配置事务的通知/增强 -->
      <tx:advice id="interceptor">
          <!-- 配置事务的属性 -->
          <!-- isolation:事务隔离级别,默认使用数据库的隔离级别
               no-rollback-for:指定一个异常,除了该异常都回滚。
               propagation:事务传播行为,默认是required一定有事务,增删改设置required,查询设置supports
               read-only:是否只读。只有查询才能设置true。默认是false支持读写。
               rollback-for:指定一个异常,出现该异常就回滚,其他异常不回滚。
               timeout:事务超时时间,默认-1,永不超时。指定了以秒为单位。 -->
          <tx:attributes>
              <!-- 指定在哪种规则的方法上添加事务 -->
              <tx:method name="transfer*"/>
          </tx:attributes>
      </tx:advice>
      
      3、事务管理器和切入点表达式关联起来
      <aop:config>
          <!-- service包下所有类的所有方法都添加事务 -->
          <aop:pointcut id="commonPointcut" expression="execution(* com.sample.service.*.*(..))"/>
          <!-- 将事务管理器和切入点表达式关联起来 -->
          <aop:advisor advice-ref="interceptor" pointcut-ref="commonPointcut"/>
      </aop:config>
      

      2、注解式:

      1、配置事务管理器
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <property name="dataSource" ref="dataSource"/></bean>
      
      2、开启对事务注解的支持
      <!-- 开启对事务注解的支持 -->
      <tx:annotation-driven/>
      
      3、在要添加事物的类上添加注解:@Transactional
      

      3、纯注解式

      @Configuration
      @ComponentScan("com.sample")
      // 相当于<tx:annotation-driven/>
      @EnableTransactionManagement
      @PropertySource("classpath:db.properties")
      public class AppConfig {
      
          @Value("${db.driver}")
          private String driver;
          @Value("${db.url}")
          private String url;
          @Value("${db.username}")
          private String username;
          @Value("${db.password}")
          private String password;
      
          @Bean
          public DruidDataSource dataSource(){
              DruidDataSource dataSource = new DruidDataSource();
              dataSource.setDriverClassName(driver);
              dataSource.setUrl(url);
              dataSource.setUsername(username);
              dataSource.setPassword(password);
              return dataSource;
          }
      
          @Bean
          public JdbcTemplate jdbcTemplate(){
              return new JdbcTemplate(dataSource());
          }
      
          @Bean
          public DataSourceTransactionManager transactionManager(){
              return new DataSourceTransactionManager(dataSource());
          }
      
      }
      
      ==========
      在类上添加@Transactional注解即可
      
    • 编程式事务管理:通过代码去实现事务的管理,手动开启事务、提交、回滚。

  • 相关阅读:
    ASP.NET MVC 与 Web Forms
    去除两端margin的方法
    Media Queries之Respond.js
    ECMAScript5严格模式
    用rem设置文字大小
    BFC与hasLayout
    快速把项目部署到webLogic上
    判断一个坐标点是否在不规则多边形内部的算法
    Git 工作流的正确打开方式
    Java设计模式六大原则
  • 原文地址:https://www.cnblogs.com/wyl1924/p/15223015.html
Copyright © 2020-2023  润新知