Spring5----学习笔记(上)

IOC理论推导

在这里我会通过一个简单的例子来引出IOC,首先我们简单的新建一个maven项目,里面主要有dao层和service层,具体的代码如下:

//userDao层接口
public interface UserDao {
    void getUserData();
}
//获取mysql数据的实现
public class UserMysqlDaoImpl implements UserDao{
    public void getUserData() {
        System.out.println("获取Mysql数据!!!");
    }
}
//获取orancle数据的实现
public class UserOrancleDaoImpl implements UserDao{
    public void getUserData() {
        System.out.println("获取Orancle数据!!!");
    }
}
//service层:获取数据
public interface UserService {
    void getUser();
}

现在我们正常的情况下要获取数据:

public class UserServiceImpl implements UserService {
    //实例化获取mysql数据的接口
    UserDao userDao = new UserMysqlDaoImpl();
    //获取数据
    public void getUser() {
       userDao.getUserData();
    }
}

此时我们调用service层的方法进行测试:

public class MyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.getUser();  //控制台显示:获取Mysql数据!!!
    }
}

这时我们如果想要获取Orancle的数据,我们只能这样做,

public class UserServiceImpl implements UserService {
    //实例化获取mysql数据的接口
    UserDao userDao = new UserOrancleDaoImpl();
    //获取数据
    public void getUser() {
       userDao.getUserData();
    }
}

我们需要修改实例化的对象,把它修改成UserOrancleDaoImpl,这种方式我们修改了原有的代码,这就造成了每次由于业务的请求,写代码的人需要做出大量的修改,如果代码成千上万行,难道我们都要一一去修改吗,这时我们应该思考是不是这种方式不可取?,是不是还有其它的方式可以来实现?
我们可以用set动态的实现值的注入,代码如下:

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    //我们可以利用set动态的实现值的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    //获取数据
    public void getUser() {
       userDao.getUserData();
    }
}

因此我们在测试的时候可以以动态的注入Dao层的实现,仅仅只要修改初始化的实现对象:

public class MyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ((UserServiceImpl)userService).setUserDao(new UserOrancleDaoImpl());//输出:Orancele
        ((UserServiceImpl)userService).setUserDao(new UserMysqlDaoImpl());//输出:Mysql
        userService.getUser(); 
    }
}

总结:在之前没有使用set注入的业务中,用户的需求可能会影响我们原先的代码,破坏了程序的完整性,如果代码量很大,那么修改一次的成本很高。
我们使用set注入的方式,这种思想从本质上解决了这个问题,我们就不需要再去管理对象的创建了,系统的耦合性大大的降低了,就更加专注在业务的实现上了,从而引出spring的低层就是使用的这种机制,及这就是IOC的原型。

IOC的本质

控制反转IOC,是一种设计思想,DI(依赖注入)是实现IOC的一种方法,在没有IOC的程序中,我们使用面向对象编程,对象的创建和对象的依赖关系完全在我们的程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。
在这里插入图片描述

Hellow Spring

接下来我会通过一个实例来体现IOC容器:
下图显示了Spring的工作原理的高级视图。您的应用程序类与配置元数据结合在一起,因此,在ApplicationContext创建和初始化后,您将拥有一个完全配置且可执行的系统或应用程序。
在这里插入图片描述
首先我们编写一个Hellow实体类:

public class Hellow {

    private String name;

    public String getName() {
        return name;
    }

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

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

然后编写spring文件,这里命名为beans.xml,以下示例显示了基于XML的配置元数据的基本结构:

bean == 对象
id == 变量名
class == new 的对象

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 使用spring来创建对象,在spring中这些都称为bean -->
    <bean id="hellow" class="com.twy.pojo.Hellow">
        <property name="name" value="testSpring"></property>
    </bean>
</beans>

最后我们实例化容器:

    public static void main(String[] args) {
        //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //对象都在spring中管理了,因此直接取出来就可以使用了
        Hellow hellow = (Hellow) context.getBean("hellow");
        System.out.println(hellow.toString());
    }

思考问题?
(1)hellow是怎么创建的?答:hellow对象是由spring创建的
(2)hellow对象的属性是怎么设置的?答:hellow对象的属性是由spring容器设置的
上述的过程就叫控制反转
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用spring后,对象是由spring创建的
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:利用set方法来进行注入

IOC创建对象的方式

  1. 使用无参构造函数创建对象,默认!
  2. 使用有参构造创建对象:
    (1)下标赋值:
        <bean id="user" class="com.twy.pojo.User">
            <constructor-arg index="0" value="下标赋值"/>
        </bean>
    
    (2)通过类型创建,不建议使用:
        <bean id="user" class="com.twy.pojo.User">
            <constructor-arg type="java.lang.String" value="类型赋值"/>
        </bean>
    
    (3)直接通过参数名来赋值:
        <bean id="user" class="com.twy.pojo.User">
            <constructor-arg name="name" value="参数名赋值"/>
        </bean>
    

依赖注入

  1. 构造器注入
    见IOC创建对象的方式,及就是构造器注入。
  2. Set方式注入
    (1)新建一个实体类Student.java
    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 +
                    ", books=" + Arrays.toString(books) +
                    ", hobbys=" + hobbys +
                    ", card=" + card +
                    ", games=" + games +
                    ", wife='" + wife + '\'' +
                    ", info=" + info +
                    '}';
        }
    }
    
    (2)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="address" class="com.twy.pojo.Address">
            <property name="address" value="江苏"/>
        </bean>
        <bean id="student" class="com.twy.pojo.Student">
            <!-- 第一种:普通值注入-->
            <property name="name" value="xiaoming"/>
            <!-- 第二种:bean注入-->
            <property name="address" ref="address"/>
            <!-- 第三种:数组注入-->
            <property name="books">
                <array>
                    <value>01</value>
                    <value>02</value>
                    <value>03</value>
                </array>
            </property>
            <!-- 第四种:list注入-->
            <property name="hobbys">
                <list>
                    <value>01</value>
                    <value>02</value>
                    <value>03</value>
                </list>
            </property>
            <!-- 第五种:map注入-->
            <property name="card">
                <map>
                    <entry key="01" value="0101"/>
                </map>
            </property>
            <!-- 第六种:set注入-->
            <property name="games">
                <set>
                    <value>01</value>
                </set>
            </property>
            <!-- 第七种:null注入-->
            <property name="wife">
                <null/>
            </property>
            <!-- 第八种:Properties注入-->
            <property name="info">
                <props>
                    <prop key="学号">0102</prop>
                </props>
            </property>
        </bean>
    </beans>
    
bean的作用域在这里插入图片描述

(1)单例模式(Spring默认机制)
仅管理一个singleton bean的一个共享实例,并且所有对具有ID或与该bean定义相匹配的ID的bean的请求都将导致该特定的bean实例由Spring容器返回。
换句话说,当您定义一个bean定义并且其作用域为单例时,Spring IoC容器将为该bean定义所定义的对象创建一个实例。该单个实例存储在此类单例bean的高速缓存中,并且对该命名bean的所有后续请求和引用都返回该高速缓存的对象。下图显示了单例作用域的工作方式:
在这里插入图片描述
如果您在单个Spring容器中为特定类定义一个bean,则Spring容器将创建该bean定义所定义的类的一个且只有一个实例。单例作用域是Spring中的默认作用域。要将bean定义为XML中的单例,可以定义bean,如以下示例所示:

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

(2)原型模式
每次对特定bean提出请求时,bean部署的非单一原型范围都会导致创建一个新bean实例。也就是说,该Bean被注入到另一个Bean中,或者您可以通过getBean()容器上的方法调用来请求它。通常,应将原型作用域用于所有有状态Bean,将单例作用域用于无状态Bean。
下图说明了Spring原型范围:
在这里插入图片描述
数据访问对象(DAO)通常不配置为原型,因为典型的DAO不拥有任何对话状态。对于我们而言,重用单例图的核心更为容易。)

以下示例将bean定义为XML原型:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

(3)其余的request,session,application,这些只能再web开发中使用到

bean的自动装配

  • Spring会在上下文中自动寻找,并自动给bean装配属性。
  • 在Spring中有三种自动装配的方式:
    (1)在XML中显示的配置,上面我们一直用的这种方式
    (2)在Java中显示配置
    (3)隐式的自动装配bean (最重要)

  • ByName自动装配
<bean id="people" class="com.twy.pojo.People" autowire="byName">
	<property name="name" value="twy"/>
</bean>

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

  • ByType自动装配
<bean id="people" class="com.twy.pojo.People" autowire="byType">
	<property name="name" value="twy"/>
</bean>

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

  • 小结:
    byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致;
    byName的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致;

使用注解实现自动装配

jdk1.5支持注解,Spring2.5就支持注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。

使用注解的注意:

  • 导入约束:context约束
  • 配置注解的支持
<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会按byType,byName找一遍,都没有的情况下才需要qualifier注解。

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

    <bean id="dog" class="com.twy.pojo.Dog"></bean>
    <bean id="cat" class="com.twy.pojo.Cat"></bean>
    <bean id="person" class="com.twy.pojo.Person"></bean>

</beans>

Person.java:

public class Person {
    @Autowired
    private Dog dog;

    @Autowired
    private Cat cat;
    
    private String name;
    
    @Override
    public String toString() {
        return "Person{" +
                "dog=" + dog +
                ", cat=" + cat +
                ", name='" + name + '\'' +
                '}';
    }
}

从代码中可以看出使用@Autowired的简洁方便。

  • @Resource
    @Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常

小结
@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired优先使用根据byType进行标注装配,配置使用@Qualifier可完成按照名称进行装配
  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byType方式进行装配

开发中的常见注解

  • @component (把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>
    泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。

下面写这个是引入component的扫描组件 :

<context:component-scan base-package=”com.twy”>

其中base-package为需要扫描的包(含所有子包)

   1、@Service用于标注业务层组件 
   2、@Controller用于标注控制层组件(如struts中的action) 
   3、@Repository用于标注数据访问组件,即DAO组件. 
   4、@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。    
   @Service 
   public class UserServiceImpl implements UserService { }     
   @Repository 
   public class UserDaoImpl implements UserDao { } 
   getBean的默认名称是类名(头字母小写),如果想自定义,可以@Service(***)     
   这样来指定,这种bean默认是单例的,如果想改变,可以使用@Service(“beanName”)  
   @Scope(“prototype”)来改变,变成原型模式
   可以使用以下方式指定初始化方法和销毁方法(方法名任意): 
   @PostConstruct 
   public void init() { }

使用JavaConfig实现配置

JavaConfig,是在 Spring 3.0 开始从一个独立的项目并入到 Spring 中的。JavaConfig 可以看成一个用于完成 Bean 装配的 Spring 配置文件,即 Spring 容器,只不过该容器不是 XML文件,而是由程序员使用 Java 自己编写的 Java 类。

  • 实体类:
//组件,注入User
@Component
public class User {

    //给name注入值
    @Value("twy")
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
  • 配置类:
//Configuration是一个配置类,就是我上面写的beans.xml
@Configuration
@ComponentScan("com.twy.config") //默认扫描的是当前包以及子包
public class MyConfig {

    //注册一个bean,方法名相当于bean标签中的id标签
    //返回值相当于bean标签中的class属性
    @Bean("user")
    public User user(){
        return new User();
    }
}
  • 测试类:
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(user.getName());
    }
}

在这里插入图片描述
进入ApplicationContext里面我们可以看到 AnnotationConfigApplicationContext实现类。

  • 小结:
    以上这种纯Java的配置方式,在SpringBoot中随处可见!!!
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页