Spring框架入门

1、Spring框架概述

什么是spring框架

spring是J2EE应用程序框架,是一个兴起于2003年左右的开源框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,ibatis框架等组合使用;

IoC和AOP是什么

首先Spring框架是基于Bean编程的,Spring框架会将所有的对象都集中起来管理,这个操作就是使用了IoC思想;IOC是spring框架的核心,容器将对象连接到了一起,并且配置、管理他们的生命周期;

IOC不是一种技术,只是一种思想:代码松耦合、结构灵活

1、很好的体现了面向对象的设计法则之一:“好莱坞法则:别找我们,我们找你”,这里的我们指的是Java中的对象;依赖容器给予你资源,控制权在容器身上,不主动new对象,哪里需要对象,向容器发出请求,让容器帮自己new一个对象;

在这里插入图片描述

2、依赖注入(DI):在IOC容器创建完对象后 ,处理对象之间的依赖关系;所需求的对象,需要依赖容器注入;

3、spring中有三种注入方式,一种是set注入,一种是接口注入,另一种是构造方法注入;

AOP面向切面编程

比如业务1和业务2都需要一个共同的操作,与其往每个业务中都添加同样的代码,不如写一遍代码,让两个业务共同使用这段代码;

面向对象的延续,spring框架的重要内容,是函数式编程的衍生泛型,利用AOP可以对业务逻辑整个部分之间的耦合度降低,提高程序的重用性与开发的效率;

spring中面向切面变成的实现有两种方式,一种是动态代理,一种是CGLIB,动态代理必须要提供接口,而CGLIB实现是有继承。

为什么使用spring框架

在不使用spring框架之前,我们的service层中要使用dao层的对象,不得不在service层中new一个对象,各个层之间的调用都是这样的;


这使得层与层之前存在耦合,代码冗余,并且对象创建好之后,只能等待垃圾回收器自动回收,这时内存的开销也很大;

2、Spring入门案例

  • 1、创建普通项目,导入Spring框架需要依赖的jar包:

在这里插入图片描述
如果是创建Maven项目,需要依赖的jar包有:

<!-- Spring核心依赖 -->

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.7.RELEASE</version>
</dependency>
  • 2、编写Java实体类:
public class HelloSpring {
    private String str;

    public void print() {
        System.out.println(str + "======Spring======");
    }

    public String getStr() {
        return str;
    }

    //创建SET访问器,用于Spring注入值
   public void setStr(String str) {
        this.str = str;
    }
}
  • 3、创建resources文件夹,编写Spring核心配置文件:application.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">
    <!-- 将所有的对象 都交给Spring 进行管理  如何管理??? -->
    <!-- 管理:将对象创建为Bean  -->
    <!-- id:不能重复
    class:当前要创建类的位置(包名.类名) -->

    <bean id="helloSpring" class="org.westos.demo.HelloSpring">
        <!--name:传入的属性名称 (必须要有SET访问器与之匹配)-->
        <!--value:传入该对象下该属性的值-->
        <!--ref:传入的对象-->
        <property name="str" value="hello"/>
    </bean>
</beans>
  • 4、编写测试类:
@Test
public void show(){
    /*
    * 现在所有的对象都交给了Spring管理,想要使用对象就要借助于Spring
    * */

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
    HelloSpring hs = (HelloSpring) applicationContext.getBean("helloSpring");
    hs.print();
}

测试结果:

在这里插入图片描述

  • 分析整个过程,没有创建HelloSpring类的对象,但是我们可以通过Spring管理对象的方式最终调用出来对象中的方法;

【Spring框架的优点】:

1、方便解耦,简化开发 (高内聚低耦合),使用Spring的IOC容器,将对象之间的依赖关系交给Spring,让我们更专注于应用逻辑

2、Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理

3、对主流的框架提供了很好的集成支持,如Hibernate,Struts2,JPA等

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

5、Spring的高度可开放性,并不强制依赖于Spring,开发者可以自由选择Spring部分或全部

3、spring的体系结构

spring框架是一个分层架构,由7个定义良好的模块组成;spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean的方式,如图所示:

在这里插入图片描述
组成spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现,每个模块的功能如下:

核心容器

核心容器层是spring框架的基础,其他层都依赖于这一层,核心容器这一层包含以下4个模块:

Spring Core:这个模块是Spring框架的核心,提供控制反转/依赖注入功能

Spring Bean:这个模块实现Bean的工厂模式,Bean可以理解为组件,是JEE中基本的代码组织单位,Spring中Bean形式是普通Java类

Spring Context:此模块表示Spring应用的环境,通过此模块可访问任意Bean,ApplicationContext接口是模块的关键组成

Spring表达式语言(SpEL):这个模块提供对表达式语言(SpEL)支持

Spring数据访问/集成

数据访问相关,由以下5个模块组成:

JDBC:对Java JDBC接口再次包装,让Spring应用中使用JDBC更简单

ORM:ORM代表对象关系映射,该模块提供对ORM的支持

OXM: OXM代表对象XML映射器,该模块提供对OXM的支持

JMS:JMS代表Java消息传递服务,该模块提供对JMS的支持

事务: 该模块提供数据库事务的支持

Spring Web

Web:提供基本的Web功能,如文件下载、rest接口支持等

web-servlet:实现MVC(Model-View-Controller)功能

web socket:提供对web socket的支持

web portlet:提供对web portlet的支持

其他模块

AOP 提供对面向切面编程的支持

Aspects 提供与AspectJ集成,AspectJ是另一个面向切面编程的框架

Instrumentation 提供在某些应用服务器中使用的类加载实现

Messaging 提供对STOMP(Simple (or Streaming) Text Oriented Message Protocol )的支持

Test 支持JUnit或TestNG框架测试Spring组件

4、面向切面编程AOP

之前我们做的小案例都是使用JSP+servlet的形式,这其中有很多重复的代码需要书写:比如事务提交、日志,这些代码很冗余,AOP的出现可对业务逻辑各部分之间进行耦合度降低,提高开发效率;

【AOP的目标】:让我们可以**“专心做事”**

【AOP的原理】:

  • 将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决;

  • 采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能;

面向切面编程:是一种通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态添加功能的技术;

在这里插入图片描述
【AOP的相关术语】:

  • 增强处理:前置增强、后置增强、环绕增强、异常抛出增强、最终增强、切入点、连接点、切面、目标对象、AOP代理、织入;

1、target:目标类,需要被代理的类;
2、Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3、PointCut 切入点:已经被增强的连接点。例如:add()
4、advice:通知/增强,增强代码。例如:showRaram、showResult
5、Weaving(织入):是指把增强 advice 应用到目标对象 target 来创建新的代理对象proxy的过程;
6、proxy 代理类:把获得的这个新的方法称之为代理类;
7、Aspect(切面):是切入点 pointcut 和通知 advice 的结合;

【使用AOP做增强处理代码】:

增强类代码:

import org.aspectj.lang.JoinPoint;
import org.apache.log4j.Logger;
import java.util.Arrays;

public class UserServiceLogger {
    private static Logger logger = Logger.getLogger(UserServiceLogger.class);

    /***
     * JoinPoint 找一下它里面的方法简介 目标方法的类名、方法名、参数列表
     * @param jp
     */
    public void before(JoinPoint jp) {
        logger.info("调用:" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法,方法入参:" + Arrays.toString(jp.getArgs()));
    }

    /***
     * 前置增强 目标方法的类名、方法名、参数列表
     * @param jp
     * @param result 获取返回结果
     */
    public void afterReturning(JoinPoint jp, Object result) {
        logger.info("调用:" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法,方法返回值:" + result);
    }
}

dao层代码:

import org.westos.dao.UserDao;
import org.westos.entity.User;

public class UserDaoImpl implements UserDao {
    public int addUser(User user) {
        System.out.println("=========在UserDaoImpl中执行了addUser()============");
        return 1;
    }

    public int updateUser(User user) {
        System.out.println("=========在UserDaoImpl中执行了updateUser()============");
        return 1;
    }

    public int deleteUser(String userID) {
        System.out.println("=========在UserDaoImpl中执行了deleteUser()============");
        return 1;
    }
}

service层代码:

import org.westos.dao.UserDao;
import org.westos.entity.User;
import org.westos.service.UserService;

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    //创建set访问器,用于spring注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public int addUser(User user) {
        System.out.println("=========在UserServiceImpl中执行了addUser()============");
        return userDao.addUser(user);
    }

    public int updateUser(User user) {
        System.out.println("=========在UserServiceImpl中执行了updateUser()============");
        return userDao.updateUser(user);
    }

    public int deleteUser(String userID) {
        System.out.println("=========在UserServiceImpl中执行了deleteUser()============");
        return userDao.deleteUser(userID);
    }
}

核心配置文件:application.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-3.2.xsd">

    <!--注意:这里给的class的值不是接口,而是实现类,接口是一个规范而已-->
    <bean id="userDao" class="org.westos.dao.Impl.UserDaoImpl">

    </bean>

    <!--将userDao注入给userService-->
    <bean id="userService" class="org.westos.service.Impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <bean id="userServiceLogger" class="org.westos.aop.UserServiceLogger">

    </bean>

    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pointcut" expression="execution(public int addUser(org.westos.entity.User))"/>

        <!--可以有很多个增强类,具体使用哪一个增强类,取决于ref的设置-->
        <aop:aspect ref="userServiceLogger">
            <!--固定格式:前置增强-->
            <!--method:方法名称(来自增强类)-->
            <!--pointcut-ref:切入点名称-->
            <aop:before method="before" pointcut-ref="pointcut"></aop:before>
            <!--returning:有返回值参数-->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut"
                                 returning="result"></aop:after-returning>
        </aop:aspect>
    </aop:config>
</beans>

1、导入相应的Maven依赖:

 <!--aop使用到的包-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

<!--https://mvnrepository.com/artifact/org.springframework/spring-aspects-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>

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

2、在核心配置文件application.xml最上方导入aop切面编程的命名空间;

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-你的版本.xsd

3、添加<aop:config>的配置;

测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.westos.entity.User;
import org.westos.service.UserService;

public class UserTest {
    @Test
    public void addUserTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        int i = userService.addUser(new User());
        System.out.println(i);
    }
}

测试结果:

在这里插入图片描述

关于AOP的配置详解

<aop:config>
		切入点:需要增强的方法
		<aop:pointcut id="pointcut" 
		expression="execution(* com.xk.demo.service.*.*(..) )"/>
		<!--
		切入点 规则 更好的找到需要增强的业务
        public * addNewUser(entity.User): “*”表示匹配所有类型的返回值。
        public void *(entity.User): “*”表示匹配所有方法名。
        public void addNewUser(..): “..”表示匹配所有参数个数和类型。
        * com.service.*.*(..):匹配com.service包下所有类的所有方法。
        * com.service..*.*(..):匹配com.service包及其子包下所有类的所有方法
        -->	
		<aop:aspect ref="增强bean的ID">
                <!-- aop:before: 固定格式  前置增强   -->
                <!-- method 方法名称  (来自增强类) -->
                <!-- pointcut-ref  切入点名称 aop:pointcut id="pointcut" -->
                <aop:before method="before" pointcut-ref="pointcut"></aop:before>

                <!-- aop:after-returning: 固定格式  后置增强   -->
                <!-- method 方法名称  (来自增强类) -->
                <!-- pointcut-ref  切入点名称 aop:pointcut id="pointcut" -->
                <!--returning 有返回值参数   (来自增强类) -->
                <aop:after-retuirning method="afterReturning" 
                pointcut-ref="pointcut" returning="result"/>
            </aop:aspect>
</aop:config>

execution表达式: [方法访问修饰符] 方法返回值 包名.类名.方法名(方法的参数)

public * cn.itcast.spring.dao.*.*(..)
* cn.itcast.spring.dao.*.*(..)
* cn.itcast.spring.dao.UserDao+.*(..)
* cn.itcast.spring.dao..*.*(..)

*.*:表示任何类中的任何方法
..:表示任何参数

aop:aspect:可以有很多个增强类,具体是哪一个,取决于ref的值;

通知类型

在这里插入图片描述
【总结】:

面向切面编程是从系统中分离出切面,独立于业务逻辑(service)层的实现,在程序执行时织入程序中运行;

Spring创建代理的规则为:

1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB;

强制使用CGLIB生成代理


前面说过Spring使用动态代理或是CGLIB生成代理是有规则的,高版本的Spring会自动选择是使用动态代理还是CGLIB生成代理内容,当然我们也可以强制使用CGLIB生成代理,那就是<aop:config>里面有一个"proxy-target-class"属性,这个属性值如果被设置为true,那么基于类的代理将起作用,如果proxy-target-class被设置为false或者这个属性被省略,那么基于接口的代理将起作用。

5、注入其他类型数据

集合框架图:

在这里插入图片描述
集合特点概述:

1、List集合有序且允许重复元素、Set集合无序且不重复;

2、list集合调用add()返回值为true、set集合第一个添加为true,否则返回false、Map集合键值都可以为null;

3、Map 是键值对,键 Key 是唯一不能重复的,一个键对应一个值,值可以重复。TreeMap 可以保证顺序,HashMap 不保证顺序,即为无序的,

4、Map 中可以将 Key 和 Value 单独抽取出来,其中 KeySet()方法可以将所有的 keys 抽取成一个 Set,而 Values()方法可以将 map 中所有的 values 抽取成一个集合。

5、不包含重复元素的集合,set 中最多包含一个 null 元素,只能用 Iterator 实现单项遍历, Set 中没有同步方法。

6、List 有序的可重复集合,可以在任意位置增加删除元素,用 Iterator 实现单向遍历,也可用ListIterator 实现双向遍历。

7、Queue 遵从先进先出原则,使用时尽量避免 add()和 remove()方法,而是使用 offer()来添加元素,使用 poll()来移除元素,它的优点是可以通过返回值来判断是否成功,LinkedList实现了 Queue 接口,Queue 通常不允许插入 null 元素。

8、Stack 遵从后进先出原则,Stack 继承自 Vector,它通过五个操作对类 Vector 进行扩展允许将向量视为堆栈,它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek()方法、测试堆栈是否为空的 empty 方法等。

其他类型注入

spring对于特殊字符、对象、数组、List、Set、Map、Properties、空值null、空串这些数据类型的注入需要特别说明:

public class TestEntity {
    private String specialCharacter1;
    private String specialCharacter2;
    private User innerBean;
    private List<String> list;
    private String[] array;
    private Set<String> set;
    private Map<String,String> map;
    private Properties properties;
    private String emptyValue;
    private String nullValue;
    ……
    set和get访问器
}
<!--注入不同的数据类型-->
<bean id="testEntity" class="org.westos.entity.TestEntity">
    <!--特殊字符注入-->
    <property name="specialCharacter1">
        <!--<![CDATA[包含有特殊字符]]>-->
        <value><![CDATA[&&]]></value>
    </property>
    <property name="specialCharacter2">
        <!--<![CDATA[包含有特殊字符]]>-->
        <value>P&amp;G</value>
    </property>
    <!--自定义对象注入-->
    <property name="innerBean">
        <!--非常像一个匿名对象,不需要起名字,只在当前位置使用一次
        但是这个对象一直是存在的,有点浪费,可以添加scope属性,用于标识作用域
        singleton:是默认值,在SPring的IoC容器仅存在一个Bean(单例模式)
        prototype:每次从容器中调用bean时,都返回一个新的实例,例如:new xxxBean()
        request:每次HTTP请求都会创建一个新的实例,注意:该作用域仅限于webApplication环境
        session:同一个HTTP session共享一个bean,注意不同的session使用不同的bean,该作用域仅限于webApplication环境
        global-session:一般用于Portlet应用环境
        -->
        <bean class="org.westos.entity.User" scope="prototype">
            <property name="userCode" value="001"/>
            <property name="userName" value="张三"/>
            <property name="userPwd" value="123456"/>
        </bean>
    </property>

    <property name="list">
        <list>
            <value>list——足球</value>
            <value>list—篮球</value>
        </list>
    </property>

    <property name="array">
        <list>
            <value>array--足球</value>
            <value>array--篮球</value>
        </list>
    </property>

    <property name="set">
        <set>
            <value>set_足球</value>
            <value>set_篮球</value>
        </set>
    </property>

    <property name="map">
        <map>
            <entry>
                <key>
                    <value>basketball</value>
                </key>
                <value>篮球</value>
            </entry>

            <entry>
                <key>
                    <value>football</value>
                </key>
                <value>足球</value>
            </entry>
        </map>
    </property>

    <property name="properties">
        <props>
            <prop key="football">足球</prop>
            <prop key="basketball">篮球</prop>
        </props>
    </property>

    <property name="emptyValue">
        <value></value>
    </property>

    <property name="nullValue">
        <null/>
    </property>
</bean>
@Test
public void show() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
    TestEntity testEntity = (TestEntity) applicationContext.getBean("testEntity");
    System.out.println(JSON.toJSONString(testEntity, true));
}

在这里插入图片描述

6、依赖注入

构造注入

<!--下标注入-->
<bean id="test_userA" class="org.westos.entity.User">
    <constructor-arg index="0" value="001"></constructor-arg>
    <constructor-arg index="1" value="张三"></constructor-arg>
    <constructor-arg index="2" value="zhangsan"></constructor-arg>
</bean>

<!--名称注入-->
<bean id="test_userB" class="org.westos.entity.User">
    <constructor-arg name="userCode" value="002"></constructor-arg>
    <constructor-arg name="userName" value="李四"></constructor-arg>
    <constructor-arg name="userPwd" value="lisi"></constructor-arg>
</bean>

<!--type类型注入-->
<bean id="test_userC" class="org.westos.entity.User">
    <constructor-arg type="java.lang.String" value="003"></constructor-arg>
    <constructor-arg type="java.lang.String" value="王五"></constructor-arg>
    <constructor-arg type="java.lang.String" value="wnagwu"></constructor-arg>
</bean>
@Test
public void show() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
    User userA = (User) applicationContext.getBean("test_userA");
    User userB = (User) applicationContext.getBean("test_userB");
    User userC = (User) applicationContext.getBean("test_userC");
    System.out.println(JSON.toJSONString(userA, true));
    System.out.println(JSON.toJSONString(userB, true));
    System.out.println(JSON.toJSONString(userC, true));
}

在这里插入图片描述
P标签注入

<bean id="myUser" class="org.westos.entity.User" p:userCode="100" p:userName="你好" p:userPwd="hello">
    
</bean>

<bean id="service" class="org.westos.service.Impl.UserServiceImpl" p:userDao-ref="userDao"></bean>
@Test
public void show2() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
    User userA = (User) applicationContext.getBean("myUser");
    UserService service = (UserService) applicationContext.getBean("service");
    System.out.println(JSON.toJSONString(userA, true));
    System.out.println(JSON.toJSONString(service, true));
}

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值