Spring框架 IOC容器

分层开发

  • 表现层:
    • 直接和用户打交道,大部分跟界面有关;
  • 服务层:
    • 业务逻辑,由一个到多个基本的增删改查组成;
  • 持久层(数据访问层):
    • 将数据永久的保存,jdbc、mybatis;

Spring框架

特点

  • 将其他的框架进行整合,便于开发,提高程序的扩展性;
  • 声明式的事务管理:不需要编码进行事务控制,可以用xml的配置文件、用注解的方式;

spring 框架的核心思想

IOC(inversion of controll 控制反转)

public class MyServlet extends HttpServlet{
	service
	doGet
	doPost
}
//对于servlet类,并不需要程序员自己来创建实例对象,而是由tomcat来创建MySevlet
//的实例对象,由tomcat来调用servlet中的方法;就是把servlet控制权交给tomcat容器;

概念
所谓的控制反转,就是把对象的一些控制权(对象的创建,一些方法的调用)都交给容器来完成;
以后可以把很多对象的控制权交给spring容器来管理,对象的创建、对象的生命周期、对象的个数、对象的依赖关系;

spring中的IOC

  1. 添加spring依赖
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>4.3.22.RELEASE</version>
</dependency>
  1. 编写配置文件
    提供一个xml的配置文件,resources=>spring.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>
  1. 编写一个java类交给spring
public class UserService {
    
    public void insert(User user){
        System.out.println("添加用户");
    }
}

使用一个bean标签,把某一个类交给spring容器管理;

    <!-- spring 的配置
    id="唯一标识"
    class="包名.类名"
    -->
    <bean id="userService" class="service.UserService"></bean>
  1. 根据配置文件创建spring容器
    在spring初始化时根据bean标签创建实例;
public class TestSpring {

    public static void main(String[] args) {
        //1.创建spring容器
        //classpath类路径、application 应用程序、context容器 根据配置文件创建的应用程序spring容器
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring.xml");

        //2.1 根据id获取容器中的对象
        UserService userService=(UserService) context.getBean("userService");

        //2.2 根据class类型获取容器中的对象
        UserService service1 = context.getBean(UserService.class);

        //3.使用对象
        service1.insert(new User());
    }
}

spring容器控制反转都能控制那些方面

  • 对象的个数
    默认情况下一个bean标签,只会创建一个对象(单例);
    如果想使用一次就创建一个对象,在bean标签中加入:

<!-- prototype表示多例 -->
<bean id="userService" class="service.UserService" scope="prototype"></bean>
  • 控制对象的生命周期方法
    对于单例对象来讲,容器一创建就会创建这些单例对象,并且随后调用他们的初始化方法;
    对于多例,每次使用多例时就会创建一个新的对象,并调用他们的初始化方法;对于多例对象,销毁方法无效;

初始化方法:实例创建时调用

<bean init-method="初始化方法名">

销毁方法:容器关闭时调用;

<bean destroy-method="销毁方法名">
  • 控制是否懒惰初始化

用到对象时才创建其实例,默认情况下是饿汉;

<bean lazy-init="true">

还可以配置全局bean,在beans标签中配置

default-lazy-init="true"
  • 控制对象之间的依赖关系

案例:控制UserDao对象和UserService对象

public class UserDao {

    public void insertDao(){
        System.out.println("访问数据库 insert");
    }
}

因为UserDao是私有属性,所以用set方法赋值;

public class UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void insert(User user){
        System.out.println("添加用户");
        userDao.insertDao();
    }
    public void init(){
        System.out.println("初始化方法");
    }

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

在配置文件中,创建UserDao对象的bean标签,并且配置userService的userDao属性;

    <bean id="userService"
          class="service.UserService"
          scope="singleton"
          init-method="init"
          destroy-method="destroy">
          
           <!--property属性名
            ref 属性对象bean标签id-->
        <property name="userDao" ref="userDao"></property>
    </bean>
    
    <bean id="userDao"
    class="dao.UserDao"></bean>

降低层与层之间的耦合度,层与层之间要依赖接口,而不要依赖具体实现;

依赖注入

DI (dependency inject):建立对象之间依赖关系的过程和方式;

上面的案例把userService 需要的userDao注入给userService属性,使用set方法赋值称为set方法注入

常见方式:

  • set方法注入,spring调用set方法来完成对属性的赋值;
  • 利用构造方法进行注入,spring底层调用构造方法完成对属性的赋值;
<!--index:构造方法的参数下标,从0开始,ref 属性对象对应的bean标签id
<constructor-arg index="0" ref="userDao"></constructor-arg>

 <bean id="userDao"
    class="dao.impl.UserDaoFile"></bean>

简化依赖注入的办法
autowired 自动织入:要么根据名字匹配,要么根据类型匹配;

  • byName:可以唯一确定容器中的bean
autowire="byName"
  • byType:当容器中有多个类型相同的bean,就不适用了;
autowire="byType"
  • constuctor:根据构造方法去匹配;

注解方式的注入
@AutoWired 可以加在要注入的属性上,也可以加在对应的set方法或构造方法上;
修改配置文件,添加context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
    <!--启动@AutoWired等注解-->
    <context:annotation-config></context:annotation-config>

在构造器或者set方法添加注解

//    @Autowired
//    public UserService(UserDao userDao) {
//        this.userDao = userDao;
//    }
//
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

值注入

set方法注入

package service;

public class OrderService {
    private String name;
    private int price;

    public OrderService() {
    }

    public OrderService(String name, int price) {
        this.name = name;
        this.price = price;
    }

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

    public void setPrice(int price) {
        this.price = price;
    }

    public void print(){
        System.out.println("name:"+name+"price"+price);
    }
}

set方法注入

    <bean id="orderService"
          class="service.OrderService">
        <property name="name" value="订单1"></property>
        <property name="price" value="3000"></property>
    </bean>

构造方法注入

    <bean id="orderService"
          class="service.OrderService">
        <constructor-arg index="0" value="订单1"></constructor-arg>
        <constructor-arg index="1" value="2000"></constructor-arg>
    </bean>

注解注入
使用外部以.properties结尾的文件
文件内容

order.name=订单
order.price=5000

<!--读取配置文件 placeholder表示占位符-->
        <context:property-placeholder location="classpath:a.properties" file-encoding="utf-8"></context:property-placeholder>

在spring之前,可以使用工厂方法+接口+配置文件的方式实现解耦合

  • spring中的做法
@Autowired
private UserDao userDao; //被动获取,依赖注入
  • 工厂方法的做法
private UserDao userDao=UserDaoFactory.getUserDao();

简化控制反转(spring2.5开始)
提供了注解方式的控制反转

@Component 加在类,spring扫描它之后,就把它所注解的类交给spring 容器管理,没有加注解的类则跳过;

@Component("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("访问数据 insert");
    }

    @Override
    public void update() {
        System.out.println("访问数据 update");
    }
}
@Component("userDaoService") //相当于<bean id="userDaoService" class="UserDaoService" />
public class UserDaoService {

    @Autowired
    private UserDao userDao;


    public void insert(){
        System.out.println("UserService insert");
        userDao.insert();
    }

    public void update(){

    }
}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
    <!--启动注解@Autowired @Component...-->
    <context:annotation-config></context:annotation-config>

    <!--缩小搜索范围
	component-scan里面涵盖了annotation-config的功能,所以annotation-config可以省略
-->
    <context:component-scan base-package="service"></context:component-scan>
    <context:component-scan base-package="dao"/>
</beans>
  • @Controller(表现层或叫控制层)
  • @Service(业务逻辑层)
  • @Repository(对应数据访问层)
  • @Component(不属于前三层,不好分类时使用)

加在类上
单例多例注解:@Scope("singleton|prototype")
控制懒惰初始化:@Lazy 加在类上 表示这个类在容器里时懒惰初始化的

加在方法上
初始化方法注解:@PostConstruct
销毁方法注解:@PreDetroy

缺点
注解方法具有一定局限性:比如用spring管理数据库连接池则不能使用注解,所以对于这种第三方的类使用xml比较好;

实际例子
spring管理连接池,结合Mybatis
添加依赖

        </dependency>

        <!--数据库依赖-->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCp</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.13</version>
        </dependency>


        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>


        <!--mybatis和spring结合-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!--添加spring对jdbc以及transaction-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>
    </dependencies>

接口文件HeroDao.class

public interface HeroDao {

    @Insert("insert into hero (id,name,city,sex,birth,death,power)"+
    "values (#{id},#{name},#{city},#{sex},#{birth},#{death},#{power})")
    public void insert(Hero hero);

    @Update("update hero set name=#{name}, city=#{city}, sex=#{sex}, birth=#{birth}, death=#{death}, power=#{power} where id=#{id}")
    public void update(Hero hero);

    @Delete("delete from hero where id=#{id}")
    public void delete(int id);

    @Select("select * from hero limit #{m}, #{n}")
    public List<Hero> findAll(@Param("m") int a, @Param("n") int b);

    @Select("select * from hero where id = #{id}")
    public Hero findById(int id);
}

配置文件

  1. 配置连接池

配置连接池
```xml
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="westos"/>
        <property name="maxActive" value="10"/>
        <property name="minIdle" value="2"/>
    </bean>
  1. 把mybatis中的SqlSessionFactory交给spring管理
   <!--2.配置sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--把连接池对象,依赖注入给SqlSessionfactoryBean的dataSource属性-->
            <property name="dataSource" ref="dataSource"></property>
        </bean>
  1. 把mybatis中的接口mapper交给spring容器管理
    <!--3.配置有哪些mapper接口-->

    <!--方法一:每个接口都要配置,比较麻烦-->
    <!--<bean id="heroDao" class="org.mybatis.spring.mapper.MapperFactoryBean">-->
        <!--<property name="mapperInterface" value="dao.HeroDao"/>-->
        <!--<property name="sqlSessionFactory" ref="sqlSessionFactory"/>-->
    <!--</bean>-->

    <!--方法二:扫描dao包下有哪些接口需要给spring管理-->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
   
        <property name="basePackage" value="dao"></property>
    </bean>

测试

public class TestMybatis {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        HeroDao service = context.getBean(HeroDao.class);

        Hero hero = service.findById(1);
        System.out.println(hero);
    }
}

HikariCP 国外的 速度
Druid 阿里的 为监控

日志配置

  1. 添加日至相关的依赖
<!--添加日志依赖-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.2</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
  1. 提供 main/resources/logback.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration
        xmlns="http://ch.qos.logback/xml/ns/logback"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
    <!-- 输出控制,格式控制-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{32} - %m%n </pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志文件名称 -->
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天产生一个新的日志文件 -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 保留 15 天的日志 -->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{17} - %m%n </pattern>
        </encoder>
    </appender>

    <!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) -->
    <logger name="dao.HeroDao" level="DEBUG" additivity="false">
        <!--输出到控制台-->
        <appender-ref ref="STDOUT"/>
        <!--输入到文件-->
        <appender-ref ref="FILE"/>
    </logger>

    <root level="ERROR">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

声明式事务

  • 配置文件添加tx标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
>
  • 启用事务注解让@Transactional注解生效
<!--4. 启用事务注解-->
    <tx:annotation-driven/>
  • 启用事务管理器类
    用来负责具体的事务管理操作(commit 、rollback)
    • DataSourceTransactionManager(基于数据源的事务管理器 对于mybatis来讲,选这个)
    • HibernateTransactionManager(对于hibernate框架来讲)
    <!--5. 事务管理器 id="transactionManager为默认名称-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定连接池-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
  • 启动扫描
    <!--6. 扫描service-->
    <context:component-scan base-package="service"/>

注意:

  • 事务控制代码不建议放到dao包,一般放在service,一般事务控制一般调用多条sql语句;
  • spring中如果@Transaction的方法出现了RuntimeException或Error 事务回滚;@Transaction的方法出现 Exception异常则事务提交;
  • 如果想让 Exception 事务回滚则添加@Transaction(rollbackFor=Exception.class) 表示遇到什么样的异常则回滚;
@Service
public class HeroDaoService {

    @Autowired
    private HeroDao heroDao;

    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(List<Integer> list) throws Exception {
        for (Integer id : list) {
            if (id==8){
                throw new Exception("模拟exception异常");
            }
            heroDao.delete(id);
        }
    }
}

测试

public class TestMybatis {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        HeroDaoService heroDaoService = context.getBean(HeroDaoService.class);

        //事务提交成功
        heroDaoService.deleteByIds(Arrays.asList(1,2,3));

        //出现RunTimeException异常 事务回滚
       heroDaoService.deleteByIds(Arrays.asList(4,5,6));

        //出现Exception异常 事务提交
       heroDaoService.deleteByIds(Arrays.asList(4,5,6));

        //添加配置@Transactional(rollbackFor = Exception.class)
        // 出现Exception异常 事务回滚
        heroDaoService.deleteByIds(Arrays.asList(6,7,8));
    }
}

Transactional注解的参数配置

  • readOnly 只读事务:在事务的执行过程中只执行查询操作,不执行修改操作,可以提高性能;如果在只读事务内执行了增删改它会报异常;

  • timeout 事务的超时时间:默认情况下事务是无限制时间的执行,直到事务执行完成才结束;为了避免超长事务影响整个系统的性能要设置超时时间,事务运行的时间越短越好;

  • isolation 隔离级别

    • 未提交读 会产生脏读、不可重复读、幻读
    • 提交读 不会产生脏读
    • 可重复读 不会产生不可重复读
    • 序列化读 没有上面的问题了
  • propogation 传播行为:两个service类中的方法相互之间调用的时候的行为;

    • Propogation.SUPPORTS:支持的,不会主动开始事务,但是它会加入已经存在的事务;
    • Propogation.REQUIRED(默认的):必须的,如果没有事务,那么开始事务,如果有事务就加入事务;
    • Propogation.REQUIRES_NEW:需要新的,每次都会开始一个新事务

自定义注解

@Target表示注解添加的位置

  • ElementType.TYPE 表示该注解能够加在类上
  • ElementType.METHOD 表示该注解能够加在方法上
  • ElementType.FIELD 表示该注解能够加在属性上
    @Retention表示注解的作用范围
  • Source:只对源代码有效;
  • Class:对*.java源码和*.class字节码都有效;
  • Runtime:对*.java源码和*.class字节码、运行期都有效

猜你喜欢

转载自blog.csdn.net/mashaokang1314/article/details/87365074