美文网首页
Spring-入门

Spring-入门

作者: Dane_404 | 来源:发表于2019-08-27 21:40 被阅读0次

Spring出现的目的,是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。Spring的成功来自于理念,而不是技术,它最核心的理念是IoC(控制反转)和AOP面向切面编程,其中IOC是Spring的基础,AOP是其重要的功能。

Spring IoC

基本概念

控制反转(Inversion of Control,IoC),看名字比较抽象,它还有另外一种说法,叫依赖注入(Dependency injection,DI),只是从不同的角度描述相同的概念。

用一个编程来表示的实际生活例子来解释下Ioc和DI,比如说,你想吃面包,在没有面包店的情况下,你需要自己主动制作面包,也就是说你是调用者,需要调用另一个对象,会采用"new 面包"这样的方式创建面包,而Spring的出现,对象不需要调用者创建,而是由Spring容器(面包店)来创建,这样一来,你再也不能直接控制面包了,而是面包店控制了面包,控制权由调用者转移到了Spring容器,控制权发生了反转,就叫Spring的控制反转。这是站在调用者的角度看。

站在Spring容器角度看,面包店知道你需要面包,它会把对应的面包给你,也就是调用者依赖了另一个对象,Spring为调用者注入了它所依赖的对象,这过程叫做依赖注入。

所以,Spring中实现控制反转的是IoC容器,IoC容器怎么做到的呢,它是通过依赖注入,也就是依赖注入是它的实现方法

简单使用

引入Srping:

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
</dependency>
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.8.RELEASE</version>
</dependency>
public interface TestDao {
    public void helloSpring();
}

public class TestDaoImpl implements TestDao {
    @Override
    public void helloSpring() {
        System.out.println("Hello,Spring");
    }
}

在resources下新建applicationContext.xml,或者叫spring-config.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="test" class="com.dane.spring.dao.TestDaoImpl"/>

</beans>

测试代码:

public static void main(String[] args) {
    //早期Spring采用的是BeanFactory,它与ApplicationContext的区别是:
    //BeanFactory采取延迟加载,第一次getBean时才会初始化Bean 
    //ApplicationContext在加载spring‐config.xml时候就会创建具体的Bean对象的实例
    ApplicationContext context
            = new ClassPathXmlApplicationContext("applicationContext.xml");
    TestDao testDao = (TestDao) context.getBean("test");
    testDao.helloSpring();
}

有参构造的方式创建对象

<?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="user" class="com.dane.spring.domain.User">
        <constructor-arg name="id" value="1"/>
        <constructor-arg name="account" value="dane"/>
        <constructor-arg name="name" value="dane"/>
        <constructor-arg name="sex" value="1"/>
        <constructor-arg name="home_id" value="1"/>
    </bean>
    <!--也可以使用使用index指定,对应构造参数-->
    <bean id="user1" class="com.dane.spring.domain.User">
        <constructor-arg index="0" value="1"/>
        <constructor-arg index="1" value="dane"/>
        <constructor-arg index="2" value="dane"/>
        <constructor-arg index="3" value="1"/>
        <constructor-arg index="4" value="1"/>

    </bean>
    <!--也可以使用按照顺序,对应构造参数-->
    <bean id="user2" class="com.dane.spring.domain.User">
        <constructor-arg  value="1"/>
        <constructor-arg  value="dane"/>
        <constructor-arg  value="dane"/>
        <constructor-arg  value="1"/>
        <constructor-arg  value="1"/>
    </bean>
    
</beans>

注入的方式创建对象

来个综合类

public class Home {

    private int id;
    /**注入实体Bean*/
    private User user;
    /**注入数组*/
    private Object[] array;
    /**注入List集合*/
    private List<Object> list;
    /**注入Set集合*/
    private Set<Object> set;
    /**注入Map键值对*/
    private Map<Object, Object> map;
    /**注入properties类型*/
    private Properties properties;
    ....
}

然后看下配置文件怎么写:

<?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="home" class="com.dane.spring.domain.Home">
      <property name="id" value="1"/>
      <property name="user">
          <bean class="com.dane.spring.domain.User">
              <property name="id" value="1"/>
              <property name="account" value="dane"/>
              <property name="sex" value="1"/>
              <property name="home_id" value="1"/>
              <property name="name" value="dane"/>
          </bean>
      </property>
      <property name="array">
          <array>
              <value>array1</value>
              <value>array2</value>
              <value>array3</value>
          </array>
      </property>
      <property name="list">
          <list>
              <value>list1</value>
              <value>list2</value>
              <value>list3</value>
          </list>
      </property>
      <property name="map">
          <map>
              <entry>
                  <key>
                      <value>1</value>
                  </key>
                  <value>1.1</value>
              </entry>
              <entry>
                  <key>
                      <value>2</value>
                  </key>
                  <value>2.2</value>
              </entry>
          </map>
      </property>
      <property name="set">
          <set>
              <value>set1</value> 
              <value>set2</value> 
              <value>set3</value>
          </set>
      </property>
      <property name="properties">
          <props>
              <prop key="prop1">propv1</prop>
              <prop key="prop2">propv2</prop>
          </props>
      </property>
  </bean>
</beans>

SpringIOC 创建对象细节

<?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">
    
    <!--scope :用来设置单例或多例
           singleton:单例,默认的,工厂加载xml后,会产生对象,每次去获取是同一个
           prototype:多例,工厂加载xml后,不会产生对象,每次获取创建一个对象
    -->
    <bean id="user" class="com.dane.spring.domain.User" scope="prototype"/>

    <!--lazy-init:延迟加载,只对单例有效
            true:工厂加载xml后,不会产生对象
            false:工厂加载xml后,会产生对象
    -->
    <bean id="user1" class="com.dane.spring.domain.User" scope="singleton" lazy-init="true"/>

    <!-- init-method:bean初始化的时候调用
         destroy-method:bean销毁的时候调用
    -->
    <bean id="user1" class="com.dane.spring.domain.User" init-method="init" destroy-method="destroy"/>
</beans>

SpringIOC注解的使用

首先要在spring-config.xml引入context约束:

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

然后配置 <context:component-scan>,具体如下:

<?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.dane.spring.*"/>
</beans>

然后就可以使用注解了:

/**
 * Spring中提供@Component的三个衍生注解,功能目前来讲是一致的
 *     @Controller WEB层
 *     @Service  业务层
 *     @Repository 持久层
 */
@Component("home")
public class Home {

    /**
     * 属性注入
     */
    @Value("1")
    private int id;


    /**
     *  Autowired注解表示自动装载,它会在容器找user
     *  另外,@Resource和@Autowired一样,区别在于@Autowired默认按照类型进行装载,
     *  @Resource默认根据名字来装载。
     *  @Autowired想使用名称来加载,要和@Qualifier配合使用。
     */
    @Autowired
    private User user;

    @Override
    public String toString() {
        return "Home{" +
                "id=" + id +
                ", user=" + user +
                '}';
    }
}

SpringEL的使用

SpringEL是一种表达式,可以动态的为Bean的属性赋值,Spring3后才支持。

<?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="user" class="com.dane.spring.domain.User"/>
    <bean id="person" class="com.dane.spring.domain.Person"/>

    <bean id="el" class="com.dane.spring.domain.SpringELTest">
        <property name="num" value="#{3*5}"></property>
        <property name="sayWhat" value="#{person.say('hello')}"> </property>
        <property name="name" value="#{person.getName()}"></property>
        <property name="user" value="#{user}"></property>
        <property name="rand" value="#{T(Math).random()}"></property>
        <property name="flag" value="#{user.id==10003}"></property>
    </bean>

</beans>

Spring整合单元测试

引入配置:

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
</dependency>
<dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <version>5.1.8.RELEASE</version>
</dependency>

然后这样写测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-config.xml")
public class AppTest 
{
    @Autowired
    private IUserDao userDao;

    @Test
    public void test1(){
        userDao.getUsers();
    }
}

SpringAOP

基本概念

AOP是Aspect Oriented Programming的缩写,意思是面向切面编程,AOP可以在不修改代码的情况下,对程序进行加强,比如权限校验、日志记录、性能监控、事务控制等。

AOP的相关术语

Joinpoint(连接点):指程序运行中的一些时间点,在spring中,与方法有关的前前后后,都是连接点,比如调用前、调用后、抛出异常时等。

Pointcut(切入点):就是我们从Joinpoint(连接点)中找出的那些想要拦截处理的点。

Advice(通知/增强):指拦截到的Joinpoint(连接点)后的通知回调,分为前置通知、后置通知、异常通知、最终通知、环绕通知。

Target(目标对象):代理的目标对象。

Weaving(织入):指的是通过代理对象,把切面应用到目标对象的过程,SpringAOP采用动态代理切入。

Proxy(代理):Weaving(织入)后产生的代理对象。

Aspect(切面):是切入点和通知的结合。

SpringAOP动态代理

SpringAOP底层的实现采用的是代理技术,有两种方式,一种是基于JDK动态代理,用于实现类是接口,另一种是CGLIB动态代理,用于没有接口的实现类,CGLIB是对目标类产生一个子类,并进行增强。

图解AspectJ方法执行流程

image.png

SpringAOP XML形式的开发

引入配置:

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.8.RELEASE</version>
</dependency>

写个切面类:

public class UserDaoAspect {

    /**
     *  方法执行前的通知
     */
    public void before(){
        System.out.println("before");
    }

    /**
     *  方法执行通知
     *  pjp.proceed()就是执行目标方法,返回值就目标方法的返回值,
     *  可以在这里对返回值进行处理修改
     */
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("around");
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    /**
     *  方法正常常返回通知,抛出异常才会调用
     */
    public void afterReturning(){
        System.out.println("afterReturning");
    }

    /**
     *  方法异常返回通知
     */
    public void afterReturnThrowing(){
        System.out.println("afterReturnThrowing");
    }

    /**
     *  方法执行后的通知
     */
    public void after(){
        System.out.println("after");
    }
}

然后在applicationContext.xml或spring-config.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       "
>
    <bean id="userDao" class="com.dane.spring.dao.UserDaoImpl"/>

   <bean id="aspect" class="com.dane.spring.aspect.UserDaoAspect"/>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(public * com.dane.spring.dao.UserDaoImpl.getUsers())"/>
        <aop:aspect ref="aspect">
            <aop:before method="before" pointcut-ref="pt"/>
            <aop:around method="around" pointcut-ref="pt"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pt"/>
            <aop:after-throwing method="afterReturnThrowing" pointcut-ref="pt"/>
            <aop:after method="after" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>

</beans>

execution表达式语法

基本格式:
execution(方法的修饰符 方法的返回值类型 方法所属的类 方法名 方法中参数列表 
          方法抛出的异常)
简化格式:
//可以不写包名和类:代表是工程所有addUser方法都有效
execution(方法的返回值类型 方法名(方法中参数列表))
execution(public void addUser() throws Exception)
//可以不写包名和类,一个参数可以使用 * 号代替 
execution(public void addUser(*)throws Exception)
//可以不写包名和类,任意参数(包括没有参数)可以使用 ..
execution(public void addUser(..)throws Exception)

//省略修饰符,返回值用*代替
execution(* com.dane.spring.dao.UserDaoImpl.addUser(..))
//可以不写包名和类,返回值用*代替,声明异常 
execution(* addUser() throws Exception)
//可以不写包名和类,任意方法名:*
execution(public void *(..)throws Exception) 
//可以不写包名和类,方法名以add开始
execution(public void add*(..)throws Exception) 
//可以不写包名和类,方法名以service结尾
execution(public void *service(..)throws Exception)

//省略修饰符,可以不写包名和类,返回值用*代替 
execution(* add())
//省略修饰符,可以不写包名和类,返回值用*代替,任意方法名:*,任意参 数(包括没有参数)可以使用 ..
execution(* *(..))
省略修饰符,com.dane.spring.dao包和其子包都有效果,方法名以add开始
execution(* com.dane.spring.dao..insert*(..))

SpringAOP 注解形式的开发

在applicationContext.xml或spring-config.xml编写相关配置:

<context:component-scan base-package="com.dane.spring.*"/>
<aop:aspectj-autoproxy proxy-target-class="false"/>

然后这样使用注解:

@Component()
@Aspect()
public class UserDaoAspect {


    @Pointcut("execution(public * com.dane.spring.dao.UserDaoImpl.getUsers())")
    public void pointCut(){

    }


    @Before("pointCut()")
    public void before() {
        System.out.println("before");
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("around");
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    @AfterReturning("pointCut()")
    public void afterReturning() {
        System.out.println("afterReturning");
    }

    @AfterThrowing("pointCut()")
    public void afterReturnThrowing() {
        System.out.println("afterReturnThrowing");
    }

    @After("pointCut()")
    public void after() {
        System.out.println("after");
    }
}

Spring事务

XML方式使用事务

基本使用的话,完成了配置基本上就可以了,所以这里不贴业务代码了。

引入spring-tx:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.1.8.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.1.8.RELEASE</version>
</dependency>

<dependency>
  <groupId>com.mchange</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.5.4</version>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.15</version>
</dependency>

编写配置文件:

<?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" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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 
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
    <!--扫描指定的包,使注解生效-->
    <context:component-scan base-package="com.dane.spring"/>
    <!--配置数据源,这里使用c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/dane?characterEncoding=utf-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--添加数据源到事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--编写通知声明事务,这里表示任意方法-->
    <tx:advice id="myAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.dane.spring.service.*.*())"/>
        <aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

</beans>

注解使用方式

<?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" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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 
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
    <!--扫描指定的包,使注解生效-->
    <context:component-scan base-package="com.dane.spring"/>
    <!--配置数据源,这里使用c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/dane?characterEncoding=utf-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--添加数据源到事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
     <!--注册注解驱动-->
    <tx:annotation-driven transaction-manager="txManager"/>

</beans>

然后使用@Transactional

@Transactional
public void addUser(){
    User1 user1=new User1();
    user1.setName("张三");
    user1Service.addUser(user1);
}

事务处理捕获异常

如果我们在业务代码中加了try...catch语句,出异常时Spring事务将无法回滚,例如:

public void addUser(){
    try {
      User1 user1=new User1();
      user1.setName("张三");
      user1Service.addUser(user1);
    }catch (Exception e) {
      System.out.println("出现异常")
    }
}

若是采用XML方式使用注解,你必须将 <tx:method name="*"/>修改为:

<tx:method name="*" rollback-for="java.lang.Exception"/>

然后在catch添加throw new RuntimeException():

public void addUser(){
    try {
      User1 user1=new User1();
      user1.setName("张三");
      user1Service.addUser(user1);
    }catch (Exception e) {
      throw new RuntimeException():
      System.out.println("出现异常")
    }
}

若是注解,则:

@Transactional(rollbackFor={Exception.class})
public void addUser(){
    try {
      User1 user1=new User1();
      user1.setName("张三");
      user1Service.addUser(user1);
    }catch (Exception e) {
      throw new RuntimeException():
      System.out.println("出现异常")
    }
}

事务的传播行为

PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

相关文章

网友评论

      本文标题:Spring-入门

      本文链接:https://www.haomeiwen.com/subject/gtafkctx.html