Spring

原生web开发中存在的问题:

        1、传统Web开发中代码程序过度耦合

        2、部分JavaEE API较为复杂  使用率底

        3、侵入性强,移植性差

Spring框架

介绍:1、一个项目管理框架  

           2、众多优秀设计模式的整合(工厂模式、单例模式、代理模式等等)

           3、Spring并未代替原有的框架产品  而是对众多框架产品进行一个整合

Spring的访问与下载:

官方网站:https://spring.io/
下载地址:http://repo.spring.io/release/org/springframework/spring/

Spring架构的组成

● 核心技术:**依赖注入**事件,资源,i18n,验证,数据绑定,类型转换,SpEL,**AOP**。
● 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
● 数据访问:事务,DAO支持,JDBC,ORM,封送XML。
● Spring MVC和 Spring WebFlux Web框架。
● 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
● 语言:Kotlin,Groovy,动态语言。

常用的几个依赖包的解释:

本次学习从四个方面学习Spring框架

Ioc(Inversion of Control)/ DI   (控制反转) :将控制权由其他转到了Spring   UserDao注入到Spring后  UserService通过反射拿到UserDao 

原理:XML解析+利用反射创建一个Bean  将初始化以后得Bean存入到Spring中

具体用法:
1. 引入 Spring 依赖 spring-context。
2. 创建 Spring 配置文件,在配置文件中,向 Spring 容器去注册一个 Bean。
    2.1 基本属性注入
    2.2 复杂属性注入
    2.3 静态工厂注入/实例工厂注入
    2.4 FactoryBean 工厂注入
3. 默认清空下,当 Spring 容器启动的时候,所以注册到 Spring 容器中的 Bean 就会自动被初始化。
4. 接下来就可以从 Spring 容器中去获取一个 Bean 了。

 

AOP  :

JdbcTemplate :

事务:

Spring_Ioc:

public class BeanContext {

    private Map<String, Object> map = new HashMap<>();

    public Map<String, Object> getMap() {
        return map;
    }

    /**
     * 参数是配置文件的名字
     */
    public BeanContext(String beanFileName) {
        try {
            SpringBean springBean = new ObjectMapper().readValue(BeanContext.class.getResourceAsStream(beanFileName), SpringBean.class);
            //读取 JSON 中的 clazz 属性,利用反射创建出来一个 Bean
            Class<?> aClass = Class.forName(springBean.getClazz());
            Object o = aClass.getConstructor().newInstance();
            //将初始化之后的 Bean 存入到 Spring 容器中
            map.put(springBean.getName(), o);
        } catch (IOException | NoSuchMethodException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

 Spring_IOC属性注入:

xml配置:UserDao配置后就会上传到Bean中

三种方式注入对象属性值:

1.通过有参和无参构造构造方法

2.通过提供的set方法

3.通过开辟P空间的方法  (底层逻辑也是set方法)

    <!--向 Spring 容器注册一个 Bean-->
    <bean name="userDao" class="com.qfedu.demo.dao.UserDao"/>
    <bean name="userDao2" class="com.qfedu.demo.dao.UserDao"/>

    <!--
    由于 Spring 是通过反射去获取 User 的实例的,而通过反射获取一个类的实例也是需要通过构造方法的,所以我们需要确保这里的配置要有相匹配的构造方法
    -->
    <bean id="u1" class="com.qfedu.demo.model.User">
        <!--Spring将来会调用 User 类中包含了三个参数的构造方法去初始化 User-->
        <constructor-arg name="address" value="guangzhou"/>
        <constructor-arg name="id" value="99"/>
        <constructor-arg name="username" value="zhangsan"/>
    </bean>

    <bean id="u2" class="com.qfedu.demo.model.User">
        <!--这个会自动跟只有两个参数的构造方法匹配-->
        <constructor-arg name="id" value="100"/>
        <constructor-arg name="username" value="lisi"/>
    </bean>

    <bean id="u3" class="com.qfedu.demo.model.User">
 <!--通过 set 方法为属性注入值,下面这三个配置,分别会调用到各自的
 set 方法,注意 name 中的值,是 根据 set 方法推断出来的值,不是属性的名字-->
        <property name="id" value="101"/>
        <property name="username" value="wangwu"/>
        <property name="address" value="shenzhen"/>
    </bean>

    <!--p 名称空间注入,本质上依然是 set 方法注入-->
    <bean id="u4" class="com.qfedu.demo.model.User" p:id="102" p:address="shanghai" p:username="zhaoliu"/>

    <bean id="cat" class="com.qfedu.demo.model.Cat">
        <property name="name" value="小白"/>
        <property name="color" value="白色"/>
        <property name="age" value="3"/>
    </bean>

    <bean id="d1" class="com.qfedu.demo.model.Dog">
        <property name="name" value="小黑"/>
    </bean>

 实例化对象的代码块:

注意:这里的名称要和上面xml注入的保持一致

public class User {
    private Integer id;
    private String username;
    private String address;

    public User() {
    }

    public User(Integer id, String username) {
        this.id = id;
        this.username = username;
    }

    public User(Integer id, String username, String address) {
        this.id = id;
        this.username = username;
        this.address = address;
    }

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

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

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

方法测试的核心代码块:



public class Demo01 {
    public static void main(String[] args) {
        //默认会去 classpath 下查找配置文件
        //初始化 Spring 容器,默认情况下,当容器初始化的时候,UserDao 就已经被实例化了
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //跟 Spring 容器去要一个名为 userDao 的 Bean
        UserDao userDao = (UserDao) ctx.getBean("userDao");
        System.out.println("userDao.sayHello() = " + userDao.sayHello());

        //注意 Bean 的名字不要写错,没有找到UserDao2 则报错,会抛出 NoSuchBeanDefinitionException 异常
        UserDao userDao01 = (UserDao) ctx.getBean("userDao2");

        //跟 Spring 容器要 Bean 的时候,同时告诉 Spring 容器,我们需要的 Bean 是一个 UserDao
        UserDao ud2 = ctx.getBean("userDao", UserDao.class);
        System.out.println("ud2.sayHello() = " + ud2.sayHello());

        //直接根据类型去 Spring 容器中查找需要的 Bean
        //如果使用类型去查找 Bean,需要确保 Spring 容器中只有这一个 Bean,否则会抛出异常:NoUniqueBeanDefinitionException
        //以下语句运行后会报错 因为没有唯一的Bean
        UserDao ud3 = ctx.getBean(UserDao.class);
        System.out.println("ud3.sayHello() = " + ud3.sayHello());
        
        User u1 = ctx.getBean("u1", User.class);
        System.out.println("u1 = " + u1);
        User u3 = ctx.getBean("u3", User.class);
        System.out.println("u3 = " + u3);
        User u4 = ctx.getBean("u4", User.class);
        System.out.println("u4 = " + u4);
    }
}

 运行结果:

 复杂属性注入Bean

属性类:

public class User2 {
    private String name;
    private Cat cat;
    private List<String> favorites;
    private Date birthday;
    private Map<String,Object> info;
    private Properties info2;
    private Dog[] dogs;

    public User2() {
    }

    public User2(String name, Cat cat, List<String> favorites, Date birthday, Map<String, Object> info, Properties info2, Dog[] dogs) {
        this.name = name;
        this.cat = cat;
        this.favorites = favorites;
        this.birthday = birthday;
        this.info = info;
        this.info2 = info2;
        this.dogs = dogs;
    }

    @Override
    public String toString() {
        return "User2{" +
                "name='" + name + '\'' +
                ", cat=" + cat +
                ", favorites=" + favorites +
                ", birthday=" + birthday +
                ", info=" + info +
                ", info2=" + info2 +
                ", dogs=" + Arrays.toString(dogs) +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public Cat getCat() {
        return cat;
    }

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

    public List<String> getFavorites() {
        return favorites;
    }

    public void setFavorites(List<String> favorites) {
        this.favorites = favorites;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Map<String, Object> getInfo() {
        return info;
    }

    public void setInfo(Map<String, Object> info) {
        this.info = info;
    }

    public Properties getInfo2() {
        return info2;
    }

    public void setInfo2(Properties info2) {
        this.info2 = info2;
    }

    public Dog[] getDogs() {
        return dogs;
    }

    public void setDogs(Dog[] dogs) {
        this.dogs = dogs;
    }
}

对应的xml 注入方式

给猫这个对象设置属性值
<bean id="cat" class="com.qfedu.demo.model.Cat">
        <property name="name" value="小白"/>
        <property name="color" value="白色"/>
        <property name="age" value="3"/>
    </bean>

给狗这个对象设置属性值
    <bean id="d1" class="com.qfedu.demo.model.Dog">
        <property name="name" value="小黑"/>
    </bean>

利用构造方法加上引用给dog对象赋值
  <bean id="u6" class="com.qfedu.demo.model.User2">
        <constructor-arg name="cat">
        </constructor-arg>
        </bean>

    <bean id="u5" class="com.qfedu.demo.model.User2">
        <property name="name" value="zhangsan"/>
        <!--ref表示引用一个外部的变量  下面那一行就是引用上面小白那只猫-->
        <property name="cat" ref="cat"/>

兴趣爱好定义为一个list集合 这是注入Bean的写法
        <property name="favorites">
            <list>
                <value>足球</value>
                <value>篮球</value>
            </list>
        </property>

map的注入写法
        <property name="info">
            <map>
                <entry key="gender" value="男"/>
                <entry key="cat" value-ref="cat"/>
            </map>
        </property>


        <property name="dogs">
            <array>
                <!--引用一个提前定义好的对象-->
                <ref bean="d1"/>
                <!--现场定义一个 Dog 对象-->
                <bean class="com.qfedu.demo.model.Dog">
                    <property name="name" value="小黄"/>
                </bean>
            </array>
        </property>

properties文件设置值然后注入Bean
        <property name="info2">
            <props>
                <prop key="age">99</prop>
                <prop key="address">guangzhou</prop>
            </props>
        </property>
        <property name="birthday" value="2022/09/20"/>
    </bean>

第三方工具类注入Bean的方法:

以OkHttpClienFactory为例,该工具作用是获取到该网页的源代码并返回

没注入Spring前的第三方工具类写法:每次都需要new 对象出来调用方法

public class Demo03 {
    public static void main(String[] args) {
        //1. 构建 OkHttpClient 对象
        OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS).build();
        //2. 构建你的请求
        Request request = new Request.Builder()
                //设置请求方法是一个 GET 请求
                .get()
                //设置请求的 URL 地址
                .url("http://www.baidu.com")
                .build();
        //3. 获取一个请求调用对象
        Call call = okHttpClient.newCall(request);
enqueue是一个异步请求  遇到方法先执行下去 有响应了再返回
        call.enqueue(new Callback() {
            /**
             * 请求调用失败的回调
             * @param call
             * @param e
             */
            @Override
            public void onFailure(Call call, IOException e) {

            }

            /**
             * 请求调用成功的回调
             * @param call
             * @param response
             * @throws IOException
             */
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                System.out.println("response.body().string() = " + response.body().string());
            }
        });
    }
}

一、静态工厂注入: 

1.添加OkHttpClienFactory的依赖:

dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.12.10</version>
</dependency>

写一个OkHttpClienFactory 将其单独拿出来封装  以后可以方便调用:

import okhttp3.OkHttpClient;

import java.util.concurrent.TimeUnit;

public class OkHttpClientFactory {
connectTimeout(5, TimeUnit.SECONDS) 连接时间给5s 5s內没接上则是超时
readTimeout(5, TimeUnit.SECONDS)读取请求时间给5s 5s內没接上则是超时
 private static OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS).build();

    public static OkHttpClient getInstance() {
        return okHttpClient;
    }
}

Xml配置:将上面的方法注入到Bean中

<!--静态工厂注入-->
    <!--注意,factory-method 属性所指定的方法的返回值,最终会被注入到 SPring 容器中-->
    <!--根据id="okHttpClient2"拿到的是"getInstance"方法的返回值-->
    <bean class="com.qfedu.demo.factory.OkHttpClientFactory" factory-method="getInstance" id="okHttpClient"/>

 

Test 测试类:这是通过向Bean取要一个 okHttpClient对象来调取方法

public class Demo02 {

    private ClassPathXmlApplicationContext ctx;

    @Test
    public void test02() {
        OkHttpClient okHttpClient = ctx.getBean("okHttpClient", OkHttpClient.class);
        //2. 构建你的请求
        Request request = new Request.Builder()
                //设置请求方法是一个 GET 请求
                .get()
                //设置请求的 URL 地址
                .url("http://www.baidu.com")
                .build();
        //3. 获取一个请求调用对象
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            /**
             * 请求调用失败的回调
             * @param call
             * @param e
             */
            @Override
            public void onFailure(Call call, IOException e) {

            }

            /**
             * 请求调用成功的回调
             * @param call
             * @param response
             * @throws IOException
             */
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                System.out.println("response.body().string() = " + response.body().string());
            }
        });
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

实例工厂注入:

先写实例方法:

public class OkHttpClientFactory2 {

    private OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS).build();

    public OkHttpClient getInstance() {
        return okHttpClient;
    }
}

xml注入Bean中:

实例工厂注入,注意,这里需要先创建一个工厂的实例 先创建id="clientFactory2"这个对象  再通过对象调用getInstance方法
    
    <bean class="com.qfedu.demo.factory.OkHttpClientFactory2" id="clientFactory2"/>
<!--    okHttpClient2提供给测试的一个id-->
    <bean class="okhttp3.OkHttpClient" factory-bean="clientFactory2" factory-method="getInstance" id="okHttpClient2"/>

Teat:通过向Bean取要一个 okHttpClient2对象来调取方法  代码和上面的一样。

FactoryBean注入:

第一步:引入官方推荐的FactoryBean框架:

package com.demo.factory;

import com.demo.dao.UserDao;
import org.springframework.beans.factory.FactoryBean;

/**
 * 这个是官方推荐的工厂 Bean
 * 在 Spring 容器中,很多需要工厂方法提供的 Bean,基本上都是通过实现 FactoryBean 来完成的
 */
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    /**
     * 工厂返回的 Bean 是否是单例的
     * false 表示返回的 Bean 不是单例的,意味着每次从 Spring 容器中获取到的 Bean 都是一个全新的 Bean
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }

    /**
     * 具体返回 Bean 的方法
     * @return
     * @throws Exception
     */
    @Override
    public UserDao getObject() throws Exception {
        return UserDao.getInstance();
    }

    /**
     * 返回的 Bean 类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}

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 容器中去注册 UserDao
    注意,这里最终注册到 Spring 容器中的 Bean 有两个:
    1. UserDao
    2. UserDaoFactoryBean
    -->
    <bean class="com.qfedu.demo.factory.UserDaoFactoryBean" id="userDao"/>
   

</beans>

Test:

public class Demo01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //拿到UserDao的Bean对象
        UserDao ud = ctx.getBean("userDao", UserDao.class);
        UserDao ud2 = ctx.getBean("userDao", UserDao.class);
        System.out.println("ud = " + ud);
        System.out.println("ud==ud2 = " + (ud == ud2));
        //在 bean 的 id 之前,加上一个 & 符号,就表示获取到工厂 Bean 的实例
        UserDaoFactoryBean factoryBean = (UserDaoFactoryBean) ctx.getBean("&userDao");
        System.out.println("factoryBean = " + factoryBean);

Bean对象调用的生命周期问题:

这里我们写一个UserDao,里边有构造方法和初始化方法以及销毁方法:

public class User {
    private String username;

    public User() {
        System.out.println("构造方法执行");
    }
//方法名随意取  只要和xml里面的对应起来就可以了
    public void init() {
        System.out.println("bean 的初始化方法");
    }

    public void destroy() {
        System.out.println("bean 的销毁方法");
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

xml中进行配置 :将User注入Spring容器中

 <!--
    默认情况下,这个 Bean 是单例的,多次从 Spring 容器中获取,拿到的是同一个 Bean
    通过 scope 属性可以修改这个行为:
    singleton:表示这个 Bean 是单例的

    prototype:表示这个 Bean 是多例的,每一次获取都拿到一个新的 Bean

    request:在 Web 环境下,每一次请求中,多次拿到这个 Bean,都是同一个

    session:在 Web 环境下,在同一个会话中,多次拿到这个 Bean,都是同一个

    application:在 Web 环境下,在一个应用实例中,多次拿到这个 Bean,都是同一个

    lazy-init="true" 表示当跟 Spring 容器要这个 Bean 的时候,该 Bean 才会初始化

    scope="prototype"  添加了这个后 销毁的方法将不会被执行
    -->
    <bean class="com.qfedu.demo.model.User" id="user" init-method="init" destroy-method="destroy" lazy-init="true" scope="prototype"/>

Test:

public class Demo01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        User u1 = ctx.getBean(User.class);
        User u2 = ctx.getBean(User.class);
        //测试拿到的两个User对象是否是同一个
        //同一个User初始化一次就行
        System.out.println("u1==u2 = " + (u1 == u2));
        //销毁容器
        ctx.close();
    }
}

 运行结果:

注意:先进行构造方法的执行  在进行Bean的初始化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值