Spring入门笔记整理

目录

1.简介

1.1安装使用

1.2组成

1.3发展

2.IOC(重点) 

2.1问题产生

2.2解决方式

3.使用ioc创建对象 

3.1无参构造

3.2有参构造

3.2.1下标赋值

3.2.2类型赋值

3.2.3参数名赋值

3.3总结 

4.Spring的配置 

4.1 bean

4.2 alias 

4.3 import 

5.依赖注入(DI)

5.1 构造器注入

5.2使用Set注入 

5.3拓展注入 

5.3.1 P命名空间注入

5.3.2 C命名空间注入

5.4Bean对象作用域

5.4.1单例模式

5.4.2原型模式

6.Bean的自动装配机制

6.1环境建立

6.2byName方式

6.3byType方式 

6.4使用注解装配

7.注解开发

7.1属性注入

7.2衍生注解 

7.3区别

7.4作用域 

7.5注意点

8.AOP 

8.1代理模式

8.1.1静态代理 

8.1.2静态代理优缺点

8.2动态代理 

8.2使用Spring实现AOP

8.2.1方式一:使用Spring的API接口

8.2.2使用自定义类实现AOP

8.2.3使用注解实现AOP

9.整合Mybatis

10.事务 


1.简介

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

Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

优点:

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

控制反转(IOC)和面向切面编程(AOP)贯穿始终,是Spring学习的重中之重  

1.1安装使用

导入maven依赖

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

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

1.2组成

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

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

1.3发展

2.IOC(重点) 

  •  采用XML方式配置bean的时候,bean的定义信息和实现是分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
  • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,实现方法是依赖注入(DI)

2.1问题产生

代码框架

 

 编写一个UserDao接口

public interface UserDao {
   public void getUser();
}

编写实现类,UserDaoImpl 

public class UserDaoImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("User实现类");
  }
}

编写UserService的接口 

public interface UserService {
   public void getUser();
}

编写UserServiceImpl实现类 

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoImpl();

   @Override
   public void getUser() {
       userDao.getUser();
  }
}

 测试

@Test
public void MyTest(){
   UserService service = new UserServiceImpl();
   service.getUser();
}

 那么随着UserDao实现类的增加,比如UserDaoMysqlImpl,UserDaoOracleImpl就不得不改变UserServiceImpl中的代码。很明显这样是不符合开闭原则的。

2.2解决方式

对UserServiceImpl做出如下改变:

public class UserServiceImpl implements UserService {
   private UserDao userDao;
   //利用set方法动态实现注入
   public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
  }

   @Override
   public void getUser() {
       userDao.getUser();
  }
}
  • 之前,程序是主动创造对象!控制权在程序员手上
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象! 

这种思想,从本质上解决了问题,程序员不用再去管理对象的创建。系统的耦合性大大降低,可以更加专注在业务的实现上。这是IOC控制反转的原型! 

3.使用ioc创建对象 

 1.编写实体类User

package com.kuang.pojo;

public class User {

    private String name;
    public User() {
        System.out.println("user无参构造");
    }

    public User(String name) {
        System.out.println("user有参构造");
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

}

2.配置beans.xml文件 ,并放在resource文件夹下

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


</beans>

 3.在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">
<!--    使用spring创建对象-->
<!--    User user=new User()-->
<!--    bean=对象-->
<!--    id=变量名-->
<!--    class=new 的对象-->
<!--    property 相当于给对象的属性赋值-->

    <bean id="user" class="com.kuang.pojo.User">
        <property name="name" value="user1"></property>
    </bean>



</beans>

 4.测试

import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {
        //获取Spring的上下文对象
        ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象都在Spring中管理了,我们要使用直接去里面取出来就可以
        //方式一:需要强转
        User user =(User) context.getBean("user");
        System.out.println(user);

        //方式二:
        //User user1 = context.getBean("user", User.class);
        //System.out.println(user1);
    }
}

System.out.println(user==user1)显示true,表明这两个user实际是同一个对象。

3.1无参构造

property方法本质是通过反射调用setName方法给属性进行赋值

    <bean id="user" class="com.kuang.pojo.User">
        <property name="name" value="user1"></property>
    </bean>

3.2有参构造

3.2.1下标赋值

通过构造器中参数的下标对属性进行赋值

    <bean id="user2" class="com.kuang.pojo.User">
        <constructor-arg index="0" value="user2"></constructor-arg>
    </bean>

3.2.2类型赋值

通过构造器中参数的类型进行赋值,由于可能有多个属性类型相同,故不推荐使用这种方法

    <bean id="user3" class="com.kuang.pojo.User">
        <constructor-arg type="java.lang.String" value="user3"></constructor-arg>
    </bean>

3.2.3参数名赋值

 通过构造器中参数的名称赋值


    <bean id="user4" class="com.kuang.pojo.User" >
        <constructor-arg name="name" value="user4"></constructor-arg>
    </bean>

 注意,如果属性不是java八大基本类型以及String类,需要将value改为ref

3.3总结 

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

4.Spring的配置 

4.1 bean

4.2 alias 

用来给对象设置别名,例如给user取别名newuser

<alias name="user" alias="newuser"/>

4.3 import 

import:用于团队合作时,将多人的配置文件通过import合并为一个。

5.依赖注入(DI)

  • 依赖注入(Dependency Injection,DI)。

  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .

  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

5.1 构造器注入

上文已讲,见3.使用IOC创建对象

5.2使用Set注入 

 实体类Student,拥有各种类型的属性

package com.kuang.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}
 <bean id="address" class="com.kuang.pojo.Address" scope="prototype">

    </bean>
    <bean id="student" class="com.kuang.pojo.Student">
        <!--普通注入-->
        <property name="name" value="小王"/>
        <!--Bean注入-->
        <property name="address" ref="address"></property>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>三国演义</value>
            </array>
        </property>
        <!--List注入-->
        <property name="hobbys">
            <list>
                <value>游戏</value>
                <value>电影</value>
            </list>
        </property>
        <!--Map注入-->
        <property name="card">
            <map>
                <entry key="身份证" value="3205"/>
                <entry key="银行卡" value="9999"/>
            </map>
        </property>
        <!--Set注入-->
        <property name="games">
            <set>
                <value>lol</value>
                <value>csgo</value>
                <value>pubg</value>
            </set>
        </property>
        <!--null注入-->
        <property name="wife">
            <null></null>
        </property>
        <!--Properties注入-->
        <property name="info">
            <props>
                <prop key="学号">123456789</prop>
                <prop key="性别">男</prop>
                <prop key="root">local</prop>
                <prop key="psw">123456</prop>
            </props>
        </property>
    </bean>

5.3拓展注入 

导入如下约束:

5.3.1 P命名空间注入

5.3.2 C命名空间注入

5.4Bean对象作用域

 重点了解singleton(单例模式)和prototype(原型模式)

5.4.1单例模式

Spring默认模式,Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。 

    <bean id="address" class="com.kuang.pojo.Address">
import com.kuang.pojo.Address;
import com.kuang.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //Student student =(Student) context.getBean("student");
        Address address1 = context.getBean("address", Address.class);
        Address address2 = context.getBean("address", Address.class);
        System.out.println(address2==address1);

    }
}

 输出结果:true。表明实际上address1和address2实际上是同一个对象,共享bean实例

5.4.2原型模式

 当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。

    <bean id="address" class="com.kuang.pojo.Address" scope="prototype">
import com.kuang.pojo.Address;
import com.kuang.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //Student student =(Student) context.getBean("student");
        Address address1 = context.getBean("address", Address.class);
        Address address2 = context.getBean("address", Address.class);
        System.out.println(address2==address1);

    }
}

 输出结果:false。表明原型模式下,每次请求的bean对象都是不同的!


6.Bean的自动装配机制

 

Sping中有三种自动装配方式 ,以下介绍第三种装配方式。

6.1环境建立

实体类Cat

package com.kuang.pojo;

public class Cat {
    public void shout(){
        System.out.println("miaomiao~");
    }
}

实体类Person 

package com.kuang.pojo;

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

public class Person {
    @Autowired
    private Cat cat;

    public Person(Cat cat) {
        this.cat = cat;
    }

    public Person() {
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Person{" +
                "cat=" + cat +
                '}';
    }
}

 配置文件

    <bean id="cat" class="com.kuang.pojo.Cat"/>
    <bean id="person" class="com.kuang.pojo.Person"/>

测试

import com.kuang.pojo.Address;
import com.kuang.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        System.out.println(person);


    }
}

输出结果:Person{cat=com.kuang.pojo.Cat@1ad282e0} 

6.2byName方式

寻找所有set方法,例如setCat和setDog。获得首字母小写的属性名cat,dog。在spring容器中查找id为cat和dog的bean对象。如果有,取出并自动装配;没有则报空指针异常

6.3byType方式 

在容器上下文中查找和set方法参数类型一致的bean,且必须唯一。否则会报错!

6.4使用注解装配

导入约束和注解 

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context.xsd">

开启注解支持 

    <context:annotation-config/>

 

 

@Autowird是根据byName和byType自动识别的。如果ioc容器中只有一个该类型的对象则通过byType实现。否则通过byName

如果有多个同类型对象可以用如下方式指定:

7.注解开发

什么是注解开发?就是用特定的注解代替beans.xml文件中注册对象的步骤。

1.配置环境,导入红框context约束

 

 2.配置扫描包注解

    <context:annotation-config/>
    <!--指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.kuang.pojo"/>

 3.在类上标上注解

package com.kuang.pojo;

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

//等价于<bean id="user" class="com.kuang.pojo.User"/>
//Component组件
@Component
public class User {
    //等价于<property name="name" value="user1"/>
    @Value("user1")
    private String name;
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

 4.测试

@Test
public void test(){
   ApplicationContext applicationContext =
       new ClassPathXmlApplicationContext("beans.xml");
   User user = (User) applicationContext.getBean("user");
   System.out.println(user.name);
}

 注意:注解开发对象默认的id为将首字母小写的类名,如User则默认为user

7.1属性注入

 方法一:直接在属性上使用@value()注解

    //等价于<property name="name" value="user1"/>
    @Value("user1")
    private String name;

 方法二:如果有set方法,可以在set方法上使用@Value()注解

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

7.2衍生注解 

 

7.3区别

7.4作用域 

使用注解@scope标明对象的作用域

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
//注册一个id="user" name="秦疆" 作用域为原型模式的bean
@Controller("user")
@Scope("prototype")
public class User {
   @Value("秦疆")
   public String name;
}

7.5注意点

使用注解开发一定要开启扫描注解

<context:annotation-config/>  

 

8.AOP 

8.1代理模式

在理解AOP前,有必要先了解以下什么是代理模式 。因为AOP底层使用代理模式实现

8.1.1静态代理 

 静态代理角色分析

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

  • 真实角色 : 被代理的角色

  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .

  • 客户 : 使用代理角色来进行一些操作 .

举例说明上述不同角色:现有一个租房例子

 

 抽象角色:

 

 真实角色:

 代理角色:代理真实角色,代理真实角色后,一般会做附属操作

 

客户 :

 

8.1.2静态代理优缺点

 

8.2动态代理 

 动态代理底层使用了反射实现,所以需要先了解反射机制。

 抽象角色:

 真实角色:

 代理角色:代理真实角色,代理真实角色后,一般会做附属操作

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) throwsThrowable {
        //方法增强
        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();
  }
}

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

 

8.2使用Spring实现AOP

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

 

以下名词需要了解下:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

 

8.2.1方式一:使用Spring的API接口

1.导入依赖包

<dependency>
    <groupId> org.aspectj</groupId >
    <artifactId> aspectjweaver</artifactId >
    <version> 1.9.4</version >
</dependency>

 2.注册bean和配置aop

    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.kuang.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

3. 测试

8.2.2使用自定义类实现AOP

 1.自定义切入点

 

2注册bean和配置aop 

 

8.2.3使用注解实现AOP

1.写切面类并加以aspect注解

 

 2.配置

 

 3.测试

 

9.整合Mybatis

10.事务 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leg丶Gasai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值