Spring框架,简单入门。

后端 / 2020-03-10

1. Spring简介

1.1 什么是Spring

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

  • 目的:解决企业应用开发的复杂性

  • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

  • 范围:任何Java应用

Spring是一个轻量级控制反转(**IoC)**和面向切面(AOP)的容器框架。

1.2 三层架构

  • 表示层
  • 业务逻辑层
  • 数据访问层

Controller

Service

Dao

1.3 Spring 程序

Maven导入依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>comg.lu</groupId>
    <artifactId>spring-easy</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <dependencies>
        <!-- 导入 spring 框架 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>
    </dependencies>

</project>

ApplicationContext.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">
</beans>

IOC :把对象控制权 交给 Spring

<bean id="accountService" class="com.lu.service.impl.AccountServiceImpl"/>
<bean id="accountDao" class="com.lu.dao.impl.AccountDaoImpl"  />

注入 accountServiceaccountDao 对象

读取配置

通过 ClassPathXmlApplicationContext 读取配置文件然后获取对象

 ApplicationContext ac  = new ClassPathXmlApplicationContext("ApplicationContext.xml");

这里的 ac 就是 sprng 核心容器

因此 我们可以通过 ac 来 获取对象

获取对象

这里以 IAccountService 为例 进行 获取对象


别名

<!-- 对象 起别名  -->
<alias name="user" alias="myUser"/>

bean配置

  • id唯一标识符
  • class对象全限定名:报名+类型
  • name别名(可以同时取多个别名)
    <bean id="user" class="com.lu.pojo.User" name="u,user2">
        <constructor-arg type="java.lang.String" value="luzhenfang"/>
    </bean>
  • import 导入配置文件
    • 导入多个配置文件 合并为 一个 (一般用于团队开发)
<!-- 导入 -->
<import resource="xxxx.xml"/>
<import resource="xxxx.xml"/>
<import resource="xxxx.xml"/>
<import resource="xxxx.xml"/> 

1.4 依赖注入

Class:Student

package com.lu.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String [] books;
    private List<String> hobys;
    private Map<String,String> card;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.getAddress() +
                ", books=" + Arrays.toString(books) +
                ", hobys=" + hobys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }

    private Set<String> games;
    private String wife;
    private Properties info;

    public Properties getInfo() {
        return info;
    }

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

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

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

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

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

    public List<String> getHobys() {
        return hobys;
    }

    public void setHobys(List<String> hobys) {
        this.hobys = hobys;
    }

    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;
    }
}

Class:Address

package com.lu.pojo;

public class Address {
    private  String address;

    public String getAddress() {
        return address;
    }

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

Resoruces:Complex.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:context="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="address" class="com.lu.pojo.Address">
        <property name="address" value="ShanXi"/>
    </bean>


    <!-- DI 注入 复杂类型 -->
    <bean id="student" class="com.lu.pojo.Student">
        <!-- 普通注入 -->
        <property name="name" value="luzhenfang"/>
        <!-- bean 注入  ref -->
        <property name="address" ref="address"/>
        <!-- 数组注入 -->
        <property name="books">
            <array>
                <value>Java程序设计</value>
                <value>C#程序设计</value>
                <value>Python3爬虫实战</value>
                <value>C++ Primer</value>
                <value>算法导论</value>
                <value>大话数据结构</value>
            </array>
        </property>

        <!-- list 注入 -->
        <property name="hobys">
            <list>
                <value>听歌</value>
                <value>写BUG</value>
                <value>补BUG</value>
                <value>瞎折腾</value>
            </list>
        </property>
        <!-- map 注入 -->
        <property name="card">
            <map>
                <entry key="QQ" value="1318659507"/>
                <entry key="Wechat" value="lzfxxxx"/>
            </map>
        </property>
        <!-- set 注入  -->
        <property name="games">
            <set>
                <value>CS:GO</value>
                <value>Human:Fail</value>
            </set>
        </property>

        <!-- null pointer 注入 -->
        <property name="wife">
            <null/>
        </property>

        <!-- properties 注入 -->
        <property name="info">
            <props>
                <prop key="学号">1822032023</prop>
                <prop key="性别">男</prop>
            </props>
        </property>
    </bean>
</beans>

普通 注入

<bean id="address" class="com.lu.pojo.Address">
    <property name="address" value="ShanXi"/>
</bean>

bean 注入

 <bean id="student" class="com.lu.pojo.Student">
       <property name="address" ref="address"/>
</bean>

数组 注入

<bean id="student" class="com.lu.pojo.Student">
    <property name="books">
        <array>
            <value>Java程序设计</value>
            <value>C#程序设计</value>
            <value>Python3爬虫实战</value>
            <value>C++ Primer</value>
            <value>算法导论</value>
            <value>大话数据结构</value>
        </array>
    </property>
</bean>

list 注入

<bean id="student" class="com.lu.pojo.Student">
    <property name="books">
        <list>
            <value>听歌</value>
            <value>写BUG</value>
            <value>补BUG</value>
            <value>瞎折腾</value>
        </list>
    </property>
</bean>

map 注入

<bean id="student" class="com.lu.pojo.Student">
    <property name="card">
        <map>
            <entry key="QQ" value="1318659507"/>
            <entry key="Wechat" value="lzfxxxx"/>
        </map>
    </property>
</bean>

set 注入

<bean id="student" class="com.lu.pojo.Student">
    <property name="games">
        <set>
            <value>CS:GO</value>
            <value>Human:Fail</value>
        </set>
    </property>
</bean>

NullPointer 注入

<bean id="student" class="com.lu.pojo.Student">
    <property name="wife">
        <null/>
    </property>
</bean>

1.5 命名空间

P nameSpace

property 属性注入

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

    <!-- p 命名空就注入 property 注入 -->
    <bean id="user" class="com.lu.pojo.User" p:name="luzhenfang" p:age="20"/>
</beans>

C nameSpace

construct-args 构造器注入

<?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:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- c 命名空间 注入 construct-args -->
    <bean id="user2" class="com.lu.pojo.User" c:name="lu" c:age="20"/>
</beans>

1.6 Bean作用域

ScopeDescription解释
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.单例
prototypeScopes a single bean definition to any number of object instances.原型
requestScopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
sessionScopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
applicationScopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocketScopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

singleton

单例模式

单例模式:无论创建多少次 都只有一个实例

原型模式

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


<!-- 单例 -->
<bean id ="singleUser" class="com.lu.pojo.User" scope="singleton"/>
<!-- 原型 -->
<bean id ="protoUser" class="com.lu.pojo.User" scope="prototype"/>

1.7 自动装配

在Spring中 有3种装配的方式

  1. 在 xml 中 显式配置
  2. 在 java 种 显式配置
  3. 隐式 自动装配 ★

ByName

会自动在容器上下文中查找,和自己对象set方法后面的值对应的 beanid

ByType

会自动在容器上下文中查找,和自己对象属性类型相同的 bean

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

byName 时候,需要保证所有class 唯一,并且这个bean 需要和自动注入的属的类型一致!

byName:前提:id 唯一

byType 前提:对象 唯一

2. 注解

The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML. The short answer is “it depends.”

要使用注解的话需要 导入注解依赖

然后开启注解支持

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

    <!-- 开启注解支持 -->
    <context:annotation-config/>

</beans>

@Autowired

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

  • required 属性为 false 说明这个对象可以为 null,否则不允许为空

@Nullable

自动标记这个注解 说明这个字段可以为 null

@Qualifier

  • value 指定对象id

当出现二义性时 指定 匹配对象 (和@Autowired在一块使用)

例如:

<bean id="dog" class="com.lu.pojo.animal.impl.ImplDog"/>
<bean id="dog2" class="com.lu.pojo.animal.impl.ImplDog"/>

@Autowired
@Qualifier(value = "dog2")
private ImplDog dog;

@Component

把这个类交给Spring管理 --> 等同于

<bean id ="xx" class ="xxx"/>

衍生注解


  • dao @Repository
  • service @Service
  • controller @Controller

这几个 注解功能都是一样的:将某个类注册到Spring 容器中装配bean

@Value

给属性注入值 -- > 等同于

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

也可以放到 set 方法上面

@Scope

  • value

Spring 作用域

例如:

@Scope("singleton")

推荐

  • xml 管理bean
  • 注解 负责属性的注入

3.1 JavaConfig

使用java 类配置完全替代掉 xml 配置

@Configuration

代表这是一个配置类

等同于 beans.xml

@ComponentScan

告诉Spring 从哪里 找Bean

由你来定义哪些包需要被扫描。一旦你指定了,Spring将会将在被指定的包及其下级包(sub packages)中寻找bean.

@Bean

注册一个 bean ,相当于 之前写的 bean 标签

这个方法的名字 就相当于 bean 标签中的 id

方法返回值 就相当于 bean 标签种的class 属性

DEMO

ClassConfig.java

package com.lu.pojo.config;

import com.lu.pojo.config.component.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.lu.pojo.config.component")
public class AppConfig {
    @Bean
    public User user(){
        return new User();
    }
}
/**
	Configuration注解来告诉spring 这是一个配置类
	ComponentScan 告诉spring  扫描范围  如果不写  默认 为 当前 所在包 
	Bean 注册一个 bean  名字 就是要 用 的 id 
	return: 返回一个 new 后的 对象
*/

User.java

package com.lu.pojo.config.component;

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

public class User {
    @Value("luzhenfang")
    private String name;

    public String getName() {
        return name;
    }

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

/**
	这里就是一个简单的  set 注入 
	使用 value 注解 实现值的注入
*/

ConfigClassTest.java


import com.lu.pojo.config.AppConfig;
import com.lu.pojo.config.component.User;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ConfigTest {
    @Test
    public void Test(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = (User) context.getBean("user");
        System.out.println(user.getName());

    }
}

/**
	AnnotationConfigApplicationContext 是 spring 注解 装载后的容器 

*/

4. 代理模式

4.1 静态代理

静态代理模式:这种模式可以实现帮助被代理者完成一些前期的准备工作和后期的善后工作,但是核心的业务逻辑仍然是由被代理者完成。

代码结构由三部分组成

(1)接口:主题

(2)代理类

(3)被代理类

实现方式:代理类和被代理类要实现同一个主题接口,而且代理类中要有一个被代理类的属性(target),这样才能把核心业务逻辑交还给被代理类完成;而一些与核心业务逻辑无关的逻辑,并且需求是多变的,那么这些逻辑就可以交给代理类来完成

UserService.java

package com.lu.pojo.proxyPattery;

/**
 * 业务接口
 * 增删改查
 *
 */
public interface UserService {
    void select();
    void add();
    void delete();
    void alter();
}

UserServiceImpl.java

package com.lu.pojo.proxyPattery;

public class UserServiceImpl implements UserService {

    public void select() {

    }

    public void add() {

    }

    public void delete() {

    }

    public void alter() {

    }

}

UserServiceProxy.java

package com.lu.pojo.proxyPattery;

public class UserServiceProxy implements UserService{
   private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void select() {
        log("查询数据");
        userService.select();
    }

    public void add() {
        log("添加数据");
        userService.add();
    }

    public void delete() {
        log("删除数据");
        userService.delete();
    }

    public void alter() {
        log("修改数据");
        userService.alter();
    }
    public void log(String str){
        System.out.println("[DEBUG]:"+str);
    }
}

ProxyTest.java

package com.lu.pojo.proxyPattery;

import org.junit.Test;

public class ProxyTest {

    @Test
    public void Test(){
        /**
         * 代理模式测试
         *  创建一个 被代理对象 UserService
         *  然后创建一个 代理对象
         *  通过 set 注入  让 代理对象 去代理 UserService
         *  然后 实现解耦
         */
        UserServiceImpl service = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(service);
        proxy.add();
        proxy.select();

    }
}

代理模式好处:

  • 可以使真实角色操作更加纯粹
  • 公共交给代理角色 实现业务分工
  • 公共业务发生扩展时 方便集中管理

缺点:

  • 一个真实角色 就会产生一个代理角色;代码量翻倍,开发效率变低.

4.2 动态代理

  • 动态代理和静态代理角色一样
  • 动态代理类是动态生成的,不用我们直接写

分类

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

landlord.java

package com.lu.pojo.proxyInvocation;

public interface Landlord {
    void rent();
}

LandlordImpl.java

package com.lu.pojo.proxyInvocation;

public class LandlordImpl implements Landlord {

    public void rent() {
        System.out.println("房租租房");
    }
}

ProxyInvocationHandler.java

package com.lu.pojo.proxyInvocation;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {
    private LandlordImpl landlord;

    public void setLandlord(LandlordImpl landlord) {
        this.landlord = landlord;
    }

    // 返回代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),landlord.getClass().getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        Object result  = method.invoke(landlord,args);
        return result;
    }

    public void seeHouse(){
        System.out.println("中介看房");
    }
    public void log(String log){
        System.out.println("[DEBUG]: "+log);
    }
}

InvocationTest.java

import com.lu.pojo.proxyInvocation.Landlord;
import com.lu.pojo.proxyInvocation.LandlordImpl;
import com.lu.pojo.proxyInvocation.ProxyInvocationHandler;
import org.junit.Test;

public class InvocationTest {
    @Test
    public void Test(){
        // 真实角色
        LandlordImpl landlord = new LandlordImpl();
        // 代理角色
        ProxyInvocationHandler pih  = new ProxyInvocationHandler();
        pih.setLandlord(landlord);
        Landlord proxy = (Landlord) pih.getProxy();
        proxy.rent();
    }
}

5. AOP

5.1 原生SpringAPI实现

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


    <!-- 注册 bean -->
    <bean id="userService" class="com.lu.pojo.AOP.UserServiceImpl"/>
    <bean id="log" class="com.lu.pojo.AOP.log.Log"/>
    <bean id="afterLog" class="com.lu.pojo.AOP.log.AfterLog"/>

    <!-- 配置 AOP --> 
    <!-- 原生 api  接口 -->
    <aop:config>
        <!-- 切入点 expression 表达式 execution (要执行的 位置  * * *) -->
        <aop:pointcut id="pointcut" expression="execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))"/>
        <!-- 执行 环绕增加 -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

    </aop:config>


</beans>

Log.java

package com.lu.pojo.AOP.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log  implements MethodBeforeAdvice {
    // method 要执行的目标对象方法
    // objects 参数
    // target 目标对象
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

5.2 自定义类实现

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


    <!-- 注册 bean -->
    <bean id="userService" class="com.lu.pojo.AOP.UserServiceImpl"/>
    <bean id="log" class="com.lu.pojo.AOP.log.Log"/>
    <bean id="afterLog" class="com.lu.pojo.AOP.log.AfterLog"/>


    <!-- 自定义  -->
    <bean id="diy" class="com.lu.pojo.AOP.diy.PointCut"/>
    <aop:config>
        <!-- 要引用的 类 -->
        <aop:aspect ref="diy">
            <!-- 切入点 -->
            <aop:pointcut id="point" expression="execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))"/>
            <!-- 通知 -->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

pointCut.java

package com.lu.pojo.AOP.diy;

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

5.3 注解实现

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


    <!-- 注册 bean -->
    <bean id="userService" class="com.lu.pojo.AOP.UserServiceImpl"/>
    <bean id="log" class="com.lu.pojo.AOP.log.Log"/>
    <bean id="afterLog" class="com.lu.pojo.AOP.log.AfterLog"/>


    <bean id="annoPointCut" class="com.lu.pojo.AOP.annotation.PointCut"/>
    <!-- 开启注解支持 -->
    <aop:aspectj-autoproxy/>

</beans>

pointCut.java

package com.lu.pojo.AOP.annotation;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

// 标注 这个类 是一个切面
@Aspect
public class PointCut {
    @Before("execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("AnnotationPointCut:方法执行前");
    }
    @After("execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("AnnotationPointCut:方法执行后");
    }
}

6. 整合Mybatis