spring-1-spring介绍和IOC容器开发

一、介绍

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IOC)面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架

Spring的优点

 1、方便解耦,简化开发 (高内聚低耦合):Spring就是一个大工厂(容器), Spring工厂是用于生成bean,可以将所有对象创建和依赖关系维护,交给Spring管理                                               
 
 2、AOP编程的支持:Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
	
 3、声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程
 
 4、方便程序的测试 :Spring对Junit4支持,可以通过注解方便的测试Spring程序
	
5、方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis等)的直接支持,方便整合其他框架
	
6、降低JavaEE API的使用难度:Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低

Spring的控制反转(IOC):

Inversion of Control 又称为 依赖注入 (DI)  Dependency Injection                      
1、把对象的创建和方法的调用的控制权(反转)交由给容器管理,由容器控制程序之间的依赖关系                   2、IOC就是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是通过容器生成             3、同时,要是产生的是单例的bean,他还可以给管理bean的生命周期

IOC理解:

它并不是一种技术实现,而是一种设计思想。在任何一个有实际开发意义的程序项目中,我们会使用很多类来描述它们特有的功能,并且通过类与类之间的相互协作来完成特定的业务逻辑。这个时候,每个类都需要负责管理与自己有交互的类的引用和依赖,代码将会变的异常难以维护和极度的高耦合。而IOC的出现正是用来解决这个问题,我们通过IOC将这些相互依赖对象的创建、协调工作交给Spring容器去处理,每个对象只需要关注其自身的业务逻辑关系就可以了。在这样的角度上来看,获得依赖的对象的方式,进行了反转,变成了由spring容器控制对象如何获取外部资源(包括其他对象和文件资料等等)

IOC和DI的区别:

  IOC:控制反转,把创建对象交给Spring进行配置
  DI:依赖注入,向类里面的属性中赋值

Spring的面向切面编程(AOP):

将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。 比如: 很多方法可能会抛异常,你要记录这个异常到日志中去,可以写个拦截器类,在这个类中记录日志,在spring.xml中配置一个对这些要记录日志的方法的aop拦截器 在这个方法执行后调用这个拦截器,记录日志。这样就不用每次抛异常都要手动记录日志。 spring的事务管理用到的就是aop 这样也可以提高程序的内聚性

二、入门案例

流程:

导入依赖

<!--四个核心依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<!--下面均为切片依赖-->
<!--aop-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<!--aspectjrt-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.1</version>
</dependency>
<!--aspectjweaver-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.1</version>
</dependency>
<!--aspects-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.7.RELEASE</version>
</dependency>
<!--aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
<!--/////////////////////////-->
<!--junit测试板块-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

创建一个类

public class User {

    private String name;

    private Integer age;

    public void add(){
        System.out.println("add>>>>>>");
    }

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

创建spring的核心配置文件

在该配置文件中表明创建了一个User类的bean

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

    <!--配置User对象创建-->
    <!--这里创建了一个user的实例对象-->
    <bean id="user" class="com.wang.Spring5.User"></bean>

</beans>

调用这个bean

@Test
public void testAdd(){
    //1、加载配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    //2、获取配置创建的对象
    //getBean()第一个参数为这个类的别名,第二个参数为返回类型
    User user = context.getBean("user", User.class);
    System.out.println(user);
    user.add();
}

三、DI注入

在入门案例中,User的bean实例中的变量没有赋值,DI就是解决这个问题的

常见属性注入:

注意这里需要类中有属性的set方法

<bean id="user" class="com.wang.User">
    <property name="name" value="tom"/>
    <property name="age" value="12"/>
</bean>

简化,使用p名称空间注入

<?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">
    <bean id="book" class="com.wang.Spring5.Book" p:bname="jiu" p:time="无名氏"></bean>
        

有参构造注入:

注意:类中必须要有相应的构造函数

<bean id="order" class="com.wang.Spring5.Order">
    <constructor-arg name="oname" value="12"></constructor-arg>
    <constructor-arg name="address" value="江西"></constructor-arg>
</bean>
    ——另一种方式(通过索引)
<bean id="order" class="com.wang.Spring5.Order">
	<constructor-arg index="0" value="34"></constructor-arg>
    <constructor-arg index="1" value="北京"></constructor-arg>
</bean>

特殊类型注入:

null,包含了<>的字符串 等

-- 含null
<constructor-arg name="oname">
    <null></null>
</constructor-arg>
-- 含<>符号
<constructor-arg name="address">
    <value><![CDATA[<<北京>>]]></value>
</constructor-arg>

级联赋值:

可以理解为一个bean对象的属性指向另一个bean对象
1、创建service类和dao类
2、在service中调用dao里的方法

- 外部bean
<bean id="userService" class="com.wang.service.UserService">
    <!--注入userDao对象
        name属性:类里面属性名称
        ref属性:创建userDao对象bean标签id
    -->
    <property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.wang.dao.UserDaoImp"></bean>

- 内部bean:和上面那一种方法达到的效果是一样的
<bean id="emp" class="com.wang.company.Employee">
    <property name="ename" value="wangwang"></property>
    <property name="eage" value="18"></property>
    <property name="dept">
        <bean id="dept" class="com.wang.company.Department">
            <property name="dname" value="安保部门"></property>
        </bean>
    </property>
</bean>

- 级联复制(一般不用)
<bean id="emp" class="com.wang.company.Employee">
    <property name="eage" value="18"></property>
    <property name="ename" value="wang"></property>
    <property name="dept" ref="dept"></property>
    <property name="dept.dname" value="人力资源部"></property>
</bean>
<bean id="dept" class="com.wang.company.Department"></bean>

集合类型注入:

在对象里设置集合类型

public class Person {

    //1.数组类型属性
    private String[] array;

    //2.list集合类型属性
    private List<String> list;

    //3.map集合类型属性
    private Map<String, String> map;
}

注入

<bean id="ryan" class="com.ryan.spring5.inputCollection.Person">
    
		...
    
        <!--map类型属性注入-->
        <property name="map">
            <map>
                <entry key="map-key1" value="map-value1"></entry>
                <entry key="map-key2" value="map-value2"></entry>
            </map>
        </property>
    
		...
    
</bean>

集合下的级联赋值

即处理下面这种数据,集合里面是示例对象的情况

private List<Family> familyList;

public void setFamilyList(List<Family> familyList) {
    this.familyList = familyList;
}

使用ref属性标签

<bean>
	<property name="familyList">
        <list>
            <ref bean="family"></ref>
            <ref bean="family"></ref>
            <ref bean="family"></ref>
        </list>
    </property>
</bean>

<bean id="family" class="com.wang.gather.Family">
    <property name="f" value="13"></property>
</bean>

公共注入提取独立:

理解:即把下面这个部分独立出来,便于多次调用,有点类似于创建一个公用数组,这样大家都可以调用了

<property name="array">
    <array>
        <value>arr1</value>
        <value>arr2</value>
        <value>arr3</value>
    </array>
</property>

操作

<!--这个地方类似于一个公开数组-->
<util:list id="list1">
    <value>张辽</value>
    <value>hello</value>
    <value>太死</value>
</util:list>

<bean id="person" class="com.wang.gather.Person">
    <property name="list" ref="list1"></property>
</bean>

四、bean管理(xml)

1、工厂bean

概念:

作用:用java代码来代替一些比较赋值比较麻烦的bean,通过在xml文件中配置我们的工厂bean,然后在工厂bean中实现返回配置好的我们需要的bean,然后我们接收这个bean时只需要引入这个工厂bean的id即可

Spring有两种类型的bean, 一种普通bean, 一种工厂bean(FactoryBean)

普通Bean: 在配置文件中定义的bean类型就是返回类型;

工厂bean: 在配置文件中定义的bean类型可以和返回类型不一样,

有点意思是传入一个参数得到另外一个参数

流程:

创建类, 实现接口FactoryBean

实现接口里面的方法, 在实现的方法中定义返回的bean类型.

public class MybeanImp implements FactoryBean<Mybean> {
    public Mybean getObject() throws Exception {
        //在这个地方完成对mybean的配置
        Mybean mybean = new Mybean();
        mybean.setName("dashazi");
        return mybean;
    }

    public Class<?> getObjectType() {
        return null;
    }

    public boolean isSingleton() {
        return false;
    }
}

配置文件中

<!--注意创建的是工厂bean-->
<bean id="mybean" class="com.wang.Factorybean.MybeanImp"></bean>

实现

@Test
public void test7(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Mybean mybean = context.getBean("mybean", Mybean.class);
    System.out.println(mybean);
}

2、bean作用

概念:

 单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的;

 多例则指每个请求用一个新的对象来处理,比如action;

  • 如何设置单例和多例

可以用Spring配置文件bean标签里面的scope属性, 来设置单实例/多实例.

Spring默认单实例, 即scope=singleton

可以通过设置scope=prototype来设置多实例

如:

<bean id="testBean" class="com.ryan.spring5.testScope.TestBean" scope="prototype"></bean>

这就是一个多例对象

  • singleton和prototype区别

    • singleton单例,prototype多例
    • 设置scope值是单例时,加载spring配置文件时就会创建单实例对象,而scope值时多例对象时,加载spring配置文件时不会创建对象,使用getbean方法时才会调用
  • 另外还有:request(一次请求),session(一此对话),不做解释

3、bean生命周期

过程:

即bean实例产生到销毁过程

  1. 通过构造器创建bean实例(无参数构造);
  2. 设置bean的属性值和对其他bean引用(调用set方法);
  3. (可以不进行)把bean实例传递给bean前置处理器的方法postProcessBeforeInitialization
  4. 调用bean的初始化的方法(需要配置初始化方法);
  5. (可以不进行)把bean实例传递给bean后置处理器的方法postProcessAfterInitialization
  6. bean可以使用了(获取到了对象);
  7. 当容器关闭的时候, 调用bean的销毁方法(需要配置销毁方法)

演示:

public class Orders {
    
    private String name;

    public Orders() {
        System.out.println("1、无参数构建");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("2、调用set方法");
    }

    public void add(){
        System.out.println("6、获取到了对象");
    }

    //初始化方法,对应initMethod属性
    public void initMethods(){
        System.out.println("4、调用初始化方法");
    }
    //销毁方法,对应destroyMethod属性
    public void destroyMethods(){
        System.out.println("5、调用销毁方法");
    }
}

IOC 前/后 置处理器

public class MybeanPost implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("3、初始化前的操作");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5、初始化之后的操作");
        return bean;
    }
}

核心配置文件

<bean id="orders" class="com.wang.Factorybean.Orders" init-method="initMethods" destroy-method="destroyMethods">
    <property name="name" value="hello世家"></property>
</bean>

<!--后置处理器-->
<bean id="beanpost" class="com.wang.Factorybean.MybeanPost"></bean>
<!--注意点:后置处理器一旦在xml文件中实例化,所有的bean示例都会配置这个处理器-->

尝试调用

@Test
public void test8(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Orders orders = context.getBean("orders", Orders.class);
    orders.add();
    context.close();
}

4、xml自动装配

自动装配: 根据指定装配规则(属性名称或属性类型), Spring自动将匹配的属性值进行注入

名字匹配:

    <!--原来的配置方式-->
<!--    <bean id="dep" class="com.wang.autowire.Dep"></bean>-->
<!--    <bean id="emp" class="com.wang.autowire.Emp">-->
<!--        <property name="dep" ref="dep"></property>-->
<!--    </bean>-->

    <!--后来的配置方式-->
    <bean id="emp" class="com.wang.autowire.Emp" autowire="byName"></bean>
    <!--它会根据emp里的属性的名称自动匹配id也式同样名字的属性-->
    <bean id="dep" class="com.wang.autowire.Dep"></bean>

类型匹配:

注意点:一旦自动装配选择了bytype,那么我们需要的那个属性的类就只能创建一个实例

<bean id="emp" class="com.wang.autowire.Emp" autowire="byType"></bean>

<bean id="dep" class="com.wang.autowire.Dep"></bean>

5、引入外部属性的文件

流程:

外部文件

prop.driverClass = com.mysql.jdbc.Driver
prop.url = jdbc:mysql://localhost:3306/userDB
prop.userName = root
prop.password = 123456

核心配置文件

<!--1、配置命名空间-->
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!--2、引入外部文件配置数据库-->
<context:property-placeholder location="classpath:bean.properties"></context:property-placeholder>
<!--3、调用数据-->
<bean id="myjdbc" class="com.wang.jdbc.MyJdbc">
    <property name="driverClass" value="${prop.driverClass}"></property>
    <property name="url" value="${prop.url}"></property>
    <property name="userName" value="${prop.userName}"></property>
    <property name="password" value="${prop.password}"></property>
</bean>

五、Bean管理(注解)

注解是代码标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)

使用注解配置的目的:简化xml配置

  • spring针对bean对象管理有四个注解
    • 注意的是这些注解的底层是一样的,只不过为了逻辑更加清晰
    • @Component 用于普通组件
    • @Service 用于业务逻辑层
    • @Controller 用于web层
    • @Repository 用于dao层

开启注解扫描:

  • 开启组件扫描:分两步: 引入context名称空间 -> 设置要扫描的包
<?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 http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启组件扫描, 要扫描多个包则用逗号隔开-->
    <context:component-scan base-package="com.wang.dao,com.wang.service"></context:component-scan>
</beans>

测试调用

@Test
public void test1(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean_1.xml");
    UserService userservice = context.getBean("userservice", UserService.class);
    System.out.println(userservice);
}

经过测试调用,发现事实上注解的作用就是把那个对应的类帮助我们在xml文件里简化了开发而已,并不是另起炉灶,所以我们获取实例对象仍然是在使用原来的方式获取

配置注解扫描:

可以令一些配置组件不被扫描,而另外一些配置组件扫描

  • 只扫描部分组件
<context:component-scan base-package="com.wang.dao,com.wang.service" use-default-filters="false">
    <!--设置只监听特定注解的内容-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 设置不扫描部分组件
<context:component-scan base-package="com.wang.service,com.wang.dao" >
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

属性注入-基于注解:

  • 属性注入的注解

    1. @Autowired: 根据属性类型进行自动装配
    2. @Qualifier: 根据属性名称进行注入
    3. @Resource: 可以根据类型注入, 可以根据名称注入
    4. @Value: 注入普通类型属性
  • @Autowired和@Qualifier注解

//先创建一个实例
@Component(value = "userDao")
public class UserDaoImp implements UserDao {
}

//在另外一个类中
@Component(value = "userservice")
public class UserService {
    //这个会自动识别到我们在UserService中添加的注解,并添加到这个属性来
    //这个时根据属性名称自动装配(必须要实例的名称一样才能注入)
    @Autowired
    //当找不到时就会去寻找教userdao的实例对象
    @Qualifier(value = "userDao")
    private UserDao userDao;

}
  • @resource注解
//根据属性注入
@Resource
//根据名称注入
@Resource(name = "userService")
private UserDao userDao;
  • @Value注解
@Value(value = "13")
private int age;

六、完全注解开发

可以使用配置类代替配置文件, 从而使用纯注解来写代码(一般使用在SpringBoot中)

  • 新建SpringConfig类并添加注解, 以代替原来的xml文件:
//作为配置类,替代xml配置文件
@Configuration
//扫描包的注解
@ComponentScan(basePackages = {"com.wang"})
//其他地方完全一致,但获取实例对象的地方变化了
public class springconfig {
}
  • 改写测试程序
@Test
public void test2(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(springconfig.class);
    UserService userservice = context.getBean("userservice", UserService.class);
    userservice.add();
}
posted @ 2021-06-07 15:52  Coder-Wang  阅读(63)  评论(0编辑  收藏  举报