Spring 学习


仓库地址: https://github.com/biuaxia/spring

Spring

IOC/DI

Spring是一个基于IOC和AOP的结构J2EE系统的框架
IOC 反转控制 是Spring的基础,Inversion Of Control
简单说就是创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象
DI 依赖注入 Dependency Inject. 简单地说就是拿到的对象的属性,已经被注入好相关值了,直接使用即可。

必读: 基于框架的程序要成功运行,对于JAR包的版本,配置文件的正确性有着苛刻的要求,任何一个地方出错了,都会导致框架程序运行失败。 如果你是第一次学习本框架,务必严格按照教程的指导,完全模仿操作,直到成功看到运行效果。 第一次成功之后,信心,思路都会有较好的铺垫,然后再根据自己的疑惑,在“成功”的代码上做原本想做的改动和调整,这样可以大大节约学习的时间,提高效率,切勿一来就擅自改动,给自己的学习制造障碍

建立普通的Java项目

导入jar包

建立pojo Category用于演示IOC/DI

package vip.javer.pojo;

/**
 * @author Administrator
 */
public class Category {
    private int id;
    private String name;
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

applicationContext.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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean name="c" class="vip.javer.pojo.Category">
        <property name="name" value="category 1"/>
    </bean>

</beans>

TestSpring

测试代码,演示通过spring获取Category对象,以及该对象被注入的name属性。

总结和原理解释

总结

到目前为止,简单的使用就是

  1. 对应的实体类
  2. 在applicationContext.xml中配置对应的bean,并在bean中写入对应的property属性name和value
  3. 需要使用的地方采取ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml")得到的app对象getBean即可

IOC就是交由Spring管理对象
DI就是依赖注入,简单说就是拿到对象属性

原理解释

以获取对象的方式进行比较

  1. 传统方式
    通过new关键字主动创建一个对象

  2. IOC方式
    对象的生命周期由Spring进行管理,直接去Spring那里获取一个对象,IOC是反转控制的缩写,就好比控制权原来在自己手里,现在转给了Spring

打个比喻:

  • 传统方式:相当于你自己去菜市场new 了一只鸡,不过是生鸡,要自己拔毛,去内脏,再上花椒,酱油,烤制,经过各种工序之后,才可以食用。
  • 用IOC:相当于去馆子(Spring)点了一只鸡,交到你手上的时候,已经五味俱全,你就只管吃就行了。

练习获取一个Product对象

  1. 对应的实体类
  2. xml追加bean配置
<bean name="p" class="vip.javer.pojo.Product">
    <property name="id" value="1"/>
    <property name="name" value="产品1"/>
</bean>
  1. 在测试类中获取即可
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
Product p = app.getBean("p",Product.class);
System.out.println(p.getId() + "\t" + p.getName());

注入对象

在上例中,对Category的name属性注入了"category 1"字符串
在本例中 ,对Product对象,注入一个Category对象

  1. xml追加bean定义
<bean name="c" class="vip.javer.pojo.Category">
    <property name="id" value="1"/>
    <property name="name" value="分类1"/>
</bean>
<bean name="p1" class="vip.javer.pojo.Product">
    <property name="id" value="2"/>
    <property name="name" value="产品2"/>
    <property name="category" ref="c"/>
</bean>
  1. 测试类测试
@Test
public void injectedObject() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    Product p = app.getBean("p1", Product.class);
    System.out.println(p);
}

注解方式实现IOC/DI

在本知识点,会演示如何使用注解的方式完成注入对象中的效果

  1. xml添加<context:annotation-config/>表示告诉Spring要使用注解的方式进行配置

  2. 干掉之前的注入<property>,我的实际代码只是干掉了一行(随便哪行都行,方便后面进行注解完成该操作)

    <?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:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
        <!--开启注解-->
        <context:annotation-config/>
        <bean name="c" class="vip.javer.pojo.Category">
            <property name="name" value="category 1"/>
            <property name="id" value="233"/>
        </bean>
    
        <bean name="p" class="vip.javer.pojo.Product">
            <property name="id" value="1"/>
            <property name="name" value="产品"/>
        </bean>
    
        <bean name="p1" class="vip.javer.pojo.Product">
            <property name="id" value="2"/>
            <property name="name" value="产品2"/>
            <!--<property name="category" ref="c"/>-->
        </bean>
    
    </beans>
    
  3. 在Product的category属性前加上注解@Autowired

    package vip.javer.pojo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    /**
     * @author Administrator
     */
    public class Product {
        int id;
        String name;
        @Autowired
        Category category;
        
        @Override
        public String toString() {
            return "Product{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", category=" + category +
                    '}';
        }
        
        public int getId() {
            return id;
        }
        
        public void setId(int id) {
            this.id = id;
        }
        
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public Category getCategory() {
            return category;
        }
        
        public void setCategory(Category category) {
            this.category = category;
        }
    }
    
  4. 测试

    @Test
    public void injectedObject() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        Product p = app.getBean("p1", Product.class);
        System.out.println(p);
    }
    

@Autowired的位置

除了在属性前加上@Autowired,还可以在setCategory方法前加上@Autowired,是相同的效果

除了@Autowired还有@Resource

@Resource(name = "c")
Category category;

怎么注入bean(缩减xml配置)

上述例子是对注入对象行为的注解,那么bean对象本身,比如Category,Product可不可以移出applicationContext.xml配置文件,也通过注解进行呢?
接下来就讲解如何对Bean进行注解配置

applicationContext.xml

修改applicationContext.xml,什么都去掉,只新增一行代码

<context:component-scan base-package="vip.javer.pojo"/>

该代码的作用是告诉Spring,bean都放在了vip.javer.pojo这个包下

@Component

  1. 分别为Product和Category类加上@Component,表明该类是bean,写法@Component(“c”)或者@Component(value = “p”)都可以
  2. 因为配置从xml移出来了,所以属性初始化放在属性声明上进行了

运行可以看到和之前的效果一样,之前的p1由于@Component声明的p,自然失败了

AOP

AOP 即 Aspect Oriented Program 面向切面编程

首先,在面向切面编程的思想里面,把功能分为核心业务功能 ,和周边功能
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
所谓的周边功能,比如性能统计,日志,事务管理等等

周边功能在Spring的面向切面编程AOP思想里,即被定义为切面

在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发
然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP

例如现在我也业务核心代码是sout,我想对核心代码切入日志功能

  1. 准备业务类

    package vip.javer.service;
    
    /**
     * @author Administrator
     */
    public class ProductService {
        public void doSomeService() {
            System.out.println("doSomeService");
        }
    }
    
  2. 准备日志切面LoggerAspect

    package vip.javer.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    /**
     * @author Administrator
     */
    public class LoggerAspect {
        
        public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("start log:\t" + joinPoint.getSignature().getName());
            Object object = joinPoint.proceed();
            System.out.println("eng log:\t" + joinPoint.getSignature().getName());
            return object;
        }
    }
    

    需要解释一下的是,该日志切面的功能是在调用核心功能之前和之后分别打印日志

    Object object = joinPoint.proceed();就是将来与某个核心功能编制之后,用于执行核心功能的代码

  3. applicationContext.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:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
        <!--声明业务对象-->
        <bean name="s" class="vip.javer.service.ProductService"/>
    
        <!--声明日志切面-->
        <bean id="log" class="vip.javer.aspect.LoggerAspect"/>
    
        <!--4. 里面的核心业务和辅助功能写好后,通过aop:config把业务对象与辅助功能编制在一起-->
        <aop:config>
            <!--1. 指定核心业务功能 登录,查询,生成订单-->
            <aop:pointcut id="loggerCutpoint" expression="execution(* vip.javer.service.ProductService.*(..))"/>
            <!--2. 指定辅助功能 性能统计,日志输出,事务管理-->
            <aop:aspect id="logAspect" ref="log">
            <!--3. 切入点为loggerCuppoint,切入方法为log对象的log方法,切入方式为前后切入-->
                <aop:around method="log" pointcut-ref="loggerCutpoint"/>
            </aop:aspect>
        </aop:config>
    </beans>
    

    按照注释的顺序查看

    解释一下execution(* vip.javer.service.ProductService.*(..)),这表示对满足如下条件的方法调用,进行切面操作

    * 返回任意类型

    vip.javer.service.ProductService.*vip.javer.service.ProductService开头的类的任意方法

    (..) 参数是任意数量和类型

  4. 测试

TestSpring 代码没有发生任何变化,通过配置的方式,把切面和核心业务类编制在了一起。

运行测试,可以发现在编织之后,业务方法运行之前和之后分别会打印日志

练习-性能统计切面

参考上述的日志切面,做一个性能统计切面,并编织在业务方法上面。

注: 在业务方法方法中,做一些JDBC访问,以增加耗时

答案

  1. 由于已有主业务类ProductService,故略过

  2. 性能统计切面PerformanceStatisticsAspect

    package vip.javer.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    /**
     * @author Administrator
     */
    public class PerformanceStatisticsAspect {
        
        public Object statistics(ProceedingJoinPoint joinPoint) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object object = joinPoint.proceed();
            Thread.sleep(500);
            long endTime = System.currentTimeMillis();
            long runningTime = endTime - startTime;
            System.out.println("运行时间:\t[" + runningTime + "ms]");
            return object;
        }
    }
    
  3. 配置applicationContext.xml

    <!--性能统计切面-->
    <bean name="per" class="vip.javer.aspect.PerformanceStatisticsAspect"/>
    
    <!--aop配置-->
    <aop:config>
       <aop:pointcut id="ps" expression="execution(* vip.javer.service.ProductService.*(..))"/>
       <aop:aspect id="statistics" ref="per">
           <!--环绕通知-->
           <aop:around method="statistics" pointcut-ref="ps"/>
       </aop:aspect>
    </aop:config>
    
  4. 测试

    @Test
    public void aopCutIntoTheAnswer() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ProductService s = app.getBean("s", ProductService.class);
        s.doSomeService();
    }
    

补充一下aop:config配置的一些说明

e1a15f0edbe99b96b27f6119d65b4cc8__4.GIF

注解AOP

注解很简单,基于上面的例子

  1. 为ProductService添加@Component(“s”)标记
  2. 去除application.xml中的无用和重复配置
  3. application.xml添加三行代码(分别是两行<context:component-scan base-package=“切面,service包”/>,以及一行aop的自动配置<aop:aspectj-autoproxy/>)
  4. 测试即可

补充一点

不同切面类可通过实现org.springframework.core.Ordered 接口实现切面类的优先级控制,具体为重写getOrder方法,定制返回值,返回值(int 类型)越小优先级越大

例如我想让性能监控最先

package vip.javer.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * @author Administrator
 */
@Aspect
@Component
public class PerformanceStatisticsAspect implements Ordered {
    
    @Around("execution(* vip.javer.service.ProductService.*(..))")
    public Object statistics(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("测试内容");
        long startTime = System.currentTimeMillis();
        Object object = joinPoint.proceed();
        Thread.sleep(500);
        long endTime = System.currentTimeMillis();
        long runningTime = endTime - startTime;
        System.out.println("运行时间:\t[" + runningTime + "ms]");
        return object;
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

Snipaste_2019-02-12_16-37-33.png

注解测试

注解方式用到了junit,所以需要下载:
junit-4.12.jar和hamcrest-all-1.3.jar

  1. 导包

  2. 修改TestSpring

    修改TestSpring, 并运行

    1. @RunWith(SpringJUnit4ClassRunner.class)
      表示这是一个Spring的测试类

    2. @ContextConfiguration(“classpath:applicationContext.xml”)
      定位Spring的配置文件

    3. @Autowired
      给这个测试类装配Category对象

    4. @Test
      测试逻辑,打印c对象的名称

    package vip.javer.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import vip.javer.pojo.Category;
    import vip.javer.pojo.Product;
    import vip.javer.service.ProductService;
    
    /**
     * @author Administrator
     */
    /*1. 表明这是Spring的测试类*/
    @RunWith(SpringJUnit4ClassRunner.class)
    /*2. 定位Spring的配置文件*/
    @ContextConfiguration("classpath:applicationContext.xml")
    public class TestSpring {
        
        /*3. 给测试类装配Category对象*/
        @Autowired
        private Category c;
        
        /*4. 测试逻辑,打印c的名称*/
        @Test
        public void annotationTest() {
            System.out.println(c.getName());
        }
        
        @Test
        public void gettingObjectsUsingSpring() {
            // ApplicationContext app = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            Category c = (Category) app.getBean("c");
            System.out.println(c.getId() + "\t" + c.getName());
            
            Product p = app.getBean("p", Product.class);
            System.out.println(p.getId() + "\t" + p.getName());
        }
        
        @Test
        public void annotationMode() {
            ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            Product p = app.getBean("p", Product.class);
            System.out.println(p);
        }
        
        @Test
        public void aopTest() {
            ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            ProductService ps = app.getBean("s", ProductService.class);
            ps.doSomeService();
        }
    }
    
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值